diff options
Diffstat (limited to 'drivers/usb/host')
| -rw-r--r-- | drivers/usb/host/Kconfig | 2 | ||||
| -rw-r--r-- | drivers/usb/host/max3421-hcd.c | 2 | ||||
| -rw-r--r-- | drivers/usb/host/ohci-s3c2410.c | 8 | ||||
| -rw-r--r-- | drivers/usb/host/sl811-hcd.c | 1 | ||||
| -rw-r--r-- | drivers/usb/host/xhci-caps.h | 2 | ||||
| -rw-r--r-- | drivers/usb/host/xhci-dbgcap.c | 15 | ||||
| -rw-r--r-- | drivers/usb/host/xhci-dbgcap.h | 1 | ||||
| -rw-r--r-- | drivers/usb/host/xhci-dbgtty.c | 23 | ||||
| -rw-r--r-- | drivers/usb/host/xhci-mem.c | 85 | ||||
| -rw-r--r-- | drivers/usb/host/xhci-pci.c | 43 | ||||
| -rw-r--r-- | drivers/usb/host/xhci-plat.c | 57 | ||||
| -rw-r--r-- | drivers/usb/host/xhci-plat.h | 2 | ||||
| -rw-r--r-- | drivers/usb/host/xhci-rcar-regs.h | 49 | ||||
| -rw-r--r-- | drivers/usb/host/xhci-rcar.c | 100 | ||||
| -rw-r--r-- | drivers/usb/host/xhci-ring.c | 45 | ||||
| -rw-r--r-- | drivers/usb/host/xhci-rzg3e-regs.h | 12 | ||||
| -rw-r--r-- | drivers/usb/host/xhci-sideband.c | 124 | ||||
| -rw-r--r-- | drivers/usb/host/xhci-tegra.c | 84 | ||||
| -rw-r--r-- | drivers/usb/host/xhci-trace.h | 34 | ||||
| -rw-r--r-- | drivers/usb/host/xhci.c | 17 | ||||
| -rw-r--r-- | drivers/usb/host/xhci.h | 5 |
21 files changed, 507 insertions, 204 deletions
diff --git a/drivers/usb/host/Kconfig b/drivers/usb/host/Kconfig index 109100cc77a3..c4f17ce5c77b 100644 --- a/drivers/usb/host/Kconfig +++ b/drivers/usb/host/Kconfig @@ -93,7 +93,7 @@ config USB_XHCI_RCAR default ARCH_RENESAS help Say 'Y' to enable the support for the xHCI host controller - found in Renesas R-Car ARM SoCs. + found in Renesas R-Car and RZ/G3E alike ARM SoCs. config USB_XHCI_RZV2M bool "xHCI support for Renesas RZ/V2M SoC" diff --git a/drivers/usb/host/max3421-hcd.c b/drivers/usb/host/max3421-hcd.c index dcf31a592f5d..4b5f03f683f7 100644 --- a/drivers/usb/host/max3421-hcd.c +++ b/drivers/usb/host/max3421-hcd.c @@ -1916,7 +1916,7 @@ error: if (hcd) { kfree(max3421_hcd->tx); kfree(max3421_hcd->rx); - if (max3421_hcd->spi_thread) + if (!IS_ERR_OR_NULL(max3421_hcd->spi_thread)) kthread_stop(max3421_hcd->spi_thread); usb_put_hcd(hcd); } diff --git a/drivers/usb/host/ohci-s3c2410.c b/drivers/usb/host/ohci-s3c2410.c index 66d970854357..e623e24d3f8e 100644 --- a/drivers/usb/host/ohci-s3c2410.c +++ b/drivers/usb/host/ohci-s3c2410.c @@ -448,13 +448,6 @@ static const struct dev_pm_ops ohci_hcd_s3c2410_pm_ops = { .resume = ohci_hcd_s3c2410_drv_resume, }; -static const struct of_device_id ohci_hcd_s3c2410_dt_ids[] = { - { .compatible = "samsung,s3c2410-ohci" }, - { /* sentinel */ } -}; - -MODULE_DEVICE_TABLE(of, ohci_hcd_s3c2410_dt_ids); - static struct platform_driver ohci_hcd_s3c2410_driver = { .probe = ohci_hcd_s3c2410_probe, .remove = ohci_hcd_s3c2410_remove, @@ -462,7 +455,6 @@ static struct platform_driver ohci_hcd_s3c2410_driver = { .driver = { .name = "s3c2410-ohci", .pm = &ohci_hcd_s3c2410_pm_ops, - .of_match_table = ohci_hcd_s3c2410_dt_ids, }, }; diff --git a/drivers/usb/host/sl811-hcd.c b/drivers/usb/host/sl811-hcd.c index ea3cab99c5d4..5d6dba681e50 100644 --- a/drivers/usb/host/sl811-hcd.c +++ b/drivers/usb/host/sl811-hcd.c @@ -1748,6 +1748,7 @@ sl811h_suspend(struct platform_device *dev, pm_message_t state) break; case PM_EVENT_SUSPEND: case PM_EVENT_HIBERNATE: + case PM_EVENT_POWEROFF: case PM_EVENT_PRETHAW: /* explicitly discard hw state */ port_power(sl811, 0); break; diff --git a/drivers/usb/host/xhci-caps.h b/drivers/usb/host/xhci-caps.h index 4b8ff4815644..89bc83e4f1eb 100644 --- a/drivers/usb/host/xhci-caps.h +++ b/drivers/usb/host/xhci-caps.h @@ -89,3 +89,5 @@ #define HCC2_GSC(p) ((p) & (1 << 8)) /* true: HC support Virtualization Based Trusted I/O Capability */ #define HCC2_VTC(p) ((p) & (1 << 9)) +/* true: HC support Double BW on a eUSB2 HS ISOC EP */ +#define HCC2_EUSB2_DIC(p) ((p) & (1 << 11)) diff --git a/drivers/usb/host/xhci-dbgcap.c b/drivers/usb/host/xhci-dbgcap.c index 63edf2d8f245..ecda964e018a 100644 --- a/drivers/usb/host/xhci-dbgcap.c +++ b/drivers/usb/host/xhci-dbgcap.c @@ -892,7 +892,8 @@ static enum evtreturn xhci_dbc_do_handle_events(struct xhci_dbc *dbc) dev_info(dbc->dev, "DbC configured\n"); portsc = readl(&dbc->regs->portsc); writel(portsc, &dbc->regs->portsc); - return EVT_GSER; + ret = EVT_GSER; + break; } return EVT_DONE; @@ -954,7 +955,8 @@ static enum evtreturn xhci_dbc_do_handle_events(struct xhci_dbc *dbc) break; case TRB_TYPE(TRB_TRANSFER): dbc_handle_xfer_event(dbc, evt); - ret = EVT_XFER_DONE; + if (ret != EVT_GSER) + ret = EVT_XFER_DONE; break; default: break; @@ -1390,8 +1392,15 @@ int xhci_dbc_suspend(struct xhci_hcd *xhci) if (!dbc) return 0; - if (dbc->state == DS_CONFIGURED) + switch (dbc->state) { + case DS_ENABLED: + case DS_CONNECTED: + case DS_CONFIGURED: dbc->resume_required = 1; + break; + default: + break; + } xhci_dbc_stop(dbc); diff --git a/drivers/usb/host/xhci-dbgcap.h b/drivers/usb/host/xhci-dbgcap.h index 47ac72c2286d..5426c971d2d3 100644 --- a/drivers/usb/host/xhci-dbgcap.h +++ b/drivers/usb/host/xhci-dbgcap.h @@ -114,6 +114,7 @@ struct dbc_port { unsigned int tx_boundary; bool registered; + bool tx_running; }; struct dbc_driver { diff --git a/drivers/usb/host/xhci-dbgtty.c b/drivers/usb/host/xhci-dbgtty.c index d894081d8d15..57cdda4e09c8 100644 --- a/drivers/usb/host/xhci-dbgtty.c +++ b/drivers/usb/host/xhci-dbgtty.c @@ -47,7 +47,7 @@ dbc_kfifo_to_req(struct dbc_port *port, char *packet) return len; } -static int dbc_start_tx(struct dbc_port *port) +static int dbc_do_start_tx(struct dbc_port *port) __releases(&port->port_lock) __acquires(&port->port_lock) { @@ -57,6 +57,8 @@ static int dbc_start_tx(struct dbc_port *port) bool do_tty_wake = false; struct list_head *pool = &port->write_pool; + port->tx_running = true; + while (!list_empty(pool)) { req = list_entry(pool->next, struct dbc_request, list_pool); len = dbc_kfifo_to_req(port, req->buf); @@ -77,12 +79,25 @@ static int dbc_start_tx(struct dbc_port *port) } } + port->tx_running = false; + if (do_tty_wake && port->port.tty) tty_wakeup(port->port.tty); return status; } +/* must be called with port->port_lock held */ +static int dbc_start_tx(struct dbc_port *port) +{ + lockdep_assert_held(&port->port_lock); + + if (port->tx_running) + return -EBUSY; + + return dbc_do_start_tx(port); +} + static void dbc_start_rx(struct dbc_port *port) __releases(&port->port_lock) __acquires(&port->port_lock) @@ -535,6 +550,12 @@ static void xhci_dbc_tty_unregister_device(struct xhci_dbc *dbc) if (!port->registered) return; + /* + * Hang up the TTY. This wakes up any blocked + * writers and causes subsequent writes to fail. + */ + tty_vhangup(port->port.tty); + tty_unregister_device(dbc_tty_driver, port->minor); xhci_dbc_tty_exit_port(port); port->registered = false; diff --git a/drivers/usb/host/xhci-mem.c b/drivers/usb/host/xhci-mem.c index c4a6544aa107..6e5b6057de79 100644 --- a/drivers/usb/host/xhci-mem.c +++ b/drivers/usb/host/xhci-mem.c @@ -1330,18 +1330,33 @@ static unsigned int xhci_get_endpoint_interval(struct usb_device *udev, return interval; } -/* The "Mult" field in the endpoint context is only set for SuperSpeed isoc eps. +/* + * xHCs without LEC use the "Mult" field in the endpoint context for SuperSpeed + * isoc eps, and High speed isoc eps that support bandwidth doubling. Standard * High speed endpoint descriptors can define "the number of additional * transaction opportunities per microframe", but that goes in the Max Burst * endpoint context field. */ -static u32 xhci_get_endpoint_mult(struct usb_device *udev, - struct usb_host_endpoint *ep) +static u32 xhci_get_endpoint_mult(struct xhci_hcd *xhci, + struct usb_device *udev, + struct usb_host_endpoint *ep) { - if (udev->speed < USB_SPEED_SUPER || - !usb_endpoint_xfer_isoc(&ep->desc)) - return 0; - return ep->ss_ep_comp.bmAttributes; + bool lec; + + /* xHCI 1.1 with LEC set does not use mult field, except intel eUSB2 */ + lec = xhci->hci_version > 0x100 && HCC2_LEC(xhci->hcc_params2); + + /* eUSB2 double isoc bw devices are the only USB2 devices using mult */ + if (usb_endpoint_is_hs_isoc_double(udev, ep) && + (!lec || xhci->quirks & XHCI_INTEL_HOST)) + return 1; + + /* SuperSpeed isoc transfers on hosts without LEC uses mult field */ + if (udev->speed >= USB_SPEED_SUPER && + usb_endpoint_xfer_isoc(&ep->desc) && !lec) + return ep->ss_ep_comp.bmAttributes; + + return 0; } static u32 xhci_get_endpoint_max_burst(struct usb_device *udev, @@ -1353,8 +1368,16 @@ static u32 xhci_get_endpoint_max_burst(struct usb_device *udev, if (udev->speed == USB_SPEED_HIGH && (usb_endpoint_xfer_isoc(&ep->desc) || - usb_endpoint_xfer_int(&ep->desc))) + usb_endpoint_xfer_int(&ep->desc))) { + /* + * USB 2 Isochronous Double IN Bandwidth ECN uses fixed burst + * size and max packets bits 12:11 are invalid. + */ + if (usb_endpoint_is_hs_isoc_double(udev, ep)) + return 2; + return usb_endpoint_maxp_mult(&ep->desc) - 1; + } return 0; } @@ -1378,36 +1401,6 @@ static u32 xhci_get_endpoint_type(struct usb_host_endpoint *ep) return 0; } -/* Return the maximum endpoint service interval time (ESIT) payload. - * Basically, this is the maxpacket size, multiplied by the burst size - * and mult size. - */ -static u32 xhci_get_max_esit_payload(struct usb_device *udev, - struct usb_host_endpoint *ep) -{ - int max_burst; - int max_packet; - - /* Only applies for interrupt or isochronous endpoints */ - if (usb_endpoint_xfer_control(&ep->desc) || - usb_endpoint_xfer_bulk(&ep->desc)) - return 0; - - /* SuperSpeedPlus Isoc ep sending over 48k per esit */ - if ((udev->speed >= USB_SPEED_SUPER_PLUS) && - USB_SS_SSP_ISOC_COMP(ep->ss_ep_comp.bmAttributes)) - return le32_to_cpu(ep->ssp_isoc_ep_comp.dwBytesPerInterval); - - /* SuperSpeed or SuperSpeedPlus Isoc ep with less than 48k per esit */ - if (udev->speed >= USB_SPEED_SUPER) - return le16_to_cpu(ep->ss_ep_comp.wBytesPerInterval); - - max_packet = usb_endpoint_maxp(&ep->desc); - max_burst = usb_endpoint_maxp_mult(&ep->desc); - /* A 0 in max burst means 1 transfer per ESIT */ - return max_packet * max_burst; -} - /* Set up an endpoint with one ring segment. Do not allocate stream rings. * Drivers will have to call usb_alloc_streams() to do that. */ @@ -1439,13 +1432,20 @@ int xhci_endpoint_init(struct xhci_hcd *xhci, ring_type = usb_endpoint_type(&ep->desc); + /* Ensure host supports double isoc bandwidth for eUSB2 devices */ + if (usb_endpoint_is_hs_isoc_double(udev, ep) && + !HCC2_EUSB2_DIC(xhci->hcc_params2)) { + dev_dbg(&udev->dev, "Double Isoc Bandwidth not supported by xhci\n"); + return -EINVAL; + } + /* * Get values to fill the endpoint context, mostly from ep descriptor. * The average TRB buffer lengt for bulk endpoints is unclear as we * have no clue on scatter gather list entry size. For Isoc and Int, * set it to max available. See xHCI 1.1 spec 4.14.1.1 for details. */ - max_esit_payload = xhci_get_max_esit_payload(udev, ep); + max_esit_payload = usb_endpoint_max_periodic_payload(udev, ep); interval = xhci_get_endpoint_interval(udev, ep); /* Periodic endpoint bInterval limit quirk */ @@ -1462,8 +1462,8 @@ int xhci_endpoint_init(struct xhci_hcd *xhci, } } - mult = xhci_get_endpoint_mult(udev, ep); - max_packet = usb_endpoint_maxp(&ep->desc); + mult = xhci_get_endpoint_mult(xhci, udev, ep); + max_packet = xhci_usb_endpoint_maxp(udev, ep); max_burst = xhci_get_endpoint_max_burst(udev, ep); avg_trb_len = max_esit_payload; @@ -1484,9 +1484,6 @@ int xhci_endpoint_init(struct xhci_hcd *xhci, /* xHCI 1.0 and 1.1 indicates that ctrl ep avg TRB Length should be 8 */ if (usb_endpoint_xfer_control(&ep->desc) && xhci->hci_version >= 0x100) avg_trb_len = 8; - /* xhci 1.1 with LEC support doesn't use mult field, use RsvdZ */ - if ((xhci->hci_version > 0x100) && HCC2_LEC(xhci->hcc_params2)) - mult = 0; /* Set up the endpoint ring */ virt_dev->eps[ep_index].new_ring = diff --git a/drivers/usb/host/xhci-pci.c b/drivers/usb/host/xhci-pci.c index 00fac8b233d2..f67a4d956204 100644 --- a/drivers/usb/host/xhci-pci.c +++ b/drivers/usb/host/xhci-pci.c @@ -582,6 +582,8 @@ static int xhci_pci_setup(struct usb_hcd *hcd) if (!usb_hcd_is_primary_hcd(hcd)) return 0; + xhci->allow_single_roothub = 1; + if (xhci->quirks & XHCI_PME_STUCK_QUIRK) xhci_pme_acpi_rtd3_enable(pdev); @@ -610,7 +612,7 @@ int xhci_pci_common_probe(struct pci_dev *dev, const struct pci_device_id *id) { int retval; struct xhci_hcd *xhci; - struct usb_hcd *hcd; + struct usb_hcd *hcd, *usb3_hcd; struct reset_control *reset; reset = devm_reset_control_get_optional_exclusive(&dev->dev, NULL); @@ -636,26 +638,31 @@ int xhci_pci_common_probe(struct pci_dev *dev, const struct pci_device_id *id) hcd = dev_get_drvdata(&dev->dev); xhci = hcd_to_xhci(hcd); xhci->reset = reset; - xhci->shared_hcd = usb_create_shared_hcd(&xhci_pci_hc_driver, &dev->dev, - pci_name(dev), hcd); - if (!xhci->shared_hcd) { - retval = -ENOMEM; - goto dealloc_usb2_hcd; - } - retval = xhci_ext_cap_init(xhci); - if (retval) - goto put_usb3_hcd; + if (!xhci_has_one_roothub(xhci)) { + xhci->shared_hcd = usb_create_shared_hcd(&xhci_pci_hc_driver, &dev->dev, + pci_name(dev), hcd); + if (!xhci->shared_hcd) { + retval = -ENOMEM; + goto dealloc_usb2_hcd; + } - retval = usb_add_hcd(xhci->shared_hcd, dev->irq, - IRQF_SHARED); - if (retval) - goto put_usb3_hcd; - /* Roothub already marked as USB 3.0 speed */ + retval = xhci_ext_cap_init(xhci); + if (retval) + goto put_usb3_hcd; + + retval = usb_add_hcd(xhci->shared_hcd, dev->irq, IRQF_SHARED); + if (retval) + goto put_usb3_hcd; + } else { + retval = xhci_ext_cap_init(xhci); + if (retval) + goto dealloc_usb2_hcd; + } - if (!(xhci->quirks & XHCI_BROKEN_STREAMS) && - HCC_MAX_PSA(xhci->hcc_params) >= 4) - xhci->shared_hcd->can_do_streams = 1; + usb3_hcd = xhci_get_usb3_hcd(xhci); + if (usb3_hcd && !(xhci->quirks & XHCI_BROKEN_STREAMS) && HCC_MAX_PSA(xhci->hcc_params) >= 4) + usb3_hcd->can_do_streams = 1; /* USB-2 and USB-3 roothubs initialized, allow runtime pm suspend */ pm_runtime_put_noidle(&dev->dev); diff --git a/drivers/usb/host/xhci-plat.c b/drivers/usb/host/xhci-plat.c index 5eb51797de32..074d9c731639 100644 --- a/drivers/usb/host/xhci-plat.c +++ b/drivers/usb/host/xhci-plat.c @@ -20,6 +20,7 @@ #include <linux/acpi.h> #include <linux/usb/of.h> #include <linux/reset.h> +#include <linux/usb/xhci-sideband.h> #include "xhci.h" #include "xhci-plat.h" @@ -74,6 +75,16 @@ static int xhci_priv_resume_quirk(struct usb_hcd *hcd) return priv->resume_quirk(hcd); } +static int xhci_priv_post_resume_quirk(struct usb_hcd *hcd) +{ + struct xhci_plat_priv *priv = hcd_to_xhci_priv(hcd); + + if (!priv->post_resume_quirk) + return 0; + + return priv->post_resume_quirk(hcd); +} + static void xhci_plat_quirks(struct device *dev, struct xhci_hcd *xhci) { struct xhci_plat_priv *priv = xhci_to_priv(xhci); @@ -171,6 +182,7 @@ int xhci_plat_probe(struct platform_device *pdev, struct device *sysdev, const s return ret; pm_runtime_set_active(&pdev->dev); + pm_runtime_use_autosuspend(&pdev->dev); pm_runtime_enable(&pdev->dev); pm_runtime_get_noresume(&pdev->dev); @@ -454,7 +466,7 @@ void xhci_plat_remove(struct platform_device *dev) } EXPORT_SYMBOL_GPL(xhci_plat_remove); -static int xhci_plat_suspend(struct device *dev) +static int xhci_plat_suspend_common(struct device *dev) { struct usb_hcd *hcd = dev_get_drvdata(dev); struct xhci_hcd *xhci = hcd_to_xhci(hcd); @@ -482,6 +494,25 @@ static int xhci_plat_suspend(struct device *dev) return 0; } +static int xhci_plat_suspend(struct device *dev) +{ + struct usb_hcd *hcd = dev_get_drvdata(dev); + struct xhci_plat_priv *priv = hcd_to_xhci_priv(hcd); + + if (xhci_sideband_check(hcd)) { + priv->sideband_at_suspend = 1; + dev_dbg(dev, "sideband instance active, skip suspend.\n"); + return 0; + } + + return xhci_plat_suspend_common(dev); +} + +static int xhci_plat_freeze(struct device *dev) +{ + return xhci_plat_suspend_common(dev); +} + static int xhci_plat_resume_common(struct device *dev, bool power_lost) { struct usb_hcd *hcd = dev_get_drvdata(dev); @@ -509,6 +540,10 @@ static int xhci_plat_resume_common(struct device *dev, bool power_lost) if (ret) goto disable_clks; + ret = xhci_priv_post_resume_quirk(hcd); + if (ret) + goto disable_clks; + pm_runtime_disable(dev); pm_runtime_set_active(dev); pm_runtime_enable(dev); @@ -526,6 +561,20 @@ disable_clks: static int xhci_plat_resume(struct device *dev) { + struct usb_hcd *hcd = dev_get_drvdata(dev); + struct xhci_plat_priv *priv = hcd_to_xhci_priv(hcd); + + if (priv->sideband_at_suspend) { + priv->sideband_at_suspend = 0; + dev_dbg(dev, "sideband instance active, skip resume.\n"); + return 0; + } + + return xhci_plat_resume_common(dev, false); +} + +static int xhci_plat_thaw(struct device *dev) +{ return xhci_plat_resume_common(dev, false); } @@ -558,9 +607,9 @@ static int __maybe_unused xhci_plat_runtime_resume(struct device *dev) const struct dev_pm_ops xhci_plat_pm_ops = { .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), + .freeze = pm_sleep_ptr(xhci_plat_freeze), + .thaw = pm_sleep_ptr(xhci_plat_thaw), + .poweroff = pm_sleep_ptr(xhci_plat_freeze), .restore = pm_sleep_ptr(xhci_plat_restore), SET_RUNTIME_PM_OPS(xhci_plat_runtime_suspend, diff --git a/drivers/usb/host/xhci-plat.h b/drivers/usb/host/xhci-plat.h index fe4f95e690fa..00751d851831 100644 --- a/drivers/usb/host/xhci-plat.h +++ b/drivers/usb/host/xhci-plat.h @@ -16,10 +16,12 @@ struct xhci_plat_priv { const char *firmware_name; unsigned long long quirks; bool power_lost; + unsigned sideband_at_suspend:1; void (*plat_start)(struct usb_hcd *); int (*init_quirk)(struct usb_hcd *); int (*suspend_quirk)(struct usb_hcd *); int (*resume_quirk)(struct usb_hcd *); + int (*post_resume_quirk)(struct usb_hcd *); }; #define hcd_to_xhci_priv(h) ((struct xhci_plat_priv *)hcd_to_xhci(h)->priv) diff --git a/drivers/usb/host/xhci-rcar-regs.h b/drivers/usb/host/xhci-rcar-regs.h new file mode 100644 index 000000000000..5ecbda858be0 --- /dev/null +++ b/drivers/usb/host/xhci-rcar-regs.h @@ -0,0 +1,49 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef __XHCI_RCAR_H +#define __XHCI_RCAR_H + +/*** Register Offset ***/ +#define RCAR_USB3_AXH_STA 0x104 /* AXI Host Control Status */ +#define RCAR_USB3_INT_ENA 0x224 /* Interrupt Enable */ +#define RCAR_USB3_DL_CTRL 0x250 /* FW Download Control & Status */ +#define RCAR_USB3_FW_DATA0 0x258 /* FW Data0 */ + +#define RCAR_USB3_LCLK 0xa44 /* LCLK Select */ +#define RCAR_USB3_CONF1 0xa48 /* USB3.0 Configuration1 */ +#define RCAR_USB3_CONF2 0xa5c /* USB3.0 Configuration2 */ +#define RCAR_USB3_CONF3 0xaa8 /* USB3.0 Configuration3 */ +#define RCAR_USB3_RX_POL 0xab0 /* USB3.0 RX Polarity */ +#define RCAR_USB3_TX_POL 0xab8 /* USB3.0 TX Polarity */ + +/*** Register Settings ***/ +/* AXI Host Control Status */ +#define RCAR_USB3_AXH_STA_B3_PLL_ACTIVE 0x00010000 +#define RCAR_USB3_AXH_STA_B2_PLL_ACTIVE 0x00000001 +#define RCAR_USB3_AXH_STA_PLL_ACTIVE_MASK (RCAR_USB3_AXH_STA_B3_PLL_ACTIVE | \ + RCAR_USB3_AXH_STA_B2_PLL_ACTIVE) + +/* Interrupt Enable */ +#define RCAR_USB3_INT_XHC_ENA 0x00000001 +#define RCAR_USB3_INT_PME_ENA 0x00000002 +#define RCAR_USB3_INT_HSE_ENA 0x00000004 +#define RCAR_USB3_INT_ENA_VAL (RCAR_USB3_INT_XHC_ENA | \ + RCAR_USB3_INT_PME_ENA | RCAR_USB3_INT_HSE_ENA) + +/* FW Download Control & Status */ +#define RCAR_USB3_DL_CTRL_ENABLE 0x00000001 +#define RCAR_USB3_DL_CTRL_FW_SUCCESS 0x00000010 +#define RCAR_USB3_DL_CTRL_FW_SET_DATA0 0x00000100 + +/* LCLK Select */ +#define RCAR_USB3_LCLK_ENA_VAL 0x01030001 + +/* USB3.0 Configuration */ +#define RCAR_USB3_CONF1_VAL 0x00030204 +#define RCAR_USB3_CONF2_VAL 0x00030300 +#define RCAR_USB3_CONF3_VAL 0x13802007 + +/* USB3.0 Polarity */ +#define RCAR_USB3_RX_POL_VAL BIT(21) +#define RCAR_USB3_TX_POL_VAL BIT(4) + +#endif /* __XHCI_RCAR_H */ diff --git a/drivers/usb/host/xhci-rcar.c b/drivers/usb/host/xhci-rcar.c index 1cc082a3b793..8a993ee21c87 100644 --- a/drivers/usb/host/xhci-rcar.c +++ b/drivers/usb/host/xhci-rcar.c @@ -11,9 +11,12 @@ #include <linux/platform_device.h> #include <linux/of.h> #include <linux/usb/phy.h> +#include <linux/reset.h> #include "xhci.h" #include "xhci-plat.h" +#include "xhci-rcar-regs.h" +#include "xhci-rzg3e-regs.h" #include "xhci-rzv2m.h" #define XHCI_RCAR_FIRMWARE_NAME_V1 "r8a779x_usb3_v1.dlmem" @@ -29,50 +32,6 @@ MODULE_FIRMWARE(XHCI_RCAR_FIRMWARE_NAME_V1); MODULE_FIRMWARE(XHCI_RCAR_FIRMWARE_NAME_V3); -/*** Register Offset ***/ -#define RCAR_USB3_AXH_STA 0x104 /* AXI Host Control Status */ -#define RCAR_USB3_INT_ENA 0x224 /* Interrupt Enable */ -#define RCAR_USB3_DL_CTRL 0x250 /* FW Download Control & Status */ -#define RCAR_USB3_FW_DATA0 0x258 /* FW Data0 */ - -#define RCAR_USB3_LCLK 0xa44 /* LCLK Select */ -#define RCAR_USB3_CONF1 0xa48 /* USB3.0 Configuration1 */ -#define RCAR_USB3_CONF2 0xa5c /* USB3.0 Configuration2 */ -#define RCAR_USB3_CONF3 0xaa8 /* USB3.0 Configuration3 */ -#define RCAR_USB3_RX_POL 0xab0 /* USB3.0 RX Polarity */ -#define RCAR_USB3_TX_POL 0xab8 /* USB3.0 TX Polarity */ - -/*** Register Settings ***/ -/* AXI Host Control Status */ -#define RCAR_USB3_AXH_STA_B3_PLL_ACTIVE 0x00010000 -#define RCAR_USB3_AXH_STA_B2_PLL_ACTIVE 0x00000001 -#define RCAR_USB3_AXH_STA_PLL_ACTIVE_MASK (RCAR_USB3_AXH_STA_B3_PLL_ACTIVE | \ - RCAR_USB3_AXH_STA_B2_PLL_ACTIVE) - -/* Interrupt Enable */ -#define RCAR_USB3_INT_XHC_ENA 0x00000001 -#define RCAR_USB3_INT_PME_ENA 0x00000002 -#define RCAR_USB3_INT_HSE_ENA 0x00000004 -#define RCAR_USB3_INT_ENA_VAL (RCAR_USB3_INT_XHC_ENA | \ - RCAR_USB3_INT_PME_ENA | RCAR_USB3_INT_HSE_ENA) - -/* FW Download Control & Status */ -#define RCAR_USB3_DL_CTRL_ENABLE 0x00000001 -#define RCAR_USB3_DL_CTRL_FW_SUCCESS 0x00000010 -#define RCAR_USB3_DL_CTRL_FW_SET_DATA0 0x00000100 - -/* LCLK Select */ -#define RCAR_USB3_LCLK_ENA_VAL 0x01030001 - -/* USB3.0 Configuration */ -#define RCAR_USB3_CONF1_VAL 0x00030204 -#define RCAR_USB3_CONF2_VAL 0x00030300 -#define RCAR_USB3_CONF3_VAL 0x13802007 - -/* USB3.0 Polarity */ -#define RCAR_USB3_RX_POL_VAL BIT(21) -#define RCAR_USB3_TX_POL_VAL BIT(4) - static void xhci_rcar_start_gen2(struct usb_hcd *hcd) { /* LCLK Select */ @@ -110,6 +69,48 @@ static void xhci_rcar_start(struct usb_hcd *hcd) } } +static void xhci_rzg3e_start(struct usb_hcd *hcd) +{ + u32 int_en; + + if (hcd->regs) { + /* Update the controller initial setting */ + writel(0x03130200, hcd->regs + RZG3E_USB3_HOST_U3P0PIPESC(0)); + writel(0x00160200, hcd->regs + RZG3E_USB3_HOST_U3P0PIPESC(1)); + writel(0x03150000, hcd->regs + RZG3E_USB3_HOST_U3P0PIPESC(2)); + writel(0x03130200, hcd->regs + RZG3E_USB3_HOST_U3P0PIPESC(3)); + writel(0x00180000, hcd->regs + RZG3E_USB3_HOST_U3P0PIPESC(4)); + + /* Interrupt Enable */ + int_en = readl(hcd->regs + RZG3E_USB3_HOST_INTEN); + int_en |= RZG3E_USB3_HOST_INTEN_ENA; + writel(int_en, hcd->regs + RZG3E_USB3_HOST_INTEN); + } +} + +static int xhci_rzg3e_resume(struct usb_hcd *hcd) +{ + struct xhci_hcd *xhci = hcd_to_xhci(hcd); + + return reset_control_deassert(xhci->reset); +} + +static int xhci_rzg3e_post_resume(struct usb_hcd *hcd) +{ + xhci_rzg3e_start(hcd); + + return 0; +} + +static int xhci_rzg3e_suspend(struct usb_hcd *hcd) +{ + struct xhci_hcd *xhci = hcd_to_xhci(hcd); + + reset_control_assert(xhci->reset); + + return 0; +} + static int xhci_rcar_download_firmware(struct usb_hcd *hcd) { struct device *dev = hcd->self.controller; @@ -233,6 +234,14 @@ static const struct xhci_plat_priv xhci_plat_renesas_rzv2m = { .plat_start = xhci_rzv2m_start, }; +static const struct xhci_plat_priv xhci_plat_renesas_rzg3e = { + .quirks = XHCI_NO_64BIT_SUPPORT | XHCI_RESET_ON_RESUME | XHCI_SUSPEND_RESUME_CLKS, + .plat_start = xhci_rzg3e_start, + .suspend_quirk = xhci_rzg3e_suspend, + .resume_quirk = xhci_rzg3e_resume, + .post_resume_quirk = xhci_rzg3e_post_resume, +}; + static const struct of_device_id usb_xhci_of_match[] = { { .compatible = "renesas,xhci-r8a7790", @@ -250,6 +259,9 @@ static const struct of_device_id usb_xhci_of_match[] = { .compatible = "renesas,xhci-r8a7796", .data = &xhci_plat_renesas_rcar_gen3, }, { + .compatible = "renesas,r9a09g047-xhci", + .data = &xhci_plat_renesas_rzg3e, + }, { .compatible = "renesas,rcar-gen2-xhci", .data = &xhci_plat_renesas_rcar_gen2, }, { diff --git a/drivers/usb/host/xhci-ring.c b/drivers/usb/host/xhci-ring.c index 4f8f5aab109d..5bdcf9ab2b99 100644 --- a/drivers/usb/host/xhci-ring.c +++ b/drivers/usb/host/xhci-ring.c @@ -711,7 +711,7 @@ static int xhci_move_dequeue_past_td(struct xhci_hcd *xhci, return -ENODEV; } - hw_dequeue = xhci_get_hw_deq(xhci, dev, ep_index, stream_id); + hw_dequeue = xhci_get_hw_deq(xhci, dev, ep_index, stream_id) & TR_DEQ_PTR_MASK; new_seg = ep_ring->deq_seg; new_deq = ep_ring->dequeue; new_cycle = le32_to_cpu(td->end_trb->generic.field[3]) & TRB_CYCLE; @@ -723,7 +723,7 @@ static int xhci_move_dequeue_past_td(struct xhci_hcd *xhci, */ do { if (!hw_dequeue_found && xhci_trb_virt_to_dma(new_seg, new_deq) - == (dma_addr_t)(hw_dequeue & ~0xf)) { + == (dma_addr_t)hw_dequeue) { hw_dequeue_found = true; if (td_last_trb_found) break; @@ -1066,7 +1066,7 @@ static int xhci_invalidate_cancelled_tds(struct xhci_virt_ep *ep) */ hw_deq = xhci_get_hw_deq(xhci, ep->vdev, ep->ep_index, td->urb->stream_id); - hw_deq &= ~0xf; + hw_deq &= TR_DEQ_PTR_MASK; if (td->cancel_status == TD_HALTED || trb_in_td(td, hw_deq)) { switch (td->cancel_status) { @@ -1156,7 +1156,7 @@ static struct xhci_td *find_halted_td(struct xhci_virt_ep *ep) if (!list_empty(&ep->ring->td_list)) { /* Not streams compatible */ hw_deq = xhci_get_hw_deq(ep->xhci, ep->vdev, ep->ep_index, 0); - hw_deq &= ~0xf; + hw_deq &= TR_DEQ_PTR_MASK; td = list_first_entry(&ep->ring->td_list, struct xhci_td, td_list); if (trb_in_td(td, hw_deq)) return td; @@ -1262,19 +1262,17 @@ reset_done: * Stopped state, but it will soon change to Running. * * Assume this bug on unexpected Stop Endpoint failures. - * Keep retrying until the EP starts and stops again. + * Keep retrying until the EP starts and stops again or + * up to a timeout (a defective HC may never start, or a + * driver bug may cause stopping an already stopped EP). */ + if (time_is_before_jiffies(ep->stop_time + msecs_to_jiffies(100))) + break; fallthrough; case EP_STATE_RUNNING: /* Race, HW handled stop ep cmd before ep was running */ xhci_dbg(xhci, "Stop ep completion ctx error, ctx_state %d\n", GET_EP_CTX_STATE(ep_ctx)); - /* - * Don't retry forever if we guessed wrong or a defective HC never starts - * the EP or says 'Running' but fails the command. We must give back TDs. - */ - if (time_is_before_jiffies(ep->stop_time + msecs_to_jiffies(100))) - break; command = xhci_alloc_command(xhci, false, GFP_ATOMIC); if (!command) { @@ -1481,7 +1479,7 @@ static void xhci_handle_cmd_set_deq(struct xhci_hcd *xhci, int slot_id, u64 deq; /* 4.6.10 deq ptr is written to the stream ctx for streams */ if (ep->ep_state & EP_HAS_STREAMS) { - deq = le64_to_cpu(stream_ctx->stream_ring) & SCTX_DEQ_MASK; + deq = le64_to_cpu(stream_ctx->stream_ring) & TR_DEQ_PTR_MASK; /* * Cadence xHCI controllers store some endpoint state @@ -1497,7 +1495,7 @@ static void xhci_handle_cmd_set_deq(struct xhci_hcd *xhci, int slot_id, stream_ctx->reserved[1] = 0; } } else { - deq = le64_to_cpu(ep_ctx->deq) & ~EP_CTX_CYCLE_MASK; + deq = le64_to_cpu(ep_ctx->deq) & TR_DEQ_PTR_MASK; } xhci_dbg_trace(xhci, trace_xhci_dbg_cancel_urb, "Successful Set TR Deq Ptr cmd, deq = @%08llx", deq); @@ -1987,6 +1985,7 @@ static void xhci_cavium_reset_phy_quirk(struct xhci_hcd *xhci) static void handle_port_status(struct xhci_hcd *xhci, union xhci_trb *event) { + struct xhci_virt_device *vdev = NULL; struct usb_hcd *hcd; u32 port_id; u32 portsc, cmd_reg; @@ -2018,6 +2017,9 @@ static void handle_port_status(struct xhci_hcd *xhci, union xhci_trb *event) goto cleanup; } + if (port->slot_id) + vdev = xhci->devs[port->slot_id]; + /* We might get interrupts after shared_hcd is removed */ if (port->rhub == &xhci->usb3_rhub && xhci->shared_hcd == NULL) { xhci_dbg(xhci, "ignore port event for removed USB3 hcd\n"); @@ -2040,10 +2042,11 @@ static void handle_port_status(struct xhci_hcd *xhci, union xhci_trb *event) usb_hcd_resume_root_hub(hcd); } - if (hcd->speed >= HCD_USB3 && - (portsc & PORT_PLS_MASK) == XDEV_INACTIVE) { - if (port->slot_id && xhci->devs[port->slot_id]) - xhci->devs[port->slot_id]->flags |= VDEV_PORT_ERROR; + if (vdev && (portsc & PORT_PLS_MASK) == XDEV_INACTIVE) { + if (!(portsc & PORT_RESET)) + vdev->flags |= VDEV_PORT_ERROR; + } else if (vdev && portsc & PORT_RC) { + vdev->flags &= ~VDEV_PORT_ERROR; } if ((portsc & PORT_PLC) && (portsc & PORT_PLS_MASK) == XDEV_RESUME) { @@ -2101,7 +2104,7 @@ static void handle_port_status(struct xhci_hcd *xhci, union xhci_trb *event) * so the roothub behavior is consistent with external * USB 3.0 hub behavior. */ - if (port->slot_id && xhci->devs[port->slot_id]) + if (vdev) xhci_ring_device(xhci, port->slot_id); if (bus_state->port_remote_wakeup & (1 << hcd_portnum)) { xhci_test_and_clear_bit(xhci, port, PORT_PLC); @@ -3550,7 +3553,7 @@ static u32 xhci_td_remainder(struct xhci_hcd *xhci, int transferred, if ((xhci->quirks & XHCI_MTK_HOST) && (xhci->hci_version < 0x100)) trb_buff_len = 0; - maxp = usb_endpoint_maxp(&urb->ep->desc); + maxp = xhci_usb_endpoint_maxp(urb->dev, urb->ep); total_packet_count = DIV_ROUND_UP(td_total_len, maxp); /* Queueing functions don't count the current TRB into transferred */ @@ -3567,7 +3570,7 @@ static int xhci_align_td(struct xhci_hcd *xhci, struct urb *urb, u32 enqd_len, u32 new_buff_len; size_t len; - max_pkt = usb_endpoint_maxp(&urb->ep->desc); + max_pkt = xhci_usb_endpoint_maxp(urb->dev, urb->ep); unalign = (enqd_len + *trb_buff_len) % max_pkt; /* we got lucky, last normal TRB data on segment is packet aligned */ @@ -4138,7 +4141,7 @@ static int xhci_queue_isoc_tx(struct xhci_hcd *xhci, gfp_t mem_flags, addr = start_addr + urb->iso_frame_desc[i].offset; td_len = urb->iso_frame_desc[i].length; td_remain_len = td_len; - max_pkt = usb_endpoint_maxp(&urb->ep->desc); + max_pkt = xhci_usb_endpoint_maxp(urb->dev, urb->ep); total_pkt_count = DIV_ROUND_UP(td_len, max_pkt); /* A zero-length transfer still involves at least one packet. */ diff --git a/drivers/usb/host/xhci-rzg3e-regs.h b/drivers/usb/host/xhci-rzg3e-regs.h new file mode 100644 index 000000000000..7a244a47b882 --- /dev/null +++ b/drivers/usb/host/xhci-rzg3e-regs.h @@ -0,0 +1,12 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef __XHCI_RZG3E_H +#define __XHCI_RZG3E_H + +#define RZG3E_USB3_HOST_INTEN 0x1044 /* Interrupt Enable */ +#define RZG3E_USB3_HOST_U3P0PIPESC(x) (0x10c0 + (x) * 4) /* PIPE Status and Control Register */ + +#define RZG3E_USB3_HOST_INTEN_XHC BIT(0) +#define RZG3E_USB3_HOST_INTEN_HSE BIT(2) +#define RZG3E_USB3_HOST_INTEN_ENA (RZG3E_USB3_HOST_INTEN_XHC | RZG3E_USB3_HOST_INTEN_HSE) + +#endif /* __XHCI_RZG3E_H */ diff --git a/drivers/usb/host/xhci-sideband.c b/drivers/usb/host/xhci-sideband.c index d49f9886dd84..a85f62a73313 100644 --- a/drivers/usb/host/xhci-sideband.c +++ b/drivers/usb/host/xhci-sideband.c @@ -73,9 +73,12 @@ err: return NULL; } +/* Caller must hold sb->mutex */ static void __xhci_sideband_remove_endpoint(struct xhci_sideband *sb, struct xhci_virt_ep *ep) { + lockdep_assert_held(&sb->mutex); + /* * Issue a stop endpoint command when an endpoint is removed. * The stop ep cmd handler will handle the ring cleanup. @@ -86,6 +89,25 @@ __xhci_sideband_remove_endpoint(struct xhci_sideband *sb, struct xhci_virt_ep *e sb->eps[ep->ep_index] = NULL; } +/* Caller must hold sb->mutex */ +static void +__xhci_sideband_remove_interrupter(struct xhci_sideband *sb) +{ + struct usb_device *udev; + + lockdep_assert_held(&sb->mutex); + + if (!sb->ir) + return; + + xhci_remove_secondary_interrupter(xhci_to_hcd(sb->xhci), sb->ir); + sb->ir = NULL; + udev = sb->vdev->udev; + + if (udev->state != USB_STATE_NOTATTACHED) + usb_offload_put(udev); +} + /* sideband api functions */ /** @@ -131,14 +153,16 @@ xhci_sideband_add_endpoint(struct xhci_sideband *sb, struct xhci_virt_ep *ep; unsigned int ep_index; - mutex_lock(&sb->mutex); + guard(mutex)(&sb->mutex); + + if (!sb->vdev) + return -ENODEV; + ep_index = xhci_get_endpoint_index(&host_ep->desc); ep = &sb->vdev->eps[ep_index]; - if (ep->ep_state & EP_HAS_STREAMS) { - mutex_unlock(&sb->mutex); + if (ep->ep_state & EP_HAS_STREAMS) return -EINVAL; - } /* * Note, we don't know the DMA mask of the audio DSP device, if its @@ -148,14 +172,11 @@ xhci_sideband_add_endpoint(struct xhci_sideband *sb, * and let this function add the endpoint and allocate the ring buffer * with the smallest common DMA mask */ - if (sb->eps[ep_index] || ep->sideband) { - mutex_unlock(&sb->mutex); + if (sb->eps[ep_index] || ep->sideband) return -EBUSY; - } ep->sideband = sb; sb->eps[ep_index] = ep; - mutex_unlock(&sb->mutex); return 0; } @@ -180,18 +201,16 @@ xhci_sideband_remove_endpoint(struct xhci_sideband *sb, struct xhci_virt_ep *ep; unsigned int ep_index; - mutex_lock(&sb->mutex); + guard(mutex)(&sb->mutex); + ep_index = xhci_get_endpoint_index(&host_ep->desc); ep = sb->eps[ep_index]; - if (!ep || !ep->sideband || ep->sideband != sb) { - mutex_unlock(&sb->mutex); + if (!ep || !ep->sideband || ep->sideband != sb) return -ENODEV; - } __xhci_sideband_remove_endpoint(sb, ep); xhci_initialize_ring_info(ep->ring); - mutex_unlock(&sb->mutex); return 0; } @@ -267,6 +286,31 @@ xhci_sideband_get_event_buffer(struct xhci_sideband *sb) EXPORT_SYMBOL_GPL(xhci_sideband_get_event_buffer); /** + * xhci_sideband_check - check the existence of active sidebands + * @hcd: the host controller driver associated with the target host controller + * + * Allow other drivers, such as usb controller driver, to check if there are + * any sideband activity on the host controller. This information could be used + * for power management or other forms of resource management. The caller should + * ensure downstream usb devices are all either suspended or marked as + * "offload_at_suspend" to ensure the correctness of the return value. + * + * Returns true on any active sideband existence, false otherwise. + */ +bool xhci_sideband_check(struct usb_hcd *hcd) +{ + struct usb_device *udev = hcd->self.root_hub; + bool active; + + usb_lock_device(udev); + active = usb_offload_check(udev); + usb_unlock_device(udev); + + return active; +} +EXPORT_SYMBOL_GPL(xhci_sideband_check); + +/** * xhci_sideband_create_interrupter - creates a new interrupter for this sideband * @sb: sideband instance for this usb device * @num_seg: number of event ring segments to allocate @@ -286,28 +330,29 @@ xhci_sideband_create_interrupter(struct xhci_sideband *sb, int num_seg, bool ip_autoclear, u32 imod_interval, int intr_num) { int ret = 0; + struct usb_device *udev; if (!sb || !sb->xhci) return -ENODEV; - mutex_lock(&sb->mutex); - if (sb->ir) { - ret = -EBUSY; - goto out; - } + guard(mutex)(&sb->mutex); + + if (!sb->vdev) + return -ENODEV; + + if (sb->ir) + return -EBUSY; sb->ir = xhci_create_secondary_interrupter(xhci_to_hcd(sb->xhci), num_seg, imod_interval, intr_num); - if (!sb->ir) { - ret = -ENOMEM; - goto out; - } + if (!sb->ir) + return -ENOMEM; - sb->ir->ip_autoclear = ip_autoclear; + udev = sb->vdev->udev; + ret = usb_offload_get(udev); -out: - mutex_unlock(&sb->mutex); + sb->ir->ip_autoclear = ip_autoclear; return ret; } @@ -323,14 +368,12 @@ EXPORT_SYMBOL_GPL(xhci_sideband_create_interrupter); void xhci_sideband_remove_interrupter(struct xhci_sideband *sb) { - if (!sb || !sb->ir) + if (!sb) return; - mutex_lock(&sb->mutex); - xhci_remove_secondary_interrupter(xhci_to_hcd(sb->xhci), sb->ir); + guard(mutex)(&sb->mutex); - sb->ir = NULL; - mutex_unlock(&sb->mutex); + __xhci_sideband_remove_interrupter(sb); } EXPORT_SYMBOL_GPL(xhci_sideband_remove_interrupter); @@ -429,6 +472,7 @@ EXPORT_SYMBOL_GPL(xhci_sideband_register); void xhci_sideband_unregister(struct xhci_sideband *sb) { + struct xhci_virt_device *vdev; struct xhci_hcd *xhci; int i; @@ -437,17 +481,23 @@ xhci_sideband_unregister(struct xhci_sideband *sb) xhci = sb->xhci; - mutex_lock(&sb->mutex); - for (i = 0; i < EP_CTX_PER_DEV; i++) - if (sb->eps[i]) - __xhci_sideband_remove_endpoint(sb, sb->eps[i]); - mutex_unlock(&sb->mutex); + scoped_guard(mutex, &sb->mutex) { + vdev = sb->vdev; + if (!vdev) + return; + + for (i = 0; i < EP_CTX_PER_DEV; i++) + if (sb->eps[i]) + __xhci_sideband_remove_endpoint(sb, sb->eps[i]); - xhci_sideband_remove_interrupter(sb); + __xhci_sideband_remove_interrupter(sb); + + sb->vdev = NULL; + } spin_lock_irq(&xhci->lock); sb->xhci = NULL; - sb->vdev->sideband = NULL; + vdev->sideband = NULL; spin_unlock_irq(&xhci->lock); kfree(sb); diff --git a/drivers/usb/host/xhci-tegra.c b/drivers/usb/host/xhci-tegra.c index 0c7af44d4dae..5255b1002893 100644 --- a/drivers/usb/host/xhci-tegra.c +++ b/drivers/usb/host/xhci-tegra.c @@ -155,6 +155,8 @@ #define FW_IOCTL_TYPE_SHIFT 24 #define FW_IOCTL_CFGTBL_READ 17 +#define WAKE_IRQ_START_INDEX 2 + struct tegra_xusb_fw_header { __le32 boot_loadaddr_in_imem; __le32 boot_codedfi_offset; @@ -228,6 +230,7 @@ struct tegra_xusb_soc { unsigned int num_supplies; const struct tegra_xusb_phy_type *phy_types; unsigned int num_types; + unsigned int max_num_wakes; const struct tegra_xusb_context_soc *context; struct { @@ -263,6 +266,7 @@ struct tegra_xusb { int xhci_irq; int mbox_irq; int padctl_irq; + int *wake_irqs; void __iomem *ipfs_base; void __iomem *fpci_base; @@ -313,6 +317,7 @@ struct tegra_xusb { bool suspended; struct tegra_xusb_context context; u8 lp0_utmi_pad_mask; + int num_wakes; }; static struct hc_driver __read_mostly tegra_xhci_hc_driver; @@ -1482,7 +1487,7 @@ static int tegra_xhci_id_notify(struct notifier_block *nb, tegra->otg_usb2_port = tegra_xusb_get_usb2_port(tegra, usbphy); - tegra->host_mode = (usbphy->last_event == USB_EVENT_ID) ? true : false; + tegra->host_mode = usbphy->last_event == USB_EVENT_ID; schedule_work(&tegra->id_work); @@ -1537,6 +1542,58 @@ static void tegra_xusb_deinit_usb_phy(struct tegra_xusb *tegra) otg_set_host(tegra->usbphy[i]->otg, NULL); } +static int tegra_xusb_setup_wakeup(struct platform_device *pdev, struct tegra_xusb *tegra) +{ + unsigned int i; + + if (tegra->soc->max_num_wakes == 0) + return 0; + + tegra->wake_irqs = devm_kcalloc(tegra->dev, + tegra->soc->max_num_wakes, + sizeof(*tegra->wake_irqs), GFP_KERNEL); + if (!tegra->wake_irqs) + return -ENOMEM; + + /* + * USB wake events are independent of each other, so it is not necessary for a platform + * to utilize all wake-up events supported for a given device. The USB host can operate + * even if wake-up events are not defined or fail to be configured. Therefore, we only + * return critical errors, such as -ENOMEM. + */ + for (i = 0; i < tegra->soc->max_num_wakes; i++) { + struct irq_data *data; + + tegra->wake_irqs[i] = platform_get_irq(pdev, i + WAKE_IRQ_START_INDEX); + if (tegra->wake_irqs[i] < 0) + break; + + data = irq_get_irq_data(tegra->wake_irqs[i]); + if (!data) { + dev_warn(tegra->dev, "get wake event %d irq data fail\n", i); + irq_dispose_mapping(tegra->wake_irqs[i]); + break; + } + + irq_set_irq_type(tegra->wake_irqs[i], irqd_get_trigger_type(data)); + } + + tegra->num_wakes = i; + dev_dbg(tegra->dev, "setup %d wake events\n", tegra->num_wakes); + + return 0; +} + +static void tegra_xusb_dispose_wake(struct tegra_xusb *tegra) +{ + unsigned int i; + + for (i = 0; i < tegra->num_wakes; i++) + irq_dispose_mapping(tegra->wake_irqs[i]); + + tegra->num_wakes = 0; +} + static int tegra_xusb_probe(struct platform_device *pdev) { struct tegra_xusb *tegra; @@ -1587,9 +1644,15 @@ static int tegra_xusb_probe(struct platform_device *pdev) if (tegra->mbox_irq < 0) return tegra->mbox_irq; + err = tegra_xusb_setup_wakeup(pdev, tegra); + if (err) + return err; + tegra->padctl = tegra_xusb_padctl_get(&pdev->dev); - if (IS_ERR(tegra->padctl)) - return PTR_ERR(tegra->padctl); + if (IS_ERR(tegra->padctl)) { + err = PTR_ERR(tegra->padctl); + goto dispose_wake; + } np = of_parse_phandle(pdev->dev.of_node, "nvidia,xusb-padctl", 0); if (!np) { @@ -1913,6 +1976,8 @@ put_powerdomains: put_padctl: of_node_put(np); tegra_xusb_padctl_put(tegra->padctl); +dispose_wake: + tegra_xusb_dispose_wake(tegra); return err; } @@ -1945,6 +2010,8 @@ static void tegra_xusb_remove(struct platform_device *pdev) if (tegra->padctl_irq) pm_runtime_disable(&pdev->dev); + tegra_xusb_dispose_wake(tegra); + pm_runtime_put(&pdev->dev); tegra_xusb_disable(tegra); @@ -2355,8 +2422,13 @@ out: pm_runtime_disable(dev); if (device_may_wakeup(dev)) { + unsigned int i; + if (enable_irq_wake(tegra->padctl_irq)) dev_err(dev, "failed to enable padctl wakes\n"); + + for (i = 0; i < tegra->num_wakes; i++) + enable_irq_wake(tegra->wake_irqs[i]); } } @@ -2384,8 +2456,13 @@ static __maybe_unused int tegra_xusb_resume(struct device *dev) } if (device_may_wakeup(dev)) { + unsigned int i; + if (disable_irq_wake(tegra->padctl_irq)) dev_err(dev, "failed to disable padctl wakes\n"); + + for (i = 0; i < tegra->num_wakes; i++) + disable_irq_wake(tegra->wake_irqs[i]); } tegra->suspended = false; mutex_unlock(&tegra->lock); @@ -2636,6 +2713,7 @@ static const struct tegra_xusb_soc tegra234_soc = { .num_supplies = ARRAY_SIZE(tegra194_supply_names), .phy_types = tegra194_phy_types, .num_types = ARRAY_SIZE(tegra194_phy_types), + .max_num_wakes = 7, .context = &tegra186_xusb_context, .ports = { .usb3 = { .offset = 0, .count = 4, }, diff --git a/drivers/usb/host/xhci-trace.h b/drivers/usb/host/xhci-trace.h index bfb5c5c17012..9abc904f1749 100644 --- a/drivers/usb/host/xhci-trace.h +++ b/drivers/usb/host/xhci-trace.h @@ -541,23 +541,23 @@ DEFINE_EVENT(xhci_log_ring, xhci_inc_deq, ); DECLARE_EVENT_CLASS(xhci_log_portsc, - TP_PROTO(struct xhci_port *port, u32 portsc), - TP_ARGS(port, portsc), - TP_STRUCT__entry( - __field(u32, busnum) - __field(u32, portnum) - __field(u32, portsc) - ), - TP_fast_assign( - __entry->busnum = port->rhub->hcd->self.busnum; - __entry->portnum = port->hcd_portnum; - __entry->portsc = portsc; - ), - TP_printk("port %d-%d: %s", - __entry->busnum, - __entry->portnum, - xhci_decode_portsc(__get_buf(XHCI_MSG_MAX), __entry->portsc) - ) + TP_PROTO(struct xhci_port *port, u32 portsc), + TP_ARGS(port, portsc), + TP_STRUCT__entry( + __field(u32, busnum) + __field(u32, portnum) + __field(u32, portsc) + ), + TP_fast_assign( + __entry->busnum = port->rhub->hcd->self.busnum; + __entry->portnum = port->hcd_portnum + 1; + __entry->portsc = portsc; + ), + TP_printk("port %d-%d: %s", + __entry->busnum, + __entry->portnum, + xhci_decode_portsc(__get_buf(XHCI_MSG_MAX), __entry->portsc) + ) ); DEFINE_EVENT(xhci_log_portsc, xhci_handle_port_status, diff --git a/drivers/usb/host/xhci.c b/drivers/usb/host/xhci.c index 742c23826e17..a148a1280126 100644 --- a/drivers/usb/host/xhci.c +++ b/drivers/usb/host/xhci.c @@ -1336,7 +1336,7 @@ static bool xhci_urb_temp_buffer_required(struct usb_hcd *hcd, struct scatterlist *tail_sg; tail_sg = urb->sg; - max_pkt = usb_endpoint_maxp(&urb->ep->desc); + max_pkt = xhci_usb_endpoint_maxp(urb->dev, urb->ep); if (!urb->num_sgs) return ret; @@ -2924,6 +2924,20 @@ out: } EXPORT_SYMBOL_GPL(xhci_stop_endpoint_sync); +/* + * xhci_usb_endpoint_maxp - get endpoint max packet size + * @host_ep: USB host endpoint to be checked + * + * Returns max packet from the correct descriptor + */ +int xhci_usb_endpoint_maxp(struct usb_device *udev, + struct usb_host_endpoint *host_ep) +{ + if (usb_endpoint_is_hs_isoc_double(udev, host_ep)) + return le16_to_cpu(host_ep->eusb2_isoc_ep_comp.wMaxPacketSize); + return usb_endpoint_maxp(&host_ep->desc); +} + /* Issue a configure endpoint command or evaluate context command * and wait for it to finish. */ @@ -3993,6 +4007,7 @@ static int xhci_discover_or_reset_device(struct usb_hcd *hcd, xhci_get_slot_state(xhci, virt_dev->out_ctx)); xhci_dbg(xhci, "Not freeing device rings.\n"); /* Don't treat this as an error. May change my mind later. */ + virt_dev->flags = 0; ret = 0; goto command_cleanup; case COMP_SUCCESS: diff --git a/drivers/usb/host/xhci.h b/drivers/usb/host/xhci.h index 85d5b964bf1e..58a51f09cceb 100644 --- a/drivers/usb/host/xhci.h +++ b/drivers/usb/host/xhci.h @@ -500,7 +500,8 @@ struct xhci_ep_ctx { /* deq bitmasks */ #define EP_CTX_CYCLE_MASK (1 << 0) -#define SCTX_DEQ_MASK (~0xfL) +/* bits 63:4 - TR Dequeue Pointer */ +#define TR_DEQ_PTR_MASK GENMASK_ULL(63, 4) /** @@ -1958,6 +1959,8 @@ void xhci_update_erst_dequeue(struct xhci_hcd *xhci, struct xhci_interrupter *ir, bool clear_ehb); void xhci_add_interrupter(struct xhci_hcd *xhci, unsigned int intr_num); +int xhci_usb_endpoint_maxp(struct usb_device *udev, + struct usb_host_endpoint *host_ep); /* xHCI roothub code */ void xhci_set_link_state(struct xhci_hcd *xhci, struct xhci_port *port, |