host.c 3.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139
  1. // SPDX-License-Identifier: GPL-2.0
  2. /*
  3. * host.c - DesignWare USB3 DRD Controller Host Glue
  4. *
  5. * Copyright (C) 2011 Texas Instruments Incorporated - https://www.ti.com
  6. *
  7. * Authors: Felipe Balbi <[email protected]>,
  8. */
  9. #include <linux/irq.h>
  10. #include <linux/of.h>
  11. #include <linux/platform_device.h>
  12. #include "core.h"
  13. static void dwc3_host_fill_xhci_irq_res(struct dwc3 *dwc,
  14. int irq, char *name)
  15. {
  16. struct platform_device *pdev = to_platform_device(dwc->dev);
  17. struct device_node *np = dev_of_node(&pdev->dev);
  18. dwc->xhci_resources[1].start = irq;
  19. dwc->xhci_resources[1].end = irq;
  20. dwc->xhci_resources[1].flags = IORESOURCE_IRQ | irq_get_trigger_type(irq);
  21. if (!name && np)
  22. dwc->xhci_resources[1].name = of_node_full_name(pdev->dev.of_node);
  23. else
  24. dwc->xhci_resources[1].name = name;
  25. }
  26. static int dwc3_host_get_irq(struct dwc3 *dwc)
  27. {
  28. struct platform_device *dwc3_pdev = to_platform_device(dwc->dev);
  29. int irq;
  30. irq = platform_get_irq_byname_optional(dwc3_pdev, "host");
  31. if (irq > 0) {
  32. dwc3_host_fill_xhci_irq_res(dwc, irq, "host");
  33. goto out;
  34. }
  35. if (irq == -EPROBE_DEFER)
  36. goto out;
  37. irq = platform_get_irq_byname_optional(dwc3_pdev, "dwc_usb3");
  38. if (irq > 0) {
  39. dwc3_host_fill_xhci_irq_res(dwc, irq, "dwc_usb3");
  40. goto out;
  41. }
  42. if (irq == -EPROBE_DEFER)
  43. goto out;
  44. irq = platform_get_irq(dwc3_pdev, 0);
  45. if (irq > 0) {
  46. dwc3_host_fill_xhci_irq_res(dwc, irq, NULL);
  47. goto out;
  48. }
  49. if (!irq)
  50. irq = -EINVAL;
  51. out:
  52. return irq;
  53. }
  54. int dwc3_host_init(struct dwc3 *dwc)
  55. {
  56. struct property_entry props[4];
  57. struct platform_device *xhci;
  58. int ret, irq;
  59. int prop_idx = 0;
  60. irq = dwc3_host_get_irq(dwc);
  61. if (irq < 0)
  62. return irq;
  63. xhci = platform_device_alloc("xhci-hcd", PLATFORM_DEVID_AUTO);
  64. if (!xhci) {
  65. dev_err(dwc->dev, "couldn't allocate xHCI device\n");
  66. return -ENOMEM;
  67. }
  68. xhci->dev.parent = dwc->dev;
  69. dwc->xhci = xhci;
  70. ret = platform_device_add_resources(xhci, dwc->xhci_resources,
  71. DWC3_XHCI_RESOURCES_NUM);
  72. if (ret) {
  73. dev_err(dwc->dev, "couldn't add resources to xHCI device\n");
  74. goto err;
  75. }
  76. memset(props, 0, sizeof(struct property_entry) * ARRAY_SIZE(props));
  77. if (dwc->usb3_lpm_capable)
  78. props[prop_idx++] = PROPERTY_ENTRY_BOOL("usb3-lpm-capable");
  79. if (dwc->usb2_lpm_disable)
  80. props[prop_idx++] = PROPERTY_ENTRY_BOOL("usb2-lpm-disable");
  81. /**
  82. * WORKAROUND: dwc3 revisions <=3.00a have a limitation
  83. * where Port Disable command doesn't work.
  84. *
  85. * The suggested workaround is that we avoid Port Disable
  86. * completely.
  87. *
  88. * This following flag tells XHCI to do just that.
  89. */
  90. if (DWC3_VER_IS_WITHIN(DWC3, ANY, 300A))
  91. props[prop_idx++] = PROPERTY_ENTRY_BOOL("quirk-broken-port-ped");
  92. if (prop_idx) {
  93. ret = device_create_managed_software_node(&xhci->dev, props, NULL);
  94. if (ret) {
  95. dev_err(dwc->dev, "failed to add properties to xHCI\n");
  96. goto err;
  97. }
  98. }
  99. ret = platform_device_add(xhci);
  100. if (ret) {
  101. dev_err(dwc->dev, "failed to register xHCI device\n");
  102. goto err;
  103. }
  104. return 0;
  105. err:
  106. platform_device_put(xhci);
  107. return ret;
  108. }
  109. void dwc3_host_exit(struct dwc3 *dwc)
  110. {
  111. platform_device_unregister(dwc->xhci);
  112. dwc->xhci = NULL;
  113. }