Skip to content

Commit

Permalink
USB: ehci: tegra: fix USB1 port reset issue
Browse files Browse the repository at this point in the history
Tegra USB1 port needs to issue Port Reset twice internally, otherwise it
fails to enumerate devices attached to it

Signed-off-by: Jim Lin <jilin@nvidia.com>
Signed-off-by: Olof Johansson <olofj@chromium.org>
[ squash two patches into one and minor style cleanups ]
Signed-off-by: Mike Rapoport <mike@compulab.co.il>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
  • Loading branch information
Jim Lin authored and Greg Kroah-Hartman committed Apr 30, 2011
1 parent 3c86c07 commit 1f594b6
Showing 1 changed file with 72 additions and 0 deletions.
72 changes: 72 additions & 0 deletions drivers/usb/host/ehci-tegra.c
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,71 @@ static void tegra_ehci_power_down(struct usb_hcd *hcd)
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(
struct usb_hcd *hcd,
u16 typeReq,
Expand Down Expand Up @@ -121,6 +186,13 @@ static int tegra_ehci_hub_control(
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
* when the port control state switches to HS or FS Idle. This behavior
Expand Down

0 comments on commit 1f594b6

Please sign in to comment.