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