summaryrefslogtreecommitdiff
path: root/drivers/usb/host
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/usb/host')
-rw-r--r--drivers/usb/host/Kconfig36
-rw-r--r--drivers/usb/host/ehci-marvell.c4
-rw-r--r--drivers/usb/host/ehci-mx6.c23
-rw-r--r--drivers/usb/host/ehci-omap.c126
-rw-r--r--drivers/usb/host/xhci-ring.c31
5 files changed, 114 insertions, 106 deletions
diff --git a/drivers/usb/host/Kconfig b/drivers/usb/host/Kconfig
index 7743c962cfa..8f77412cc71 100644
--- a/drivers/usb/host/Kconfig
+++ b/drivers/usb/host/Kconfig
@@ -191,45 +191,13 @@ config USB_EHCI_MXS
config USB_EHCI_OMAP
bool "Support for OMAP3+ on-chip EHCI USB controller"
depends on ARCH_OMAP2PLUS
+ select PHY
+ imply NOP_PHY
default y
---help---
Enables support for the on-chip EHCI controller on OMAP3 and later
SoCs.
-if USB_EHCI_OMAP
-
-config HAS_OMAP_EHCI_PHY1_RESET_GPIO
- bool "PHY #1 requires a GPIO hold to it in RESET while PHY settles"
- help
- Enable this to be able to configure the GPIO number used to hold the
- PHY in RESET for enough time until the PHY is settled and ready.
-
-config OMAP_EHCI_PHY1_RESET_GPIO
- int "GPIO number to hold PHY #1 in RESET"
- depends on HAS_OMAP_EHCI_PHY1_RESET_GPIO
-
-config HAS_OMAP_EHCI_PHY2_RESET_GPIO
- bool "PHY #2 requires a GPIO hold to it in RESET while PHY settles"
- help
- Enable this to be able to configure the GPIO number used to hold the
- PHY in RESET for enough time until the PHY is settled and ready.
-
-config OMAP_EHCI_PHY2_RESET_GPIO
- int "GPIO number to hold PHY #2 in RESET"
- depends on HAS_OMAP_EHCI_PHY2_RESET_GPIO
-
-config HAS_OMAP_EHCI_PHY3_RESET_GPIO
- bool "PHY #3 requires a GPIO hold to it in RESET while PHY settles"
- help
- Enable this to be able to configure the GPIO number used to hold the
- PHY in RESET for enough time until the PHY is settled and ready.
-
-config OMAP_EHCI_PHY3_RESET_GPIO
- int "GPIO number to hold PHY #3 in RESET"
- depends on HAS_OMAP_EHCI_PHY3_RESET_GPIO
-
-endif
-
config USB_EHCI_VF
bool "Support for Vybrid on-chip EHCI USB controller"
depends on ARCH_VF610
diff --git a/drivers/usb/host/ehci-marvell.c b/drivers/usb/host/ehci-marvell.c
index 5420bb9772b..b7e60c690a4 100644
--- a/drivers/usb/host/ehci-marvell.c
+++ b/drivers/usb/host/ehci-marvell.c
@@ -123,7 +123,7 @@ static int ehci_mvebu_probe(struct udevice *dev)
* Also, the address decoder doesn't need to get setup with this
* SoC, so don't call usb_brg_adrdec_setup().
*/
- if (device_is_compatible(dev, "marvell,armada3700-ehci"))
+ if (device_is_compatible(dev, "marvell,armada-3700-ehci"))
marvell_ehci_ops.powerup_fixup = marvell_ehci_powerup_fixup;
else
usb_brg_adrdec_setup((void *)priv->hcd_base);
@@ -142,7 +142,7 @@ static int ehci_mvebu_probe(struct udevice *dev)
static const struct udevice_id ehci_usb_ids[] = {
{ .compatible = "marvell,orion-ehci", },
- { .compatible = "marvell,armada3700-ehci", },
+ { .compatible = "marvell,armada-3700-ehci", },
{ }
};
diff --git a/drivers/usb/host/ehci-mx6.c b/drivers/usb/host/ehci-mx6.c
index 1bd6147c76a..060b02accc5 100644
--- a/drivers/usb/host/ehci-mx6.c
+++ b/drivers/usb/host/ehci-mx6.c
@@ -543,7 +543,7 @@ static int ehci_usb_phy_mode(struct udevice *dev)
plat->init_type = USB_INIT_DEVICE;
else
plat->init_type = USB_INIT_HOST;
- } else if (is_mx7()) {
+ } else if (is_mx7() || is_imx8mm() || is_imx8mn()) {
phy_status = (void __iomem *)(addr +
USBNC_PHY_STATUS_OFFSET);
val = readl(phy_status);
@@ -573,9 +573,8 @@ static int ehci_usb_of_to_plat(struct udevice *dev)
case USB_DR_MODE_PERIPHERAL:
plat->init_type = USB_INIT_DEVICE;
break;
- case USB_DR_MODE_OTG:
- case USB_DR_MODE_UNKNOWN:
- return ehci_usb_phy_mode(dev);
+ default:
+ plat->init_type = USB_INIT_UNKNOWN;
};
return 0;
@@ -677,6 +676,20 @@ static int ehci_usb_probe(struct udevice *dev)
mdelay(1);
#endif
+ /*
+ * If the device tree didn't specify host or device,
+ * the default is USB_INIT_UNKNOWN, so we need to check
+ * the register. For imx8mm and imx8mn, the clocks need to be
+ * running first, so we defer the check until they are.
+ */
+ if (priv->init_type == USB_INIT_UNKNOWN) {
+ ret = ehci_usb_phy_mode(dev);
+ if (ret)
+ goto err_clk;
+ else
+ priv->init_type = plat->init_type;
+ }
+
#if CONFIG_IS_ENABLED(DM_REGULATOR)
ret = device_get_supply_regulator(dev, "vbus-supply",
&priv->vbus_supply);
@@ -741,8 +754,8 @@ err_regulator:
#if CONFIG_IS_ENABLED(DM_REGULATOR)
if (priv->vbus_supply)
regulator_set_enable(priv->vbus_supply, false);
-err_clk:
#endif
+err_clk:
#if CONFIG_IS_ENABLED(CLK)
clk_disable(&priv->clk);
#else
diff --git a/drivers/usb/host/ehci-omap.c b/drivers/usb/host/ehci-omap.c
index d5facf10e12..765336a3c42 100644
--- a/drivers/usb/host/ehci-omap.c
+++ b/drivers/usb/host/ehci-omap.c
@@ -128,62 +128,25 @@ static void omap_ehci_soft_phy_reset(int port)
}
#endif
-#if defined(CONFIG_OMAP_EHCI_PHY1_RESET_GPIO) || \
- defined(CONFIG_OMAP_EHCI_PHY2_RESET_GPIO) || \
- defined(CONFIG_OMAP_EHCI_PHY3_RESET_GPIO)
-/* controls PHY(s) reset signal(s) */
-static inline void omap_ehci_phy_reset(int on, int delay)
-{
- /*
- * Refer ISSUE1:
- * Hold the PHY in RESET for enough time till
- * PHY is settled and ready
- */
- if (delay && !on)
- udelay(delay);
-#ifdef CONFIG_OMAP_EHCI_PHY1_RESET_GPIO
- gpio_request(CONFIG_OMAP_EHCI_PHY1_RESET_GPIO, "USB PHY1 reset");
- gpio_direction_output(CONFIG_OMAP_EHCI_PHY1_RESET_GPIO, !on);
-#endif
-#ifdef CONFIG_OMAP_EHCI_PHY2_RESET_GPIO
- gpio_request(CONFIG_OMAP_EHCI_PHY2_RESET_GPIO, "USB PHY2 reset");
- gpio_direction_output(CONFIG_OMAP_EHCI_PHY2_RESET_GPIO, !on);
-#endif
-#ifdef CONFIG_OMAP_EHCI_PHY3_RESET_GPIO
- gpio_request(CONFIG_OMAP_EHCI_PHY3_RESET_GPIO, "USB PHY3 reset");
- gpio_direction_output(CONFIG_OMAP_EHCI_PHY3_RESET_GPIO, !on);
-#endif
-
- /* Hold the PHY in RESET for enough time till DIR is high */
- /* Refer: ISSUE1 */
- if (delay && on)
- udelay(delay);
-}
-#else
-#define omap_ehci_phy_reset(on, delay) do {} while (0)
+struct ehci_omap_priv_data {
+ struct ehci_ctrl ctrl;
+ struct omap_ehci *ehci;
+#ifdef CONFIG_DM_REGULATOR
+ struct udevice *vbus_supply;
#endif
-
-/* Reset is needed otherwise the kernel-driver will throw an error. */
-int omap_ehci_hcd_stop(void)
-{
- debug("Resetting OMAP EHCI\n");
- omap_ehci_phy_reset(1, 0);
-
- if (omap_uhh_reset() < 0)
- return -1;
-
- if (omap_ehci_tll_reset() < 0)
- return -1;
-
- return 0;
-}
+ enum usb_init_type init_type;
+ int portnr;
+ struct phy phy[OMAP_HS_USB_PORTS];
+ int nports;
+};
/*
* Initialize the OMAP EHCI controller and PHY.
* Based on "drivers/usb/host/ehci-omap.c" from Linux 3.1
* See there for additional Copyrights.
*/
-int omap_ehci_hcd_init(int index, struct omap_usbhs_board_data *usbhs_pdata)
+static int omap_ehci_hcd_init(int index, struct omap_usbhs_board_data *usbhs_pdata,
+ struct udevice *dev)
{
int ret;
unsigned int i, reg = 0, rev = 0;
@@ -194,8 +157,9 @@ int omap_ehci_hcd_init(int index, struct omap_usbhs_board_data *usbhs_pdata)
if (ret < 0)
return ret;
- /* Put the PHY in RESET */
- omap_ehci_phy_reset(1, 10);
+ /* Hold the PHY in RESET for enough time till DIR is high */
+ /* Refer: ISSUE1 */
+ udelay(10);
ret = omap_uhh_reset();
if (ret < 0)
@@ -274,7 +238,12 @@ int omap_ehci_hcd_init(int index, struct omap_usbhs_board_data *usbhs_pdata)
if (is_ehci_hsic_mode(usbhs_pdata->port_mode[i]))
omap_usbhs_hsic_init(i);
- omap_ehci_phy_reset(0, 10);
+ /*
+ * Refer ISSUE1:
+ * Hold the PHY in RESET for enough time till
+ * PHY is settled and ready
+ */
+ udelay(10);
/*
* An undocumented "feature" in the OMAP3 EHCI controller,
@@ -327,7 +296,7 @@ static int omap_usbhs_probe(struct udevice *dev)
omap_usbhs_set_mode(i, mode);
}
- return omap_ehci_hcd_init(0, &usbhs_bdata);
+ return 0;
}
static const struct udevice_id omap_usbhs_dt_ids[] = {
@@ -343,18 +312,6 @@ U_BOOT_DRIVER(usb_omaphs_host) = {
.flags = DM_FLAG_ALLOC_PRIV_DMA,
};
-struct ehci_omap_priv_data {
- struct ehci_ctrl ctrl;
- struct omap_ehci *ehci;
-#ifdef CONFIG_DM_REGULATOR
- struct udevice *vbus_supply;
-#endif
- enum usb_init_type init_type;
- int portnr;
- struct phy phy[OMAP_HS_USB_PORTS];
- int nports;
-};
-
static int ehci_usb_of_to_plat(struct udevice *dev)
{
struct usb_plat *plat = dev_get_plat(dev);
@@ -364,12 +321,33 @@ static int ehci_usb_of_to_plat(struct udevice *dev)
return 0;
}
+/*
+ * This driver references phys based on the USB port. If
+ * the port is unused, the corresponding phy is listed as NULL
+ * which generic_phy_init_bulk treats as an error, so we need
+ * a custom one that tolerates empty phys
+ */
+static int omap_ehci_phy_get(struct udevice *dev)
+{
+ struct ehci_omap_priv_data *priv = dev_get_priv(dev);
+ int i, ret;
+
+ for (i = 0; i < OMAP_HS_USB_PORTS; i++) {
+ ret = generic_phy_get_by_index(dev, i, &priv->phy[i]);
+ if (ret && ret != -ENOENT)
+ return ret;
+ };
+
+ return 0;
+};
+
static int omap_ehci_probe(struct udevice *dev)
{
struct usb_plat *plat = dev_get_plat(dev);
struct ehci_omap_priv_data *priv = dev_get_priv(dev);
struct ehci_hccr *hccr;
struct ehci_hcor *hcor;
+ int ret;
priv->ehci = dev_read_addr_ptr(dev);
priv->portnr = dev_seq(dev);
@@ -378,6 +356,24 @@ static int omap_ehci_probe(struct udevice *dev)
hccr = (struct ehci_hccr *)&priv->ehci->hccapbase;
hcor = (struct ehci_hcor *)&priv->ehci->usbcmd;
+ /* Identify Phys */
+ ret = omap_ehci_phy_get(dev);
+ if (ret) {
+ printf("Failed to get phys\n");
+ return ret;
+ }
+
+ /* Register the EHCI */
+ ret = ehci_register(dev, hccr, hcor, NULL, 0, USB_INIT_HOST);
+ if (ret) {
+ printf("Failed to register EHCI\n");
+ return ret;
+ }
+
+ ret = omap_ehci_hcd_init(0, &usbhs_bdata, dev);
+ if (ret)
+ return ret;
+
return ehci_register(dev, hccr, hcor, NULL, 0, USB_INIT_HOST);
}
diff --git a/drivers/usb/host/xhci-ring.c b/drivers/usb/host/xhci-ring.c
index 0b3e7a2f550..eb6dfcdb09e 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)