9
0
Fork 0

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>
This commit is contained in:
Peter Mamonov 2017-01-26 16:03:03 +03:00 committed by Sascha Hauer
parent db791c7fb2
commit 6d205422e5
1 changed files with 21 additions and 6 deletions

View File

@ -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: