diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2024-01-18 11:43:55 -0800 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2024-01-18 11:43:55 -0800 |
commit | 8c94ccc7cd691472461448f98e2372c75849406c (patch) | |
tree | d3907cad2a1fbbbcf71847274fdbdcf5a2aeb9a2 /drivers/usb/host | |
parent | bd736f38c014ba70ba7ec3bdc6af6fe5368d6612 (diff) | |
parent | 933bb7b878ddd0f8c094db45551a7daddf806e00 (diff) |
Merge tag 'usb-6.8-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/usb
Pull USB / Thunderbolt updates from Greg KH:
"Here is the big set of USB and Thunderbolt changes for 6.8-rc1.
Included in here are the following:
- Thunderbolt subsystem and driver updates for USB 4 hardware and
issues reported by real devices
- xhci driver updates
- dwc3 driver updates
- uvc_video gadget driver updates
- typec driver updates
- gadget string functions cleaned up
- other small changes
All of these have been in the linux-next tree for a while with no
reported issues"
* tag 'usb-6.8-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/usb: (169 commits)
usb: typec: tipd: fix use of device-specific init function
usb: typec: tipd: Separate reset for TPS6598x
usb: mon: Fix atomicity violation in mon_bin_vma_fault
usb: gadget: uvc: Remove nested locking
usb: gadget: uvc: Fix use are free during STREAMOFF
usb: typec: class: fix typec_altmode_put_partner to put plugs
dt-bindings: usb: dwc3: Limit num-hc-interrupters definition
dt-bindings: usb: xhci: Add num-hc-interrupters definition
xhci: add support to allocate several interrupters
USB: core: Use device_driver directly in struct usb_driver and usb_device_driver
arm64: dts: mediatek: mt8195: Add 'rx-fifo-depth' for cherry
usb: xhci-mtk: fix a short packet issue of gen1 isoc-in transfer
dt-bindings: usb: mtk-xhci: add a property for Gen1 isoc-in transfer issue
arm64: dts: qcom: msm8996: Remove PNoC clock from MSS
arm64: dts: qcom: msm8996: Remove AGGRE2 clock from SLPI
arm64: dts: qcom: msm8998: Remove AGGRE2 clock from SLPI
arm64: dts: qcom: msm8939: Drop RPM bus clocks
arm64: dts: qcom: sdm630: Drop RPM bus clocks
arm64: dts: qcom: qcs404: Drop RPM bus clocks
arm64: dts: qcom: msm8996: Drop RPM bus clocks
...
Diffstat (limited to 'drivers/usb/host')
-rw-r--r-- | drivers/usb/host/max3421-hcd.c | 18 | ||||
-rw-r--r-- | drivers/usb/host/xhci-dbgcap.c | 132 | ||||
-rw-r--r-- | drivers/usb/host/xhci-dbgcap.h | 1 | ||||
-rw-r--r-- | drivers/usb/host/xhci-debugfs.c | 2 | ||||
-rw-r--r-- | drivers/usb/host/xhci-mem.c | 110 | ||||
-rw-r--r-- | drivers/usb/host/xhci-mtk.c | 40 | ||||
-rw-r--r-- | drivers/usb/host/xhci-mtk.h | 2 | ||||
-rw-r--r-- | drivers/usb/host/xhci-pci.c | 140 | ||||
-rw-r--r-- | drivers/usb/host/xhci-plat.c | 26 | ||||
-rw-r--r-- | drivers/usb/host/xhci-ring.c | 13 | ||||
-rw-r--r-- | drivers/usb/host/xhci.c | 213 | ||||
-rw-r--r-- | drivers/usb/host/xhci.h | 19 |
12 files changed, 425 insertions, 291 deletions
diff --git a/drivers/usb/host/max3421-hcd.c b/drivers/usb/host/max3421-hcd.c index d152d72de126..9fe4f48b1898 100644 --- a/drivers/usb/host/max3421-hcd.c +++ b/drivers/usb/host/max3421-hcd.c @@ -1158,12 +1158,12 @@ dump_eps(struct usb_hcd *hcd) end = dp + sizeof(ubuf); *dp = '\0'; list_for_each_entry(urb, &ep->urb_list, urb_list) { - ret = snprintf(dp, end - dp, " %p(%d.%s %d/%d)", urb, - usb_pipetype(urb->pipe), - usb_urb_dir_in(urb) ? "IN" : "OUT", - urb->actual_length, - urb->transfer_buffer_length); - if (ret < 0 || ret >= end - dp) + ret = scnprintf(dp, end - dp, " %p(%d.%s %d/%d)", urb, + usb_pipetype(urb->pipe), + usb_urb_dir_in(urb) ? "IN" : "OUT", + urb->actual_length, + urb->transfer_buffer_length); + if (ret == end - dp - 1) break; /* error or buffer full */ dp += ret; } @@ -1255,9 +1255,9 @@ max3421_handle_irqs(struct usb_hcd *hcd) end = sbuf + sizeof(sbuf); *dp = '\0'; for (i = 0; i < 16; ++i) { - int ret = snprintf(dp, end - dp, " %lu", - max3421_hcd->err_stat[i]); - if (ret < 0 || ret >= end - dp) + int ret = scnprintf(dp, end - dp, " %lu", + max3421_hcd->err_stat[i]); + if (ret == end - dp - 1) break; /* error or buffer full */ dp += ret; } diff --git a/drivers/usb/host/xhci-dbgcap.c b/drivers/usb/host/xhci-dbgcap.c index b40d9238d447..d82935d31126 100644 --- a/drivers/usb/host/xhci-dbgcap.c +++ b/drivers/usb/host/xhci-dbgcap.c @@ -6,9 +6,24 @@ * * Author: Lu Baolu <baolu.lu@linux.intel.com> */ +#include <linux/bug.h> +#include <linux/device.h> #include <linux/dma-mapping.h> -#include <linux/slab.h> +#include <linux/errno.h> +#include <linux/kstrtox.h> +#include <linux/list.h> #include <linux/nls.h> +#include <linux/pm_runtime.h> +#include <linux/slab.h> +#include <linux/spinlock.h> +#include <linux/string.h> +#include <linux/sysfs.h> +#include <linux/types.h> +#include <linux/workqueue.h> + +#include <linux/io-64-nonatomic-lo-hi.h> + +#include <asm/byteorder.h> #include "xhci.h" #include "xhci-trace.h" @@ -28,7 +43,7 @@ static void dbc_ring_free(struct device *dev, struct xhci_ring *ring) if (!ring) return; - if (ring->first_seg && ring->first_seg->trbs) { + if (ring->first_seg) { dma_free_coherent(dev, TRB_SEGMENT_SIZE, ring->first_seg->trbs, ring->first_seg->dma); @@ -374,13 +389,13 @@ static void xhci_dbc_eps_init(struct xhci_dbc *dbc) static void xhci_dbc_eps_exit(struct xhci_dbc *dbc) { - memset(dbc->eps, 0, sizeof(struct dbc_ep) * ARRAY_SIZE(dbc->eps)); + memset(dbc->eps, 0, sizeof_field(struct xhci_dbc, eps)); } static int dbc_erst_alloc(struct device *dev, struct xhci_ring *evt_ring, struct xhci_erst *erst, gfp_t flags) { - erst->entries = dma_alloc_coherent(dev, sizeof(struct xhci_erst_entry), + erst->entries = dma_alloc_coherent(dev, sizeof(*erst->entries), &erst->erst_dma_addr, flags); if (!erst->entries) return -ENOMEM; @@ -394,9 +409,8 @@ static int dbc_erst_alloc(struct device *dev, struct xhci_ring *evt_ring, static void dbc_erst_free(struct device *dev, struct xhci_erst *erst) { - if (erst->entries) - dma_free_coherent(dev, sizeof(struct xhci_erst_entry), - erst->entries, erst->erst_dma_addr); + dma_free_coherent(dev, sizeof(*erst->entries), erst->entries, + erst->erst_dma_addr); erst->entries = NULL; } @@ -495,7 +509,7 @@ static int xhci_dbc_mem_init(struct xhci_dbc *dbc, gfp_t flags) goto ctx_fail; /* Allocate the string table: */ - dbc->string_size = sizeof(struct dbc_str_descs); + dbc->string_size = sizeof(*dbc->string); dbc->string = dma_alloc_coherent(dev, dbc->string_size, &dbc->string_dma, flags); if (!dbc->string) @@ -543,11 +557,8 @@ static void xhci_dbc_mem_cleanup(struct xhci_dbc *dbc) xhci_dbc_eps_exit(dbc); - if (dbc->string) { - dma_free_coherent(dbc->dev, dbc->string_size, - dbc->string, dbc->string_dma); - dbc->string = NULL; - } + dma_free_coherent(dbc->dev, dbc->string_size, dbc->string, dbc->string_dma); + dbc->string = NULL; dbc_free_ctx(dbc->dev, dbc->ctx); dbc->ctx = NULL; @@ -597,7 +608,7 @@ static int xhci_do_dbc_start(struct xhci_dbc *dbc) static int xhci_do_dbc_stop(struct xhci_dbc *dbc) { if (dbc->state == DS_DISABLED) - return -1; + return -EINVAL; writel(0, &dbc->regs->control); dbc->state = DS_DISABLED; @@ -650,11 +661,11 @@ static void xhci_dbc_stop(struct xhci_dbc *dbc) spin_lock_irqsave(&dbc->lock, flags); ret = xhci_do_dbc_stop(dbc); spin_unlock_irqrestore(&dbc->lock, flags); + if (ret) + return; - if (!ret) { - xhci_dbc_mem_cleanup(dbc); - pm_runtime_put_sync(dbc->dev); /* note, was self.controller */ - } + xhci_dbc_mem_cleanup(dbc); + pm_runtime_put_sync(dbc->dev); /* note, was self.controller */ } static void @@ -914,41 +925,29 @@ static void xhci_dbc_handle_events(struct work_struct *work) mod_delayed_work(system_wq, &dbc->event_work, 1); } +static const char * const dbc_state_strings[DS_MAX] = { + [DS_DISABLED] = "disabled", + [DS_INITIALIZED] = "initialized", + [DS_ENABLED] = "enabled", + [DS_CONNECTED] = "connected", + [DS_CONFIGURED] = "configured", + [DS_STALLED] = "stalled", +}; + static ssize_t dbc_show(struct device *dev, struct device_attribute *attr, char *buf) { - const char *p; struct xhci_dbc *dbc; struct xhci_hcd *xhci; xhci = hcd_to_xhci(dev_get_drvdata(dev)); dbc = xhci->dbc; - switch (dbc->state) { - case DS_DISABLED: - p = "disabled"; - break; - case DS_INITIALIZED: - p = "initialized"; - break; - case DS_ENABLED: - p = "enabled"; - break; - case DS_CONNECTED: - p = "connected"; - break; - case DS_CONFIGURED: - p = "configured"; - break; - case DS_STALLED: - p = "stalled"; - break; - default: - p = "unknown"; - } + if (dbc->state >= ARRAY_SIZE(dbc_state_strings)) + return sysfs_emit(buf, "unknown\n"); - return sprintf(buf, "%s\n", p); + return sysfs_emit(buf, "%s\n", dbc_state_strings[dbc->state]); } static ssize_t dbc_store(struct device *dev, @@ -961,9 +960,9 @@ static ssize_t dbc_store(struct device *dev, xhci = hcd_to_xhci(dev_get_drvdata(dev)); dbc = xhci->dbc; - if (!strncmp(buf, "enable", 6)) + if (sysfs_streq(buf, "enable")) xhci_dbc_start(dbc); - else if (!strncmp(buf, "disable", 7)) + else if (sysfs_streq(buf, "disable")) xhci_dbc_stop(dbc); else return -EINVAL; @@ -981,7 +980,7 @@ static ssize_t dbc_idVendor_show(struct device *dev, xhci = hcd_to_xhci(dev_get_drvdata(dev)); dbc = xhci->dbc; - return sprintf(buf, "%04x\n", dbc->idVendor); + return sysfs_emit(buf, "%04x\n", dbc->idVendor); } static ssize_t dbc_idVendor_store(struct device *dev, @@ -993,9 +992,11 @@ static ssize_t dbc_idVendor_store(struct device *dev, void __iomem *ptr; u16 value; u32 dev_info; + int ret; - if (kstrtou16(buf, 0, &value)) - return -EINVAL; + ret = kstrtou16(buf, 0, &value); + if (ret) + return ret; xhci = hcd_to_xhci(dev_get_drvdata(dev)); dbc = xhci->dbc; @@ -1021,7 +1022,7 @@ static ssize_t dbc_idProduct_show(struct device *dev, xhci = hcd_to_xhci(dev_get_drvdata(dev)); dbc = xhci->dbc; - return sprintf(buf, "%04x\n", dbc->idProduct); + return sysfs_emit(buf, "%04x\n", dbc->idProduct); } static ssize_t dbc_idProduct_store(struct device *dev, @@ -1033,9 +1034,11 @@ static ssize_t dbc_idProduct_store(struct device *dev, void __iomem *ptr; u32 dev_info; u16 value; + int ret; - if (kstrtou16(buf, 0, &value)) - return -EINVAL; + ret = kstrtou16(buf, 0, &value); + if (ret) + return ret; xhci = hcd_to_xhci(dev_get_drvdata(dev)); dbc = xhci->dbc; @@ -1060,7 +1063,7 @@ static ssize_t dbc_bcdDevice_show(struct device *dev, xhci = hcd_to_xhci(dev_get_drvdata(dev)); dbc = xhci->dbc; - return sprintf(buf, "%04x\n", dbc->bcdDevice); + return sysfs_emit(buf, "%04x\n", dbc->bcdDevice); } static ssize_t dbc_bcdDevice_store(struct device *dev, @@ -1072,9 +1075,11 @@ static ssize_t dbc_bcdDevice_store(struct device *dev, void __iomem *ptr; u32 dev_info; u16 value; + int ret; - if (kstrtou16(buf, 0, &value)) - return -EINVAL; + ret = kstrtou16(buf, 0, &value); + if (ret) + return ret; xhci = hcd_to_xhci(dev_get_drvdata(dev)); dbc = xhci->dbc; @@ -1100,7 +1105,7 @@ static ssize_t dbc_bInterfaceProtocol_show(struct device *dev, xhci = hcd_to_xhci(dev_get_drvdata(dev)); dbc = xhci->dbc; - return sprintf(buf, "%02x\n", dbc->bInterfaceProtocol); + return sysfs_emit(buf, "%02x\n", dbc->bInterfaceProtocol); } static ssize_t dbc_bInterfaceProtocol_store(struct device *dev, @@ -1114,9 +1119,13 @@ static ssize_t dbc_bInterfaceProtocol_store(struct device *dev, u8 value; int ret; - /* bInterfaceProtocol is 8 bit, but xhci only supports values 0 and 1 */ + /* bInterfaceProtocol is 8 bit, but... */ ret = kstrtou8(buf, 0, &value); - if (ret || value > 1) + if (ret) + return ret; + + /* ...xhci only supports values 0 and 1 */ + if (value > 1) return -EINVAL; xhci = hcd_to_xhci(dev_get_drvdata(dev)); @@ -1139,7 +1148,7 @@ static DEVICE_ATTR_RW(dbc_idProduct); static DEVICE_ATTR_RW(dbc_bcdDevice); static DEVICE_ATTR_RW(dbc_bInterfaceProtocol); -static struct attribute *dbc_dev_attributes[] = { +static struct attribute *dbc_dev_attrs[] = { &dev_attr_dbc.attr, &dev_attr_dbc_idVendor.attr, &dev_attr_dbc_idProduct.attr, @@ -1147,10 +1156,7 @@ static struct attribute *dbc_dev_attributes[] = { &dev_attr_dbc_bInterfaceProtocol.attr, NULL }; - -static const struct attribute_group dbc_dev_attrib_grp = { - .attrs = dbc_dev_attributes, -}; +ATTRIBUTE_GROUPS(dbc_dev); struct xhci_dbc * xhci_alloc_dbc(struct device *dev, void __iomem *base, const struct dbc_driver *driver) @@ -1176,7 +1182,7 @@ xhci_alloc_dbc(struct device *dev, void __iomem *base, const struct dbc_driver * INIT_DELAYED_WORK(&dbc->event_work, xhci_dbc_handle_events); spin_lock_init(&dbc->lock); - ret = sysfs_create_group(&dev->kobj, &dbc_dev_attrib_grp); + ret = sysfs_create_groups(&dev->kobj, dbc_dev_groups); if (ret) goto err; @@ -1195,7 +1201,7 @@ void xhci_dbc_remove(struct xhci_dbc *dbc) xhci_dbc_stop(dbc); /* remove sysfs files */ - sysfs_remove_group(&dbc->dev->kobj, &dbc_dev_attrib_grp); + sysfs_remove_groups(&dbc->dev->kobj, dbc_dev_groups); kfree(dbc); } diff --git a/drivers/usb/host/xhci-dbgcap.h b/drivers/usb/host/xhci-dbgcap.h index 51a7ab3ba0ca..e39e3ae1677a 100644 --- a/drivers/usb/host/xhci-dbgcap.h +++ b/drivers/usb/host/xhci-dbgcap.h @@ -82,6 +82,7 @@ enum dbc_state { DS_CONNECTED, DS_CONFIGURED, DS_STALLED, + DS_MAX }; struct dbc_ep { diff --git a/drivers/usb/host/xhci-debugfs.c b/drivers/usb/host/xhci-debugfs.c index 6d142cd61bd6..f8ba15e7c225 100644 --- a/drivers/usb/host/xhci-debugfs.c +++ b/drivers/usb/host/xhci-debugfs.c @@ -693,7 +693,7 @@ void xhci_debugfs_init(struct xhci_hcd *xhci) "command-ring", xhci->debugfs_root); - xhci_debugfs_create_ring_dir(xhci, &xhci->interrupter->event_ring, + xhci_debugfs_create_ring_dir(xhci, &xhci->interrupters[0]->event_ring, "event-ring", xhci->debugfs_root); diff --git a/drivers/usb/host/xhci-mem.c b/drivers/usb/host/xhci-mem.c index 62116586848b..4460fa7e9fab 100644 --- a/drivers/usb/host/xhci-mem.c +++ b/drivers/usb/host/xhci-mem.c @@ -323,6 +323,7 @@ void xhci_initialize_ring_info(struct xhci_ring *ring, */ ring->num_trbs_free = ring->num_segs * (TRBS_PER_SEGMENT - 1) - 1; } +EXPORT_SYMBOL_GPL(xhci_initialize_ring_info); /* Allocate segments and link them for a ring */ static int xhci_alloc_segments_for_ring(struct xhci_hcd *xhci, @@ -1739,6 +1740,8 @@ struct xhci_command *xhci_alloc_command(struct xhci_hcd *xhci, } command->status = 0; + /* set default timeout to 5000 ms */ + command->timeout_ms = XHCI_CMD_DEFAULT_TIMEOUT; INIT_LIST_HEAD(&command->cmd_list); return command; } @@ -1853,6 +1856,31 @@ xhci_free_interrupter(struct xhci_hcd *xhci, struct xhci_interrupter *ir) kfree(ir); } +void xhci_remove_secondary_interrupter(struct usb_hcd *hcd, struct xhci_interrupter *ir) +{ + struct xhci_hcd *xhci = hcd_to_xhci(hcd); + unsigned int intr_num; + + /* interrupter 0 is primary interrupter, don't touch it */ + if (!ir || !ir->intr_num || ir->intr_num >= xhci->max_interrupters) + xhci_dbg(xhci, "Invalid secondary interrupter, can't remove\n"); + + /* fixme, should we check xhci->interrupter[intr_num] == ir */ + /* fixme locking */ + + spin_lock_irq(&xhci->lock); + + intr_num = ir->intr_num; + + xhci_remove_interrupter(xhci, ir); + xhci->interrupters[intr_num] = NULL; + + spin_unlock_irq(&xhci->lock); + + xhci_free_interrupter(xhci, ir); +} +EXPORT_SYMBOL_GPL(xhci_remove_secondary_interrupter); + void xhci_mem_cleanup(struct xhci_hcd *xhci) { struct device *dev = xhci_to_hcd(xhci)->self.sysdev; @@ -1860,10 +1888,14 @@ void xhci_mem_cleanup(struct xhci_hcd *xhci) cancel_delayed_work_sync(&xhci->cmd_timer); - xhci_remove_interrupter(xhci, xhci->interrupter); - xhci_free_interrupter(xhci, xhci->interrupter); - xhci->interrupter = NULL; - xhci_dbg_trace(xhci, trace_xhci_dbg_init, "Freed primary event ring"); + for (i = 0; i < xhci->max_interrupters; i++) { + if (xhci->interrupters[i]) { + xhci_remove_interrupter(xhci, xhci->interrupters[i]); + xhci_free_interrupter(xhci, xhci->interrupters[i]); + xhci->interrupters[i] = NULL; + } + } + xhci_dbg_trace(xhci, trace_xhci_dbg_init, "Freed interrupters"); if (xhci->cmd_ring) xhci_ring_free(xhci, xhci->cmd_ring); @@ -1933,6 +1965,7 @@ no_bw: for (i = 0; i < xhci->num_port_caps; i++) kfree(xhci->port_caps[i].psi); kfree(xhci->port_caps); + kfree(xhci->interrupters); xhci->num_port_caps = 0; xhci->usb2_rhub.ports = NULL; @@ -1941,6 +1974,7 @@ no_bw: xhci->rh_bw = NULL; xhci->ext_caps = NULL; xhci->port_caps = NULL; + xhci->interrupters = NULL; xhci->page_size = 0; xhci->page_shift = 0; @@ -2246,18 +2280,20 @@ static int xhci_setup_port_arrays(struct xhci_hcd *xhci, gfp_t flags) } static struct xhci_interrupter * -xhci_alloc_interrupter(struct xhci_hcd *xhci, gfp_t flags) +xhci_alloc_interrupter(struct xhci_hcd *xhci, int segs, gfp_t flags) { struct device *dev = xhci_to_hcd(xhci)->self.sysdev; struct xhci_interrupter *ir; - unsigned int num_segs; + unsigned int num_segs = segs; int ret; ir = kzalloc_node(sizeof(*ir), flags, dev_to_node(dev)); if (!ir) return NULL; - num_segs = min_t(unsigned int, 1 << HCS_ERST_MAX(xhci->hcs_params2), + /* number of ring segments should be greater than 0 */ + if (segs <= 0) + num_segs = min_t(unsigned int, 1 << HCS_ERST_MAX(xhci->hcs_params2), ERST_MAX_SEGS); ir->event_ring = xhci_ring_alloc(xhci, num_segs, 1, TYPE_EVENT, 0, @@ -2292,6 +2328,13 @@ xhci_add_interrupter(struct xhci_hcd *xhci, struct xhci_interrupter *ir, return -EINVAL; } + if (xhci->interrupters[intr_num]) { + xhci_warn(xhci, "Interrupter %d\n already set up", intr_num); + return -EINVAL; + } + + xhci->interrupters[intr_num] = ir; + ir->intr_num = intr_num; ir->ir_set = &xhci->run_regs->ir_set[intr_num]; /* set ERST count with the number of entries in the segment table */ @@ -2311,10 +2354,52 @@ xhci_add_interrupter(struct xhci_hcd *xhci, struct xhci_interrupter *ir, return 0; } +struct xhci_interrupter * +xhci_create_secondary_interrupter(struct usb_hcd *hcd, int num_seg) +{ + struct xhci_hcd *xhci = hcd_to_xhci(hcd); + struct xhci_interrupter *ir; + unsigned int i; + int err = -ENOSPC; + + if (!xhci->interrupters || xhci->max_interrupters <= 1) + return NULL; + + ir = xhci_alloc_interrupter(xhci, num_seg, GFP_KERNEL); + if (!ir) + return NULL; + + spin_lock_irq(&xhci->lock); + + /* Find available secondary interrupter, interrupter 0 is reserved for primary */ + for (i = 1; i < xhci->max_interrupters; i++) { + if (xhci->interrupters[i] == NULL) { + err = xhci_add_interrupter(xhci, ir, i); + break; + } + } + + spin_unlock_irq(&xhci->lock); + + if (err) { + xhci_warn(xhci, "Failed to add secondary interrupter, max interrupters %d\n", + xhci->max_interrupters); + xhci_free_interrupter(xhci, ir); + return NULL; + } + + xhci_dbg(xhci, "Add secondary interrupter %d, max interrupters %d\n", + i, xhci->max_interrupters); + + return ir; +} +EXPORT_SYMBOL_GPL(xhci_create_secondary_interrupter); + int xhci_mem_init(struct xhci_hcd *xhci, gfp_t flags) { - dma_addr_t dma; + struct xhci_interrupter *ir; struct device *dev = xhci_to_hcd(xhci)->self.sysdev; + dma_addr_t dma; unsigned int val, val2; u64 val_64; u32 page_size, temp; @@ -2438,11 +2523,14 @@ int xhci_mem_init(struct xhci_hcd *xhci, gfp_t flags) /* Allocate and set up primary interrupter 0 with an event ring. */ xhci_dbg_trace(xhci, trace_xhci_dbg_init, "Allocating primary event ring"); - xhci->interrupter = xhci_alloc_interrupter(xhci, flags); - if (!xhci->interrupter) + xhci->interrupters = kcalloc_node(xhci->max_interrupters, sizeof(*xhci->interrupters), + flags, dev_to_node(dev)); + + ir = xhci_alloc_interrupter(xhci, 0, flags); + if (!ir) goto fail; - if (xhci_add_interrupter(xhci, xhci->interrupter, 0)) + if (xhci_add_interrupter(xhci, ir, 0)) goto fail; xhci->isoc_bei_interval = AVOID_BEI_INTERVAL_MAX; diff --git a/drivers/usb/host/xhci-mtk.c b/drivers/usb/host/xhci-mtk.c index bbdf1b0b7be1..3252e3d2d79c 100644 --- a/drivers/usb/host/xhci-mtk.c +++ b/drivers/usb/host/xhci-mtk.c @@ -7,6 +7,7 @@ * Chunfeng Yun <chunfeng.yun@mediatek.com> */ +#include <linux/bitfield.h> #include <linux/dma-mapping.h> #include <linux/iopoll.h> #include <linux/kernel.h> @@ -73,6 +74,9 @@ #define FRMCNT_LEV1_RANG (0x12b << 8) #define FRMCNT_LEV1_RANG_MASK GENMASK(19, 8) +#define HSCH_CFG1 0x960 +#define SCH3_RXFIFO_DEPTH_MASK GENMASK(21, 20) + #define SS_GEN2_EOF_CFG 0x990 #define SSG2EOF_OFFSET 0x3c @@ -114,6 +118,8 @@ #define SSC_IP_SLEEP_EN BIT(4) #define SSC_SPM_INT_EN BIT(1) +#define SCH_FIFO_TO_KB(x) ((x) >> 10) + enum ssusb_uwk_vers { SSUSB_UWK_V1 = 1, SSUSB_UWK_V2, @@ -165,6 +171,35 @@ static void xhci_mtk_set_frame_interval(struct xhci_hcd_mtk *mtk) writel(value, hcd->regs + SS_GEN2_EOF_CFG); } +/* + * workaround: usb3.2 gen1 isoc rx hw issue + * host send out unexpected ACK afer device fininsh a burst transfer with + * a short packet. + */ +static void xhci_mtk_rxfifo_depth_set(struct xhci_hcd_mtk *mtk) +{ + struct usb_hcd *hcd = mtk->hcd; + u32 value; + + if (!mtk->rxfifo_depth) + return; + + value = readl(hcd->regs + HSCH_CFG1); + value &= ~SCH3_RXFIFO_DEPTH_MASK; + value |= FIELD_PREP(SCH3_RXFIFO_DEPTH_MASK, + SCH_FIFO_TO_KB(mtk->rxfifo_depth) - 1); + writel(value, hcd->regs + HSCH_CFG1); +} + +static void xhci_mtk_init_quirk(struct xhci_hcd_mtk *mtk) +{ + /* workaround only for mt8195 */ + xhci_mtk_set_frame_interval(mtk); + + /* workaround for SoCs using SSUSB about before IPM v1.6.0 */ + xhci_mtk_rxfifo_depth_set(mtk); +} + static int xhci_mtk_host_enable(struct xhci_hcd_mtk *mtk) { struct mu3c_ippc_regs __iomem *ippc = mtk->ippc_regs; @@ -448,8 +483,7 @@ static int xhci_mtk_setup(struct usb_hcd *hcd) if (ret) return ret; - /* workaround only for mt8195 */ - xhci_mtk_set_frame_interval(mtk); + xhci_mtk_init_quirk(mtk); } ret = xhci_gen_setup(hcd, xhci_mtk_quirks); @@ -527,6 +561,8 @@ static int xhci_mtk_probe(struct platform_device *pdev) of_property_read_u32(node, "mediatek,u2p-dis-msk", &mtk->u2p_dis_msk); + of_property_read_u32(node, "rx-fifo-depth", &mtk->rxfifo_depth); + ret = usb_wakeup_of_property_parse(mtk, node); if (ret) { dev_err(dev, "failed to parse uwk property\n"); diff --git a/drivers/usb/host/xhci-mtk.h b/drivers/usb/host/xhci-mtk.h index 39f7ae7d3087..f5e2bd66bb1b 100644 --- a/drivers/usb/host/xhci-mtk.h +++ b/drivers/usb/host/xhci-mtk.h @@ -171,6 +171,8 @@ struct xhci_hcd_mtk { struct regmap *uwk; u32 uwk_reg_base; u32 uwk_vers; + /* quirk */ + u32 rxfifo_depth; }; static inline struct xhci_hcd_mtk *hcd_to_mtk(struct usb_hcd *hcd) diff --git a/drivers/usb/host/xhci-pci.c b/drivers/usb/host/xhci-pci.c index d6fc08e5db8f..b534ca9752be 100644 --- a/drivers/usb/host/xhci-pci.c +++ b/drivers/usb/host/xhci-pci.c @@ -95,10 +95,9 @@ static void xhci_msix_sync_irqs(struct xhci_hcd *xhci) if (hcd->msix_enabled) { struct pci_dev *pdev = to_pci_dev(hcd->self.controller); - int i; - for (i = 0; i < xhci->msix_count; i++) - synchronize_irq(pci_irq_vector(pdev, i)); + /* for now, the driver only supports one primary interrupter */ + synchronize_irq(pci_irq_vector(pdev, 0)); } } @@ -112,100 +111,18 @@ static void xhci_cleanup_msix(struct xhci_hcd *xhci) if (hcd->irq > 0) return; - if (hcd->msix_enabled) { - int i; - - for (i = 0; i < xhci->msix_count; i++) - free_irq(pci_irq_vector(pdev, i), xhci_to_hcd(xhci)); - } else { - free_irq(pci_irq_vector(pdev, 0), xhci_to_hcd(xhci)); - } - + free_irq(pci_irq_vector(pdev, 0), xhci_to_hcd(xhci)); pci_free_irq_vectors(pdev); hcd->msix_enabled = 0; } -/* - * Set up MSI - */ -static int xhci_setup_msi(struct xhci_hcd *xhci) -{ - int ret; - /* - * TODO:Check with MSI Soc for sysdev - */ - struct pci_dev *pdev = to_pci_dev(xhci_to_hcd(xhci)->self.controller); - - ret = pci_alloc_irq_vectors(pdev, 1, 1, PCI_IRQ_MSI); - if (ret < 0) { - xhci_dbg_trace(xhci, trace_xhci_dbg_init, - "failed to allocate MSI entry"); - return ret; - } - - ret = request_irq(pdev->irq, xhci_msi_irq, - 0, "xhci_hcd", xhci_to_hcd(xhci)); - if (ret) { - xhci_dbg_trace(xhci, trace_xhci_dbg_init, - "disable MSI interrupt"); - pci_free_irq_vectors(pdev); - } - - return ret; -} - -/* - * Set up MSI-X - */ -static int xhci_setup_msix(struct xhci_hcd *xhci) -{ - int i, ret; - struct usb_hcd *hcd = xhci_to_hcd(xhci); - struct pci_dev *pdev = to_pci_dev(hcd->self.controller); - - /* - * calculate number of msi-x vectors supported. - * - HCS_MAX_INTRS: the max number of interrupts the host can handle, - * with max number of interrupters based on the xhci HCSPARAMS1. - * - num_online_cpus: maximum msi-x vectors per CPUs core. - * Add additional 1 vector to ensure always available interrupt. - */ - xhci->msix_count = min(num_online_cpus() + 1, - HCS_MAX_INTRS(xhci->hcs_params1)); - - ret = pci_alloc_irq_vectors(pdev, xhci->msix_count, xhci->msix_count, - PCI_IRQ_MSIX); - if (ret < 0) { - xhci_dbg_trace(xhci, trace_xhci_dbg_init, - "Failed to enable MSI-X"); - return ret; - } - - for (i = 0; i < xhci->msix_count; i++) { - ret = request_irq(pci_irq_vector(pdev, i), xhci_msi_irq, 0, - "xhci_hcd", xhci_to_hcd(xhci)); - if (ret) - goto disable_msix; - } - - hcd->msix_enabled = 1; - return ret; - -disable_msix: - xhci_dbg_trace(xhci, trace_xhci_dbg_init, "disable MSI-X interrupt"); - while (--i >= 0) - free_irq(pci_irq_vector(pdev, i), xhci_to_hcd(xhci)); - pci_free_irq_vectors(pdev); - return ret; -} - +/* Try enabling MSI-X with MSI and legacy IRQ as fallback */ static int xhci_try_enable_msi(struct usb_hcd *hcd) { + struct pci_dev *pdev = to_pci_dev(hcd->self.controller); struct xhci_hcd *xhci = hcd_to_xhci(hcd); - struct pci_dev *pdev; int ret; - pdev = to_pci_dev(xhci_to_hcd(xhci)->self.controller); /* * Some Fresco Logic host controllers advertise MSI, but fail to * generate interrupts. Don't even try to enable MSI. @@ -218,32 +135,53 @@ static int xhci_try_enable_msi(struct usb_hcd *hcd) free_irq(hcd->irq, hcd); hcd->irq = 0; - ret = xhci_setup_msix(xhci); - if (ret) - /* fall back to msi*/ - ret = xhci_setup_msi(xhci); + /* + * calculate number of MSI-X vectors supported. + * - HCS_MAX_INTRS: the max number of interrupts the host can handle, + * with max number of interrupters based on the xhci HCSPARAMS1. + * - num_online_cpus: maximum MSI-X vectors per CPUs core. + * Add additional 1 vector to ensure always available interrupt. + */ + xhci->nvecs = min(num_online_cpus() + 1, + HCS_MAX_INTRS(xhci->hcs_params1)); - if (!ret) { - hcd->msi_enabled = 1; - return 0; + /* TODO: Check with MSI Soc for sysdev */ + xhci->nvecs = pci_alloc_irq_vectors(pdev, 1, xhci->nvecs, + PCI_IRQ_MSIX | PCI_IRQ_MSI); + if (xhci->nvecs < 0) { + xhci_dbg_trace(xhci, trace_xhci_dbg_init, + "failed to allocate IRQ vectors"); + goto legacy_irq; } + ret = request_irq(pci_irq_vector(pdev, 0), xhci_msi_irq, 0, "xhci_hcd", + xhci_to_hcd(xhci)); + if (ret) + goto free_irq_vectors; + + hcd->msi_enabled = 1; + hcd->msix_enabled = pdev->msix_enabled; + return 0; + +free_irq_vectors: + xhci_dbg_trace(xhci, trace_xhci_dbg_init, "disable %s interrupt", + pdev->msix_enabled ? "MSI-X" : "MSI"); + pci_free_irq_vectors(pdev); + +legacy_irq: if (!pdev->irq) { xhci_err(xhci, "No msi-x/msi found and no IRQ in BIOS\n"); return -EINVAL; } - legacy_irq: if (!strlen(hcd->irq_descr)) snprintf(hcd->irq_descr, sizeof(hcd->irq_descr), "%s:usb%d", hcd->driver->description, hcd->self.busnum); - /* fall back to legacy interrupt*/ - ret = request_irq(pdev->irq, &usb_hcd_irq, IRQF_SHARED, - hcd->irq_descr, hcd); + /* fall back to legacy interrupt */ + ret = request_irq(pdev->irq, &usb_hcd_irq, IRQF_SHARED, hcd->irq_descr, hcd); if (ret) { - xhci_err(xhci, "request interrupt %d failed\n", - pdev->irq); + xhci_err(xhci, "request interrupt %d failed\n", pdev->irq); return ret; } hcd->irq = pdev->irq; diff --git a/drivers/usb/host/xhci-plat.c b/drivers/usb/host/xhci-plat.c index 732cdeb73920..f04fde19f551 100644 --- a/drivers/usb/host/xhci-plat.c +++ b/drivers/usb/host/xhci-plat.c @@ -131,6 +131,9 @@ static const struct of_device_id usb_xhci_of_match[] = { .compatible = "brcm,xhci-brcm-v2", .data = &xhci_plat_brcm, }, { + .compatible = "brcm,bcm2711-xhci", + .data = &xhci_plat_brcm, + }, { .compatible = "brcm,bcm7445-xhci", .data = &xhci_plat_brcm, }, @@ -433,7 +436,7 @@ void xhci_plat_remove(struct platform_device *dev) } EXPORT_SYMBOL_GPL(xhci_plat_remove); -static int __maybe_unused xhci_plat_suspend(struct device *dev) +static int xhci_plat_suspend(struct device *dev) { struct usb_hcd *hcd = dev_get_drvdata(dev); struct xhci_hcd *xhci = hcd_to_xhci(hcd); @@ -461,7 +464,7 @@ static int __maybe_unused xhci_plat_suspend(struct device *dev) return 0; } -static int __maybe_unused xhci_plat_resume(struct device *dev) +static int xhci_plat_resume_common(struct device *dev, struct pm_message pmsg) { struct usb_hcd *hcd = dev_get_drvdata(dev); struct xhci_hcd *xhci = hcd_to_xhci(hcd); @@ -483,7 +486,7 @@ static int __maybe_unused xhci_plat_resume(struct device *dev) if (ret) goto disable_clks; - ret = xhci_resume(xhci, PMSG_RESUME); + ret = xhci_resume(xhci, pmsg); if (ret) goto disable_clks; @@ -502,6 +505,16 @@ disable_clks: return ret; } +static int xhci_plat_resume(struct device *dev) +{ + return xhci_plat_resume_common(dev, PMSG_RESUME); +} + +static int xhci_plat_restore(struct device *dev) +{ + return xhci_plat_resume_common(dev, PMSG_RESTORE); +} + static int __maybe_unused xhci_plat_runtime_suspend(struct device *dev) { struct usb_hcd *hcd = dev_get_drvdata(dev); @@ -524,7 +537,12 @@ static int __maybe_unused xhci_plat_runtime_resume(struct device *dev) } const struct dev_pm_ops xhci_plat_pm_ops = { - SET_SYSTEM_SLEEP_PM_OPS(xhci_plat_suspend, xhci_plat_resume) + .suspend = pm_sleep_ptr(xhci_plat_suspend), + .resume = pm_sleep_ptr(xhci_plat_resume), + .freeze = pm_sleep_ptr(xhci_plat_suspend), + .thaw = pm_sleep_ptr(xhci_plat_resume), + .poweroff = pm_sleep_ptr(xhci_plat_suspend), + .restore = pm_sleep_ptr(xhci_plat_restore), SET_RUNTIME_PM_OPS(xhci_plat_runtime_suspend, xhci_plat_runtime_resume, diff --git a/drivers/usb/host/xhci-ring.c b/drivers/usb/host/xhci-ring.c index f3b5e6345858..33806ae966f9 100644 --- a/drivers/usb/host/xhci-ring.c +++ b/drivers/usb/host/xhci-ring.c @@ -366,9 +366,10 @@ void xhci_ring_cmd_db(struct xhci_hcd *xhci) readl(&xhci->dba->doorbell[0]); } -static bool xhci_mod_cmd_timer(struct xhci_hcd *xhci, unsigned long delay) +static bool xhci_mod_cmd_timer(struct xhci_hcd *xhci) { - return mod_delayed_work(system_wq, &xhci->cmd_timer, delay); + return mod_delayed_work(system_wq, &xhci->cmd_timer, + msecs_to_jiffies(xhci->current_cmd->timeout_ms)); } static struct xhci_command *xhci_next_queued_cmd(struct xhci_hcd *xhci) @@ -412,7 +413,7 @@ static void xhci_handle_stopped_cmd_ring(struct xhci_hcd *xhci, if ((xhci->cmd_ring->dequeue != xhci->cmd_ring->enqueue) && !(xhci->xhc_state & XHCI_STATE_DYING)) { xhci->current_cmd = cur_cmd; - xhci_mod_cmd_timer(xhci, XHCI_CMD_DEFAULT_TIMEOUT); + xhci_mod_cmd_timer(xhci); xhci_ring_cmd_db(xhci); } } @@ -1787,7 +1788,7 @@ static void handle_cmd_completion(struct xhci_hcd *xhci, if (!list_is_singular(&xhci->cmd_list)) { xhci->current_cmd = list_first_entry(&cmd->cmd_list, struct xhci_command, cmd_list); - xhci_mod_cmd_timer(xhci, XHCI_CMD_DEFAULT_TIMEOUT); + xhci_mod_cmd_timer(xhci); } else if (xhci->current_cmd == cmd) { xhci->current_cmd = NULL; } @@ -3060,7 +3061,7 @@ irqreturn_t xhci_irq(struct usb_hcd *hcd) writel(status, &xhci->op_regs->status); /* This is the handler of the primary interrupter */ - ir = xhci->interrupter; + ir = xhci->interrupters[0]; if (!hcd->msi_enabled) { u32 irq_pending; irq_pending = readl(&ir->ir_set->irq_pending); @@ -4287,7 +4288,7 @@ static int queue_command(struct xhci_hcd *xhci, struct xhci_command *cmd, /* if there are no other commands queued we start the timeout timer */ if (list_empty(&xhci->cmd_list)) { xhci->current_cmd = cmd; - xhci_mod_cmd_timer(xhci, XHCI_CMD_DEFAULT_TIMEOUT); + xhci_mod_cmd_timer(xhci); } list_add_tail(&cmd->cmd_list, &xhci->cmd_list); diff --git a/drivers/usb/host/xhci.c b/drivers/usb/host/xhci.c index 884b0898d9c9..c057c42c36f4 100644 --- a/drivers/usb/host/xhci.c +++ b/drivers/usb/host/xhci.c @@ -480,7 +480,7 @@ static int xhci_init(struct usb_hcd *hcd) static int xhci_run_finished(struct xhci_hcd *xhci) { - struct xhci_interrupter *ir = xhci->interrupter; + struct xhci_interrupter *ir = xhci->interrupters[0]; unsigned long flags; u32 temp; @@ -532,7 +532,7 @@ int xhci_run(struct usb_hcd *hcd) u64 temp_64; int ret; struct xhci_hcd *xhci = hcd_to_xhci(hcd); - struct xhci_interrupter *ir = xhci->interrupter; + struct xhci_interrupter *ir = xhci->interrupters[0]; /* Start the xHCI host controller running only after the USB 2.0 roothub * is setup. */ @@ -596,7 +596,7 @@ void xhci_stop(struct usb_hcd *hcd) { u32 temp; struct xhci_hcd *xhci = hcd_to_xhci(hcd); - struct xhci_interrupter *ir = xhci->interrupter; + struct xhci_interrupter *ir = xhci->interrupters[0]; mutex_lock(&xhci->mutex); @@ -692,36 +692,51 @@ EXPORT_SYMBOL_GPL(xhci_shutdown); #ifdef CONFIG_PM static void xhci_save_registers(struct xhci_hcd *xhci) { - struct xhci_interrupter *ir = xhci->interrupter; + struct xhci_interrupter *ir; + unsigned int i; xhci->s3.command = readl(&xhci->op_regs->command); xhci->s3.dev_nt = readl(&xhci->op_regs->dev_notification); xhci->s3.dcbaa_ptr = xhci_read_64(xhci, &xhci->op_regs->dcbaa_ptr); xhci->s3.config_reg = readl(&xhci->op_regs->config_reg); - if (!ir) - return; + /* save both primary and all secondary interrupters */ + /* fixme, shold we lock to prevent race with remove secondary interrupter? */ + for (i = 0; i < xhci->max_interrupters; i++) { + ir = xhci->interrupters[i]; + if (!ir) + continue; - ir->s3_erst_size = readl(&ir->ir_set->erst_size); - ir->s3_erst_base = xhci_read_64(xhci, &ir->ir_set->erst_base); - ir->s3_erst_dequeue = xhci_read_64(xhci, &ir->ir_set->erst_dequeue); - ir->s3_irq_pending = readl(&ir->ir_set->irq_pending); - ir->s3_irq_control = readl(&ir->ir_set->irq_control); + ir->s3_erst_size = readl(&ir->ir_set->erst_size); + ir->s3_erst_base = xhci_read_64(xhci, &ir->ir_set->erst_base); + ir->s3_erst_dequeue = xhci_read_64(xhci, &ir->ir_set->erst_dequeue); + ir->s3_irq_pending = readl(&ir->ir_set->irq_pending); + ir->s3_irq_control = readl(&ir->ir_set->irq_control); + } } static void xhci_restore_registers(struct xhci_hcd *xhci) { - struct xhci_interrupter *ir = xhci->interrupter; + struct xhci_interrupter *ir; + unsigned int i; writel(xhci->s3.command, &xhci->op_regs->command); writel(xhci->s3.dev_nt, &xhci->op_regs->dev_notification); xhci_write_64(xhci, xhci->s3.dcbaa_ptr, &xhci->op_regs->dcbaa_ptr); writel(xhci->s3.config_reg, &xhci->op_regs->config_reg); - writel(ir->s3_erst_size, &ir->ir_set->erst_size); - xhci_write_64(xhci, ir->s3_erst_base, &ir->ir_set->erst_base); - xhci_write_64(xhci, ir->s3_erst_dequeue, &ir->ir_set->erst_dequeue); - writel(ir->s3_irq_pending, &ir->ir_set->irq_pending); - writel(ir->s3_irq_control, &ir->ir_set->irq_control); + + /* FIXME should we lock to protect against freeing of interrupters */ + for (i = 0; i < xhci->max_interrupters; i++) { + ir = xhci->interrupters[i]; + if (!ir) + continue; + + writel(ir->s3_erst_size, &ir->ir_set->erst_size); + xhci_write_64(xhci, ir->s3_erst_base, &ir->ir_set->erst_base); + xhci_write_64(xhci, ir->s3_erst_dequeue, &ir->ir_set->erst_dequeue); + writel(ir->s3_irq_pending, &ir->ir_set->irq_pending); + writel(ir->s3_irq_control, &ir->ir_set->irq_control); + } } static void xhci_set_cmd_ring_deq(struct xhci_hcd *xhci) @@ -1084,7 +1099,7 @@ int xhci_resume(struct xhci_hcd *xhci, pm_message_t msg) xhci_dbg(xhci, "// Disabling event ring interrupts\n"); temp = readl(&xhci->op_regs->status); writel((temp & ~0x1fff) | STS_EINT, &xhci->op_regs->status); - xhci_disable_interrupter(xhci->interrupter); + xhci_disable_interrupter(xhci->interrupters[0]); xhci_dbg(xhci, "cleaning up memory\n"); xhci_mem_cleanup(xhci); @@ -1438,10 +1453,8 @@ static int xhci_configure_endpoint(struct xhci_hcd *xhci, * descriptor. If the usb_device's max packet size changes after that point, * we need to issue an evaluate context command and wait on it. */ -static int xhci_check_maxpacket(struct xhci_hcd *xhci, unsigned int slot_id, - unsigned int ep_index, struct urb *urb, gfp_t mem_flags) +static int xhci_check_ep0_maxpacket(struct xhci_hcd *xhci, struct xhci_virt_device *vdev) { - struct xhci_container_ctx *out_ctx; struct xhci_input_control_ctx *ctrl_ctx; struct xhci_ep_ctx *ep_ctx; struct xhci_command *command; @@ -1449,11 +1462,15 @@ static int xhci_check_maxpacket(struct xhci_hcd *xhci, unsigned int slot_id, int hw_max_packet_size; int ret = 0; - out_ctx = xhci->devs[slot_id]->out_ctx; - ep_ctx = xhci_get_ep_ctx(xhci, out_ctx, ep_index); + ep_ctx = xhci_get_ep_ctx(xhci, vdev->out_ctx, 0); hw_max_packet_size = MAX_PACKET_DECODED(le32_to_cpu(ep_ctx->ep_info2)); - max_packet_size = usb_endpoint_maxp(&urb->dev->ep0.desc); - if (hw_max_packet_size != max_packet_size) { + max_packet_size = usb_endpoint_maxp(&vdev->udev->ep0.desc); + + if (hw_max_packet_size == max_packet_size) + return 0; + + switch (max_packet_size) { + case 8: case 16: case 32: case 64: case 9: xhci_dbg_trace(xhci, trace_xhci_dbg_context_change, "Max Packet Size for ep 0 changed."); xhci_dbg_trace(xhci, trace_xhci_dbg_context_change, @@ -1465,28 +1482,22 @@ static int xhci_check_maxpacket(struct xhci_hcd *xhci, unsigned int slot_id, xhci_dbg_trace(xhci, trace_xhci_dbg_context_change, "Issuing evaluate context command."); - /* Set up the input context flags for the command */ - /* FIXME: This won't work if a non-default control endpoint - * changes max packet sizes. - */ - - command = xhci_alloc_command(xhci, true, mem_flags); + command = xhci_alloc_command(xhci, true, GFP_KERNEL); if (!command) return -ENOMEM; - command->in_ctx = xhci->devs[slot_id]->in_ctx; + command->in_ctx = vdev->in_ctx; ctrl_ctx = xhci_get_input_control_ctx(command->in_ctx); if (!ctrl_ctx) { xhci_warn(xhci, "%s: Could not get input context, bad type.\n", __func__); ret = -ENOMEM; - goto command_cleanup; + break; } /* Set up the modified control endpoint 0 */ - xhci_endpoint_copy(xhci, xhci->devs[slot_id]->in_ctx, - xhci->devs[slot_id]->out_ctx, ep_index); + xhci_endpoint_copy(xhci, vdev->in_ctx, vdev->out_ctx, 0); - ep_ctx = xhci_get_ep_ctx(xhci, command->in_ctx, ep_index); + ep_ctx = xhci_get_ep_ctx(xhci, command->in_ctx, 0); ep_ctx->ep_info &= cpu_to_le32(~EP_STATE_MASK);/* must clear */ ep_ctx->ep_info2 &= cpu_to_le32(~MAX_PACKET_MASK); ep_ctx->ep_info2 |= cpu_to_le32(MAX_PACKET(max_packet_size)); @@ -1494,17 +1505,20 @@ static int xhci_check_maxpacket(struct xhci_hcd *xhci, unsigned int slot_id, ctrl_ctx->add_flags = cpu_to_le32(EP0_FLAG); ctrl_ctx->drop_flags = 0; - ret = xhci_configure_endpoint(xhci, urb->dev, command, - true, false); - - /* Clean up the input context for later use by bandwidth - * functions. - */ + ret = xhci_configure_endpoint(xhci, vdev->udev, command, + true, false); + /* Clean up the input context for later use by bandwidth functions */ ctrl_ctx->add_flags = cpu_to_le32(SLOT_FLAG); -command_cleanup: - kfree(command->completion); - kfree(command); + break; + default: + dev_dbg(&vdev->udev->dev, "incorrect max packet size %d for ep0\n", + max_packet_size); + return -EINVAL; } + + kfree(command->completion); + kfree(command); + return ret; } @@ -1522,24 +1536,7 @@ static int xhci_urb_enqueue(struct usb_hcd *hcd, struct urb *urb, gfp_t mem_flag struct urb_priv *urb_priv; int num_tds; - if (!urb) - return -EINVAL; - ret = xhci_check_args(hcd, urb->dev, urb->ep, - true, true, __func__); - if (ret <= 0) - return ret ? ret : -EINVAL; - - slot_id = urb->dev->slot_id; ep_index = xhci_get_endpoint_index(&urb->ep->desc); - ep_state = &xhci->devs[slot_id]->eps[ep_index].ep_state; - - if (!HCD_HW_ACCESSIBLE(hcd)) - return -ESHUTDOWN; - - if (xhci->devs[slot_id]->flags & VDEV_PORT_ERROR) { - xhci_dbg(xhci, "Can't queue urb, port error, link inactive\n"); - return -ENODEV; - } if (usb_endpoint_xfer_isoc(&urb->ep->desc)) num_tds = urb->number_of_packets; @@ -1561,22 +1558,27 @@ static int xhci_urb_enqueue(struct usb_hcd *hcd, struct urb *urb, gfp_t mem_flag trace_xhci_urb_enqueue(urb); - if (usb_endpoint_xfer_control(&urb->ep->desc)) { - /* Check to see if the max packet size for the default control - * endpoint changed during FS device enumeration - */ - if (urb->dev->speed == USB_SPEED_FULL) { - ret = xhci_check_maxpacket(xhci, slot_id, - ep_index, urb, mem_flags); - if (ret < 0) { - xhci_urb_free_priv(urb_priv); - urb->hcpriv = NULL; - return ret; - } - } + spin_lock_irqsave(&xhci->lock, flags); + + ret = xhci_check_args(hcd, urb->dev, urb->ep, + true, true, __func__); + if (ret <= 0) { + ret = ret ? ret : -EINVAL; + goto free_priv; } - spin_lock_irqsave(&xhci->lock, flags); + slot_id = urb->dev->slot_id; + + if (!HCD_HW_ACCESSIBLE(hcd)) { + ret = -ESHUTDOWN; + goto free_priv; + } + + if (xhci->devs[slot_id]->flags & VDEV_PORT_ERROR) { + xhci_dbg(xhci, "Can't queue urb, port error, link inactive\n"); + ret = -ENODEV; + goto free_priv; + } if (xhci->xhc_state & XHCI_STATE_DYING) { xhci_dbg(xhci, "Ep 0x%x: URB %p submitted for non-responsive xHCI host.\n", @@ -1584,6 +1586,9 @@ static int xhci_urb_enqueue(struct usb_hcd *hcd, struct urb *urb, gfp_t mem_flag ret = -ESHUTDOWN; goto free_priv; } + + ep_state = &xhci->devs[slot_id]->eps[ep_index].ep_state; + if (*ep_state & (EP_GETTING_STREAMS | EP_GETTING_NO_STREAMS)) { xhci_warn(xhci, "WARN: Can't enqueue URB, ep in streams transition state %x\n", *ep_state); @@ -3087,6 +3092,9 @@ done: * of an endpoint that isn't in the halted state this function will issue a * configure endpoint command with the Drop and Add bits set for the target * endpoint. Refer to the additional note in xhci spcification section 4.6.8. + * + * vdev may be lost due to xHC restore error and re-initialization during S3/S4 + * resume. A new vdev will be allocated later by xhci_discover_or_reset_device() */ static void xhci_endpoint_reset(struct usb_hcd *hcd, @@ -3104,19 +3112,37 @@ static void xhci_endpoint_reset(struct usb_hcd *hcd, int err; xhci = hcd_to_xhci(hcd); + ep_index = xhci_get_endpoint_index(&host_ep->desc); + + /* + * Usb core assumes a max packet value for ep0 on FS devices until the + * real value is read from the descriptor. Core resets Ep0 if values + * mismatch. Reconfigure the xhci ep0 endpoint context here in that case + */ + if (usb_endpoint_xfer_control(&host_ep->desc) && ep_index == 0) { + + udev = container_of(host_ep, struct usb_device, ep0); + if (udev->speed != USB_SPEED_FULL || !udev->slot_id) + return; + + vdev = xhci->devs[udev->slot_id]; + if (!vdev || vdev->udev != udev) + return; + + xhci_check_ep0_maxpacket(xhci, vdev); + + /* Nothing else should be done here for ep0 during ep reset */ + return; + } + if (!host_ep->hcpriv) return; udev = (struct usb_device *) host_ep->hcpriv; vdev = xhci->devs[udev->slot_id]; - /* - * vdev may be lost due to xHC restore error and re-initialization - * during S3/S4 resume. A new vdev will be allocated later by - * xhci_discover_or_reset_device() - */ if (!udev->slot_id || !vdev) return; - ep_index = xhci_get_endpoint_index(&host_ep->desc); + ep = &vdev->eps[ep_index]; /* Bail out if toggle is already being cleared by a endpoint reset */ @@ -4029,12 +4055,18 @@ disable_slot: return 0; } -/* - * Issue an Address Device command and optionally send a corresponding - * SetAddress request to the device. +/** + * xhci_setup_device - issues an Address Device command to assign a unique + * USB bus address. + * @hcd: USB host controller data structure. + * @udev: USB dev structure representing the connected device. + * @setup: Enum specifying setup mode: address only or with context. + * @timeout_ms: Max wait time (ms) for the command operation to complete. + * + * Return: 0 if successful; otherwise, negative error code. */ static int xhci_setup_device(struct usb_hcd *hcd, struct usb_device *udev, - enum xhci_setup_dev setup) + enum xhci_setup_dev setup, unsigned int timeout_ms) { const char *act = setup == SETUP_CONTEXT_ONLY ? "context" : "address"; unsigned long flags; @@ -4091,6 +4123,7 @@ static int xhci_setup_device(struct usb_hcd *hcd, struct usb_device *udev, } command->in_ctx = virt_dev->in_ctx; + command->timeout_ms = timeout_ms; slot_ctx = xhci_get_slot_ctx(xhci, virt_dev->in_ctx); ctrl_ctx = xhci_get_input_control_ctx(virt_dev->in_ctx); @@ -4217,14 +4250,16 @@ out: return ret; } -static int xhci_address_device(struct usb_hcd *hcd, struct usb_device *udev) +static int xhci_address_device(struct usb_hcd *hcd, struct usb_device *udev, + unsigned int timeout_ms) { - return xhci_setup_device(hcd, udev, SETUP_CONTEXT_ADDRESS); + return xhci_setup_device(hcd, udev, SETUP_CONTEXT_ADDRESS, timeout_ms); } static int xhci_enable_device(struct usb_hcd *hcd, struct usb_device *udev) { - return xhci_setup_device(hcd, udev, SETUP_CONTEXT_ONLY); + return xhci_setup_device(hcd, udev, SETUP_CONTEXT_ONLY, + XHCI_CMD_DEFAULT_TIMEOUT); } /* diff --git a/drivers/usb/host/xhci.h b/drivers/usb/host/xhci.h index 3ea5c092bba7..a5c72a634e6a 100644 --- a/drivers/usb/host/xhci.h +++ b/drivers/usb/host/xhci.h @@ -791,6 +791,8 @@ struct xhci_command { struct completion *completion; union xhci_trb *command_trb; struct list_head cmd_list; + /* xHCI command response timeout in milliseconds */ + unsigned int timeout_ms; }; /* drop context bitmasks */ @@ -1550,8 +1552,11 @@ struct xhci_td { unsigned int num_trbs; }; -/* xHCI command default timeout value */ -#define XHCI_CMD_DEFAULT_TIMEOUT (5 * HZ) +/* + * xHCI command default timeout value in milliseconds. + * USB 3.2 spec, section 9.2.6.1 + */ +#define XHCI_CMD_DEFAULT_TIMEOUT 5000 /* command descriptor */ struct xhci_cd { @@ -1760,8 +1765,8 @@ struct xhci_hcd { int page_size; /* Valid values are 12 to 20, inclusive */ int page_shift; - /* msi-x vectors */ - int msix_count; + /* MSI-X/MSI vectors */ + int nvecs; /* optional clocks */ struct clk *clk; struct clk *reg_clk; @@ -1769,7 +1774,7 @@ struct xhci_hcd { struct reset_control *reset; /* data structures */ struct xhci_device_context_array *dcbaa; - struct xhci_interrupter *interrupter; + struct xhci_interrupter **interrupters; struct xhci_ring *cmd_ring; unsigned int cmd_ring_state; #define CMD_RING_STATE_RUNNING (1 << 0) @@ -2080,6 +2085,10 @@ struct xhci_container_ctx *xhci_alloc_container_ctx(struct xhci_hcd *xhci, int type, gfp_t flags); void xhci_free_container_ctx(struct xhci_hcd *xhci, struct xhci_container_ctx *ctx); +struct xhci_interrupter * +xhci_create_secondary_interrupter(struct usb_hcd *hcd, int num_seg); +void xhci_remove_secondary_interrupter(struct usb_hcd + *hcd, struct xhci_interrupter *ir); /* xHCI host controller glue */ typedef void (*xhci_get_quirks_t)(struct device *, struct xhci_hcd *); |