aboutsummaryrefslogtreecommitdiffstats
path: root/drivers
diff options
context:
space:
mode:
authorJim Lin <jilin@nvidia.com>2011-04-17 04:58:25 -0400
committerGreg Kroah-Hartman <gregkh@suse.de>2011-04-29 20:24:37 -0400
commit1f594b64a4f74ece0b7166ca4db05a71a64bd685 (patch)
treeb997ecc45c7eba7d410af1d360d3190231c85a56 /drivers
parent3c86c07baaa22e1ebae1922b5285f79a39e93d83 (diff)
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: 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>
Diffstat (limited to 'drivers')
-rw-r--r--drivers/usb/host/ehci-tegra.c72
1 files changed, 72 insertions, 0 deletions
diff --git a/drivers/usb/host/ehci-tegra.c b/drivers/usb/host/ehci-tegra.c
index a516af28c29..7359bcbe417 100644
--- a/drivers/usb/host/ehci-tegra.c
+++ b/drivers/usb/host/ehci-tegra.c
@@ -58,6 +58,71 @@ static void tegra_ehci_power_down(struct usb_hcd *hcd)
58 clk_disable(tegra->emc_clk); 58 clk_disable(tegra->emc_clk);
59} 59}
60 60
61static int tegra_ehci_internal_port_reset(
62 struct ehci_hcd *ehci,
63 u32 __iomem *portsc_reg
64)
65{
66 u32 temp;
67 unsigned long flags;
68 int retval = 0;
69 int i, tries;
70 u32 saved_usbintr;
71
72 spin_lock_irqsave(&ehci->lock, flags);
73 saved_usbintr = ehci_readl(ehci, &ehci->regs->intr_enable);
74 /* disable USB interrupt */
75 ehci_writel(ehci, 0, &ehci->regs->intr_enable);
76 spin_unlock_irqrestore(&ehci->lock, flags);
77
78 /*
79 * Here we have to do Port Reset at most twice for
80 * Port Enable bit to be set.
81 */
82 for (i = 0; i < 2; i++) {
83 temp = ehci_readl(ehci, portsc_reg);
84 temp |= PORT_RESET;
85 ehci_writel(ehci, temp, portsc_reg);
86 mdelay(10);
87 temp &= ~PORT_RESET;
88 ehci_writel(ehci, temp, portsc_reg);
89 mdelay(1);
90 tries = 100;
91 do {
92 mdelay(1);
93 /*
94 * Up to this point, Port Enable bit is
95 * expected to be set after 2 ms waiting.
96 * USB1 usually takes extra 45 ms, for safety,
97 * we take 100 ms as timeout.
98 */
99 temp = ehci_readl(ehci, portsc_reg);
100 } while (!(temp & PORT_PE) && tries--);
101 if (temp & PORT_PE)
102 break;
103 }
104 if (i == 2)
105 retval = -ETIMEDOUT;
106
107 /*
108 * Clear Connect Status Change bit if it's set.
109 * We can't clear PORT_PEC. It will also cause PORT_PE to be cleared.
110 */
111 if (temp & PORT_CSC)
112 ehci_writel(ehci, PORT_CSC, portsc_reg);
113
114 /*
115 * Write to clear any interrupt status bits that might be set
116 * during port reset.
117 */
118 temp = ehci_readl(ehci, &ehci->regs->status);
119 ehci_writel(ehci, temp, &ehci->regs->status);
120
121 /* restore original interrupt enable bits */
122 ehci_writel(ehci, saved_usbintr, &ehci->regs->intr_enable);
123 return retval;
124}
125
61static int tegra_ehci_hub_control( 126static int tegra_ehci_hub_control(
62 struct usb_hcd *hcd, 127 struct usb_hcd *hcd,
63 u16 typeReq, 128 u16 typeReq,
@@ -121,6 +186,13 @@ static int tegra_ehci_hub_control(
121 goto done; 186 goto done;
122 } 187 }
123 188
189 /* For USB1 port we need to issue Port Reset twice internally */
190 if (tegra->phy->instance == 0 &&
191 (typeReq == SetPortFeature && wValue == USB_PORT_FEAT_RESET)) {
192 spin_unlock_irqrestore(&ehci->lock, flags);
193 return tegra_ehci_internal_port_reset(ehci, status_reg);
194 }
195
124 /* 196 /*
125 * Tegra host controller will time the resume operation to clear the bit 197 * Tegra host controller will time the resume operation to clear the bit
126 * when the port control state switches to HS or FS Idle. This behavior 198 * when the port control state switches to HS or FS Idle. This behavior