diff options
Diffstat (limited to 'drivers/usb/host/xhci-hub.c')
| -rw-r--r-- | drivers/usb/host/xhci-hub.c | 508 |
1 files changed, 269 insertions, 239 deletions
diff --git a/drivers/usb/host/xhci-hub.c b/drivers/usb/host/xhci-hub.c index df3522dab31b..04cc3d681495 100644 --- a/drivers/usb/host/xhci-hub.c +++ b/drivers/usb/host/xhci-hub.c @@ -10,8 +10,9 @@ #include <linux/slab.h> -#include <asm/unaligned.h> +#include <linux/unaligned.h> #include <linux/bitfield.h> +#include <linux/pci.h> #include "xhci.h" #include "xhci-trace.h" @@ -109,7 +110,7 @@ static int xhci_create_usb3x_bos_desc(struct xhci_hcd *xhci, char *buf, ss_cap->bU2DevExitLat = 0; /* set later */ reg = readl(&xhci->cap_regs->hcc_params); - if (HCC_LTC(reg)) + if (reg & HCC_LTC) ss_cap->bmAttributes |= USB_LTM_SUPPORT; if ((xhci->quirks & XHCI_LPM_SUPPORT)) { @@ -262,7 +263,7 @@ static void xhci_common_hub_descriptor(struct xhci_hcd *xhci, desc->bNbrPorts = ports; temp = 0; /* Bits 1:0 - support per-port power switching, or power always on */ - if (HCC_PPC(xhci->hcc_params)) + if (xhci->hcc_params & HCC_PPC) temp |= HUB_CHAR_INDV_PORT_LPSM; else temp |= HUB_CHAR_NO_LPSM; @@ -298,7 +299,7 @@ static void xhci_usb2_hub_descriptor(struct usb_hcd *hcd, struct xhci_hcd *xhci, */ memset(port_removable, 0, sizeof(port_removable)); for (i = 0; i < ports; i++) { - portsc = readl(rhub->ports[i]->addr); + portsc = xhci_portsc_readl(rhub->ports[i]); /* If a device is removable, PORTSC reports a 0, same as in the * hub descriptor DeviceRemovable bits. */ @@ -355,7 +356,7 @@ static void xhci_usb3_hub_descriptor(struct usb_hcd *hcd, struct xhci_hcd *xhci, port_removable = 0; /* bit 0 is reserved, bit 1 is for port 1, etc. */ for (i = 0; i < ports; i++) { - portsc = readl(rhub->ports[i]->addr); + portsc = xhci_portsc_readl(rhub->ports[i]); if (portsc & PORT_DEV_REMOVE) port_removable |= 1 << (i + 1); } @@ -426,45 +427,27 @@ static unsigned int xhci_port_speed(unsigned int port_status) */ #define XHCI_PORT_RZ ((1<<2) | (1<<24) | (0xf<<28)) -/* +/** + * xhci_port_state_to_neutral() - Clean up read portsc value back into writeable + * @state: u32 port value read from portsc register to be cleanup up + * * Given a port state, this function returns a value that would result in the * port being in the same state, if the value was written to the port status * control register. * Save Read Only (RO) bits and save read/write bits where * writing a 0 clears the bit and writing a 1 sets the bit (RWS). * For all other types (RW1S, RW1CS, RW, and RZ), writing a '0' has no effect. + * + * Return: u32 value that can be written back to portsc register without + * changing port state. */ + u32 xhci_port_state_to_neutral(u32 state) { /* Save read-only status and port state */ return (state & XHCI_PORT_RO) | (state & XHCI_PORT_RWS); } - -/* - * find slot id based on port number. - * @port: The one-based port number from one of the two split roothubs. - */ -int xhci_find_slot_id_by_port(struct usb_hcd *hcd, struct xhci_hcd *xhci, - u16 port) -{ - int slot_id; - int i; - enum usb_device_speed speed; - - slot_id = 0; - for (i = 0; i < MAX_HC_SLOTS; i++) { - if (!xhci->devs[i] || !xhci->devs[i]->udev) - continue; - speed = xhci->devs[i]->udev->speed; - if (((speed >= USB_SPEED_SUPER) == (hcd->speed >= HCD_USB3)) - && xhci->devs[i]->fake_port == port) { - slot_id = i; - break; - } - } - - return slot_id; -} +EXPORT_SYMBOL_GPL(xhci_port_state_to_neutral); /* * Stop device @@ -564,13 +547,16 @@ void xhci_ring_device(struct xhci_hcd *xhci, int slot_id) return; } -static void xhci_disable_port(struct usb_hcd *hcd, struct xhci_hcd *xhci, - u16 wIndex, __le32 __iomem *addr, u32 port_status) +static void xhci_disable_port(struct xhci_hcd *xhci, struct xhci_port *port) { + struct usb_hcd *hcd; + u32 portsc; + + hcd = port->rhub->hcd; + /* Don't allow the USB core to disable SuperSpeed ports. */ if (hcd->speed >= HCD_USB3) { - xhci_dbg(xhci, "Ignoring request to disable " - "SuperSpeed port.\n"); + xhci_dbg(xhci, "Ignoring request to disable SuperSpeed port.\n"); return; } @@ -580,15 +566,19 @@ static void xhci_disable_port(struct usb_hcd *hcd, struct xhci_hcd *xhci, return; } + portsc = xhci_portsc_readl(port); + portsc = xhci_port_state_to_neutral(portsc); + /* Write 1 to disable the port */ - writel(port_status | PORT_PE, addr); - port_status = readl(addr); + xhci_portsc_writel(port, portsc | PORT_PE); + + portsc = xhci_portsc_readl(port); xhci_dbg(xhci, "disable port %d-%d, portsc: 0x%x\n", - hcd->self.busnum, wIndex + 1, port_status); + hcd->self.busnum, port->hcd_portnum + 1, portsc); } static void xhci_clear_port_change_bit(struct xhci_hcd *xhci, u16 wValue, - u16 wIndex, __le32 __iomem *addr, u32 port_status) + u16 wIndex, struct xhci_port *port, u32 port_status) { char *port_change_bit; u32 status; @@ -631,8 +621,8 @@ static void xhci_clear_port_change_bit(struct xhci_hcd *xhci, u16 wValue, return; } /* Change bits are all write 1 to clear */ - writel(port_status | status, addr); - port_status = readl(addr); + xhci_portsc_writel(port, port_status | status); + port_status = xhci_portsc_readl(port); xhci_dbg(xhci, "clear port%d %s change, portsc: 0x%x\n", wIndex + 1, port_change_bit, port_status); @@ -652,38 +642,36 @@ struct xhci_hub *xhci_get_rhub(struct usb_hcd *hcd) * It will release and re-aquire the lock while calling ACPI * method. */ -static void xhci_set_port_power(struct xhci_hcd *xhci, struct usb_hcd *hcd, - u16 index, bool on, unsigned long *flags) +static void xhci_set_port_power(struct xhci_hcd *xhci, struct xhci_port *port, + bool on, unsigned long *flags) __must_hold(&xhci->lock) { - struct xhci_hub *rhub; - struct xhci_port *port; + struct usb_hcd *hcd; u32 temp; - rhub = xhci_get_rhub(hcd); - port = rhub->ports[index]; - temp = readl(port->addr); + hcd = port->rhub->hcd; + temp = xhci_portsc_readl(port); xhci_dbg(xhci, "set port power %d-%d %s, portsc: 0x%x\n", - hcd->self.busnum, index + 1, on ? "ON" : "OFF", temp); + hcd->self.busnum, port->hcd_portnum + 1, on ? "ON" : "OFF", temp); temp = xhci_port_state_to_neutral(temp); if (on) { /* Power on */ - writel(temp | PORT_POWER, port->addr); - readl(port->addr); + xhci_portsc_writel(port, temp | PORT_POWER); + xhci_portsc_readl(port); } else { /* Power off */ - writel(temp & ~PORT_POWER, port->addr); + xhci_portsc_writel(port, temp & ~PORT_POWER); } spin_unlock_irqrestore(&xhci->lock, *flags); temp = usb_acpi_power_manageable(hcd->self.root_hub, - index); + port->hcd_portnum); if (temp) usb_acpi_set_power_state(hcd->self.root_hub, - index, on); + port->hcd_portnum, on); spin_lock_irqsave(&xhci->lock, *flags); } @@ -695,9 +683,9 @@ static void xhci_port_set_test_mode(struct xhci_hcd *xhci, /* xhci only supports test mode for usb2 ports */ port = xhci->usb2_rhub.ports[wIndex]; - temp = readl(port->addr + PORTPMSC); + temp = readl(&port->port_reg->portpmsc); temp |= test_mode << PORT_TEST_MODE_SHIFT; - writel(temp, port->addr + PORTPMSC); + writel(temp, &port->port_reg->portpmsc); xhci->test_mode = test_mode; if (test_mode == USB_TEST_FORCE_ENABLE) xhci_start(xhci); @@ -712,12 +700,11 @@ static int xhci_enter_test_mode(struct xhci_hcd *xhci, /* Disable all Device Slots */ xhci_dbg(xhci, "Disable all slots\n"); spin_unlock_irqrestore(&xhci->lock, *flags); - for (i = 1; i <= HCS_MAX_SLOTS(xhci->hcs_params1); i++) { + for (i = 1; i <= xhci->max_slots; i++) { if (!xhci->devs[i]) continue; - retval = xhci_disable_slot(xhci, i); - xhci_free_virt_device(xhci, i); + retval = xhci_disable_and_free_slot(xhci, i); if (retval) xhci_err(xhci, "Failed to disable slot %d, %d. Enter test mode anyway\n", i, retval); @@ -727,10 +714,10 @@ static int xhci_enter_test_mode(struct xhci_hcd *xhci, xhci_dbg(xhci, "Disable all port (PP = 0)\n"); /* Power off USB3 ports*/ for (i = 0; i < xhci->usb3_rhub.num_ports; i++) - xhci_set_port_power(xhci, xhci->shared_hcd, i, false, flags); + xhci_set_port_power(xhci, xhci->usb3_rhub.ports[i], false, flags); /* Power off USB2 ports*/ for (i = 0; i < xhci->usb2_rhub.num_ports; i++) - xhci_set_port_power(xhci, xhci->main_hcd, i, false, flags); + xhci_set_port_power(xhci, xhci->usb2_rhub.ports[i], false, flags); /* Stop the controller */ xhci_dbg(xhci, "Stop controller\n"); retval = xhci_halt(xhci); @@ -762,7 +749,50 @@ static int xhci_exit_test_mode(struct xhci_hcd *xhci) } pm_runtime_allow(xhci_to_hcd(xhci)->self.controller); xhci->test_mode = 0; - return xhci_reset(xhci); + return xhci_reset(xhci, XHCI_RESET_SHORT_USEC); +} + +/** + * xhci_port_is_tunneled() - Check if USB3 connection is tunneled over USB4 + * @xhci: xhci host controller + * @port: USB3 port to be checked. + * + * Some hosts can detect if a USB3 connection is native USB3 or tunneled over + * USB4. Intel hosts expose this via vendor specific extended capability 206 + * eSS PORT registers TUNEN (tunnel enabled) bit. + * + * A USB3 device must be connected to the port to detect the tunnel. + * + * Return: link tunnel mode enum, USB_LINK_UNKNOWN if host is incapable of + * detecting USB3 over USB4 tunnels. USB_LINK_NATIVE or USB_LINK_TUNNELED + * otherwise. + */ +enum usb_link_tunnel_mode xhci_port_is_tunneled(struct xhci_hcd *xhci, + struct xhci_port *port) +{ + struct usb_hcd *hcd; + void __iomem *base; + u32 offset; + + /* Don't try and probe this capability for non-Intel hosts */ + hcd = xhci_to_hcd(xhci); + if (!dev_is_pci(hcd->self.controller) || + to_pci_dev(hcd->self.controller)->vendor != PCI_VENDOR_ID_INTEL) + return USB_LINK_UNKNOWN; + + base = &xhci->cap_regs->hc_capbase; + offset = xhci_find_next_ext_cap(base, 0, XHCI_EXT_CAPS_INTEL_SPR_SHADOW); + + if (offset && offset <= XHCI_INTEL_SPR_ESS_PORT_OFFSET) { + offset = XHCI_INTEL_SPR_ESS_PORT_OFFSET + port->hcd_portnum * 0x20; + + if (readl(base + offset) & XHCI_INTEL_SPR_TUNEN) + return USB_LINK_TUNNELED; + else + return USB_LINK_NATIVE; + } + + return USB_LINK_UNKNOWN; } void xhci_set_link_state(struct xhci_hcd *xhci, struct xhci_port *port, @@ -771,11 +801,11 @@ void xhci_set_link_state(struct xhci_hcd *xhci, struct xhci_port *port, u32 temp; u32 portsc; - portsc = readl(port->addr); + portsc = xhci_portsc_readl(port); temp = xhci_port_state_to_neutral(portsc); temp &= ~PORT_PLS_MASK; temp |= PORT_LINK_STROBE | link_state; - writel(temp, port->addr); + xhci_portsc_writel(port, temp); xhci_dbg(xhci, "Set port %d-%d link state, portsc: 0x%x, write 0x%x", port->rhub->hcd->self.busnum, port->hcd_portnum + 1, @@ -787,7 +817,7 @@ static void xhci_set_remote_wake_mask(struct xhci_hcd *xhci, { u32 temp; - temp = readl(port->addr); + temp = xhci_portsc_readl(port); temp = xhci_port_state_to_neutral(temp); if (wake_mask & USB_PORT_FEAT_REMOTE_WAKE_CONNECT) @@ -805,7 +835,7 @@ static void xhci_set_remote_wake_mask(struct xhci_hcd *xhci, else temp &= ~PORT_WKOC_E; - writel(temp, port->addr); + xhci_portsc_writel(port, temp); } /* Test and clear port RWC bit */ @@ -814,11 +844,11 @@ void xhci_test_and_clear_bit(struct xhci_hcd *xhci, struct xhci_port *port, { u32 temp; - temp = readl(port->addr); + temp = xhci_portsc_readl(port); if (temp & port_bit) { temp = xhci_port_state_to_neutral(temp); temp |= port_bit; - writel(temp, port->addr); + xhci_portsc_writel(port, temp); } } @@ -895,7 +925,7 @@ static void xhci_del_comp_mod_timer(struct xhci_hcd *xhci, u32 status, if ((xhci->port_status_u0 != all_ports_seen_u0) && port_in_u0) { xhci->port_status_u0 |= 1 << wIndex; if (xhci->port_status_u0 == all_ports_seen_u0) { - del_timer_sync(&xhci->comp_mode_recovery_timer); + timer_delete_sync(&xhci->comp_mode_recovery_timer); xhci_dbg_trace(xhci, trace_xhci_dbg_quirks, "All USB3 ports have entered U0 already!"); xhci_dbg_trace(xhci, trace_xhci_dbg_quirks, @@ -905,13 +935,12 @@ static void xhci_del_comp_mod_timer(struct xhci_hcd *xhci, u32 status, } static int xhci_handle_usb2_port_link_resume(struct xhci_port *port, - u32 *status, u32 portsc, + u32 portsc, unsigned long *flags) { struct xhci_bus_state *bus_state; struct xhci_hcd *xhci; struct usb_hcd *hcd; - int slot_id; u32 wIndex; hcd = port->rhub->hcd; @@ -920,14 +949,13 @@ static int xhci_handle_usb2_port_link_resume(struct xhci_port *port, wIndex = port->hcd_portnum; if ((portsc & PORT_RESET) || !(portsc & PORT_PE)) { - *status = 0xffffffff; return -EINVAL; } /* did port event handler already start resume timing? */ - if (!bus_state->resume_done[wIndex]) { - /* If not, maybe we are in a host initated resume? */ + if (!port->resume_timestamp) { + /* If not, maybe we are in a host initiated resume? */ if (test_bit(wIndex, &bus_state->resuming_ports)) { - /* Host initated resume doesn't time the resume + /* Host initiated resume doesn't time the resume * signalling using resume_done[]. * It manually sets RESUME state, sleeps 20ms * and sets U0 state. This should probably be @@ -941,61 +969,56 @@ static int xhci_handle_usb2_port_link_resume(struct xhci_port *port, msecs_to_jiffies(USB_RESUME_TIMEOUT); set_bit(wIndex, &bus_state->resuming_ports); - bus_state->resume_done[wIndex] = timeout; + port->resume_timestamp = timeout; mod_timer(&hcd->rh_timer, timeout); usb_hcd_start_port_resume(&hcd->self, wIndex); } /* Has resume been signalled for USB_RESUME_TIME yet? */ - } else if (time_after_eq(jiffies, bus_state->resume_done[wIndex])) { + } else if (time_after_eq(jiffies, port->resume_timestamp)) { int time_left; xhci_dbg(xhci, "resume USB2 port %d-%d\n", hcd->self.busnum, wIndex + 1); - bus_state->resume_done[wIndex] = 0; + port->resume_timestamp = 0; clear_bit(wIndex, &bus_state->resuming_ports); - set_bit(wIndex, &bus_state->rexit_ports); + reinit_completion(&port->rexit_done); + port->rexit_active = true; xhci_test_and_clear_bit(xhci, port, PORT_PLC); xhci_set_link_state(xhci, port, XDEV_U0); spin_unlock_irqrestore(&xhci->lock, *flags); time_left = wait_for_completion_timeout( - &bus_state->rexit_done[wIndex], + &port->rexit_done, msecs_to_jiffies(XHCI_MAX_REXIT_TIMEOUT_MS)); spin_lock_irqsave(&xhci->lock, *flags); if (time_left) { - slot_id = xhci_find_slot_id_by_port(hcd, xhci, - wIndex + 1); - if (!slot_id) { + if (!port->slot_id) { xhci_dbg(xhci, "slot_id is zero\n"); - *status = 0xffffffff; return -ENODEV; } - xhci_ring_device(xhci, slot_id); + xhci_ring_device(xhci, port->slot_id); } else { - int port_status = readl(port->addr); + int port_status = xhci_portsc_readl(port); xhci_warn(xhci, "Port resume timed out, port %d-%d: 0x%x\n", hcd->self.busnum, wIndex + 1, port_status); - *status |= USB_PORT_STAT_SUSPEND; - clear_bit(wIndex, &bus_state->rexit_ports); + /* + * keep rexit_active set if U0 transition failed so we + * know to report PORT_STAT_SUSPEND status back to + * usbcore. It will be cleared later once the port is + * out of RESUME/U3 state + */ } usb_hcd_end_port_resume(&hcd->self, wIndex); bus_state->port_c_suspend |= 1 << wIndex; bus_state->suspended_ports &= ~(1 << wIndex); - } else { - /* - * The resume has been signaling for less than - * USB_RESUME_TIME. Report the port status as SUSPEND, - * let the usbcore check port status again and clear - * resume signaling later. - */ - *status |= USB_PORT_STAT_SUSPEND; } + return 0; } @@ -1047,19 +1070,19 @@ static void xhci_get_usb3_port_status(struct xhci_port *port, u32 *status, *status |= USB_PORT_STAT_C_CONFIG_ERROR << 16; /* USB3 specific wPortStatus bits */ - if (portsc & PORT_POWER) { + if (portsc & PORT_POWER) *status |= USB_SS_PORT_STAT_POWER; - /* link state handling */ - if (link_state == XDEV_U0) - bus_state->suspended_ports &= ~(1 << portnum); - } - /* remote wake resume signaling complete */ - if (bus_state->port_remote_wakeup & (1 << portnum) && + /* no longer suspended or resuming */ + if (link_state != XDEV_U3 && link_state != XDEV_RESUME && link_state != XDEV_RECOVERY) { - bus_state->port_remote_wakeup &= ~(1 << portnum); - usb_hcd_end_port_resume(&hcd->self, portnum); + /* remote wake resume signaling complete */ + if (bus_state->port_remote_wakeup & (1 << portnum)) { + bus_state->port_remote_wakeup &= ~(1 << portnum); + usb_hcd_end_port_resume(&hcd->self, portnum); + } + bus_state->suspended_ports &= ~(1 << portnum); } xhci_hub_report_usb3_link_state(xhci, status, portsc); @@ -1072,7 +1095,7 @@ static void xhci_get_usb2_port_status(struct xhci_port *port, u32 *status, struct xhci_bus_state *bus_state; u32 link_state; u32 portnum; - int ret; + int err; bus_state = &port->rhub->bus_state; link_state = portsc & PORT_PLS_MASK; @@ -1088,20 +1111,36 @@ static void xhci_get_usb2_port_status(struct xhci_port *port, u32 *status, if (link_state == XDEV_U2) *status |= USB_PORT_STAT_L1; if (link_state == XDEV_U0) { - bus_state->resume_done[portnum] = 0; - clear_bit(portnum, &bus_state->resuming_ports); if (bus_state->suspended_ports & (1 << portnum)) { bus_state->suspended_ports &= ~(1 << portnum); bus_state->port_c_suspend |= 1 << portnum; } } if (link_state == XDEV_RESUME) { - ret = xhci_handle_usb2_port_link_resume(port, status, - portsc, flags); - if (ret) - return; + err = xhci_handle_usb2_port_link_resume(port, portsc, + flags); + if (err < 0) + *status = 0xffffffff; + else if (port->resume_timestamp || port->rexit_active) + *status |= USB_PORT_STAT_SUSPEND; } } + + /* + * Clear usb2 resume signalling variables if port is no longer suspended + * or resuming. Port either resumed to U0/U1/U2, disconnected, or in a + * error state. Resume related variables should be cleared in all those cases. + */ + if (link_state != XDEV_U3 && link_state != XDEV_RESUME) { + if (port->resume_timestamp || + test_bit(portnum, &bus_state->resuming_ports)) { + port->resume_timestamp = 0; + clear_bit(portnum, &bus_state->resuming_ports); + usb_hcd_end_port_resume(&port->rhub->hcd->self, portnum); + } + port->rexit_active = 0; + bus_state->suspended_ports &= ~(1 << portnum); + } } /* @@ -1156,18 +1195,6 @@ static u32 xhci_get_port_status(struct usb_hcd *hcd, else xhci_get_usb2_port_status(port, &status, raw_port_status, flags); - /* - * Clear stale usb2 resume signalling variables in case port changed - * state during resume signalling. For example on error - */ - if ((bus_state->resume_done[wIndex] || - test_bit(wIndex, &bus_state->resuming_ports)) && - (raw_port_status & PORT_PLS_MASK) != XDEV_U3 && - (raw_port_status & PORT_PLS_MASK) != XDEV_RESUME) { - bus_state->resume_done[wIndex] = 0; - clear_bit(wIndex, &bus_state->resuming_ports); - usb_hcd_end_port_resume(&hcd->self, wIndex); - } if (bus_state->port_c_suspend & (1 << wIndex)) status |= USB_PORT_STAT_C_SUSPEND << 16; @@ -1183,7 +1210,6 @@ int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, unsigned long flags; u32 temp, status; int retval = 0; - int slot_id; struct xhci_bus_state *bus_state; u16 link_state = 0; u16 wake_mask = 0; @@ -1191,11 +1217,14 @@ int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, u16 test_mode = 0; struct xhci_hub *rhub; struct xhci_port **ports; + struct xhci_port *port; + int portnum1; rhub = xhci_get_rhub(hcd); ports = rhub->ports; max_ports = rhub->num_ports; bus_state = &rhub->bus_state; + portnum1 = wIndex & 0xff; spin_lock_irqsave(&xhci->lock, flags); switch (typeReq) { @@ -1229,23 +1258,25 @@ int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, spin_unlock_irqrestore(&xhci->lock, flags); return retval; case GetPortStatus: - if (!wIndex || wIndex > max_ports) + if (!portnum1 || portnum1 > max_ports) goto error; + wIndex--; - temp = readl(ports[wIndex]->addr); + port = ports[portnum1 - 1]; + temp = xhci_portsc_readl(port); if (temp == ~(u32)0) { xhci_hc_died(xhci); retval = -ENODEV; break; } - trace_xhci_get_port_status(wIndex, temp); + trace_xhci_get_port_status(port, temp); status = xhci_get_port_status(hcd, bus_state, wIndex, temp, &flags); if (status == 0xffffffff) goto error; xhci_dbg(xhci, "Get port status %d-%d read: 0x%x, return 0x%x", - hcd->self.busnum, wIndex + 1, temp, status); + hcd->self.busnum, portnum1, temp, status); put_unaligned(cpu_to_le32(status), (__le32 *) buf); /* if USB 3.1 extended port status return additional 4 bytes */ @@ -1257,7 +1288,7 @@ int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, retval = -EINVAL; break; } - port_li = readl(ports[wIndex]->addr + PORTLI); + port_li = readl(&port->port_reg->portli); status = xhci_get_ext_port_status(temp, port_li); put_unaligned_le32(status, &buf[4]); } @@ -1271,11 +1302,14 @@ int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, test_mode = (wIndex & 0xff00) >> 8; /* The MSB of wIndex is the U1/U2 timeout */ timeout = (wIndex & 0xff00) >> 8; + wIndex &= 0xff; - if (!wIndex || wIndex > max_ports) + if (!portnum1 || portnum1 > max_ports) goto error; + + port = ports[portnum1 - 1]; wIndex--; - temp = readl(ports[wIndex]->addr); + temp = xhci_portsc_readl(port); if (temp == ~(u32)0) { xhci_hc_died(xhci); retval = -ENODEV; @@ -1285,11 +1319,10 @@ int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, /* FIXME: What new port features do we need to support? */ switch (wValue) { case USB_PORT_FEAT_SUSPEND: - temp = readl(ports[wIndex]->addr); + temp = xhci_portsc_readl(port); if ((temp & PORT_PLS_MASK) != XDEV_U0) { /* Resume the port to U0 first */ - xhci_set_link_state(xhci, ports[wIndex], - XDEV_U0); + xhci_set_link_state(xhci, port, XDEV_U0); spin_unlock_irqrestore(&xhci->lock, flags); msleep(10); spin_lock_irqsave(&xhci->lock, flags); @@ -1298,40 +1331,38 @@ int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, * a port unless the port reports that it is in the * enabled (PED = ‘1’,PLS < ‘3’) state. */ - temp = readl(ports[wIndex]->addr); + temp = xhci_portsc_readl(port); if ((temp & PORT_PE) == 0 || (temp & PORT_RESET) || (temp & PORT_PLS_MASK) >= XDEV_U3) { xhci_warn(xhci, "USB core suspending port %d-%d not in U0/U1/U2\n", - hcd->self.busnum, wIndex + 1); + hcd->self.busnum, portnum1); goto error; } - slot_id = xhci_find_slot_id_by_port(hcd, xhci, - wIndex + 1); - if (!slot_id) { + if (!port->slot_id) { xhci_warn(xhci, "slot_id is zero\n"); goto error; } /* unlock to execute stop endpoint commands */ spin_unlock_irqrestore(&xhci->lock, flags); - xhci_stop_device(xhci, slot_id, 1); + xhci_stop_device(xhci, port->slot_id, 1); spin_lock_irqsave(&xhci->lock, flags); - xhci_set_link_state(xhci, ports[wIndex], XDEV_U3); + xhci_set_link_state(xhci, port, XDEV_U3); spin_unlock_irqrestore(&xhci->lock, flags); msleep(10); /* wait device to enter */ spin_lock_irqsave(&xhci->lock, flags); - temp = readl(ports[wIndex]->addr); + temp = xhci_portsc_readl(port); bus_state->suspended_ports |= 1 << wIndex; break; case USB_PORT_FEAT_LINK_STATE: - temp = readl(ports[wIndex]->addr); + temp = xhci_portsc_readl(port); /* Disable port */ if (link_state == USB_SS_PORT_LS_SS_DISABLED) { xhci_dbg(xhci, "Disable port %d-%d\n", - hcd->self.busnum, wIndex + 1); + hcd->self.busnum, portnum1); temp = xhci_port_state_to_neutral(temp); /* * Clear all change bits, so that we get a new @@ -1340,18 +1371,17 @@ int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, temp |= PORT_CSC | PORT_PEC | PORT_WRC | PORT_OCC | PORT_RC | PORT_PLC | PORT_CEC; - writel(temp | PORT_PE, ports[wIndex]->addr); - temp = readl(ports[wIndex]->addr); + xhci_portsc_writel(port, temp | PORT_PE); + temp = xhci_portsc_readl(port); break; } /* Put link in RxDetect (enable port) */ if (link_state == USB_SS_PORT_LS_RX_DETECT) { xhci_dbg(xhci, "Enable port %d-%d\n", - hcd->self.busnum, wIndex + 1); - xhci_set_link_state(xhci, ports[wIndex], - link_state); - temp = readl(ports[wIndex]->addr); + hcd->self.busnum, portnum1); + xhci_set_link_state(xhci, port, link_state); + temp = xhci_portsc_readl(port); break; } @@ -1370,7 +1400,7 @@ int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, * automatically entered as on 1.0 and prior. */ if (link_state == USB_SS_PORT_LS_COMP_MOD) { - if (!HCC2_CTC(xhci->hcc_params2)) { + if (!(xhci->hcc_params2 & HCC2_CTC)) { xhci_dbg(xhci, "CTC flag is 0, port already supports entering compliance mode\n"); break; } @@ -1381,11 +1411,10 @@ int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, } xhci_dbg(xhci, "Enable compliance mode transition for port %d-%d\n", - hcd->self.busnum, wIndex + 1); - xhci_set_link_state(xhci, ports[wIndex], - link_state); + hcd->self.busnum, portnum1); + xhci_set_link_state(xhci, port, link_state); - temp = readl(ports[wIndex]->addr); + temp = xhci_portsc_readl(port); break; } /* Port must be enabled */ @@ -1396,8 +1425,7 @@ int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, /* Can't set port link state above '3' (U3) */ if (link_state > USB_SS_PORT_LS_U3) { xhci_warn(xhci, "Cannot set port %d-%d link state %d\n", - hcd->self.busnum, wIndex + 1, - link_state); + hcd->self.busnum, portnum1, link_state); goto error; } @@ -1419,48 +1447,45 @@ int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, pls == XDEV_RESUME || pls == XDEV_RECOVERY) { wait_u0 = true; - reinit_completion(&bus_state->u3exit_done[wIndex]); + reinit_completion(&port->u3exit_done); } if (pls <= XDEV_U3) /* U1, U2, U3 */ - xhci_set_link_state(xhci, ports[wIndex], - USB_SS_PORT_LS_U0); + xhci_set_link_state(xhci, port, USB_SS_PORT_LS_U0); if (!wait_u0) { if (pls > XDEV_U3) goto error; break; } spin_unlock_irqrestore(&xhci->lock, flags); - if (!wait_for_completion_timeout(&bus_state->u3exit_done[wIndex], - msecs_to_jiffies(100))) + if (!wait_for_completion_timeout(&port->u3exit_done, + msecs_to_jiffies(500))) xhci_dbg(xhci, "missing U0 port change event for port %d-%d\n", - hcd->self.busnum, wIndex + 1); + hcd->self.busnum, portnum1); spin_lock_irqsave(&xhci->lock, flags); - temp = readl(ports[wIndex]->addr); + temp = xhci_portsc_readl(port); break; } if (link_state == USB_SS_PORT_LS_U3) { int retries = 16; - slot_id = xhci_find_slot_id_by_port(hcd, xhci, - wIndex + 1); - if (slot_id) { + if (port->slot_id) { /* unlock to execute stop endpoint * commands */ spin_unlock_irqrestore(&xhci->lock, flags); - xhci_stop_device(xhci, slot_id, 1); + xhci_stop_device(xhci, port->slot_id, 1); spin_lock_irqsave(&xhci->lock, flags); } - xhci_set_link_state(xhci, ports[wIndex], USB_SS_PORT_LS_U3); + xhci_set_link_state(xhci, port, USB_SS_PORT_LS_U3); spin_unlock_irqrestore(&xhci->lock, flags); while (retries--) { usleep_range(4000, 8000); - temp = readl(ports[wIndex]->addr); + temp = xhci_portsc_readl(port); if ((temp & PORT_PLS_MASK) == XDEV_U3) break; } spin_lock_irqsave(&xhci->lock, flags); - temp = readl(ports[wIndex]->addr); + temp = xhci_portsc_readl(port); bus_state->suspended_ports |= 1 << wIndex; } break; @@ -1471,43 +1496,42 @@ int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, * However, hub_wq will ignore the roothub events until * the roothub is registered. */ - xhci_set_port_power(xhci, hcd, wIndex, true, &flags); + xhci_set_port_power(xhci, port, true, &flags); break; case USB_PORT_FEAT_RESET: temp = (temp | PORT_RESET); - writel(temp, ports[wIndex]->addr); + xhci_portsc_writel(port, temp); - temp = readl(ports[wIndex]->addr); + temp = xhci_portsc_readl(port); xhci_dbg(xhci, "set port reset, actual port %d-%d status = 0x%x\n", - hcd->self.busnum, wIndex + 1, temp); + hcd->self.busnum, portnum1, temp); break; case USB_PORT_FEAT_REMOTE_WAKE_MASK: - xhci_set_remote_wake_mask(xhci, ports[wIndex], - wake_mask); - temp = readl(ports[wIndex]->addr); + xhci_set_remote_wake_mask(xhci, port, wake_mask); + temp = xhci_portsc_readl(port); xhci_dbg(xhci, "set port remote wake mask, actual port %d-%d status = 0x%x\n", - hcd->self.busnum, wIndex + 1, temp); + hcd->self.busnum, portnum1, temp); break; case USB_PORT_FEAT_BH_PORT_RESET: temp |= PORT_WR; - writel(temp, ports[wIndex]->addr); - temp = readl(ports[wIndex]->addr); + xhci_portsc_writel(port, temp); + temp = xhci_portsc_readl(port); break; case USB_PORT_FEAT_U1_TIMEOUT: if (hcd->speed < HCD_USB3) goto error; - temp = readl(ports[wIndex]->addr + PORTPMSC); + temp = readl(&port->port_reg->portpmsc); temp &= ~PORT_U1_TIMEOUT_MASK; temp |= PORT_U1_TIMEOUT(timeout); - writel(temp, ports[wIndex]->addr + PORTPMSC); + writel(temp, &port->port_reg->portpmsc); break; case USB_PORT_FEAT_U2_TIMEOUT: if (hcd->speed < HCD_USB3) goto error; - temp = readl(ports[wIndex]->addr + PORTPMSC); + temp = readl(&port->port_reg->portpmsc); temp &= ~PORT_U2_TIMEOUT_MASK; temp |= PORT_U2_TIMEOUT(timeout); - writel(temp, ports[wIndex]->addr + PORTPMSC); + writel(temp, &port->port_reg->portpmsc); break; case USB_PORT_FEAT_TEST: /* 4.19.6 Port Test Modes (USB2 Test Mode) */ @@ -1523,13 +1547,16 @@ int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, goto error; } /* unblock any posted writes */ - temp = readl(ports[wIndex]->addr); + temp = xhci_portsc_readl(port); break; case ClearPortFeature: - if (!wIndex || wIndex > max_ports) + if (!portnum1 || portnum1 > max_ports) goto error; + + port = ports[portnum1 - 1]; + wIndex--; - temp = readl(ports[wIndex]->addr); + temp = xhci_portsc_readl(port); if (temp == ~(u32)0) { xhci_hc_died(xhci); retval = -ENODEV; @@ -1539,7 +1566,7 @@ int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, temp = xhci_port_state_to_neutral(temp); switch (wValue) { case USB_PORT_FEAT_SUSPEND: - temp = readl(ports[wIndex]->addr); + temp = xhci_portsc_readl(port); xhci_dbg(xhci, "clear USB_PORT_FEAT_SUSPEND\n"); xhci_dbg(xhci, "PORTSC %04x\n", temp); if (temp & PORT_RESET) @@ -1550,25 +1577,21 @@ int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, set_bit(wIndex, &bus_state->resuming_ports); usb_hcd_start_port_resume(&hcd->self, wIndex); - xhci_set_link_state(xhci, ports[wIndex], - XDEV_RESUME); + xhci_set_link_state(xhci, port, XDEV_RESUME); spin_unlock_irqrestore(&xhci->lock, flags); msleep(USB_RESUME_TIMEOUT); spin_lock_irqsave(&xhci->lock, flags); - xhci_set_link_state(xhci, ports[wIndex], - XDEV_U0); + xhci_set_link_state(xhci, port, XDEV_U0); clear_bit(wIndex, &bus_state->resuming_ports); usb_hcd_end_port_resume(&hcd->self, wIndex); } bus_state->port_c_suspend |= 1 << wIndex; - slot_id = xhci_find_slot_id_by_port(hcd, xhci, - wIndex + 1); - if (!slot_id) { + if (!port->slot_id) { xhci_dbg(xhci, "slot_id is zero\n"); goto error; } - xhci_ring_device(xhci, slot_id); + xhci_ring_device(xhci, port->slot_id); break; case USB_PORT_FEAT_C_SUSPEND: bus_state->port_c_suspend &= ~(1 << wIndex); @@ -1580,15 +1603,13 @@ int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, case USB_PORT_FEAT_C_ENABLE: case USB_PORT_FEAT_C_PORT_LINK_STATE: case USB_PORT_FEAT_C_PORT_CONFIG_ERROR: - xhci_clear_port_change_bit(xhci, wValue, wIndex, - ports[wIndex]->addr, temp); + xhci_clear_port_change_bit(xhci, wValue, wIndex, port, temp); break; case USB_PORT_FEAT_ENABLE: - xhci_disable_port(hcd, xhci, wIndex, - ports[wIndex]->addr, temp); + xhci_disable_port(xhci, port); break; case USB_PORT_FEAT_POWER: - xhci_set_port_power(xhci, hcd, wIndex, false, &flags); + xhci_set_port_power(xhci, port, false, &flags); break; case USB_PORT_FEAT_TEST: retval = xhci_exit_test_mode(xhci); @@ -1605,6 +1626,7 @@ error: spin_unlock_irqrestore(&xhci->lock, flags); return retval; } +EXPORT_SYMBOL_GPL(xhci_hub_control); /* * Returns 0 if the status hasn't changed, or the number of bytes in buf. @@ -1644,22 +1666,33 @@ int xhci_hub_status_data(struct usb_hcd *hcd, char *buf) status = bus_state->resuming_ports; + /* + * SS devices are only visible to roothub after link training completes. + * Keep polling roothubs for a grace period after xHC start + */ + if (hcd->speed >= HCD_USB3 && xhci->run_graceperiod) { + if (time_before(jiffies, xhci->run_graceperiod)) + status = 1; + else + xhci->run_graceperiod = 0; + } + mask = PORT_CSC | PORT_PEC | PORT_OCC | PORT_PLC | PORT_WRC | PORT_CEC; /* For each port, did anything change? If so, set that bit in buf. */ for (i = 0; i < max_ports; i++) { - temp = readl(ports[i]->addr); + temp = xhci_portsc_readl(ports[i]); if (temp == ~(u32)0) { xhci_hc_died(xhci); retval = -ENODEV; break; } - trace_xhci_hub_status_data(i, temp); + trace_xhci_hub_status_data(ports[i], temp); if ((temp & mask) != 0 || (bus_state->port_c_suspend & 1 << i) || - (bus_state->resume_done[i] && time_after_eq( - jiffies, bus_state->resume_done[i]))) { + (ports[i]->resume_timestamp && time_after_eq( + jiffies, ports[i]->resume_timestamp))) { buf[(i + 1) / 8] |= 1 << (i + 1) % 8; status = 1; } @@ -1717,7 +1750,7 @@ int xhci_bus_suspend(struct usb_hcd *hcd) u32 t1, t2; int retries = 10; retry: - t1 = readl(ports[port_index]->addr); + t1 = xhci_portsc_readl(ports[port_index]); t2 = xhci_port_state_to_neutral(t1); portsc_buf[port_index] = 0; @@ -1788,17 +1821,14 @@ retry: if (!portsc_buf[port_index]) continue; if (test_bit(port_index, &bus_state->bus_suspended)) { - int slot_id; - - slot_id = xhci_find_slot_id_by_port(hcd, xhci, - port_index + 1); + int slot_id = ports[port_index]->slot_id; if (slot_id) { spin_unlock_irqrestore(&xhci->lock, flags); xhci_stop_device(xhci, slot_id, 1); spin_lock_irqsave(&xhci->lock, flags); } } - writel(portsc_buf[port_index], ports[port_index]->addr); + xhci_portsc_writel(ports[port_index], portsc_buf[port_index]); } hcd->state = HC_STATE_SUSPENDED; bus_state->next_statechange = jiffies + msecs_to_jiffies(10); @@ -1819,7 +1849,7 @@ static bool xhci_port_missing_cas_quirk(struct xhci_port *port) { u32 portsc; - portsc = readl(port->addr); + portsc = xhci_portsc_readl(port); /* if any of these are set we are not stuck */ if (portsc & (PORT_CONNECT | PORT_CAS)) @@ -1832,9 +1862,9 @@ static bool xhci_port_missing_cas_quirk(struct xhci_port *port) /* clear wakeup/change bits, and do a warm port reset */ portsc &= ~(PORT_RWC_BITS | PORT_CEC | PORT_WAKE_BITS); portsc |= PORT_WR; - writel(portsc, port->addr); + xhci_portsc_writel(port, portsc); /* flush write */ - readl(port->addr); + xhci_portsc_readl(port); return true; } @@ -1844,12 +1874,12 @@ int xhci_bus_resume(struct usb_hcd *hcd) struct xhci_bus_state *bus_state; unsigned long flags; int max_ports, port_index; - int slot_id; int sret; u32 next_state; - u32 temp, portsc; + u32 portsc; struct xhci_hub *rhub; struct xhci_port **ports; + bool disabled_irq = false; rhub = xhci_get_rhub(hcd); ports = rhub->ports; @@ -1865,20 +1895,23 @@ int xhci_bus_resume(struct usb_hcd *hcd) return -ESHUTDOWN; } - /* delay the irqs */ - temp = readl(&xhci->op_regs->command); - temp &= ~CMD_EIE; - writel(temp, &xhci->op_regs->command); - /* bus specific resume for ports we suspended at bus_suspend */ - if (hcd->speed >= HCD_USB3) + if (hcd->speed >= HCD_USB3) { next_state = XDEV_U0; - else + } else { next_state = XDEV_RESUME; - + if (bus_state->bus_suspended) { + /* + * prevent port event interrupts from interfering + * with usb2 port resume process + */ + xhci_disable_interrupter(xhci, xhci->interrupters[0]); + disabled_irq = true; + } + } port_index = max_ports; while (port_index--) { - portsc = readl(ports[port_index]->addr); + portsc = xhci_portsc_readl(ports[port_index]); /* warm reset CAS limited ports stuck in polling/compliance */ if ((xhci->quirks & XHCI_MISSING_CAS) && @@ -1901,14 +1934,14 @@ int xhci_bus_resume(struct usb_hcd *hcd) /* resume already initiated */ break; default: - /* not in a resumeable state, ignore it */ + /* not in a resumable state, ignore it */ clear_bit(port_index, &bus_state->bus_suspended); break; } /* disable wake for all ports, write new link state if needed */ portsc &= ~(PORT_RWC_BITS | PORT_CEC | PORT_WAKE_BITS); - writel(portsc, ports[port_index]->addr); + xhci_portsc_writel(ports[port_index], portsc); } /* USB2 specific resume signaling delay and U0 link state transition */ @@ -1929,7 +1962,7 @@ int xhci_bus_resume(struct usb_hcd *hcd) /* poll for U0 link state complete, both USB2 and USB3 */ for_each_set_bit(port_index, &bus_state->bus_suspended, BITS_PER_LONG) { - sret = xhci_handshake(ports[port_index]->addr, PORT_PLC, + sret = xhci_handshake(&ports[port_index]->port_reg->portsc, PORT_PLC, PORT_PLC, 10 * 1000); if (sret) { xhci_warn(xhci, "port %d-%d resume PLC timeout\n", @@ -1937,18 +1970,15 @@ int xhci_bus_resume(struct usb_hcd *hcd) continue; } xhci_test_and_clear_bit(xhci, ports[port_index], PORT_PLC); - slot_id = xhci_find_slot_id_by_port(hcd, xhci, port_index + 1); - if (slot_id) - xhci_ring_device(xhci, slot_id); + if (ports[port_index]->slot_id) + xhci_ring_device(xhci, ports[port_index]->slot_id); } (void) readl(&xhci->op_regs->command); bus_state->next_statechange = jiffies + msecs_to_jiffies(5); - /* re-enable irqs */ - temp = readl(&xhci->op_regs->command); - temp |= CMD_EIE; - writel(temp, &xhci->op_regs->command); - temp = readl(&xhci->op_regs->command); + /* re-enable interrupter */ + if (disabled_irq) + xhci_enable_interrupter(xhci->interrupters[0]); spin_unlock_irqrestore(&xhci->lock, flags); return 0; |
