From 6d205422e594aa545d74e8ba05885db4ff81144f Mon Sep 17 00:00:00 2001 From: Peter Mamonov Date: Thu, 26 Jan 2017 16:03:03 +0300 Subject: 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 Signed-off-by: Sascha Hauer --- drivers/usb/host/ehci-hcd.c | 27 +++++++++++++++++++++------ 1 file changed, 21 insertions(+), 6 deletions(-) (limited to 'drivers/usb') 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: -- cgit v1.2.3