1 | // SPDX-License-Identifier: GPL-2.0 |
2 | /* |
3 | * xhci-plat.c - xHCI host controller driver platform Bus Glue. |
4 | * |
5 | * Copyright (C) 2012 Texas Instruments Incorporated - https://www.ti.com |
6 | * Author: Sebastian Andrzej Siewior <bigeasy@linutronix.de> |
7 | * |
8 | * A lot of code borrowed from the Linux xHCI driver. |
9 | */ |
10 | |
11 | #include <linux/clk.h> |
12 | #include <linux/dma-mapping.h> |
13 | #include <linux/module.h> |
14 | #include <linux/pci.h> |
15 | #include <linux/of.h> |
16 | #include <linux/platform_device.h> |
17 | #include <linux/usb/phy.h> |
18 | #include <linux/slab.h> |
19 | #include <linux/acpi.h> |
20 | #include <linux/usb/of.h> |
21 | #include <linux/reset.h> |
22 | |
23 | #include "xhci.h" |
24 | #include "xhci-plat.h" |
25 | #include "xhci-mvebu.h" |
26 | |
27 | static struct hc_driver __read_mostly xhci_plat_hc_driver; |
28 | |
29 | static int xhci_plat_setup(struct usb_hcd *hcd); |
30 | static int xhci_plat_start(struct usb_hcd *hcd); |
31 | |
32 | static const struct xhci_driver_overrides xhci_plat_overrides __initconst = { |
33 | .extra_priv_size = sizeof(struct xhci_plat_priv), |
34 | .reset = xhci_plat_setup, |
35 | .start = xhci_plat_start, |
36 | }; |
37 | |
38 | static void xhci_priv_plat_start(struct usb_hcd *hcd) |
39 | { |
40 | struct xhci_plat_priv *priv = hcd_to_xhci_priv(hcd); |
41 | |
42 | if (priv->plat_start) |
43 | priv->plat_start(hcd); |
44 | } |
45 | |
46 | static int xhci_priv_init_quirk(struct usb_hcd *hcd) |
47 | { |
48 | struct xhci_plat_priv *priv = hcd_to_xhci_priv(hcd); |
49 | |
50 | if (!priv->init_quirk) |
51 | return 0; |
52 | |
53 | return priv->init_quirk(hcd); |
54 | } |
55 | |
56 | static int xhci_priv_suspend_quirk(struct usb_hcd *hcd) |
57 | { |
58 | struct xhci_plat_priv *priv = hcd_to_xhci_priv(hcd); |
59 | |
60 | if (!priv->suspend_quirk) |
61 | return 0; |
62 | |
63 | return priv->suspend_quirk(hcd); |
64 | } |
65 | |
66 | static int xhci_priv_resume_quirk(struct usb_hcd *hcd) |
67 | { |
68 | struct xhci_plat_priv *priv = hcd_to_xhci_priv(hcd); |
69 | |
70 | if (!priv->resume_quirk) |
71 | return 0; |
72 | |
73 | return priv->resume_quirk(hcd); |
74 | } |
75 | |
76 | static void xhci_plat_quirks(struct device *dev, struct xhci_hcd *xhci) |
77 | { |
78 | struct xhci_plat_priv *priv = xhci_to_priv(xhci); |
79 | |
80 | xhci->quirks |= priv->quirks; |
81 | } |
82 | |
83 | /* called during probe() after chip reset completes */ |
84 | static int xhci_plat_setup(struct usb_hcd *hcd) |
85 | { |
86 | int ret; |
87 | |
88 | |
89 | ret = xhci_priv_init_quirk(hcd); |
90 | if (ret) |
91 | return ret; |
92 | |
93 | return xhci_gen_setup(hcd, get_quirks: xhci_plat_quirks); |
94 | } |
95 | |
96 | static int xhci_plat_start(struct usb_hcd *hcd) |
97 | { |
98 | xhci_priv_plat_start(hcd); |
99 | return xhci_run(hcd); |
100 | } |
101 | |
102 | #ifdef CONFIG_OF |
103 | static const struct xhci_plat_priv xhci_plat_marvell_armada = { |
104 | .init_quirk = xhci_mvebu_mbus_init_quirk, |
105 | }; |
106 | |
107 | static const struct xhci_plat_priv xhci_plat_marvell_armada3700 = { |
108 | .init_quirk = xhci_mvebu_a3700_init_quirk, |
109 | }; |
110 | |
111 | static const struct xhci_plat_priv xhci_plat_brcm = { |
112 | .quirks = XHCI_RESET_ON_RESUME | XHCI_SUSPEND_RESUME_CLKS, |
113 | }; |
114 | |
115 | static const struct of_device_id usb_xhci_of_match[] = { |
116 | { |
117 | .compatible = "generic-xhci" , |
118 | }, { |
119 | .compatible = "xhci-platform" , |
120 | }, { |
121 | .compatible = "marvell,armada-375-xhci" , |
122 | .data = &xhci_plat_marvell_armada, |
123 | }, { |
124 | .compatible = "marvell,armada-380-xhci" , |
125 | .data = &xhci_plat_marvell_armada, |
126 | }, { |
127 | .compatible = "marvell,armada3700-xhci" , |
128 | .data = &xhci_plat_marvell_armada3700, |
129 | }, { |
130 | .compatible = "brcm,xhci-brcm-v2" , |
131 | .data = &xhci_plat_brcm, |
132 | }, { |
133 | .compatible = "brcm,bcm7445-xhci" , |
134 | .data = &xhci_plat_brcm, |
135 | }, |
136 | {}, |
137 | }; |
138 | MODULE_DEVICE_TABLE(of, usb_xhci_of_match); |
139 | #endif |
140 | |
141 | int xhci_plat_probe(struct platform_device *pdev, struct device *sysdev, const struct xhci_plat_priv *priv_match) |
142 | { |
143 | const struct hc_driver *driver; |
144 | struct device *tmpdev; |
145 | struct xhci_hcd *xhci; |
146 | struct resource *res; |
147 | struct usb_hcd *hcd, *usb3_hcd; |
148 | int ret; |
149 | int irq; |
150 | struct xhci_plat_priv *priv = NULL; |
151 | |
152 | |
153 | if (usb_disabled()) |
154 | return -ENODEV; |
155 | |
156 | driver = &xhci_plat_hc_driver; |
157 | |
158 | irq = platform_get_irq(pdev, 0); |
159 | if (irq < 0) |
160 | return irq; |
161 | |
162 | if (!sysdev) |
163 | sysdev = &pdev->dev; |
164 | |
165 | ret = dma_set_mask_and_coherent(dev: sysdev, DMA_BIT_MASK(64)); |
166 | if (ret) |
167 | return ret; |
168 | |
169 | pm_runtime_set_active(dev: &pdev->dev); |
170 | pm_runtime_enable(dev: &pdev->dev); |
171 | pm_runtime_get_noresume(dev: &pdev->dev); |
172 | |
173 | hcd = __usb_create_hcd(driver, sysdev, dev: &pdev->dev, |
174 | bus_name: dev_name(dev: &pdev->dev), NULL); |
175 | if (!hcd) { |
176 | ret = -ENOMEM; |
177 | goto disable_runtime; |
178 | } |
179 | |
180 | hcd->regs = devm_platform_get_and_ioremap_resource(pdev, index: 0, res: &res); |
181 | if (IS_ERR(ptr: hcd->regs)) { |
182 | ret = PTR_ERR(ptr: hcd->regs); |
183 | goto put_hcd; |
184 | } |
185 | |
186 | hcd->rsrc_start = res->start; |
187 | hcd->rsrc_len = resource_size(res); |
188 | |
189 | xhci = hcd_to_xhci(hcd); |
190 | |
191 | xhci->allow_single_roothub = 1; |
192 | |
193 | /* |
194 | * Not all platforms have clks so it is not an error if the |
195 | * clock do not exist. |
196 | */ |
197 | xhci->reg_clk = devm_clk_get_optional(dev: &pdev->dev, id: "reg" ); |
198 | if (IS_ERR(ptr: xhci->reg_clk)) { |
199 | ret = PTR_ERR(ptr: xhci->reg_clk); |
200 | goto put_hcd; |
201 | } |
202 | |
203 | xhci->clk = devm_clk_get_optional(dev: &pdev->dev, NULL); |
204 | if (IS_ERR(ptr: xhci->clk)) { |
205 | ret = PTR_ERR(ptr: xhci->clk); |
206 | goto put_hcd; |
207 | } |
208 | |
209 | xhci->reset = devm_reset_control_array_get_optional_shared(dev: &pdev->dev); |
210 | if (IS_ERR(ptr: xhci->reset)) { |
211 | ret = PTR_ERR(ptr: xhci->reset); |
212 | goto put_hcd; |
213 | } |
214 | |
215 | ret = reset_control_deassert(rstc: xhci->reset); |
216 | if (ret) |
217 | goto put_hcd; |
218 | |
219 | ret = clk_prepare_enable(clk: xhci->reg_clk); |
220 | if (ret) |
221 | goto err_reset; |
222 | |
223 | ret = clk_prepare_enable(clk: xhci->clk); |
224 | if (ret) |
225 | goto disable_reg_clk; |
226 | |
227 | if (priv_match) { |
228 | priv = hcd_to_xhci_priv(hcd); |
229 | /* Just copy data for now */ |
230 | *priv = *priv_match; |
231 | } |
232 | |
233 | device_set_wakeup_capable(dev: &pdev->dev, capable: true); |
234 | |
235 | xhci->main_hcd = hcd; |
236 | |
237 | /* imod_interval is the interrupt moderation value in nanoseconds. */ |
238 | xhci->imod_interval = 40000; |
239 | |
240 | /* Iterate over all parent nodes for finding quirks */ |
241 | for (tmpdev = &pdev->dev; tmpdev; tmpdev = tmpdev->parent) { |
242 | |
243 | if (device_property_read_bool(dev: tmpdev, propname: "usb2-lpm-disable" )) |
244 | xhci->quirks |= XHCI_HW_LPM_DISABLE; |
245 | |
246 | if (device_property_read_bool(dev: tmpdev, propname: "usb3-lpm-capable" )) |
247 | xhci->quirks |= XHCI_LPM_SUPPORT; |
248 | |
249 | if (device_property_read_bool(dev: tmpdev, propname: "quirk-broken-port-ped" )) |
250 | xhci->quirks |= XHCI_BROKEN_PORT_PED; |
251 | |
252 | device_property_read_u32(dev: tmpdev, propname: "imod-interval-ns" , |
253 | val: &xhci->imod_interval); |
254 | } |
255 | |
256 | hcd->usb_phy = devm_usb_get_phy_by_phandle(dev: sysdev, phandle: "usb-phy" , index: 0); |
257 | if (IS_ERR(ptr: hcd->usb_phy)) { |
258 | ret = PTR_ERR(ptr: hcd->usb_phy); |
259 | if (ret == -EPROBE_DEFER) |
260 | goto disable_clk; |
261 | hcd->usb_phy = NULL; |
262 | } else { |
263 | ret = usb_phy_init(x: hcd->usb_phy); |
264 | if (ret) |
265 | goto disable_clk; |
266 | } |
267 | |
268 | hcd->tpl_support = of_usb_host_tpl_support(np: sysdev->of_node); |
269 | |
270 | if (priv && (priv->quirks & XHCI_SKIP_PHY_INIT)) |
271 | hcd->skip_phy_initialization = 1; |
272 | |
273 | if (priv && (priv->quirks & XHCI_SG_TRB_CACHE_SIZE_QUIRK)) |
274 | xhci->quirks |= XHCI_SG_TRB_CACHE_SIZE_QUIRK; |
275 | |
276 | ret = usb_add_hcd(hcd, irqnum: irq, IRQF_SHARED); |
277 | if (ret) |
278 | goto disable_usb_phy; |
279 | |
280 | if (!xhci_has_one_roothub(xhci)) { |
281 | xhci->shared_hcd = __usb_create_hcd(driver, sysdev, dev: &pdev->dev, |
282 | bus_name: dev_name(dev: &pdev->dev), primary_hcd: hcd); |
283 | if (!xhci->shared_hcd) { |
284 | ret = -ENOMEM; |
285 | goto dealloc_usb2_hcd; |
286 | } |
287 | |
288 | xhci->shared_hcd->usb_phy = devm_usb_get_phy_by_phandle(dev: sysdev, |
289 | phandle: "usb-phy" , index: 1); |
290 | if (IS_ERR(ptr: xhci->shared_hcd->usb_phy)) { |
291 | xhci->shared_hcd->usb_phy = NULL; |
292 | } else { |
293 | ret = usb_phy_init(x: xhci->shared_hcd->usb_phy); |
294 | if (ret) |
295 | dev_err(sysdev, "%s init usb3phy fail (ret=%d)\n" , |
296 | __func__, ret); |
297 | } |
298 | |
299 | xhci->shared_hcd->tpl_support = hcd->tpl_support; |
300 | } |
301 | |
302 | usb3_hcd = xhci_get_usb3_hcd(xhci); |
303 | if (usb3_hcd && HCC_MAX_PSA(xhci->hcc_params) >= 4) |
304 | usb3_hcd->can_do_streams = 1; |
305 | |
306 | if (xhci->shared_hcd) { |
307 | ret = usb_add_hcd(hcd: xhci->shared_hcd, irqnum: irq, IRQF_SHARED); |
308 | if (ret) |
309 | goto put_usb3_hcd; |
310 | } |
311 | |
312 | device_enable_async_suspend(dev: &pdev->dev); |
313 | pm_runtime_put_noidle(dev: &pdev->dev); |
314 | |
315 | /* |
316 | * Prevent runtime pm from being on as default, users should enable |
317 | * runtime pm using power/control in sysfs. |
318 | */ |
319 | pm_runtime_forbid(dev: &pdev->dev); |
320 | |
321 | return 0; |
322 | |
323 | |
324 | put_usb3_hcd: |
325 | usb_put_hcd(hcd: xhci->shared_hcd); |
326 | |
327 | dealloc_usb2_hcd: |
328 | usb_remove_hcd(hcd); |
329 | |
330 | disable_usb_phy: |
331 | usb_phy_shutdown(x: hcd->usb_phy); |
332 | |
333 | disable_clk: |
334 | clk_disable_unprepare(clk: xhci->clk); |
335 | |
336 | disable_reg_clk: |
337 | clk_disable_unprepare(clk: xhci->reg_clk); |
338 | |
339 | err_reset: |
340 | reset_control_assert(rstc: xhci->reset); |
341 | |
342 | put_hcd: |
343 | usb_put_hcd(hcd); |
344 | |
345 | disable_runtime: |
346 | pm_runtime_put_noidle(dev: &pdev->dev); |
347 | pm_runtime_disable(dev: &pdev->dev); |
348 | |
349 | return ret; |
350 | } |
351 | EXPORT_SYMBOL_GPL(xhci_plat_probe); |
352 | |
353 | static int xhci_generic_plat_probe(struct platform_device *pdev) |
354 | { |
355 | const struct xhci_plat_priv *priv_match; |
356 | struct device *sysdev; |
357 | int ret; |
358 | |
359 | /* |
360 | * sysdev must point to a device that is known to the system firmware |
361 | * or PCI hardware. We handle these three cases here: |
362 | * 1. xhci_plat comes from firmware |
363 | * 2. xhci_plat is child of a device from firmware (dwc3-plat) |
364 | * 3. xhci_plat is grandchild of a pci device (dwc3-pci) |
365 | */ |
366 | for (sysdev = &pdev->dev; sysdev; sysdev = sysdev->parent) { |
367 | if (is_of_node(fwnode: sysdev->fwnode) || |
368 | is_acpi_device_node(fwnode: sysdev->fwnode)) |
369 | break; |
370 | else if (dev_is_pci(sysdev)) |
371 | break; |
372 | } |
373 | |
374 | if (!sysdev) |
375 | sysdev = &pdev->dev; |
376 | |
377 | if (WARN_ON(!sysdev->dma_mask)) { |
378 | /* Platform did not initialize dma_mask */ |
379 | ret = dma_coerce_mask_and_coherent(dev: sysdev, DMA_BIT_MASK(64)); |
380 | if (ret) |
381 | return ret; |
382 | } |
383 | |
384 | if (pdev->dev.of_node) |
385 | priv_match = of_device_get_match_data(dev: &pdev->dev); |
386 | else |
387 | priv_match = dev_get_platdata(dev: &pdev->dev); |
388 | |
389 | return xhci_plat_probe(pdev, sysdev, priv_match); |
390 | } |
391 | |
392 | void xhci_plat_remove(struct platform_device *dev) |
393 | { |
394 | struct usb_hcd *hcd = platform_get_drvdata(pdev: dev); |
395 | struct xhci_hcd *xhci = hcd_to_xhci(hcd); |
396 | struct clk *clk = xhci->clk; |
397 | struct clk *reg_clk = xhci->reg_clk; |
398 | struct usb_hcd *shared_hcd = xhci->shared_hcd; |
399 | |
400 | xhci->xhc_state |= XHCI_STATE_REMOVING; |
401 | pm_runtime_get_sync(dev: &dev->dev); |
402 | |
403 | if (shared_hcd) { |
404 | usb_remove_hcd(hcd: shared_hcd); |
405 | xhci->shared_hcd = NULL; |
406 | } |
407 | |
408 | usb_phy_shutdown(x: hcd->usb_phy); |
409 | |
410 | usb_remove_hcd(hcd); |
411 | |
412 | if (shared_hcd) |
413 | usb_put_hcd(hcd: shared_hcd); |
414 | |
415 | clk_disable_unprepare(clk); |
416 | clk_disable_unprepare(clk: reg_clk); |
417 | reset_control_assert(rstc: xhci->reset); |
418 | usb_put_hcd(hcd); |
419 | |
420 | pm_runtime_disable(dev: &dev->dev); |
421 | pm_runtime_put_noidle(dev: &dev->dev); |
422 | pm_runtime_set_suspended(dev: &dev->dev); |
423 | } |
424 | EXPORT_SYMBOL_GPL(xhci_plat_remove); |
425 | |
426 | static int __maybe_unused xhci_plat_suspend(struct device *dev) |
427 | { |
428 | struct usb_hcd *hcd = dev_get_drvdata(dev); |
429 | struct xhci_hcd *xhci = hcd_to_xhci(hcd); |
430 | int ret; |
431 | |
432 | if (pm_runtime_suspended(dev)) |
433 | pm_runtime_resume(dev); |
434 | |
435 | ret = xhci_priv_suspend_quirk(hcd); |
436 | if (ret) |
437 | return ret; |
438 | /* |
439 | * xhci_suspend() needs `do_wakeup` to know whether host is allowed |
440 | * to do wakeup during suspend. |
441 | */ |
442 | ret = xhci_suspend(xhci, do_wakeup: device_may_wakeup(dev)); |
443 | if (ret) |
444 | return ret; |
445 | |
446 | if (!device_may_wakeup(dev) && (xhci->quirks & XHCI_SUSPEND_RESUME_CLKS)) { |
447 | clk_disable_unprepare(clk: xhci->clk); |
448 | clk_disable_unprepare(clk: xhci->reg_clk); |
449 | } |
450 | |
451 | return 0; |
452 | } |
453 | |
454 | static int __maybe_unused xhci_plat_resume(struct device *dev) |
455 | { |
456 | struct usb_hcd *hcd = dev_get_drvdata(dev); |
457 | struct xhci_hcd *xhci = hcd_to_xhci(hcd); |
458 | int ret; |
459 | |
460 | if (!device_may_wakeup(dev) && (xhci->quirks & XHCI_SUSPEND_RESUME_CLKS)) { |
461 | ret = clk_prepare_enable(clk: xhci->clk); |
462 | if (ret) |
463 | return ret; |
464 | |
465 | ret = clk_prepare_enable(clk: xhci->reg_clk); |
466 | if (ret) { |
467 | clk_disable_unprepare(clk: xhci->clk); |
468 | return ret; |
469 | } |
470 | } |
471 | |
472 | ret = xhci_priv_resume_quirk(hcd); |
473 | if (ret) |
474 | goto disable_clks; |
475 | |
476 | ret = xhci_resume(xhci, PMSG_RESUME); |
477 | if (ret) |
478 | goto disable_clks; |
479 | |
480 | pm_runtime_disable(dev); |
481 | pm_runtime_set_active(dev); |
482 | pm_runtime_enable(dev); |
483 | |
484 | return 0; |
485 | |
486 | disable_clks: |
487 | if (!device_may_wakeup(dev) && (xhci->quirks & XHCI_SUSPEND_RESUME_CLKS)) { |
488 | clk_disable_unprepare(clk: xhci->clk); |
489 | clk_disable_unprepare(clk: xhci->reg_clk); |
490 | } |
491 | |
492 | return ret; |
493 | } |
494 | |
495 | static int __maybe_unused xhci_plat_runtime_suspend(struct device *dev) |
496 | { |
497 | struct usb_hcd *hcd = dev_get_drvdata(dev); |
498 | struct xhci_hcd *xhci = hcd_to_xhci(hcd); |
499 | int ret; |
500 | |
501 | ret = xhci_priv_suspend_quirk(hcd); |
502 | if (ret) |
503 | return ret; |
504 | |
505 | return xhci_suspend(xhci, do_wakeup: true); |
506 | } |
507 | |
508 | static int __maybe_unused xhci_plat_runtime_resume(struct device *dev) |
509 | { |
510 | struct usb_hcd *hcd = dev_get_drvdata(dev); |
511 | struct xhci_hcd *xhci = hcd_to_xhci(hcd); |
512 | |
513 | return xhci_resume(xhci, PMSG_AUTO_RESUME); |
514 | } |
515 | |
516 | const struct dev_pm_ops xhci_plat_pm_ops = { |
517 | SET_SYSTEM_SLEEP_PM_OPS(xhci_plat_suspend, xhci_plat_resume) |
518 | |
519 | SET_RUNTIME_PM_OPS(xhci_plat_runtime_suspend, |
520 | xhci_plat_runtime_resume, |
521 | NULL) |
522 | }; |
523 | EXPORT_SYMBOL_GPL(xhci_plat_pm_ops); |
524 | |
525 | #ifdef CONFIG_ACPI |
526 | static const struct acpi_device_id usb_xhci_acpi_match[] = { |
527 | /* XHCI-compliant USB Controller */ |
528 | { "PNP0D10" , }, |
529 | { } |
530 | }; |
531 | MODULE_DEVICE_TABLE(acpi, usb_xhci_acpi_match); |
532 | #endif |
533 | |
534 | static struct platform_driver usb_generic_xhci_driver = { |
535 | .probe = xhci_generic_plat_probe, |
536 | .remove_new = xhci_plat_remove, |
537 | .shutdown = usb_hcd_platform_shutdown, |
538 | .driver = { |
539 | .name = "xhci-hcd" , |
540 | .pm = &xhci_plat_pm_ops, |
541 | .of_match_table = of_match_ptr(usb_xhci_of_match), |
542 | .acpi_match_table = ACPI_PTR(usb_xhci_acpi_match), |
543 | }, |
544 | }; |
545 | MODULE_ALIAS("platform:xhci-hcd" ); |
546 | |
547 | static int __init xhci_plat_init(void) |
548 | { |
549 | xhci_init_driver(drv: &xhci_plat_hc_driver, over: &xhci_plat_overrides); |
550 | return platform_driver_register(&usb_generic_xhci_driver); |
551 | } |
552 | module_init(xhci_plat_init); |
553 | |
554 | static void __exit xhci_plat_exit(void) |
555 | { |
556 | platform_driver_unregister(&usb_generic_xhci_driver); |
557 | } |
558 | module_exit(xhci_plat_exit); |
559 | |
560 | MODULE_DESCRIPTION("xHCI Platform Host Controller Driver" ); |
561 | MODULE_LICENSE("GPL" ); |
562 | |