diff options
author | Hans de Goede <hdegoede@redhat.com> | 2018-03-20 08:57:09 -0400 |
---|---|---|
committer | Greg Kroah-Hartman <gregkh@linuxfoundation.org> | 2018-03-22 08:40:10 -0400 |
commit | fa31b3cb2ae143aa6e26974fcbe75689da60bdbe (patch) | |
tree | e1a275097befb2221a38635f981545687a2044d1 | |
parent | d0a0fa9d74b9ba3b24b43fd50c318c27dc34c06f (diff) |
xhci: Add Intel extended cap / otg phy mux handling
The xHCI controller on various Intel SoCs has an extended cap mmio-range
which contains registers to control the muxing to the xHCI (host mode)
or the dwc3 (device mode) and vbus-detection for the otg usb-phy.
Having a role-sw driver included in the xHCI code (under drivers/usb/host)
is not desirable. So this commit adds a simple handler for this extended
capability, which creates a platform device with the caps mmio region as
resource, this allows us to write a separate platform role-sw driver for
the role-switch.
Note this commit adds a call to the new xhci_ext_cap_init() function
to xhci_pci_probe(), it is added here because xhci_ext_cap_init() must
be called only once. If in the future we also want to handle ext-caps
on non pci xHCI HCDs from xhci_ext_cap_init() a call to it should also
be added to other bus probe paths.
Signed-off-by: Hans de Goede <hdegoede@redhat.com>
Acked-by: Mathias Nyman <mathias.nyman@linux.intel.com>
Reviewed-by: Andy Shevchenko <andy.shevchenko@gmail.com>
Signed-off-by: Heikki Krogerus <heikki.krogerus@linux.intel.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
-rw-r--r-- | drivers/usb/host/Makefile | 2 | ||||
-rw-r--r-- | drivers/usb/host/xhci-ext-caps.c | 90 | ||||
-rw-r--r-- | drivers/usb/host/xhci-ext-caps.h | 2 | ||||
-rw-r--r-- | drivers/usb/host/xhci-pci.c | 5 | ||||
-rw-r--r-- | drivers/usb/host/xhci.h | 2 |
5 files changed, 100 insertions, 1 deletions
diff --git a/drivers/usb/host/Makefile b/drivers/usb/host/Makefile index 4ede4ce12366..8a8cffe0b445 100644 --- a/drivers/usb/host/Makefile +++ b/drivers/usb/host/Makefile | |||
@@ -11,7 +11,7 @@ fhci-y += fhci-mem.o fhci-tds.o fhci-sched.o | |||
11 | 11 | ||
12 | fhci-$(CONFIG_FHCI_DEBUG) += fhci-dbg.o | 12 | fhci-$(CONFIG_FHCI_DEBUG) += fhci-dbg.o |
13 | 13 | ||
14 | xhci-hcd-y := xhci.o xhci-mem.o | 14 | xhci-hcd-y := xhci.o xhci-mem.o xhci-ext-caps.o |
15 | xhci-hcd-y += xhci-ring.o xhci-hub.o xhci-dbg.o | 15 | xhci-hcd-y += xhci-ring.o xhci-hub.o xhci-dbg.o |
16 | xhci-hcd-y += xhci-trace.o | 16 | xhci-hcd-y += xhci-trace.o |
17 | 17 | ||
diff --git a/drivers/usb/host/xhci-ext-caps.c b/drivers/usb/host/xhci-ext-caps.c new file mode 100644 index 000000000000..399113f9fc5c --- /dev/null +++ b/drivers/usb/host/xhci-ext-caps.c | |||
@@ -0,0 +1,90 @@ | |||
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); | ||
diff --git a/drivers/usb/host/xhci-ext-caps.h b/drivers/usb/host/xhci-ext-caps.h index 631e7cc62604..268328c20681 100644 --- a/drivers/usb/host/xhci-ext-caps.h +++ b/drivers/usb/host/xhci-ext-caps.h | |||
@@ -39,6 +39,8 @@ | |||
39 | #define XHCI_EXT_CAPS_ROUTE 5 | 39 | #define XHCI_EXT_CAPS_ROUTE 5 |
40 | /* IDs 6-9 reserved */ | 40 | /* IDs 6-9 reserved */ |
41 | #define XHCI_EXT_CAPS_DEBUG 10 | 41 | #define XHCI_EXT_CAPS_DEBUG 10 |
42 | /* Vendor caps */ | ||
43 | #define XHCI_EXT_CAPS_VENDOR_INTEL 192 | ||
42 | /* USB Legacy Support Capability - section 7.1.1 */ | 44 | /* USB Legacy Support Capability - section 7.1.1 */ |
43 | #define XHCI_HC_BIOS_OWNED (1 << 16) | 45 | #define XHCI_HC_BIOS_OWNED (1 << 16) |
44 | #define XHCI_HC_OS_OWNED (1 << 24) | 46 | #define XHCI_HC_OS_OWNED (1 << 24) |
diff --git a/drivers/usb/host/xhci-pci.c b/drivers/usb/host/xhci-pci.c index d9f831b67e57..f17b7eab66cf 100644 --- a/drivers/usb/host/xhci-pci.c +++ b/drivers/usb/host/xhci-pci.c | |||
@@ -178,6 +178,7 @@ static void xhci_pci_quirks(struct device *dev, struct xhci_hcd *xhci) | |||
178 | if (pdev->vendor == PCI_VENDOR_ID_INTEL && | 178 | if (pdev->vendor == PCI_VENDOR_ID_INTEL && |
179 | pdev->device == PCI_DEVICE_ID_INTEL_CHERRYVIEW_XHCI) { | 179 | pdev->device == PCI_DEVICE_ID_INTEL_CHERRYVIEW_XHCI) { |
180 | xhci->quirks |= XHCI_SSIC_PORT_UNUSED; | 180 | xhci->quirks |= XHCI_SSIC_PORT_UNUSED; |
181 | xhci->quirks |= XHCI_INTEL_USB_ROLE_SW; | ||
181 | } | 182 | } |
182 | if (pdev->vendor == PCI_VENDOR_ID_INTEL && | 183 | if (pdev->vendor == PCI_VENDOR_ID_INTEL && |
183 | (pdev->device == PCI_DEVICE_ID_INTEL_CHERRYVIEW_XHCI || | 184 | (pdev->device == PCI_DEVICE_ID_INTEL_CHERRYVIEW_XHCI || |
@@ -311,6 +312,10 @@ static int xhci_pci_probe(struct pci_dev *dev, const struct pci_device_id *id) | |||
311 | goto dealloc_usb2_hcd; | 312 | goto dealloc_usb2_hcd; |
312 | } | 313 | } |
313 | 314 | ||
315 | retval = xhci_ext_cap_init(xhci); | ||
316 | if (retval) | ||
317 | goto put_usb3_hcd; | ||
318 | |||
314 | retval = usb_add_hcd(xhci->shared_hcd, dev->irq, | 319 | retval = usb_add_hcd(xhci->shared_hcd, dev->irq, |
315 | IRQF_SHARED); | 320 | IRQF_SHARED); |
316 | if (retval) | 321 | if (retval) |
diff --git a/drivers/usb/host/xhci.h b/drivers/usb/host/xhci.h index 8acc8f8d790f..05c909b04f14 100644 --- a/drivers/usb/host/xhci.h +++ b/drivers/usb/host/xhci.h | |||
@@ -1829,6 +1829,7 @@ struct xhci_hcd { | |||
1829 | #define XHCI_ASMEDIA_MODIFY_FLOWCONTROL (1 << 28) | 1829 | #define XHCI_ASMEDIA_MODIFY_FLOWCONTROL (1 << 28) |
1830 | #define XHCI_HW_LPM_DISABLE (1 << 29) | 1830 | #define XHCI_HW_LPM_DISABLE (1 << 29) |
1831 | #define XHCI_SUSPEND_DELAY (1 << 30) | 1831 | #define XHCI_SUSPEND_DELAY (1 << 30) |
1832 | #define XHCI_INTEL_USB_ROLE_SW (1 << 31) | ||
1832 | 1833 | ||
1833 | unsigned int num_active_eps; | 1834 | unsigned int num_active_eps; |
1834 | unsigned int limit_active_eps; | 1835 | unsigned int limit_active_eps; |
@@ -2024,6 +2025,7 @@ int xhci_gen_setup(struct usb_hcd *hcd, xhci_get_quirks_t get_quirks); | |||
2024 | void xhci_init_driver(struct hc_driver *drv, | 2025 | void xhci_init_driver(struct hc_driver *drv, |
2025 | const struct xhci_driver_overrides *over); | 2026 | const struct xhci_driver_overrides *over); |
2026 | int xhci_disable_slot(struct xhci_hcd *xhci, u32 slot_id); | 2027 | int xhci_disable_slot(struct xhci_hcd *xhci, u32 slot_id); |
2028 | int xhci_ext_cap_init(struct xhci_hcd *xhci); | ||
2027 | 2029 | ||
2028 | int xhci_suspend(struct xhci_hcd *xhci, bool do_wakeup); | 2030 | int xhci_suspend(struct xhci_hcd *xhci, bool do_wakeup); |
2029 | int xhci_resume(struct xhci_hcd *xhci, bool hibernated); | 2031 | int xhci_resume(struct xhci_hcd *xhci, bool hibernated); |