--- zzzz-none-000/linux-3.10.107/drivers/usb/host/xhci-hub.c 2017-06-27 09:49:32.000000000 +0000 +++ scorpion-7490-727/linux-3.10.107/drivers/usb/host/xhci-hub.c 2021-02-04 17:41:59.000000000 +0000 @@ -20,22 +20,31 @@ * Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ -#include + +#include #include #include "xhci.h" +#include "xhci-trace.h" + +#if defined (CONFIG_AVM_KERNEL) +/* 20160219 AVM/VGJ USB3 ports switchable */ +extern int usb3port_config; +#endif #define PORT_WAKE_BITS (PORT_WKOC_E | PORT_WKDISC_E | PORT_WKCONN_E) #define PORT_RWC_BITS (PORT_CSC | PORT_PEC | PORT_WRC | PORT_OCC | \ PORT_RC | PORT_PLC | PORT_PE) -/* USB 3.0 BOS descriptor and a capability descriptor, combined */ +/* USB 3 BOS descriptor and a capability descriptors, combined. + * Fields will be adjusted and added later in xhci_create_usb3_bos_desc() + */ static u8 usb_bos_descriptor [] = { USB_DT_BOS_SIZE, /* __u8 bLength, 5 bytes */ USB_DT_BOS, /* __u8 bDescriptorType */ 0x0F, 0x00, /* __le16 wTotalLength, 15 bytes */ 0x1, /* __u8 bNumDeviceCaps */ - /* First device capability */ + /* First device capability, SuperSpeed */ USB_DT_USB_SS_CAP_SIZE, /* __u8 bLength, 10 bytes */ USB_DT_DEVICE_CAPABILITY, /* Device Capability */ USB_SS_CAP_TYPE, /* bDevCapabilityType, SUPERSPEED_USB */ @@ -44,9 +53,108 @@ 0x03, /* bFunctionalitySupport, USB 3.0 speed only */ 0x00, /* bU1DevExitLat, set later. */ - 0x00, 0x00 /* __le16 bU2DevExitLat, set later. */ + 0x00, 0x00, /* __le16 bU2DevExitLat, set later. */ + /* Second device capability, SuperSpeedPlus */ + 0x0c, /* bLength 12, will be adjusted later */ + USB_DT_DEVICE_CAPABILITY, /* Device Capability */ + USB_SSP_CAP_TYPE, /* bDevCapabilityType SUPERSPEED_PLUS */ + 0x00, /* bReserved 0 */ + 0x00, 0x00, 0x00, 0x00, /* bmAttributes, get from xhci psic */ + 0x00, 0x00, /* wFunctionalitySupport */ + 0x00, 0x00, /* wReserved 0 */ + /* Sublink Speed Attributes are added in xhci_create_usb3_bos_desc() */ }; +static int xhci_create_usb3_bos_desc(struct xhci_hcd *xhci, char *buf, + u16 wLength) +{ + int i, ssa_count; + u32 temp; + u16 desc_size, ssp_cap_size, ssa_size = 0; + bool usb3_1 = false; + + desc_size = USB_DT_BOS_SIZE + USB_DT_USB_SS_CAP_SIZE; + ssp_cap_size = sizeof(usb_bos_descriptor) - desc_size; + + /* does xhci support USB 3.1 Enhanced SuperSpeed */ + if (xhci->usb3_rhub.min_rev >= 0x01 && xhci->usb3_rhub.psi_uid_count) { + /* two SSA entries for each unique PSI ID, one RX and one TX */ + ssa_count = xhci->usb3_rhub.psi_uid_count * 2; + ssa_size = ssa_count * sizeof(u32); + desc_size += ssp_cap_size; + usb3_1 = true; + } + memcpy(buf, &usb_bos_descriptor, min(desc_size, wLength)); + + if (usb3_1) { + /* modify bos descriptor bNumDeviceCaps and wTotalLength */ + buf[4] += 1; + put_unaligned_le16(desc_size + ssa_size, &buf[2]); + } + + if (wLength < USB_DT_BOS_SIZE + USB_DT_USB_SS_CAP_SIZE) + return wLength; + + /* Indicate whether the host has LTM support. */ + temp = readl(&xhci->cap_regs->hcc_params); + if (HCC_LTC(temp)) + buf[8] |= USB_LTM_SUPPORT; + + /* Set the U1 and U2 exit latencies. */ + if ((xhci->quirks & XHCI_LPM_SUPPORT)) { + temp = readl(&xhci->cap_regs->hcs_params3); + buf[12] = HCS_U1_LATENCY(temp); + put_unaligned_le16(HCS_U2_LATENCY(temp), &buf[13]); + } + + if (usb3_1) { + u32 ssp_cap_base, bm_attrib, psi; + int offset; + + ssp_cap_base = USB_DT_BOS_SIZE + USB_DT_USB_SS_CAP_SIZE; + + if (wLength < desc_size) + return wLength; + buf[ssp_cap_base] = ssp_cap_size + ssa_size; + + /* attribute count SSAC bits 4:0 and ID count SSIC bits 8:5 */ + bm_attrib = (ssa_count - 1) & 0x1f; + bm_attrib |= (xhci->usb3_rhub.psi_uid_count - 1) << 5; + put_unaligned_le32(bm_attrib, &buf[ssp_cap_base + 4]); + + if (wLength < desc_size + ssa_size) + return wLength; + /* + * Create the Sublink Speed Attributes (SSA) array. + * The xhci PSI field and USB 3.1 SSA fields are very similar, + * but link type bits 7:6 differ for values 01b and 10b. + * xhci has also only one PSI entry for a symmetric link when + * USB 3.1 requires two SSA entries (RX and TX) for every link + */ + offset = desc_size; + for (i = 0; i < xhci->usb3_rhub.psi_count; i++) { + psi = xhci->usb3_rhub.psi[i]; + psi &= ~USB_SSP_SUBLINK_SPEED_RSVD; + if ((psi & PLT_MASK) == PLT_SYM) { + /* Symmetric, create SSA RX and TX from one PSI entry */ + put_unaligned_le32(psi, &buf[offset]); + psi |= 1 << 7; /* turn entry to TX */ + offset += 4; + if (offset >= desc_size + ssa_size) + return desc_size + ssa_size; + } else if ((psi & PLT_MASK) == PLT_ASYM_RX) { + /* Asymetric RX, flip bits 7:6 for SSA */ + psi ^= PLT_MASK; + } + put_unaligned_le32(psi, &buf[offset]); + offset += 4; + if (offset >= desc_size + ssa_size) + return desc_size + ssa_size; + } + } + /* ssa_size is 0 for other than usb 3.1 hosts */ + return desc_size + ssa_size; +} static void xhci_common_hub_descriptor(struct xhci_hcd *xhci, struct usb_hub_descriptor *desc, int ports) @@ -93,7 +201,7 @@ */ memset(port_removable, 0, sizeof(port_removable)); for (i = 0; i < ports; i++) { - portsc = xhci_readl(xhci, xhci->usb2_ports[i]); + portsc = readl(xhci->usb2_ports[i]); /* If a device is removable, PORTSC reports a 0, same as in the * hub descriptor DeviceRemovable bits. */ @@ -147,7 +255,7 @@ port_removable = 0; /* bit 0 is reserved, bit 1 is for port 1, etc. */ for (i = 0; i < ports; i++) { - portsc = xhci_readl(xhci, xhci->usb3_ports[i]); + portsc = readl(xhci->usb3_ports[i]); if (portsc & PORT_DEV_REMOVE) port_removable |= 1 << (i + 1); } @@ -159,7 +267,7 @@ struct usb_hub_descriptor *desc) { - if (hcd->speed == HCD_USB3) + if (hcd->speed >= HCD_USB3) xhci_usb3_hub_descriptor(hcd, xhci, desc); else xhci_usb2_hub_descriptor(hcd, xhci, desc); @@ -248,7 +356,7 @@ if (!xhci->devs[i]) continue; speed = xhci->devs[i]->udev->speed; - if (((speed == USB_SPEED_SUPER) == (hcd->speed == HCD_USB3)) + if (((speed >= USB_SPEED_SUPER) == (hcd->speed >= HCD_USB3)) && xhci->devs[i]->fake_port == port) { slot_id = i; break; @@ -269,7 +377,6 @@ struct xhci_virt_device *virt_dev; struct xhci_command *cmd; unsigned long flags; - int timeleft; int ret; int i; @@ -286,34 +393,31 @@ spin_lock_irqsave(&xhci->lock, flags); for (i = LAST_EP_INDEX; i > 0; i--) { - if (virt_dev->eps[i].ring && virt_dev->eps[i].ring->dequeue) - xhci_queue_stop_endpoint(xhci, slot_id, i, suspend); + if (virt_dev->eps[i].ring && virt_dev->eps[i].ring->dequeue) { + struct xhci_command *command; + command = xhci_alloc_command(xhci, false, false, + GFP_NOWAIT); + if (!command) { + spin_unlock_irqrestore(&xhci->lock, flags); + xhci_free_command(xhci, cmd); + return -ENOMEM; + + } + xhci_queue_stop_endpoint(xhci, command, slot_id, i, + suspend); + } } - cmd->command_trb = xhci_find_next_enqueue(xhci->cmd_ring); - list_add_tail(&cmd->cmd_list, &virt_dev->cmd_list); - xhci_queue_stop_endpoint(xhci, slot_id, 0, suspend); + xhci_queue_stop_endpoint(xhci, cmd, slot_id, 0, suspend); xhci_ring_cmd_db(xhci); spin_unlock_irqrestore(&xhci->lock, flags); /* Wait for last stop endpoint command to finish */ - timeleft = wait_for_completion_interruptible_timeout( - cmd->completion, - USB_CTRL_SET_TIMEOUT); - if (timeleft <= 0) { - xhci_warn(xhci, "%s while waiting for stop endpoint command\n", - timeleft == 0 ? "Timeout" : "Signal"); - spin_lock_irqsave(&xhci->lock, flags); - /* The timeout might have raced with the event ring handler, so - * only delete from the list if the item isn't poisoned. - */ - if (cmd->cmd_list.next != LIST_POISON1) - list_del(&cmd->cmd_list); - spin_unlock_irqrestore(&xhci->lock, flags); + wait_for_completion(cmd->completion); + + if (cmd->status == COMP_CMD_ABORT || cmd->status == COMP_CMD_STOP) { + xhci_warn(xhci, "Timeout while waiting for stop endpoint command\n"); ret = -ETIME; - goto command_cleanup; } - -command_cleanup: xhci_free_command(xhci, cmd); return ret; } @@ -323,12 +427,19 @@ */ void xhci_ring_device(struct xhci_hcd *xhci, int slot_id) { - int i; + int i, s; + struct xhci_virt_ep *ep; + + for (i = 0; i < LAST_EP_INDEX + 1; i++) { + ep = &xhci->devs[slot_id]->eps[i]; - for (i = 0; i < LAST_EP_INDEX + 1; i++) - if (xhci->devs[slot_id]->eps[i].ring && - xhci->devs[slot_id]->eps[i].ring->dequeue) + if (ep->ep_state & EP_HAS_STREAMS) { + for (s = 1; s < ep->stream_info->num_streams; s++) + xhci_ring_ep_doorbell(xhci, slot_id, i, s); + } else if (ep->ring && ep->ring->dequeue) { xhci_ring_ep_doorbell(xhci, slot_id, i, 0); + } + } return; } @@ -337,15 +448,15 @@ u16 wIndex, __le32 __iomem *addr, u32 port_status) { /* Don't allow the USB core to disable SuperSpeed ports. */ - if (hcd->speed == HCD_USB3) { + if (hcd->speed >= HCD_USB3) { xhci_dbg(xhci, "Ignoring request to disable " "SuperSpeed port.\n"); return; } /* Write 1 to disable the port */ - xhci_writel(xhci, port_status | PORT_PE, addr); - port_status = xhci_readl(xhci, addr); + writel(port_status | PORT_PE, addr); + port_status = readl(addr); xhci_dbg(xhci, "disable port, actual port %d status = 0x%x\n", wIndex, port_status); } @@ -385,13 +496,17 @@ status = PORT_PLC; port_change_bit = "link state"; break; + case USB_PORT_FEAT_C_PORT_CONFIG_ERROR: + status = PORT_CEC; + port_change_bit = "config error"; + break; default: /* Should never happen */ return; } /* Change bits are all write 1 to clear */ - xhci_writel(xhci, port_status | status, addr); - port_status = xhci_readl(xhci, addr); + writel(port_status | status, addr); + port_status = readl(addr); xhci_dbg(xhci, "clear port %s change, actual port %d status = 0x%x\n", port_change_bit, wIndex, port_status); } @@ -401,7 +516,7 @@ int max_ports; struct xhci_hcd *xhci = hcd_to_xhci(hcd); - if (hcd->speed == HCD_USB3) { + if (hcd->speed >= HCD_USB3) { max_ports = xhci->num_usb3_ports; *port_array = xhci->usb3_ports; } else { @@ -417,11 +532,11 @@ { u32 temp; - temp = xhci_readl(xhci, port_array[port_id]); + temp = readl(port_array[port_id]); temp = xhci_port_state_to_neutral(temp); temp &= ~PORT_PLS_MASK; temp |= PORT_LINK_STROBE | link_state; - xhci_writel(xhci, temp, port_array[port_id]); + writel(temp, port_array[port_id]); } static void xhci_set_remote_wake_mask(struct xhci_hcd *xhci, @@ -429,7 +544,7 @@ { u32 temp; - temp = xhci_readl(xhci, port_array[port_id]); + temp = readl(port_array[port_id]); temp = xhci_port_state_to_neutral(temp); if (wake_mask & USB_PORT_FEAT_REMOTE_WAKE_CONNECT) @@ -447,7 +562,7 @@ else temp &= ~PORT_WKOC_E; - xhci_writel(xhci, temp, port_array[port_id]); + writel(temp, port_array[port_id]); } /* Test and clear port RWC bit */ @@ -456,16 +571,23 @@ { u32 temp; - temp = xhci_readl(xhci, port_array[port_id]); + temp = readl(port_array[port_id]); if (temp & port_bit) { temp = xhci_port_state_to_neutral(temp); temp |= port_bit; - xhci_writel(xhci, temp, port_array[port_id]); + writel(temp, port_array[port_id]); } } +/* Updates Link Status for USB 2.1 port */ +static void xhci_hub_report_usb2_link_state(u32 *status, u32 status_reg) +{ + if ((status_reg & PORT_PLS_MASK) == XDEV_U2) + *status |= USB_PORT_STAT_L1; +} + /* Updates Link Status for super Speed port */ -static void xhci_hub_report_link_state(struct xhci_hcd *xhci, +static void xhci_hub_report_usb3_link_state(struct xhci_hcd *xhci, u32 *status, u32 status_reg) { u32 pls = status_reg & PORT_PLS_MASK; @@ -524,7 +646,8 @@ * the compliance mode timer is deleted. A port won't enter * compliance mode if it has previously entered U0. */ -void xhci_del_comp_mod_timer(struct xhci_hcd *xhci, u32 status, u16 wIndex) +static void xhci_del_comp_mod_timer(struct xhci_hcd *xhci, u32 status, + u16 wIndex) { u32 all_ports_seen_u0 = ((1 << xhci->num_usb3_ports)-1); bool port_in_u0 = ((status & PORT_PLS_MASK) == XDEV_U0); @@ -536,12 +659,215 @@ xhci->port_status_u0 |= 1 << wIndex; if (xhci->port_status_u0 == all_ports_seen_u0) { del_timer_sync(&xhci->comp_mode_recovery_timer); - xhci_dbg(xhci, "All USB3 ports have entered U0 already!\n"); - xhci_dbg(xhci, "Compliance Mode Recovery Timer Deleted.\n"); + xhci_dbg_trace(xhci, trace_xhci_dbg_quirks, + "All USB3 ports have entered U0 already!"); + xhci_dbg_trace(xhci, trace_xhci_dbg_quirks, + "Compliance Mode Recovery Timer Deleted."); } } } +static u32 xhci_get_ext_port_status(u32 raw_port_status, u32 port_li) +{ + u32 ext_stat = 0; + int speed_id; + + /* only support rx and tx lane counts of 1 in usb3.1 spec */ + speed_id = DEV_PORT_SPEED(raw_port_status); + ext_stat |= speed_id; /* bits 3:0, RX speed id */ + ext_stat |= speed_id << 4; /* bits 7:4, TX speed id */ + + ext_stat |= PORT_RX_LANES(port_li) << 8; /* bits 11:8 Rx lane count */ + ext_stat |= PORT_TX_LANES(port_li) << 12; /* bits 15:12 Tx lane count */ + + return ext_stat; +} + +/* + * Converts a raw xHCI port status into the format that external USB 2.0 or USB + * 3.0 hubs use. + * + * Possible side effects: + * - Mark a port as being done with device resume, + * and ring the endpoint doorbells. + * - Stop the Synopsys redriver Compliance Mode polling. + * - Drop and reacquire the xHCI lock, in order to wait for port resume. + */ +static u32 xhci_get_port_status(struct usb_hcd *hcd, + struct xhci_bus_state *bus_state, + __le32 __iomem **port_array, + u16 wIndex, u32 raw_port_status, + unsigned long flags) + __releases(&xhci->lock) + __acquires(&xhci->lock) +{ + struct xhci_hcd *xhci = hcd_to_xhci(hcd); + u32 status = 0; + int slot_id; + + /* wPortChange bits */ + if (raw_port_status & PORT_CSC) + status |= USB_PORT_STAT_C_CONNECTION << 16; + if (raw_port_status & PORT_PEC) + status |= USB_PORT_STAT_C_ENABLE << 16; + if ((raw_port_status & PORT_OCC)) + status |= USB_PORT_STAT_C_OVERCURRENT << 16; + if ((raw_port_status & PORT_RC)) + status |= USB_PORT_STAT_C_RESET << 16; + /* USB3.0 only */ + if (hcd->speed >= HCD_USB3) { + /* Port link change with port in resume state should not be + * reported to usbcore, as this is an internal state to be + * handled by xhci driver. Reporting PLC to usbcore may + * cause usbcore clearing PLC first and port change event + * irq won't be generated. + */ + if ((raw_port_status & PORT_PLC) && + (raw_port_status & PORT_PLS_MASK) != XDEV_RESUME) + status |= USB_PORT_STAT_C_LINK_STATE << 16; + if ((raw_port_status & PORT_WRC)) + status |= USB_PORT_STAT_C_BH_RESET << 16; + if ((raw_port_status & PORT_CEC)) + status |= USB_PORT_STAT_C_CONFIG_ERROR << 16; + } + + if (hcd->speed < HCD_USB3) { + if ((raw_port_status & PORT_PLS_MASK) == XDEV_U3 + && (raw_port_status & PORT_POWER)) + status |= USB_PORT_STAT_SUSPEND; + } + if ((raw_port_status & PORT_PLS_MASK) == XDEV_RESUME && + !DEV_SUPERSPEED_ANY(raw_port_status)) { + if ((raw_port_status & PORT_RESET) || + !(raw_port_status & PORT_PE)) + return 0xffffffff; + /* 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 (test_bit(wIndex, &bus_state->resuming_ports)) { + /* Host initated 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 + * changed, but not right now. + */ + } else { + /* port resume was discovered now and here, + * start resume timing + */ + unsigned long timeout = jiffies + + msecs_to_jiffies(USB_RESUME_TIMEOUT); + + set_bit(wIndex, &bus_state->resuming_ports); + bus_state->resume_done[wIndex] = timeout; + mod_timer(&hcd->rh_timer, timeout); + } + /* Has resume been signalled for USB_RESUME_TIME yet? */ + } else if (time_after_eq(jiffies, + bus_state->resume_done[wIndex])) { + int time_left; + + xhci_dbg(xhci, "Resume USB2 port %d\n", + wIndex + 1); + bus_state->resume_done[wIndex] = 0; + clear_bit(wIndex, &bus_state->resuming_ports); + + set_bit(wIndex, &bus_state->rexit_ports); + xhci_set_link_state(xhci, port_array, wIndex, + XDEV_U0); + + spin_unlock_irqrestore(&xhci->lock, flags); + time_left = wait_for_completion_timeout( + &bus_state->rexit_done[wIndex], + msecs_to_jiffies( + XHCI_MAX_REXIT_TIMEOUT)); + spin_lock_irqsave(&xhci->lock, flags); + + if (time_left) { + slot_id = xhci_find_slot_id_by_port(hcd, + xhci, wIndex + 1); + if (!slot_id) { + xhci_dbg(xhci, "slot_id is zero\n"); + return 0xffffffff; + } + xhci_ring_device(xhci, slot_id); + } else { + int port_status = readl(port_array[wIndex]); + xhci_warn(xhci, "Port resume took longer than %i msec, port status = 0x%x\n", + XHCI_MAX_REXIT_TIMEOUT, + port_status); + status |= USB_PORT_STAT_SUSPEND; + clear_bit(wIndex, &bus_state->rexit_ports); + } + + 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; + } + } + /* + * 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); + } + + + if ((raw_port_status & PORT_PLS_MASK) == XDEV_U0 && + (raw_port_status & PORT_POWER)) { + if (bus_state->suspended_ports & (1 << wIndex)) { + bus_state->suspended_ports &= ~(1 << wIndex); + if (hcd->speed < HCD_USB3) + bus_state->port_c_suspend |= 1 << wIndex; + } + bus_state->resume_done[wIndex] = 0; + clear_bit(wIndex, &bus_state->resuming_ports); + } + if (raw_port_status & PORT_CONNECT) { + status |= USB_PORT_STAT_CONNECTION; + status |= xhci_port_speed(raw_port_status); + } + if (raw_port_status & PORT_PE) + status |= USB_PORT_STAT_ENABLE; + if (raw_port_status & PORT_OC) + status |= USB_PORT_STAT_OVERCURRENT; + if (raw_port_status & PORT_RESET) + status |= USB_PORT_STAT_RESET; + if (raw_port_status & PORT_POWER) { + if (hcd->speed >= HCD_USB3) + status |= USB_SS_PORT_STAT_POWER; + else + status |= USB_PORT_STAT_POWER; + } + /* Update Port Link State */ + if (hcd->speed >= HCD_USB3) { + xhci_hub_report_usb3_link_state(xhci, &status, raw_port_status); + /* + * Verify if all USB3 Ports Have entered U0 already. + * Delete Compliance Mode Timer if so. + */ + xhci_del_comp_mod_timer(xhci, raw_port_status, wIndex); + } else { + xhci_hub_report_usb2_link_state(&status, raw_port_status); + } + if (bus_state->port_c_suspend & (1 << wIndex)) + status |= 1 << USB_PORT_FEAT_C_SUSPEND; + + return status; +} + int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, u16 wIndex, char *buf, u16 wLength) { @@ -571,7 +897,7 @@ * descriptor for the USB 3.0 roothub. If not, we stall the * endpoint, like external hubs do. */ - if (hcd->speed == HCD_USB3 && + if (hcd->speed >= HCD_USB3 && (wLength < USB_DT_SS_HUB_SIZE || wValue != (USB_DT_SS_HUB << 8))) { xhci_dbg(xhci, "Wrong hub descriptor type for " @@ -585,126 +911,44 @@ if ((wValue & 0xff00) != (USB_DT_BOS << 8)) goto error; - if (hcd->speed != HCD_USB3) + if (hcd->speed < HCD_USB3) goto error; - /* Set the U1 and U2 exit latencies. */ - memcpy(buf, &usb_bos_descriptor, - USB_DT_BOS_SIZE + USB_DT_USB_SS_CAP_SIZE); - temp = xhci_readl(xhci, &xhci->cap_regs->hcs_params3); - buf[12] = HCS_U1_LATENCY(temp); - put_unaligned_le16(HCS_U2_LATENCY(temp), &buf[13]); - - /* Indicate whether the host has LTM support. */ - temp = xhci_readl(xhci, &xhci->cap_regs->hcc_params); - if (HCC_LTC(temp)) - buf[8] |= USB_LTM_SUPPORT; - + retval = xhci_create_usb3_bos_desc(xhci, buf, wLength); spin_unlock_irqrestore(&xhci->lock, flags); - return USB_DT_BOS_SIZE + USB_DT_USB_SS_CAP_SIZE; + return retval; case GetPortStatus: if (!wIndex || wIndex > max_ports) goto error; wIndex--; - status = 0; - temp = xhci_readl(xhci, port_array[wIndex]); + temp = readl(port_array[wIndex]); if (temp == 0xffffffff) { retval = -ENODEV; break; } - xhci_dbg(xhci, "get port status, actual port %d status = 0x%x\n", wIndex, temp); + status = xhci_get_port_status(hcd, bus_state, port_array, + wIndex, temp, flags); + if (status == 0xffffffff) + goto error; - /* wPortChange bits */ - if (temp & PORT_CSC) - status |= USB_PORT_STAT_C_CONNECTION << 16; - if (temp & PORT_PEC) - status |= USB_PORT_STAT_C_ENABLE << 16; - if ((temp & PORT_OCC)) - status |= USB_PORT_STAT_C_OVERCURRENT << 16; - if ((temp & PORT_RC)) - status |= USB_PORT_STAT_C_RESET << 16; - /* USB3.0 only */ - if (hcd->speed == HCD_USB3) { - if ((temp & PORT_PLC)) - status |= USB_PORT_STAT_C_LINK_STATE << 16; - if ((temp & PORT_WRC)) - status |= USB_PORT_STAT_C_BH_RESET << 16; - } + xhci_dbg(xhci, "get port status, actual port %d status = 0x%x\n", + wIndex, temp); + xhci_dbg(xhci, "Get port status returned 0x%x\n", status); - if (hcd->speed != HCD_USB3) { - if ((temp & PORT_PLS_MASK) == XDEV_U3 - && (temp & PORT_POWER)) - status |= USB_PORT_STAT_SUSPEND; - } - if ((temp & PORT_PLS_MASK) == XDEV_RESUME && - !DEV_SUPERSPEED(temp)) { - if ((temp & PORT_RESET) || !(temp & PORT_PE)) - goto error; - if (time_after_eq(jiffies, - bus_state->resume_done[wIndex])) { - xhci_dbg(xhci, "Resume USB2 port %d\n", - wIndex + 1); - bus_state->resume_done[wIndex] = 0; - clear_bit(wIndex, &bus_state->resuming_ports); - xhci_set_link_state(xhci, port_array, wIndex, - XDEV_U0); - xhci_dbg(xhci, "set port %d resume\n", - wIndex + 1); - slot_id = xhci_find_slot_id_by_port(hcd, xhci, - wIndex + 1); - if (!slot_id) { - xhci_dbg(xhci, "slot_id is zero\n"); - goto error; - } - xhci_ring_device(xhci, slot_id); - bus_state->port_c_suspend |= 1 << wIndex; - bus_state->suspended_ports &= ~(1 << wIndex); - } else { - /* - * The resume has been signaling for less than - * 20ms. Report the port status as SUSPEND, - * let the usbcore check port status again - * and clear resume signaling later. - */ - status |= USB_PORT_STAT_SUSPEND; + put_unaligned(cpu_to_le32(status), (__le32 *) buf); + /* if USB 3.1 extended port status return additional 4 bytes */ + if (wValue == 0x02) { + u32 port_li; + + if (hcd->speed < HCD_USB31 || wLength != 8) { + xhci_err(xhci, "get ext port status invalid parameter\n"); + retval = -EINVAL; + break; } + port_li = readl(port_array[wIndex] + PORTLI); + status = xhci_get_ext_port_status(temp, port_li); + put_unaligned_le32(cpu_to_le32(status), &buf[4]); } - if ((temp & PORT_PLS_MASK) == XDEV_U0 - && (temp & PORT_POWER) - && (bus_state->suspended_ports & (1 << wIndex))) { - bus_state->suspended_ports &= ~(1 << wIndex); - if (hcd->speed != HCD_USB3) - bus_state->port_c_suspend |= 1 << wIndex; - } - if (temp & PORT_CONNECT) { - status |= USB_PORT_STAT_CONNECTION; - status |= xhci_port_speed(temp); - } - if (temp & PORT_PE) - status |= USB_PORT_STAT_ENABLE; - if (temp & PORT_OC) - status |= USB_PORT_STAT_OVERCURRENT; - if (temp & PORT_RESET) - status |= USB_PORT_STAT_RESET; - if (temp & PORT_POWER) { - if (hcd->speed == HCD_USB3) - status |= USB_SS_PORT_STAT_POWER; - else - status |= USB_PORT_STAT_POWER; - } - /* Update Port Link State for super speed ports*/ - if (hcd->speed == HCD_USB3) { - xhci_hub_report_link_state(xhci, &status, temp); - /* - * Verify if all USB3 Ports Have entered U0 already. - * Delete Compliance Mode Timer if so. - */ - xhci_del_comp_mod_timer(xhci, temp, wIndex); - } - if (bus_state->port_c_suspend & (1 << wIndex)) - status |= 1 << USB_PORT_FEAT_C_SUSPEND; - xhci_dbg(xhci, "Get port status returned 0x%x\n", status); - put_unaligned(cpu_to_le32(status), (__le32 *) buf); break; case SetPortFeature: if (wValue == USB_PORT_FEAT_LINK_STATE) @@ -717,7 +961,7 @@ if (!wIndex || wIndex > max_ports) goto error; wIndex--; - temp = xhci_readl(xhci, port_array[wIndex]); + temp = readl(port_array[wIndex]); if (temp == 0xffffffff) { retval = -ENODEV; break; @@ -726,7 +970,7 @@ /* FIXME: What new port features do we need to support? */ switch (wValue) { case USB_PORT_FEAT_SUSPEND: - temp = xhci_readl(xhci, port_array[wIndex]); + temp = readl(port_array[wIndex]); if ((temp & PORT_PLS_MASK) != XDEV_U0) { /* Resume the port to U0 first */ xhci_set_link_state(xhci, port_array, wIndex, @@ -739,7 +983,7 @@ * a port unless the port reports that it is in the * enabled (PED = ‘1’,PLS < ‘3’) state. */ - temp = xhci_readl(xhci, port_array[wIndex]); + temp = readl(port_array[wIndex]); if ((temp & PORT_PE) == 0 || (temp & PORT_RESET) || (temp & PORT_PLS_MASK) >= XDEV_U3) { xhci_warn(xhci, "USB core suspending device " @@ -764,11 +1008,11 @@ msleep(10); /* wait device to enter */ spin_lock_irqsave(&xhci->lock, flags); - temp = xhci_readl(xhci, port_array[wIndex]); + temp = readl(port_array[wIndex]); bus_state->suspended_ports |= 1 << wIndex; break; case USB_PORT_FEAT_LINK_STATE: - temp = xhci_readl(xhci, port_array[wIndex]); + temp = readl(port_array[wIndex]); /* Disable port */ if (link_state == USB_SS_PORT_LS_SS_DISABLED) { @@ -781,9 +1025,8 @@ temp |= PORT_CSC | PORT_PEC | PORT_WRC | PORT_OCC | PORT_RC | PORT_PLC | PORT_CEC; - xhci_writel(xhci, temp | PORT_PE, - port_array[wIndex]); - temp = xhci_readl(xhci, port_array[wIndex]); + writel(temp | PORT_PE, port_array[wIndex]); + temp = readl(port_array[wIndex]); break; } @@ -792,7 +1035,7 @@ xhci_dbg(xhci, "Enable port %d\n", wIndex); xhci_set_link_state(xhci, port_array, wIndex, link_state); - temp = xhci_readl(xhci, port_array[wIndex]); + temp = readl(port_array[wIndex]); break; } @@ -826,7 +1069,7 @@ msleep(20); /* wait device to enter */ spin_lock_irqsave(&xhci->lock, flags); - temp = xhci_readl(xhci, port_array[wIndex]); + temp = readl(port_array[wIndex]); if (link_state == USB_SS_PORT_LS_U3) bus_state->suspended_ports |= 1 << wIndex; break; @@ -834,13 +1077,25 @@ /* * Turn on ports, even if there isn't per-port switching. * HC will report connect events even before this is set. - * However, khubd will ignore the roothub events until + * However, hub_wq will ignore the roothub events until * the roothub is registered. */ - xhci_writel(xhci, temp | PORT_POWER, - port_array[wIndex]); +#if defined (CONFIG_AVM_KERNEL) + { + /* 20170811 AVM/VGJ USB3 ports switchable */ + const int port_bit = (hcd->self.busnum < 3)? 1: 2; + temp |= PORT_POWER; + if ((hcd->speed == HCD_USB3) && !(usb3port_config & port_bit) ) { + temp &= ~PORT_POWER; + printk (KERN_INFO "AVM: disable USB3 bus#%d port#%d config=%x\n", hcd->self.busnum, wIndex, usb3port_config); + } - temp = xhci_readl(xhci, port_array[wIndex]); + writel(temp, port_array[wIndex]); + } +#else + writel(temp | PORT_POWER, port_array[wIndex]); +#endif + temp = readl(port_array[wIndex]); xhci_dbg(xhci, "set port power, actual port %d status = 0x%x\n", wIndex, temp); spin_unlock_irqrestore(&xhci->lock, flags); @@ -853,52 +1108,52 @@ break; case USB_PORT_FEAT_RESET: temp = (temp | PORT_RESET); - xhci_writel(xhci, temp, port_array[wIndex]); + writel(temp, port_array[wIndex]); - temp = xhci_readl(xhci, port_array[wIndex]); + temp = readl(port_array[wIndex]); xhci_dbg(xhci, "set port reset, actual port %d status = 0x%x\n", wIndex, temp); break; case USB_PORT_FEAT_REMOTE_WAKE_MASK: xhci_set_remote_wake_mask(xhci, port_array, wIndex, wake_mask); - temp = xhci_readl(xhci, port_array[wIndex]); + temp = readl(port_array[wIndex]); xhci_dbg(xhci, "set port remote wake mask, " "actual port %d status = 0x%x\n", wIndex, temp); break; case USB_PORT_FEAT_BH_PORT_RESET: temp |= PORT_WR; - xhci_writel(xhci, temp, port_array[wIndex]); + writel(temp, port_array[wIndex]); - temp = xhci_readl(xhci, port_array[wIndex]); + temp = readl(port_array[wIndex]); break; case USB_PORT_FEAT_U1_TIMEOUT: - if (hcd->speed != HCD_USB3) + if (hcd->speed < HCD_USB3) goto error; - temp = xhci_readl(xhci, port_array[wIndex] + 1); + temp = readl(port_array[wIndex] + PORTPMSC); temp &= ~PORT_U1_TIMEOUT_MASK; temp |= PORT_U1_TIMEOUT(timeout); - xhci_writel(xhci, temp, port_array[wIndex] + 1); + writel(temp, port_array[wIndex] + PORTPMSC); break; case USB_PORT_FEAT_U2_TIMEOUT: - if (hcd->speed != HCD_USB3) + if (hcd->speed < HCD_USB3) goto error; - temp = xhci_readl(xhci, port_array[wIndex] + 1); + temp = readl(port_array[wIndex] + PORTPMSC); temp &= ~PORT_U2_TIMEOUT_MASK; temp |= PORT_U2_TIMEOUT(timeout); - xhci_writel(xhci, temp, port_array[wIndex] + 1); + writel(temp, port_array[wIndex] + PORTPMSC); break; default: goto error; } /* unblock any posted writes */ - temp = xhci_readl(xhci, port_array[wIndex]); + temp = readl(port_array[wIndex]); break; case ClearPortFeature: if (!wIndex || wIndex > max_ports) goto error; wIndex--; - temp = xhci_readl(xhci, port_array[wIndex]); + temp = readl(port_array[wIndex]); if (temp == 0xffffffff) { retval = -ENODEV; break; @@ -907,7 +1162,7 @@ temp = xhci_port_state_to_neutral(temp); switch (wValue) { case USB_PORT_FEAT_SUSPEND: - temp = xhci_readl(xhci, port_array[wIndex]); + temp = readl(port_array[wIndex]); xhci_dbg(xhci, "clear USB_PORT_FEAT_SUSPEND\n"); xhci_dbg(xhci, "PORTSC %04x\n", temp); if (temp & PORT_RESET) @@ -916,13 +1171,15 @@ if ((temp & PORT_PE) == 0) goto error; + set_bit(wIndex, &bus_state->resuming_ports); xhci_set_link_state(xhci, port_array, wIndex, XDEV_RESUME); spin_unlock_irqrestore(&xhci->lock, flags); - msleep(20); + msleep(USB_RESUME_TIMEOUT); spin_lock_irqsave(&xhci->lock, flags); xhci_set_link_state(xhci, port_array, wIndex, XDEV_U0); + clear_bit(wIndex, &bus_state->resuming_ports); } bus_state->port_c_suspend |= 1 << wIndex; @@ -942,6 +1199,7 @@ case USB_PORT_FEAT_C_OVER_CURRENT: 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, port_array[wIndex], temp); break; @@ -950,8 +1208,7 @@ port_array[wIndex], temp); break; case USB_PORT_FEAT_POWER: - xhci_writel(xhci, temp & ~PORT_POWER, - port_array[wIndex]); + writel(temp & ~PORT_POWER, port_array[wIndex]); spin_unlock_irqrestore(&xhci->lock, flags); temp = usb_acpi_power_manageable(hcd->self.root_hub, @@ -1007,12 +1264,12 @@ */ status = bus_state->resuming_ports; - mask = PORT_CSC | PORT_PEC | PORT_OCC | PORT_PLC | PORT_WRC; + mask = PORT_CSC | PORT_PEC | PORT_OCC | PORT_PLC | PORT_WRC | PORT_CEC; spin_lock_irqsave(&xhci->lock, flags); /* For each port, did anything change? If so, set that bit in buf. */ for (i = 0; i < max_ports; i++) { - temp = xhci_readl(xhci, port_array[i]); + temp = readl(port_array[i]); if (temp == 0xffffffff) { retval = -ENODEV; break; @@ -1066,7 +1323,7 @@ u32 t1, t2; int slot_id; - t1 = xhci_readl(xhci, port_array[port_index]); + t1 = readl(port_array[port_index]); t2 = xhci_port_state_to_neutral(t1); if ((t1 & PORT_PE) && !(t1 & PORT_PLS_MASK)) { @@ -1083,7 +1340,7 @@ set_bit(port_index, &bus_state->bus_suspended); } /* USB core sets remote wake mask for USB 3.0 hubs, - * including the USB 3.0 roothub, but only if CONFIG_PM_RUNTIME + * including the USB 3.0 roothub, but only if CONFIG_PM * is enabled, so also enable remote wake here. */ if (hcd->self.root_hub->do_remote_wakeup) { @@ -1099,21 +1356,7 @@ t1 = xhci_port_state_to_neutral(t1); if (t1 != t2) - xhci_writel(xhci, t2, port_array[port_index]); - - if (hcd->speed != HCD_USB3) { - /* enable remote wake up for USB 2.0 */ - __le32 __iomem *addr; - u32 tmp; - - /* Add one to the port status register address to get - * the port power control register address. - */ - addr = port_array[port_index] + 1; - tmp = xhci_readl(xhci, addr); - tmp |= PORT_RWE; - xhci_writel(xhci, tmp, addr); - } + writel(t2, port_array[port_index]); } hcd->state = HC_STATE_SUSPENDED; bus_state->next_statechange = jiffies + msecs_to_jiffies(10); @@ -1121,6 +1364,35 @@ return 0; } +/* + * Workaround for missing Cold Attach Status (CAS) if device re-plugged in S3. + * warm reset a USB3 device stuck in polling or compliance mode after resume. + * See Intel 100/c230 series PCH specification update Doc #332692-006 Errata #8 + */ +static bool xhci_port_missing_cas_quirk(int port_index, + __le32 __iomem **port_array) +{ + u32 portsc; + + portsc = readl(port_array[port_index]); + + /* if any of these are set we are not stuck */ + if (portsc & (PORT_CONNECT | PORT_CAS)) + return false; + + if (((portsc & PORT_PLS_MASK) != XDEV_POLLING) && + ((portsc & PORT_PLS_MASK) != XDEV_COMP_MODE)) + return false; + + /* 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_array[port_index]); + /* flush write */ + readl(port_array[port_index]); + return true; +} + int xhci_bus_resume(struct usb_hcd *hcd) { struct xhci_hcd *xhci = hcd_to_xhci(hcd); @@ -1129,6 +1401,10 @@ struct xhci_bus_state *bus_state; u32 temp; unsigned long flags; + unsigned long port_was_suspended = 0; + bool need_usb2_u3_exit = false; + int slot_id; + int sret; max_ports = xhci_get_ports(hcd, &port_array); bus_state = &xhci->bus_state[hcd_index(hcd)]; @@ -1143,79 +1419,80 @@ } /* delay the irqs */ - temp = xhci_readl(xhci, &xhci->op_regs->command); + temp = readl(&xhci->op_regs->command); temp &= ~CMD_EIE; - xhci_writel(xhci, temp, &xhci->op_regs->command); + writel(temp, &xhci->op_regs->command); port_index = max_ports; while (port_index--) { /* Check whether need resume ports. If needed resume port and disable remote wakeup */ u32 temp; - int slot_id; - temp = xhci_readl(xhci, port_array[port_index]); - if (DEV_SUPERSPEED(temp)) + temp = readl(port_array[port_index]); + + /* warm reset CAS limited ports stuck in polling/compliance */ + if ((xhci->quirks & XHCI_MISSING_CAS) && + (hcd->speed >= HCD_USB3) && + xhci_port_missing_cas_quirk(port_index, port_array)) { + xhci_dbg(xhci, "reset stuck port %d\n", port_index); + continue; + } + if (DEV_SUPERSPEED_ANY(temp)) temp &= ~(PORT_RWC_BITS | PORT_CEC | PORT_WAKE_BITS); else temp &= ~(PORT_RWC_BITS | PORT_WAKE_BITS); if (test_bit(port_index, &bus_state->bus_suspended) && (temp & PORT_PLS_MASK)) { - if (DEV_SUPERSPEED(temp)) { - xhci_set_link_state(xhci, port_array, - port_index, XDEV_U0); - } else { + set_bit(port_index, &port_was_suspended); + if (!DEV_SUPERSPEED_ANY(temp)) { xhci_set_link_state(xhci, port_array, port_index, XDEV_RESUME); - - spin_unlock_irqrestore(&xhci->lock, flags); - msleep(20); - spin_lock_irqsave(&xhci->lock, flags); - - xhci_set_link_state(xhci, port_array, - port_index, XDEV_U0); + need_usb2_u3_exit = true; } - /* wait for the port to enter U0 and report port link - * state change. - */ - spin_unlock_irqrestore(&xhci->lock, flags); - msleep(20); - spin_lock_irqsave(&xhci->lock, flags); - - /* Clear PLC */ - xhci_test_and_clear_bit(xhci, port_array, 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); } else - xhci_writel(xhci, temp, port_array[port_index]); + writel(temp, port_array[port_index]); + } - if (hcd->speed != HCD_USB3) { - /* disable remote wake up for USB 2.0 */ - __le32 __iomem *addr; - u32 tmp; + if (need_usb2_u3_exit) { + spin_unlock_irqrestore(&xhci->lock, flags); + msleep(USB_RESUME_TIMEOUT); + spin_lock_irqsave(&xhci->lock, flags); + } - /* Add one to the port status register address to get - * the port power control register address. - */ - addr = port_array[port_index] + 1; - tmp = xhci_readl(xhci, addr); - tmp &= ~PORT_RWE; - xhci_writel(xhci, tmp, addr); - } + port_index = max_ports; + while (port_index--) { + if (!(port_was_suspended & BIT(port_index))) + continue; + /* Clear PLC to poll it later after XDEV_U0 */ + xhci_test_and_clear_bit(xhci, port_array, port_index, PORT_PLC); + xhci_set_link_state(xhci, port_array, port_index, XDEV_U0); + } + + port_index = max_ports; + while (port_index--) { + if (!(port_was_suspended & BIT(port_index))) + continue; + /* Poll and Clear PLC */ + sret = xhci_handshake(port_array[port_index], PORT_PLC, + PORT_PLC, 10 * 1000); + if (sret) + xhci_warn(xhci, "port %d resume PLC timeout\n", + port_index); + xhci_test_and_clear_bit(xhci, port_array, 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); } - (void) xhci_readl(xhci, &xhci->op_regs->command); + (void) readl(&xhci->op_regs->command); bus_state->next_statechange = jiffies + msecs_to_jiffies(5); /* re-enable irqs */ - temp = xhci_readl(xhci, &xhci->op_regs->command); + temp = readl(&xhci->op_regs->command); temp |= CMD_EIE; - xhci_writel(xhci, temp, &xhci->op_regs->command); - temp = xhci_readl(xhci, &xhci->op_regs->command); + writel(temp, &xhci->op_regs->command); + temp = readl(&xhci->op_regs->command); spin_unlock_irqrestore(&xhci->lock, flags); return 0;