diff options
author | Peter Mamonov <pmamonov@gmail.com> | 2017-01-26 16:03:03 +0300 |
---|---|---|
committer | Sascha Hauer <s.hauer@pengutronix.de> | 2017-01-30 07:29:02 +0100 |
commit | 6d205422e594aa545d74e8ba05885db4ff81144f (patch) | |
tree | 252a574d6c6a28c303a95ad01d66c2b8b14339a9 | |
parent | db791c7fb2da0708573d7483259a6e8e971aba20 (diff) | |
download | barebox-6d205422e594aa545d74e8ba05885db4ff81144f.tar.gz barebox-6d205422e594aa545d74e8ba05885db4ff81144f.tar.xz |
usb: ehci: pass full speed devices to companion controller
According to the "Enhanced Host Controller Interface Specification for
Universal Serial Bus" after a USB port reset the EHCI Driver checks the
PortEnable bit in the PORTSC register. If set to a one, the connected device is
a high-speed device [...]. At the time the EHCI Driver receives the port reset
and enable request the LineStatus bits might indicate a low-speed device.
Additionally, when the port reset process is complete, the PortEnable field may
indicate that a full-speed device is attached. In either case the EHCI driver
sets the PortOwner bit in the PORTSC register to a one to release port
ownership to a companion host controller.
Signed-off-by: Peter Mamonov <pmamonov@gmail.com>
Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
-rw-r--r-- | drivers/usb/host/ehci-hcd.c | 27 |
1 files changed, 21 insertions, 6 deletions
diff --git a/drivers/usb/host/ehci-hcd.c b/drivers/usb/host/ehci-hcd.c index f6e9099da8..9bbdda365c 100644 --- a/drivers/usb/host/ehci-hcd.c +++ b/drivers/usb/host/ehci-hcd.c @@ -476,6 +476,18 @@ static inline void ehci_powerup_fixup(struct ehci_priv *ehci) } #endif +static void pass_to_companion(struct ehci_priv *ehci, int port) +{ + uint32_t *status_reg = (uint32_t *)&ehci->hcor->or_portsc[port - 1]; + uint32_t reg = ehci_readl(status_reg); + + reg &= ~EHCI_PS_CLEAR; + dev_dbg(ehci->dev, "port %d --> companion\n", + port - 1); + reg |= EHCI_PS_PO; + ehci_writel(status_reg, reg); +} + static int ehci_submit_root(struct usb_device *dev, unsigned long pipe, void *buffer, int length, struct devrequest *req) @@ -508,6 +520,10 @@ ehci_submit_root(struct usb_device *dev, unsigned long pipe, void *buffer, return -1; } status_reg = (uint32_t *)&ehci->hcor->or_portsc[port - 1]; + if (ehci_readl(status_reg) & EHCI_PS_PO) { + dev_dbg(ehci->dev, "Port %d is owned by companion controller\n", port); + return -1; + } break; default: status_reg = NULL; @@ -654,11 +670,7 @@ ehci_submit_root(struct usb_device *dev, unsigned long pipe, void *buffer, if ((reg & (EHCI_PS_PE | EHCI_PS_CS)) == EHCI_PS_CS && !ehci_is_TDI() && EHCI_PS_IS_LOWSPEED(reg)) { - /* Low speed device, give up ownership. */ - dev_dbg(ehci->dev, "port %d low speed --> companion\n", - port - 1); - reg |= EHCI_PS_PO; - ehci_writel(status_reg, reg); + pass_to_companion(ehci, port); break; } else { int ret; @@ -689,7 +701,10 @@ ehci_submit_root(struct usb_device *dev, unsigned long pipe, void *buffer, else dev_err(ehci->dev, "port(%d) reset error\n", port - 1); - + mdelay(200); + reg = ehci_readl(status_reg); + if (!(reg & EHCI_PS_PE)) + pass_to_companion(ehci, port); } break; default: |