xhci-ext-caps.c 2.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110
  1. // SPDX-License-Identifier: GPL-2.0
  2. /*
  3. * XHCI extended capability handling
  4. *
  5. * Copyright (c) 2017 Hans de Goede <hdegoede@redhat.com>
  6. */
  7. #include <linux/platform_device.h>
  8. #include <linux/property.h>
  9. #include <linux/pci.h>
  10. #include "xhci.h"
  11. #define USB_SW_DRV_NAME "intel_xhci_usb_sw"
  12. #define USB_SW_RESOURCE_SIZE 0x400
  13. #define PCI_DEVICE_ID_INTEL_CHERRYVIEW_XHCI 0x22b5
  14. static const struct property_entry role_switch_props[] = {
  15. PROPERTY_ENTRY_BOOL("sw_switch_disable"),
  16. {},
  17. };
  18. static void xhci_intel_unregister_pdev(void *arg)
  19. {
  20. platform_device_unregister(arg);
  21. }
  22. static int xhci_create_intel_xhci_sw_pdev(struct xhci_hcd *xhci, u32 cap_offset)
  23. {
  24. struct usb_hcd *hcd = xhci_to_hcd(xhci);
  25. struct device *dev = hcd->self.controller;
  26. struct platform_device *pdev;
  27. struct pci_dev *pci = to_pci_dev(dev);
  28. struct resource res = { 0, };
  29. int ret;
  30. pdev = platform_device_alloc(USB_SW_DRV_NAME, PLATFORM_DEVID_NONE);
  31. if (!pdev) {
  32. xhci_err(xhci, "couldn't allocate %s platform device\n",
  33. USB_SW_DRV_NAME);
  34. return -ENOMEM;
  35. }
  36. res.start = hcd->rsrc_start + cap_offset;
  37. res.end = res.start + USB_SW_RESOURCE_SIZE - 1;
  38. res.name = USB_SW_DRV_NAME;
  39. res.flags = IORESOURCE_MEM;
  40. ret = platform_device_add_resources(pdev, &res, 1);
  41. if (ret) {
  42. dev_err(dev, "couldn't add resources to intel_xhci_usb_sw pdev\n");
  43. platform_device_put(pdev);
  44. return ret;
  45. }
  46. if (pci->device == PCI_DEVICE_ID_INTEL_CHERRYVIEW_XHCI) {
  47. ret = platform_device_add_properties(pdev, role_switch_props);
  48. if (ret) {
  49. dev_err(dev, "failed to register device properties\n");
  50. platform_device_put(pdev);
  51. return ret;
  52. }
  53. }
  54. pdev->dev.parent = dev;
  55. ret = platform_device_add(pdev);
  56. if (ret) {
  57. dev_err(dev, "couldn't register intel_xhci_usb_sw pdev\n");
  58. platform_device_put(pdev);
  59. return ret;
  60. }
  61. ret = devm_add_action_or_reset(dev, xhci_intel_unregister_pdev, pdev);
  62. if (ret) {
  63. dev_err(dev, "couldn't add unregister action for intel_xhci_usb_sw pdev\n");
  64. return ret;
  65. }
  66. return 0;
  67. }
  68. int xhci_ext_cap_init(struct xhci_hcd *xhci)
  69. {
  70. void __iomem *base = &xhci->cap_regs->hc_capbase;
  71. u32 offset, val;
  72. int ret;
  73. offset = xhci_find_next_ext_cap(base, 0, 0);
  74. while (offset) {
  75. val = readl(base + offset);
  76. switch (XHCI_EXT_CAPS_ID(val)) {
  77. case XHCI_EXT_CAPS_VENDOR_INTEL:
  78. if (xhci->quirks & XHCI_INTEL_USB_ROLE_SW) {
  79. ret = xhci_create_intel_xhci_sw_pdev(xhci,
  80. offset);
  81. if (ret)
  82. return ret;
  83. }
  84. break;
  85. }
  86. offset = xhci_find_next_ext_cap(base, offset, 0);
  87. }
  88. return 0;
  89. }
  90. EXPORT_SYMBOL_GPL(xhci_ext_cap_init);