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