diff options
Diffstat (limited to 'drivers/usb/host/ehci-omap.c')
-rw-r--r-- | drivers/usb/host/ehci-omap.c | 184 |
1 files changed, 174 insertions, 10 deletions
diff --git a/drivers/usb/host/ehci-omap.c b/drivers/usb/host/ehci-omap.c index a44294d13494..c30435499a02 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 */ | ||
60 | static struct clk *utmi_p1_fck; | ||
61 | static struct clk *utmi_p2_fck; | ||
62 | static struct clk *xclk60mhsp1_ck; | ||
63 | static struct clk *xclk60mhsp2_ck; | ||
64 | static struct clk *usbhost_p1_fck; | ||
65 | static struct clk *usbhost_p2_fck; | ||
66 | static struct clk *init_60m_fclk; | ||
67 | |||
58 | /*-------------------------------------------------------------------------*/ | 68 | /*-------------------------------------------------------------------------*/ |
59 | 69 | ||
60 | static const struct hc_driver ehci_omap_hc_driver; | 70 | static 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 */ | ||
84 | static 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 | |||
73 | static void omap_ehci_soft_phy_reset(struct platform_device *pdev, u8 port) | 118 | static 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 | ||
148 | static 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); | ||
187 | done: | ||
188 | spin_unlock_irqrestore(&ehci->lock, flags); | ||
189 | return retval; | ||
190 | } | ||
191 | |||
103 | static void disable_put_regulator( | 192 | static void disable_put_regulator( |
104 | struct ehci_hcd_omap_platform_data *pdata) | 193 | struct ehci_hcd_omap_platform_data *pdata) |
105 | { | 194 | { |
@@ -192,14 +281,13 @@ static int ehci_hcd_omap_probe(struct platform_device *pdev) | |||
192 | } | 281 | } |
193 | } | 282 | } |
194 | 283 | ||
284 | /* Hold PHYs in reset while initializing EHCI controller */ | ||
195 | if (pdata->phy_reset) { | 285 | if (pdata->phy_reset) { |
196 | if (gpio_is_valid(pdata->reset_gpio_port[0])) | 286 | if (gpio_is_valid(pdata->reset_gpio_port[0])) |
197 | gpio_request_one(pdata->reset_gpio_port[0], | 287 | gpio_set_value_cansleep(pdata->reset_gpio_port[0], 0); |
198 | GPIOF_OUT_INIT_LOW, "USB1 PHY reset"); | ||
199 | 288 | ||
200 | if (gpio_is_valid(pdata->reset_gpio_port[1])) | 289 | if (gpio_is_valid(pdata->reset_gpio_port[1])) |
201 | gpio_request_one(pdata->reset_gpio_port[1], | 290 | gpio_set_value_cansleep(pdata->reset_gpio_port[1], 0); |
202 | GPIOF_OUT_INIT_LOW, "USB2 PHY reset"); | ||
203 | 291 | ||
204 | /* Hold the PHY in RESET for enough time till DIR is high */ | 292 | /* Hold the PHY in RESET for enough time till DIR is high */ |
205 | udelay(10); | 293 | udelay(10); |
@@ -241,6 +329,11 @@ static int ehci_hcd_omap_probe(struct platform_device *pdev) | |||
241 | omap_ehci->hcs_params = readl(&omap_ehci->caps->hcs_params); | 329 | omap_ehci->hcs_params = readl(&omap_ehci->caps->hcs_params); |
242 | 330 | ||
243 | ehci_reset(omap_ehci); | 331 | ehci_reset(omap_ehci); |
332 | ret = usb_add_hcd(hcd, irq, IRQF_SHARED); | ||
333 | if (ret) { | ||
334 | dev_err(dev, "failed to add hcd with err %d\n", ret); | ||
335 | goto err_add_hcd; | ||
336 | } | ||
244 | 337 | ||
245 | if (pdata->phy_reset) { | 338 | if (pdata->phy_reset) { |
246 | /* Hold the PHY in RESET for enough time till | 339 | /* Hold the PHY in RESET for enough time till |
@@ -255,17 +348,79 @@ static int ehci_hcd_omap_probe(struct platform_device *pdev) | |||
255 | gpio_set_value_cansleep(pdata->reset_gpio_port[1], 1); | 348 | gpio_set_value_cansleep(pdata->reset_gpio_port[1], 1); |
256 | } | 349 | } |
257 | 350 | ||
258 | ret = usb_add_hcd(hcd, irq, IRQF_SHARED); | 351 | /* root ports should always stay powered */ |
259 | if (ret) { | 352 | ehci_port_power(omap_ehci, 1); |
260 | dev_err(dev, "failed to add hcd with err %d\n", ret); | 353 | |
354 | /* get clocks */ | ||
355 | utmi_p1_fck = clk_get(dev, "utmi_p1_gfclk"); | ||
356 | if (IS_ERR(utmi_p1_fck)) { | ||
357 | ret = PTR_ERR(utmi_p1_fck); | ||
358 | dev_err(dev, "utmi_p1_gfclk failed error:%d\n", ret); | ||
261 | goto err_add_hcd; | 359 | goto err_add_hcd; |
262 | } | 360 | } |
263 | 361 | ||
264 | /* root ports should always stay powered */ | 362 | xclk60mhsp1_ck = clk_get(dev, "xclk60mhsp1_ck"); |
265 | ehci_port_power(omap_ehci, 1); | 363 | if (IS_ERR(xclk60mhsp1_ck)) { |
364 | ret = PTR_ERR(xclk60mhsp1_ck); | ||
365 | dev_err(dev, "xclk60mhsp1_ck failed error:%d\n", ret); | ||
366 | goto err_utmi_p1_fck; | ||
367 | } | ||
368 | |||
369 | utmi_p2_fck = clk_get(dev, "utmi_p2_gfclk"); | ||
370 | if (IS_ERR(utmi_p2_fck)) { | ||
371 | ret = PTR_ERR(utmi_p2_fck); | ||
372 | dev_err(dev, "utmi_p2_gfclk failed error:%d\n", ret); | ||
373 | goto err_xclk60mhsp1_ck; | ||
374 | } | ||
375 | |||
376 | xclk60mhsp2_ck = clk_get(dev, "xclk60mhsp2_ck"); | ||
377 | if (IS_ERR(xclk60mhsp2_ck)) { | ||
378 | ret = PTR_ERR(xclk60mhsp2_ck); | ||
379 | dev_err(dev, "xclk60mhsp2_ck failed error:%d\n", ret); | ||
380 | goto err_utmi_p2_fck; | ||
381 | } | ||
382 | |||
383 | usbhost_p1_fck = clk_get(dev, "usb_host_hs_utmi_p1_clk"); | ||
384 | if (IS_ERR(usbhost_p1_fck)) { | ||
385 | ret = PTR_ERR(usbhost_p1_fck); | ||
386 | dev_err(dev, "usbhost_p1_fck failed error:%d\n", ret); | ||
387 | goto err_xclk60mhsp2_ck; | ||
388 | } | ||
389 | |||
390 | usbhost_p2_fck = clk_get(dev, "usb_host_hs_utmi_p2_clk"); | ||
391 | if (IS_ERR(usbhost_p2_fck)) { | ||
392 | ret = PTR_ERR(usbhost_p2_fck); | ||
393 | dev_err(dev, "usbhost_p2_fck failed error:%d\n", ret); | ||
394 | goto err_usbhost_p1_fck; | ||
395 | } | ||
396 | |||
397 | init_60m_fclk = clk_get(dev, "init_60m_fclk"); | ||
398 | if (IS_ERR(init_60m_fclk)) { | ||
399 | ret = PTR_ERR(init_60m_fclk); | ||
400 | dev_err(dev, "init_60m_fclk failed error:%d\n", ret); | ||
401 | goto err_usbhost_p2_fck; | ||
402 | } | ||
266 | 403 | ||
267 | return 0; | 404 | return 0; |
268 | 405 | ||
406 | err_usbhost_p2_fck: | ||
407 | clk_put(usbhost_p2_fck); | ||
408 | |||
409 | err_usbhost_p1_fck: | ||
410 | clk_put(usbhost_p1_fck); | ||
411 | |||
412 | err_xclk60mhsp2_ck: | ||
413 | clk_put(xclk60mhsp2_ck); | ||
414 | |||
415 | err_utmi_p2_fck: | ||
416 | clk_put(utmi_p2_fck); | ||
417 | |||
418 | err_xclk60mhsp1_ck: | ||
419 | clk_put(xclk60mhsp1_ck); | ||
420 | |||
421 | err_utmi_p1_fck: | ||
422 | clk_put(utmi_p1_fck); | ||
423 | |||
269 | err_add_hcd: | 424 | err_add_hcd: |
270 | disable_put_regulator(pdata); | 425 | disable_put_regulator(pdata); |
271 | pm_runtime_put_sync(dev); | 426 | pm_runtime_put_sync(dev); |
@@ -294,6 +449,15 @@ static int ehci_hcd_omap_remove(struct platform_device *pdev) | |||
294 | disable_put_regulator(dev->platform_data); | 449 | disable_put_regulator(dev->platform_data); |
295 | iounmap(hcd->regs); | 450 | iounmap(hcd->regs); |
296 | usb_put_hcd(hcd); | 451 | usb_put_hcd(hcd); |
452 | |||
453 | clk_put(utmi_p1_fck); | ||
454 | clk_put(utmi_p2_fck); | ||
455 | clk_put(xclk60mhsp1_ck); | ||
456 | clk_put(xclk60mhsp2_ck); | ||
457 | clk_put(usbhost_p1_fck); | ||
458 | clk_put(usbhost_p2_fck); | ||
459 | clk_put(init_60m_fclk); | ||
460 | |||
297 | pm_runtime_put_sync(dev); | 461 | pm_runtime_put_sync(dev); |
298 | pm_runtime_disable(dev); | 462 | pm_runtime_disable(dev); |
299 | 463 | ||
@@ -364,7 +528,7 @@ static const struct hc_driver ehci_omap_hc_driver = { | |||
364 | * root hub support | 528 | * root hub support |
365 | */ | 529 | */ |
366 | .hub_status_data = ehci_hub_status_data, | 530 | .hub_status_data = ehci_hub_status_data, |
367 | .hub_control = ehci_hub_control, | 531 | .hub_control = omap_ehci_hub_control, |
368 | .bus_suspend = ehci_bus_suspend, | 532 | .bus_suspend = ehci_bus_suspend, |
369 | .bus_resume = ehci_bus_resume, | 533 | .bus_resume = ehci_bus_resume, |
370 | 534 | ||