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