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