Commit 1f594b64 authored by Jim Lin's avatar Jim Lin Committed by Greg Kroah-Hartman

USB: ehci: tegra: fix USB1 port reset issue

Tegra USB1 port needs to issue Port Reset twice internally, otherwise it
fails to enumerate devices attached to it
Signed-off-by: default avatarJim Lin <jilin@nvidia.com>
Signed-off-by: default avatarOlof Johansson <olofj@chromium.org>
[ squash two patches into one and minor style cleanups ]
Signed-off-by: default avatarMike Rapoport <mike@compulab.co.il>
Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@suse.de>
parent 3c86c07b
...@@ -58,6 +58,71 @@ static void tegra_ehci_power_down(struct usb_hcd *hcd) ...@@ -58,6 +58,71 @@ static void tegra_ehci_power_down(struct usb_hcd *hcd)
clk_disable(tegra->emc_clk); clk_disable(tegra->emc_clk);
} }
static int tegra_ehci_internal_port_reset(
struct ehci_hcd *ehci,
u32 __iomem *portsc_reg
)
{
u32 temp;
unsigned long flags;
int retval = 0;
int i, tries;
u32 saved_usbintr;
spin_lock_irqsave(&ehci->lock, flags);
saved_usbintr = ehci_readl(ehci, &ehci->regs->intr_enable);
/* disable USB interrupt */
ehci_writel(ehci, 0, &ehci->regs->intr_enable);
spin_unlock_irqrestore(&ehci->lock, flags);
/*
* Here we have to do Port Reset at most twice for
* Port Enable bit to be set.
*/
for (i = 0; i < 2; i++) {
temp = ehci_readl(ehci, portsc_reg);
temp |= PORT_RESET;
ehci_writel(ehci, temp, portsc_reg);
mdelay(10);
temp &= ~PORT_RESET;
ehci_writel(ehci, temp, portsc_reg);
mdelay(1);
tries = 100;
do {
mdelay(1);
/*
* Up to this point, Port Enable bit is
* expected to be set after 2 ms waiting.
* USB1 usually takes extra 45 ms, for safety,
* we take 100 ms as timeout.
*/
temp = ehci_readl(ehci, portsc_reg);
} while (!(temp & PORT_PE) && tries--);
if (temp & PORT_PE)
break;
}
if (i == 2)
retval = -ETIMEDOUT;
/*
* Clear Connect Status Change bit if it's set.
* We can't clear PORT_PEC. It will also cause PORT_PE to be cleared.
*/
if (temp & PORT_CSC)
ehci_writel(ehci, PORT_CSC, portsc_reg);
/*
* Write to clear any interrupt status bits that might be set
* during port reset.
*/
temp = ehci_readl(ehci, &ehci->regs->status);
ehci_writel(ehci, temp, &ehci->regs->status);
/* restore original interrupt enable bits */
ehci_writel(ehci, saved_usbintr, &ehci->regs->intr_enable);
return retval;
}
static int tegra_ehci_hub_control( static int tegra_ehci_hub_control(
struct usb_hcd *hcd, struct usb_hcd *hcd,
u16 typeReq, u16 typeReq,
...@@ -121,6 +186,13 @@ static int tegra_ehci_hub_control( ...@@ -121,6 +186,13 @@ static int tegra_ehci_hub_control(
goto done; goto done;
} }
/* For USB1 port we need to issue Port Reset twice internally */
if (tegra->phy->instance == 0 &&
(typeReq == SetPortFeature && wValue == USB_PORT_FEAT_RESET)) {
spin_unlock_irqrestore(&ehci->lock, flags);
return tegra_ehci_internal_port_reset(ehci, status_reg);
}
/* /*
* Tegra host controller will time the resume operation to clear the bit * Tegra host controller will time the resume operation to clear the bit
* when the port control state switches to HS or FS Idle. This behavior * when the port control state switches to HS or FS Idle. This behavior
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment