diff options
Diffstat (limited to 'drivers/usb/host')
-rw-r--r-- | drivers/usb/host/Kconfig | 36 | ||||
-rw-r--r-- | drivers/usb/host/ehci-marvell.c | 4 | ||||
-rw-r--r-- | drivers/usb/host/ehci-mx6.c | 23 | ||||
-rw-r--r-- | drivers/usb/host/ehci-omap.c | 126 | ||||
-rw-r--r-- | drivers/usb/host/xhci-ring.c | 31 |
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) |