Commit | Line | Data |
---|---|---|
7a7a4a59 HM |
1 | /* |
2 | * Generic platform ehci driver | |
3 | * | |
4 | * Copyright 2007 Steven Brown <sbrown@cortland.com> | |
5 | * Copyright 2010-2012 Hauke Mehrtens <hauke@hauke-m.de> | |
a4aeb211 | 6 | * Copyright 2014 Hans de Goede <hdegoede@redhat.com> |
7a7a4a59 HM |
7 | * |
8 | * Derived from the ohci-ssb driver | |
9 | * Copyright 2007 Michael Buesch <m@bues.ch> | |
10 | * | |
11 | * Derived from the EHCI-PCI driver | |
12 | * Copyright (c) 2000-2004 by David Brownell | |
13 | * | |
14 | * Derived from the ohci-pci driver | |
15 | * Copyright 1999 Roman Weissgaerber | |
16 | * Copyright 2000-2002 David Brownell | |
17 | * Copyright 1999 Linus Torvalds | |
18 | * Copyright 1999 Gregory P. Smith | |
19 | * | |
20 | * Licensed under the GNU/GPL. See COPYING for details. | |
21 | */ | |
776c15d0 | 22 | #include <linux/acpi.h> |
a4aeb211 | 23 | #include <linux/clk.h> |
f3bc64d6 | 24 | #include <linux/dma-mapping.h> |
148e1134 | 25 | #include <linux/err.h> |
99f91934 | 26 | #include <linux/kernel.h> |
d1bb67a7 AS |
27 | #include <linux/hrtimer.h> |
28 | #include <linux/io.h> | |
99f91934 | 29 | #include <linux/module.h> |
f3bc64d6 | 30 | #include <linux/of.h> |
a4aeb211 | 31 | #include <linux/phy/phy.h> |
7a7a4a59 | 32 | #include <linux/platform_device.h> |
2d87bbd6 | 33 | #include <linux/reset.h> |
99f91934 AS |
34 | #include <linux/usb.h> |
35 | #include <linux/usb/hcd.h> | |
7a7a4a59 HM |
36 | #include <linux/usb/ehci_pdriver.h> |
37 | ||
99f91934 AS |
38 | #include "ehci.h" |
39 | ||
40 | #define DRIVER_DESC "EHCI generic platform driver" | |
a4aeb211 HG |
41 | #define EHCI_MAX_CLKS 3 |
42 | #define hcd_to_ehci_priv(h) ((struct ehci_platform_priv *)hcd_to_ehci(h)->priv) | |
43 | ||
44 | struct ehci_platform_priv { | |
45 | struct clk *clks[EHCI_MAX_CLKS]; | |
2d87bbd6 | 46 | struct reset_control *rst; |
7e7a0e67 AR |
47 | struct phy **phys; |
48 | int num_phys; | |
b4629a7b | 49 | bool reset_on_resume; |
a4aeb211 | 50 | }; |
99f91934 AS |
51 | |
52 | static const char hcd_name[] = "ehci-platform"; | |
53 | ||
7a7a4a59 HM |
54 | static int ehci_platform_reset(struct usb_hcd *hcd) |
55 | { | |
56 | struct platform_device *pdev = to_platform_device(hcd->self.controller); | |
d4f09e28 | 57 | struct usb_ehci_pdata *pdata = dev_get_platdata(&pdev->dev); |
7a7a4a59 HM |
58 | struct ehci_hcd *ehci = hcd_to_ehci(hcd); |
59 | int retval; | |
60 | ||
7a7a4a59 | 61 | ehci->has_synopsys_hc_bug = pdata->has_synopsys_hc_bug; |
7a7a4a59 | 62 | |
743fcce0 SS |
63 | if (pdata->pre_setup) { |
64 | retval = pdata->pre_setup(hcd); | |
65 | if (retval < 0) | |
66 | return retval; | |
67 | } | |
68 | ||
7a7a4a59 HM |
69 | ehci->caps = hcd->regs + pdata->caps_offset; |
70 | retval = ehci_setup(hcd); | |
71 | if (retval) | |
72 | return retval; | |
73 | ||
4534874a FF |
74 | if (pdata->no_io_watchdog) |
75 | ehci->need_io_watchdog = 0; | |
7a7a4a59 HM |
76 | return 0; |
77 | } | |
78 | ||
a4aeb211 HG |
79 | static int ehci_platform_power_on(struct platform_device *dev) |
80 | { | |
81 | struct usb_hcd *hcd = platform_get_drvdata(dev); | |
82 | struct ehci_platform_priv *priv = hcd_to_ehci_priv(hcd); | |
7e7a0e67 | 83 | int clk, ret, phy_num; |
a4aeb211 HG |
84 | |
85 | for (clk = 0; clk < EHCI_MAX_CLKS && priv->clks[clk]; clk++) { | |
86 | ret = clk_prepare_enable(priv->clks[clk]); | |
87 | if (ret) | |
88 | goto err_disable_clks; | |
89 | } | |
90 | ||
7e7a0e67 | 91 | for (phy_num = 0; phy_num < priv->num_phys; phy_num++) { |
216e2992 AR |
92 | ret = phy_init(priv->phys[phy_num]); |
93 | if (ret) | |
94 | goto err_exit_phy; | |
95 | ret = phy_power_on(priv->phys[phy_num]); | |
96 | if (ret) { | |
97 | phy_exit(priv->phys[phy_num]); | |
98 | goto err_exit_phy; | |
7e7a0e67 | 99 | } |
a4aeb211 HG |
100 | } |
101 | ||
102 | return 0; | |
103 | ||
104 | err_exit_phy: | |
7e7a0e67 | 105 | while (--phy_num >= 0) { |
216e2992 AR |
106 | phy_power_off(priv->phys[phy_num]); |
107 | phy_exit(priv->phys[phy_num]); | |
7e7a0e67 | 108 | } |
a4aeb211 HG |
109 | err_disable_clks: |
110 | while (--clk >= 0) | |
111 | clk_disable_unprepare(priv->clks[clk]); | |
112 | ||
113 | return ret; | |
114 | } | |
115 | ||
116 | static void ehci_platform_power_off(struct platform_device *dev) | |
117 | { | |
118 | struct usb_hcd *hcd = platform_get_drvdata(dev); | |
119 | struct ehci_platform_priv *priv = hcd_to_ehci_priv(hcd); | |
7e7a0e67 | 120 | int clk, phy_num; |
a4aeb211 | 121 | |
7e7a0e67 | 122 | for (phy_num = 0; phy_num < priv->num_phys; phy_num++) { |
216e2992 AR |
123 | phy_power_off(priv->phys[phy_num]); |
124 | phy_exit(priv->phys[phy_num]); | |
a4aeb211 HG |
125 | } |
126 | ||
127 | for (clk = EHCI_MAX_CLKS - 1; clk >= 0; clk--) | |
128 | if (priv->clks[clk]) | |
129 | clk_disable_unprepare(priv->clks[clk]); | |
130 | } | |
131 | ||
99f91934 | 132 | static struct hc_driver __read_mostly ehci_platform_hc_driver; |
7a7a4a59 | 133 | |
62d08a11 | 134 | static const struct ehci_driver_overrides platform_overrides __initconst = { |
a4aeb211 HG |
135 | .reset = ehci_platform_reset, |
136 | .extra_priv_size = sizeof(struct ehci_platform_priv), | |
7a7a4a59 HM |
137 | }; |
138 | ||
a4aeb211 HG |
139 | static struct usb_ehci_pdata ehci_platform_defaults = { |
140 | .power_on = ehci_platform_power_on, | |
141 | .power_suspend = ehci_platform_power_off, | |
142 | .power_off = ehci_platform_power_off, | |
143 | }; | |
f3bc64d6 | 144 | |
41ac7b3a | 145 | static int ehci_platform_probe(struct platform_device *dev) |
7a7a4a59 HM |
146 | { |
147 | struct usb_hcd *hcd; | |
148 | struct resource *res_mem; | |
a4aeb211 HG |
149 | struct usb_ehci_pdata *pdata = dev_get_platdata(&dev->dev); |
150 | struct ehci_platform_priv *priv; | |
ad3db5da | 151 | struct ehci_hcd *ehci; |
7e7a0e67 | 152 | int err, irq, phy_num, clk = 0; |
7a7a4a59 | 153 | |
7a7a4a59 HM |
154 | if (usb_disabled()) |
155 | return -ENODEV; | |
156 | ||
f3bc64d6 | 157 | /* |
a4aeb211 HG |
158 | * Use reasonable defaults so platforms don't have to provide these |
159 | * with DT probing on ARM. | |
f3bc64d6 | 160 | */ |
a4aeb211 HG |
161 | if (!pdata) |
162 | pdata = &ehci_platform_defaults; | |
e1fd7341 | 163 | |
c99e76c5 AH |
164 | err = dma_coerce_mask_and_coherent(&dev->dev, |
165 | pdata->dma_mask_64 ? DMA_BIT_MASK(64) : DMA_BIT_MASK(32)); | |
17f69b5f JL |
166 | if (err) { |
167 | dev_err(&dev->dev, "Error: DMA mask configuration failed\n"); | |
22d9d8e8 | 168 | return err; |
17f69b5f | 169 | } |
f3bc64d6 | 170 | |
7a7a4a59 HM |
171 | irq = platform_get_irq(dev, 0); |
172 | if (irq < 0) { | |
2350cb0c | 173 | dev_err(&dev->dev, "no irq provided"); |
7a7a4a59 HM |
174 | return irq; |
175 | } | |
7a7a4a59 | 176 | |
a4aeb211 HG |
177 | hcd = usb_create_hcd(&ehci_platform_hc_driver, &dev->dev, |
178 | dev_name(&dev->dev)); | |
179 | if (!hcd) | |
180 | return -ENOMEM; | |
181 | ||
182 | platform_set_drvdata(dev, hcd); | |
183 | dev->dev.platform_data = pdata; | |
184 | priv = hcd_to_ehci_priv(hcd); | |
ad3db5da | 185 | ehci = hcd_to_ehci(hcd); |
a4aeb211 HG |
186 | |
187 | if (pdata == &ehci_platform_defaults && dev->dev.of_node) { | |
ad3db5da HG |
188 | if (of_property_read_bool(dev->dev.of_node, "big-endian-regs")) |
189 | ehci->big_endian_mmio = 1; | |
190 | ||
191 | if (of_property_read_bool(dev->dev.of_node, "big-endian-desc")) | |
192 | ehci->big_endian_desc = 1; | |
193 | ||
194 | if (of_property_read_bool(dev->dev.of_node, "big-endian")) | |
195 | ehci->big_endian_mmio = ehci->big_endian_desc = 1; | |
196 | ||
314b41b1 WL |
197 | if (of_property_read_bool(dev->dev.of_node, |
198 | "needs-reset-on-resume")) | |
b4629a7b | 199 | priv->reset_on_resume = true; |
314b41b1 | 200 | |
40f2f2a3 JE |
201 | if (of_property_read_bool(dev->dev.of_node, |
202 | "has-transaction-translator")) | |
b4629a7b | 203 | hcd->has_tt = 1; |
40f2f2a3 | 204 | |
7e7a0e67 AR |
205 | priv->num_phys = of_count_phandle_with_args(dev->dev.of_node, |
206 | "phys", "#phy-cells"); | |
7e7a0e67 | 207 | |
216e2992 AR |
208 | if (priv->num_phys > 0) { |
209 | priv->phys = devm_kcalloc(&dev->dev, priv->num_phys, | |
210 | sizeof(struct phy *), GFP_KERNEL); | |
211 | if (!priv->phys) | |
212 | return -ENOMEM; | |
213 | } else | |
214 | priv->num_phys = 0; | |
7e7a0e67 AR |
215 | |
216 | for (phy_num = 0; phy_num < priv->num_phys; phy_num++) { | |
216e2992 AR |
217 | priv->phys[phy_num] = devm_of_phy_get_by_index( |
218 | &dev->dev, dev->dev.of_node, phy_num); | |
219 | if (IS_ERR(priv->phys[phy_num])) { | |
220 | err = PTR_ERR(priv->phys[phy_num]); | |
221 | goto err_put_hcd; | |
222 | } | |
a4aeb211 HG |
223 | } |
224 | ||
225 | for (clk = 0; clk < EHCI_MAX_CLKS; clk++) { | |
226 | priv->clks[clk] = of_clk_get(dev->dev.of_node, clk); | |
227 | if (IS_ERR(priv->clks[clk])) { | |
228 | err = PTR_ERR(priv->clks[clk]); | |
229 | if (err == -EPROBE_DEFER) | |
230 | goto err_put_clks; | |
231 | priv->clks[clk] = NULL; | |
232 | break; | |
233 | } | |
234 | } | |
235 | } | |
236 | ||
2d87bbd6 BB |
237 | priv->rst = devm_reset_control_get_optional(&dev->dev, NULL); |
238 | if (IS_ERR(priv->rst)) { | |
239 | err = PTR_ERR(priv->rst); | |
240 | if (err == -EPROBE_DEFER) | |
241 | goto err_put_clks; | |
242 | priv->rst = NULL; | |
243 | } else { | |
244 | err = reset_control_deassert(priv->rst); | |
245 | if (err) | |
246 | goto err_put_clks; | |
247 | } | |
248 | ||
843d5e03 AS |
249 | if (pdata->big_endian_desc) |
250 | ehci->big_endian_desc = 1; | |
251 | if (pdata->big_endian_mmio) | |
252 | ehci->big_endian_mmio = 1; | |
b4629a7b AB |
253 | if (pdata->has_tt) |
254 | hcd->has_tt = 1; | |
255 | if (pdata->reset_on_resume) | |
256 | priv->reset_on_resume = true; | |
843d5e03 AS |
257 | |
258 | #ifndef CONFIG_USB_EHCI_BIG_ENDIAN_MMIO | |
259 | if (ehci->big_endian_mmio) { | |
260 | dev_err(&dev->dev, | |
261 | "Error: CONFIG_USB_EHCI_BIG_ENDIAN_MMIO not set\n"); | |
262 | err = -EINVAL; | |
2d87bbd6 | 263 | goto err_reset; |
843d5e03 AS |
264 | } |
265 | #endif | |
266 | #ifndef CONFIG_USB_EHCI_BIG_ENDIAN_DESC | |
267 | if (ehci->big_endian_desc) { | |
268 | dev_err(&dev->dev, | |
269 | "Error: CONFIG_USB_EHCI_BIG_ENDIAN_DESC not set\n"); | |
270 | err = -EINVAL; | |
2d87bbd6 | 271 | goto err_reset; |
843d5e03 AS |
272 | } |
273 | #endif | |
274 | ||
04216bed KM |
275 | if (pdata->power_on) { |
276 | err = pdata->power_on(dev); | |
277 | if (err < 0) | |
2d87bbd6 | 278 | goto err_reset; |
04216bed | 279 | } |
7a7a4a59 | 280 | |
2062ff48 | 281 | res_mem = platform_get_resource(dev, IORESOURCE_MEM, 0); |
148e1134 TR |
282 | hcd->regs = devm_ioremap_resource(&dev->dev, res_mem); |
283 | if (IS_ERR(hcd->regs)) { | |
284 | err = PTR_ERR(hcd->regs); | |
a4aeb211 | 285 | goto err_power; |
ec03ad85 | 286 | } |
2062ff48 VB |
287 | hcd->rsrc_start = res_mem->start; |
288 | hcd->rsrc_len = resource_size(res_mem); | |
289 | ||
7a7a4a59 HM |
290 | err = usb_add_hcd(hcd, irq, IRQF_SHARED); |
291 | if (err) | |
a4aeb211 | 292 | goto err_power; |
7a7a4a59 | 293 | |
3c9740a1 | 294 | device_wakeup_enable(hcd->self.controller); |
7a7a4a59 HM |
295 | platform_set_drvdata(dev, hcd); |
296 | ||
297 | return err; | |
298 | ||
04216bed KM |
299 | err_power: |
300 | if (pdata->power_off) | |
301 | pdata->power_off(dev); | |
2d87bbd6 BB |
302 | err_reset: |
303 | if (priv->rst) | |
304 | reset_control_assert(priv->rst); | |
a4aeb211 HG |
305 | err_put_clks: |
306 | while (--clk >= 0) | |
307 | clk_put(priv->clks[clk]); | |
308 | err_put_hcd: | |
309 | if (pdata == &ehci_platform_defaults) | |
310 | dev->dev.platform_data = NULL; | |
311 | ||
312 | usb_put_hcd(hcd); | |
04216bed | 313 | |
7a7a4a59 HM |
314 | return err; |
315 | } | |
316 | ||
fb4e98ab | 317 | static int ehci_platform_remove(struct platform_device *dev) |
7a7a4a59 HM |
318 | { |
319 | struct usb_hcd *hcd = platform_get_drvdata(dev); | |
d4f09e28 | 320 | struct usb_ehci_pdata *pdata = dev_get_platdata(&dev->dev); |
a4aeb211 HG |
321 | struct ehci_platform_priv *priv = hcd_to_ehci_priv(hcd); |
322 | int clk; | |
7a7a4a59 HM |
323 | |
324 | usb_remove_hcd(hcd); | |
7a7a4a59 | 325 | |
04216bed KM |
326 | if (pdata->power_off) |
327 | pdata->power_off(dev); | |
328 | ||
2d87bbd6 BB |
329 | if (priv->rst) |
330 | reset_control_assert(priv->rst); | |
331 | ||
a4aeb211 HG |
332 | for (clk = 0; clk < EHCI_MAX_CLKS && priv->clks[clk]; clk++) |
333 | clk_put(priv->clks[clk]); | |
334 | ||
335 | usb_put_hcd(hcd); | |
336 | ||
f3bc64d6 AB |
337 | if (pdata == &ehci_platform_defaults) |
338 | dev->dev.platform_data = NULL; | |
339 | ||
7a7a4a59 HM |
340 | return 0; |
341 | } | |
342 | ||
5e4ccd9e | 343 | #ifdef CONFIG_PM_SLEEP |
7a7a4a59 HM |
344 | static int ehci_platform_suspend(struct device *dev) |
345 | { | |
346 | struct usb_hcd *hcd = dev_get_drvdata(dev); | |
d4f09e28 | 347 | struct usb_ehci_pdata *pdata = dev_get_platdata(dev); |
04216bed KM |
348 | struct platform_device *pdev = |
349 | container_of(dev, struct platform_device, dev); | |
c5cf9212 | 350 | bool do_wakeup = device_may_wakeup(dev); |
04216bed KM |
351 | int ret; |
352 | ||
353 | ret = ehci_suspend(hcd, do_wakeup); | |
e155b5b8 VG |
354 | if (ret) |
355 | return ret; | |
7a7a4a59 | 356 | |
04216bed KM |
357 | if (pdata->power_suspend) |
358 | pdata->power_suspend(pdev); | |
359 | ||
360 | return ret; | |
7a7a4a59 HM |
361 | } |
362 | ||
363 | static int ehci_platform_resume(struct device *dev) | |
364 | { | |
365 | struct usb_hcd *hcd = dev_get_drvdata(dev); | |
d4f09e28 | 366 | struct usb_ehci_pdata *pdata = dev_get_platdata(dev); |
04216bed KM |
367 | struct platform_device *pdev = |
368 | container_of(dev, struct platform_device, dev); | |
b4629a7b | 369 | struct ehci_platform_priv *priv = hcd_to_ehci_priv(hcd); |
04216bed KM |
370 | |
371 | if (pdata->power_on) { | |
372 | int err = pdata->power_on(pdev); | |
373 | if (err < 0) | |
374 | return err; | |
375 | } | |
7a7a4a59 | 376 | |
b4629a7b | 377 | ehci_resume(hcd, priv->reset_on_resume); |
7a7a4a59 HM |
378 | return 0; |
379 | } | |
5e4ccd9e | 380 | #endif /* CONFIG_PM_SLEEP */ |
7a7a4a59 | 381 | |
f3bc64d6 AB |
382 | static const struct of_device_id vt8500_ehci_ids[] = { |
383 | { .compatible = "via,vt8500-ehci", }, | |
384 | { .compatible = "wm,prizm-ehci", }, | |
915974c3 | 385 | { .compatible = "generic-ehci", }, |
a95cfa6b | 386 | { .compatible = "cavium,octeon-6335-ehci", }, |
f3bc64d6 AB |
387 | {} |
388 | }; | |
a4aeb211 | 389 | MODULE_DEVICE_TABLE(of, vt8500_ehci_ids); |
f3bc64d6 | 390 | |
776c15d0 JL |
391 | static const struct acpi_device_id ehci_acpi_match[] = { |
392 | { "PNP0D20", 0 }, /* EHCI controller without debug */ | |
393 | { } | |
394 | }; | |
395 | MODULE_DEVICE_TABLE(acpi, ehci_acpi_match); | |
396 | ||
7a7a4a59 HM |
397 | static const struct platform_device_id ehci_platform_table[] = { |
398 | { "ehci-platform", 0 }, | |
399 | { } | |
400 | }; | |
401 | MODULE_DEVICE_TABLE(platform, ehci_platform_table); | |
402 | ||
5e4ccd9e WK |
403 | static SIMPLE_DEV_PM_OPS(ehci_platform_pm_ops, ehci_platform_suspend, |
404 | ehci_platform_resume); | |
7a7a4a59 HM |
405 | |
406 | static struct platform_driver ehci_platform_driver = { | |
407 | .id_table = ehci_platform_table, | |
408 | .probe = ehci_platform_probe, | |
7690417d | 409 | .remove = ehci_platform_remove, |
7a7a4a59 HM |
410 | .shutdown = usb_hcd_platform_shutdown, |
411 | .driver = { | |
7a7a4a59 HM |
412 | .name = "ehci-platform", |
413 | .pm = &ehci_platform_pm_ops, | |
ae5e5f7b | 414 | .of_match_table = vt8500_ehci_ids, |
776c15d0 | 415 | .acpi_match_table = ACPI_PTR(ehci_acpi_match), |
7a7a4a59 HM |
416 | } |
417 | }; | |
99f91934 AS |
418 | |
419 | static int __init ehci_platform_init(void) | |
420 | { | |
421 | if (usb_disabled()) | |
422 | return -ENODEV; | |
423 | ||
424 | pr_info("%s: " DRIVER_DESC "\n", hcd_name); | |
425 | ||
426 | ehci_init_driver(&ehci_platform_hc_driver, &platform_overrides); | |
427 | return platform_driver_register(&ehci_platform_driver); | |
428 | } | |
429 | module_init(ehci_platform_init); | |
430 | ||
431 | static void __exit ehci_platform_cleanup(void) | |
432 | { | |
433 | platform_driver_unregister(&ehci_platform_driver); | |
434 | } | |
435 | module_exit(ehci_platform_cleanup); | |
436 | ||
437 | MODULE_DESCRIPTION(DRIVER_DESC); | |
438 | MODULE_AUTHOR("Hauke Mehrtens"); | |
439 | MODULE_AUTHOR("Alan Stern"); | |
440 | MODULE_LICENSE("GPL"); |