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