aboutsummaryrefslogtreecommitdiffstats
path: root/drivers
diff options
context:
space:
mode:
authorAnand Gadiyar <gadiyar@ti.com>2012-06-05 08:34:27 -0400
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>2012-06-13 20:36:22 -0400
commit354ab8567ae3107a8cbe7228c3181990ba598aac (patch)
tree40e60416c44fd0339f6f95800f6989920451276e /drivers
parentc4828d96900453f1ad506b1488928307711a8b28 (diff)
Fix OMAP EHCI suspend/resume failure (i693)
Its observed with some PHY, the 60Mhz clock gets cut too soon for OMAP EHCI, leaving OMAP-EHCI in a bad state. So on starting port suspend, make sure the 60Mhz clock to EHCI is kept alive using an internal clock, so that EHCi can cleanly transition its hw state machine on a port suspend. Its not proven if this is the issue hit on USB3333, but the symptoms look very similar. Signed-off-by: Anand Gadiyar <gadiyar@ti.com> Signed-off-by: Vikram Pandita <vikram.pandita@ti.com> Signed-off-by: Volodymyr Mieshkov <x0182794@ti.com> Acked-by: Alan Stern <stern@rowland.harvard.edu> Acked-by: Felipe Balbi <balbi@ti.com> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Diffstat (limited to 'drivers')
-rw-r--r--drivers/usb/host/ehci-omap.c168
1 files changed, 167 insertions, 1 deletions
diff --git a/drivers/usb/host/ehci-omap.c b/drivers/usb/host/ehci-omap.c
index a44294d13494..17cfb8a1131c 100644
--- a/drivers/usb/host/ehci-omap.c
+++ b/drivers/usb/host/ehci-omap.c
@@ -43,6 +43,7 @@
43#include <linux/regulator/consumer.h> 43#include <linux/regulator/consumer.h>
44#include <linux/pm_runtime.h> 44#include <linux/pm_runtime.h>
45#include <linux/gpio.h> 45#include <linux/gpio.h>
46#include <linux/clk.h>
46 47
47/* EHCI Register Set */ 48/* EHCI Register Set */
48#define EHCI_INSNREG04 (0xA0) 49#define EHCI_INSNREG04 (0xA0)
@@ -55,6 +56,15 @@
55#define EHCI_INSNREG05_ULPI_EXTREGADD_SHIFT 8 56#define EHCI_INSNREG05_ULPI_EXTREGADD_SHIFT 8
56#define EHCI_INSNREG05_ULPI_WRDATA_SHIFT 0 57#define EHCI_INSNREG05_ULPI_WRDATA_SHIFT 0
57 58
59/* Errata i693 */
60static struct clk *utmi_p1_fck;
61static struct clk *utmi_p2_fck;
62static struct clk *xclk60mhsp1_ck;
63static struct clk *xclk60mhsp2_ck;
64static struct clk *usbhost_p1_fck;
65static struct clk *usbhost_p2_fck;
66static struct clk *init_60m_fclk;
67
58/*-------------------------------------------------------------------------*/ 68/*-------------------------------------------------------------------------*/
59 69
60static const struct hc_driver ehci_omap_hc_driver; 70static const struct hc_driver ehci_omap_hc_driver;
@@ -70,6 +80,41 @@ static inline u32 ehci_read(void __iomem *base, u32 reg)
70 return __raw_readl(base + reg); 80 return __raw_readl(base + reg);
71} 81}
72 82
83/* Erratum i693 workaround sequence */
84static void omap_ehci_erratum_i693(struct ehci_hcd *ehci)
85{
86 int ret = 0;
87
88 /* Switch to the internal 60 MHz clock */
89 ret = clk_set_parent(utmi_p1_fck, init_60m_fclk);
90 if (ret != 0)
91 ehci_err(ehci, "init_60m_fclk set parent"
92 "failed error:%d\n", ret);
93
94 ret = clk_set_parent(utmi_p2_fck, init_60m_fclk);
95 if (ret != 0)
96 ehci_err(ehci, "init_60m_fclk set parent"
97 "failed error:%d\n", ret);
98
99 clk_enable(usbhost_p1_fck);
100 clk_enable(usbhost_p2_fck);
101
102 /* Wait 1ms and switch back to the external clock */
103 mdelay(1);
104 ret = clk_set_parent(utmi_p1_fck, xclk60mhsp1_ck);
105 if (ret != 0)
106 ehci_err(ehci, "xclk60mhsp1_ck set parent"
107 "failed error:%d\n", ret);
108
109 ret = clk_set_parent(utmi_p2_fck, xclk60mhsp2_ck);
110 if (ret != 0)
111 ehci_err(ehci, "xclk60mhsp2_ck set parent"
112 "failed error:%d\n", ret);
113
114 clk_disable(usbhost_p1_fck);
115 clk_disable(usbhost_p2_fck);
116}
117
73static void omap_ehci_soft_phy_reset(struct platform_device *pdev, u8 port) 118static void omap_ehci_soft_phy_reset(struct platform_device *pdev, u8 port)
74{ 119{
75 struct usb_hcd *hcd = dev_get_drvdata(&pdev->dev); 120 struct usb_hcd *hcd = dev_get_drvdata(&pdev->dev);
@@ -100,6 +145,50 @@ static void omap_ehci_soft_phy_reset(struct platform_device *pdev, u8 port)
100 } 145 }
101} 146}
102 147
148static int omap_ehci_hub_control(
149 struct usb_hcd *hcd,
150 u16 typeReq,
151 u16 wValue,
152 u16 wIndex,
153 char *buf,
154 u16 wLength
155)
156{
157 struct ehci_hcd *ehci = hcd_to_ehci(hcd);
158 u32 __iomem *status_reg = &ehci->regs->port_status[
159 (wIndex & 0xff) - 1];
160 u32 temp;
161 unsigned long flags;
162 int retval = 0;
163
164 spin_lock_irqsave(&ehci->lock, flags);
165
166 if (typeReq == SetPortFeature && wValue == USB_PORT_FEAT_SUSPEND) {
167 temp = ehci_readl(ehci, status_reg);
168 if ((temp & PORT_PE) == 0 || (temp & PORT_RESET) != 0) {
169 retval = -EPIPE;
170 goto done;
171 }
172
173 temp &= ~PORT_WKCONN_E;
174 temp |= PORT_WKDISC_E | PORT_WKOC_E;
175 ehci_writel(ehci, temp | PORT_SUSPEND, status_reg);
176
177 omap_ehci_erratum_i693(ehci);
178
179 set_bit((wIndex & 0xff) - 1, &ehci->suspended_ports);
180 goto done;
181 }
182
183 spin_unlock_irqrestore(&ehci->lock, flags);
184
185 /* Handle the hub control events here */
186 return ehci_hub_control(hcd, typeReq, wValue, wIndex, buf, wLength);
187done:
188 spin_unlock_irqrestore(&ehci->lock, flags);
189 return retval;
190}
191
103static void disable_put_regulator( 192static void disable_put_regulator(
104 struct ehci_hcd_omap_platform_data *pdata) 193 struct ehci_hcd_omap_platform_data *pdata)
105{ 194{
@@ -264,8 +353,76 @@ static int ehci_hcd_omap_probe(struct platform_device *pdev)
264 /* root ports should always stay powered */ 353 /* root ports should always stay powered */
265 ehci_port_power(omap_ehci, 1); 354 ehci_port_power(omap_ehci, 1);
266 355
356 /* get clocks */
357 utmi_p1_fck = clk_get(dev, "utmi_p1_gfclk");
358 if (IS_ERR(utmi_p1_fck)) {
359 ret = PTR_ERR(utmi_p1_fck);
360 dev_err(dev, "utmi_p1_gfclk failed error:%d\n", ret);
361 goto err_add_hcd;
362 }
363
364 xclk60mhsp1_ck = clk_get(dev, "xclk60mhsp1_ck");
365 if (IS_ERR(xclk60mhsp1_ck)) {
366 ret = PTR_ERR(xclk60mhsp1_ck);
367 dev_err(dev, "xclk60mhsp1_ck failed error:%d\n", ret);
368 goto err_utmi_p1_fck;
369 }
370
371 utmi_p2_fck = clk_get(dev, "utmi_p2_gfclk");
372 if (IS_ERR(utmi_p2_fck)) {
373 ret = PTR_ERR(utmi_p2_fck);
374 dev_err(dev, "utmi_p2_gfclk failed error:%d\n", ret);
375 goto err_xclk60mhsp1_ck;
376 }
377
378 xclk60mhsp2_ck = clk_get(dev, "xclk60mhsp2_ck");
379 if (IS_ERR(xclk60mhsp2_ck)) {
380 ret = PTR_ERR(xclk60mhsp2_ck);
381 dev_err(dev, "xclk60mhsp2_ck failed error:%d\n", ret);
382 goto err_utmi_p2_fck;
383 }
384
385 usbhost_p1_fck = clk_get(dev, "usb_host_hs_utmi_p1_clk");
386 if (IS_ERR(usbhost_p1_fck)) {
387 ret = PTR_ERR(usbhost_p1_fck);
388 dev_err(dev, "usbhost_p1_fck failed error:%d\n", ret);
389 goto err_xclk60mhsp2_ck;
390 }
391
392 usbhost_p2_fck = clk_get(dev, "usb_host_hs_utmi_p2_clk");
393 if (IS_ERR(usbhost_p2_fck)) {
394 ret = PTR_ERR(usbhost_p2_fck);
395 dev_err(dev, "usbhost_p2_fck failed error:%d\n", ret);
396 goto err_usbhost_p1_fck;
397 }
398
399 init_60m_fclk = clk_get(dev, "init_60m_fclk");
400 if (IS_ERR(init_60m_fclk)) {
401 ret = PTR_ERR(init_60m_fclk);
402 dev_err(dev, "init_60m_fclk failed error:%d\n", ret);
403 goto err_usbhost_p2_fck;
404 }
405
267 return 0; 406 return 0;
268 407
408err_usbhost_p2_fck:
409 clk_put(usbhost_p2_fck);
410
411err_usbhost_p1_fck:
412 clk_put(usbhost_p1_fck);
413
414err_xclk60mhsp2_ck:
415 clk_put(xclk60mhsp2_ck);
416
417err_utmi_p2_fck:
418 clk_put(utmi_p2_fck);
419
420err_xclk60mhsp1_ck:
421 clk_put(xclk60mhsp1_ck);
422
423err_utmi_p1_fck:
424 clk_put(utmi_p1_fck);
425
269err_add_hcd: 426err_add_hcd:
270 disable_put_regulator(pdata); 427 disable_put_regulator(pdata);
271 pm_runtime_put_sync(dev); 428 pm_runtime_put_sync(dev);
@@ -294,6 +451,15 @@ static int ehci_hcd_omap_remove(struct platform_device *pdev)
294 disable_put_regulator(dev->platform_data); 451 disable_put_regulator(dev->platform_data);
295 iounmap(hcd->regs); 452 iounmap(hcd->regs);
296 usb_put_hcd(hcd); 453 usb_put_hcd(hcd);
454
455 clk_put(utmi_p1_fck);
456 clk_put(utmi_p2_fck);
457 clk_put(xclk60mhsp1_ck);
458 clk_put(xclk60mhsp2_ck);
459 clk_put(usbhost_p1_fck);
460 clk_put(usbhost_p2_fck);
461 clk_put(init_60m_fclk);
462
297 pm_runtime_put_sync(dev); 463 pm_runtime_put_sync(dev);
298 pm_runtime_disable(dev); 464 pm_runtime_disable(dev);
299 465
@@ -364,7 +530,7 @@ static const struct hc_driver ehci_omap_hc_driver = {
364 * root hub support 530 * root hub support
365 */ 531 */
366 .hub_status_data = ehci_hub_status_data, 532 .hub_status_data = ehci_hub_status_data,
367 .hub_control = ehci_hub_control, 533 .hub_control = omap_ehci_hub_control,
368 .bus_suspend = ehci_bus_suspend, 534 .bus_suspend = ehci_bus_suspend,
369 .bus_resume = ehci_bus_resume, 535 .bus_resume = ehci_bus_resume,
370 536