/* * Returns 0 if the status hasn't changed, or the number of bytes in buf. * Ports are 0-indexed from the HCD point of view, * and 1-indexed from the USB core pointer of view. * * Note that the status change bits will be cleared as soon as a port status * change event is generated, so we use the saved status from that event. */ int xhci_hub_status_data(struct usb_hcd *hcd, char *buf) { unsigned long flags; u32 temp, status; int i, retval; struct xhci_hcd *xhci = hcd_to_xhci(hcd); int ports; u32 __iomem *addr; ports = HCS_MAX_PORTS(xhci->hcs_params1); /* Initial status is no changes */ retval = (ports + 8) / 8; memset(buf, 0, retval); status = 0; spin_lock_irqsave(&xhci->lock, flags); /* For each port, did anything change? If so, set that bit in buf. */ for (i = 0; i < ports; i++) { addr = &xhci->op_regs->port_status_base + NUM_PORT_REGS*i; temp = xhci_readl(xhci, addr); if (temp & (PORT_CSC | PORT_PEC | PORT_OCC)) { buf[(i + 1) / 8] |= 1 << (i + 1) % 8; status = 1; } } spin_unlock_irqrestore(&xhci->lock, flags); return status ? retval : 0; }
static void xhci_hub_descriptor(struct xhci_hcd *xhci, struct usb_hub_descriptor *desc) { int ports; u16 temp; ports = HCS_MAX_PORTS(xhci->hcs_params1); /* USB 3.0 hubs have a different descriptor, but we fake this for now */ desc->bDescriptorType = 0x29; desc->bPwrOn2PwrGood = 10; /* xhci section 5.4.9 says 20ms max */ desc->bHubContrCurrent = 0; desc->bNbrPorts = ports; temp = 1 + (ports / 8); desc->bDescLength = 7 + 2 * temp; /* Why does core/hcd.h define bitmap? It's just confusing. */ memset(&desc->DeviceRemovable[0], 0, temp); memset(&desc->DeviceRemovable[temp], 0xff, temp); /* Using table 11-13 in USB 2.0 spec. */ temp = 0; /* Bits 1:0 - support port power switching, or power always on */ if (HCC_PPC(xhci->hcc_params)) temp |= 0x0001; else temp |= 0x0002; /* Bit 2 - root hubs are not part of a compound device */ /* Bits 4:3 - individual port over current protection */ temp |= 0x0008; /* Bits 6:5 - no TTs in root ports */ /* Bit 7 - no port indicators */ desc->wHubCharacteristics = (__force __u16) cpu_to_le16(temp); }
int xhci_hub_setup_ports(struct xhci_hcd *xhci) { u32 offset, tmp_offset; __le32 __iomem *addr, *tmp_addr; unsigned int num_ports; int i, cap_count = 0; offset = HCC_EXT_CAPS(xhci->hcc_params); if (offset == 0) { dev_err(xhci->dev, "No Extended Capability Registers\n"); return -ENODEV; } addr = &xhci->cap_regs->hc_capbase + offset; /* count extended protocol capability entries for later caching */ tmp_addr = addr; tmp_offset = offset; do { u32 cap_id = readl(tmp_addr); if (XHCI_EXT_CAPS_ID(cap_id) == XHCI_EXT_CAPS_PROTOCOL) cap_count++; tmp_offset = XHCI_EXT_CAPS_NEXT(cap_id); tmp_addr += tmp_offset; } while (tmp_offset); num_ports = HCS_MAX_PORTS(xhci->hcs_params1); xhci->port_array = xzalloc(num_ports * sizeof(*xhci->port_array)); xhci->ext_caps = xzalloc(cap_count * sizeof(*xhci->ext_caps)); while (1) { u32 cap_id = readl(addr); if (XHCI_EXT_CAPS_ID(cap_id) == XHCI_EXT_CAPS_PROTOCOL) xhci_add_in_port(xhci, num_ports, addr, (u8)XHCI_EXT_PORT_MAJOR(cap_id), cap_count); offset = XHCI_EXT_CAPS_NEXT(cap_id); if (!offset || xhci->num_usb_ports == num_ports) break; addr += offset; } if (xhci->num_usb_ports == 0) { dev_err(xhci->dev, "No ports on the roothubs?\n"); return -ENODEV; } xhci->usb_ports = xzalloc(num_ports * sizeof(*xhci->usb_ports)); for (i = 0; i < num_ports; i++) xhci->usb_ports[i] = &xhci->op_regs->port_status_base + NUM_PORT_REGS * i; memcpy(&xhci->usb_info, &usb_rh_info, sizeof(usb_rh_info)); xhci_setup_usb2_hub_descriptor(xhci); return 0; }
static void xhci_print_cap_regs(struct xhci_hcd *xhci) { u32 temp; xhci_dbg(xhci, "xHCI capability registers at %p:\n", xhci->cap_regs); temp = xhci_readl(xhci, &xhci->cap_regs->hc_capbase); xhci_dbg(xhci, "CAPLENGTH AND HCIVERSION 0x%x:\n", (unsigned int) temp); xhci_dbg(xhci, "CAPLENGTH: 0x%x\n", (unsigned int) HC_LENGTH(temp)); xhci_dbg(xhci, "HCIVERSION: 0x%x\n", (unsigned int) HC_VERSION(temp)); temp = xhci_readl(xhci, &xhci->cap_regs->hcs_params1); xhci_dbg(xhci, "HCSPARAMS 1: 0x%x\n", (unsigned int) temp); xhci_dbg(xhci, " Max device slots: %u\n", (unsigned int) HCS_MAX_SLOTS(temp)); xhci_dbg(xhci, " Max interrupters: %u\n", (unsigned int) HCS_MAX_INTRS(temp)); xhci_dbg(xhci, " Max ports: %u\n", (unsigned int) HCS_MAX_PORTS(temp)); temp = xhci_readl(xhci, &xhci->cap_regs->hcs_params2); xhci_dbg(xhci, "HCSPARAMS 2: 0x%x\n", (unsigned int) temp); xhci_dbg(xhci, " Isoc scheduling threshold: %u\n", (unsigned int) HCS_IST(temp)); xhci_dbg(xhci, " Maximum allowed segments in event ring: %u\n", (unsigned int) HCS_ERST_MAX(temp)); temp = xhci_readl(xhci, &xhci->cap_regs->hcs_params3); xhci_dbg(xhci, "HCSPARAMS 3 0x%x:\n", (unsigned int) temp); xhci_dbg(xhci, " Worst case U1 device exit latency: %u\n", (unsigned int) HCS_U1_LATENCY(temp)); xhci_dbg(xhci, " Worst case U2 device exit latency: %u\n", (unsigned int) HCS_U2_LATENCY(temp)); temp = xhci_readl(xhci, &xhci->cap_regs->hcc_params); xhci_dbg(xhci, "HCC PARAMS 0x%x:\n", (unsigned int) temp); xhci_dbg(xhci, " HC generates %s bit addresses\n", HCC_64BIT_ADDR(temp) ? "64" : "32"); /* FIXME */ xhci_dbg(xhci, " FIXME: more HCCPARAMS debugging\n"); temp = xhci_readl(xhci, &xhci->cap_regs->run_regs_off); xhci_dbg(xhci, "RTSOFF 0x%x:\n", temp & RTSOFF_MASK); }
static void xhci_debugfs_create_ports(struct xhci_hcd *xhci, struct dentry *parent) { unsigned int num_ports; char port_name[8]; struct xhci_port *port; struct dentry *dir; num_ports = HCS_MAX_PORTS(xhci->hcs_params1); parent = debugfs_create_dir("ports", parent); while (num_ports--) { scnprintf(port_name, sizeof(port_name), "port%02d", num_ports + 1); dir = debugfs_create_dir(port_name, parent); port = &xhci->hw_ports[num_ports]; debugfs_create_file("portsc", 0644, dir, port, &port_fops); } }
static void xhci_print_ports(struct xhci_hcd *xhci) { u32 __iomem *addr; int i, j; int ports; char *names[NUM_PORT_REGS] = { "status", "power", "link", "reserved", }; ports = HCS_MAX_PORTS(xhci->hcs_params1); addr = &xhci->op_regs->port_status_base; for (i = 0; i < ports; i++) { for (j = 0; j < NUM_PORT_REGS; ++j) { xhci_dbg(xhci, "%p port %s reg = 0x%x\n", addr, names[j], (unsigned int) xhci_readl(xhci, addr)); addr++; } } }
int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, u16 wIndex, char *buf, u16 wLength) { struct xhci_hcd *xhci = hcd_to_xhci(hcd); int ports; unsigned long flags; u32 temp, status; int retval = 0; u32 __iomem *addr; char *port_change_bit; ports = HCS_MAX_PORTS(xhci->hcs_params1); spin_lock_irqsave(&xhci->lock, flags); switch (typeReq) { case GetHubStatus: /* No power source, over-current reported per port */ memset(buf, 0, 4); break; case GetHubDescriptor: xhci_hub_descriptor(xhci, (struct usb_hub_descriptor *) buf); break; case GetPortStatus: if (!wIndex || wIndex > ports) goto error; wIndex--; status = 0; addr = &xhci->op_regs->port_status_base + NUM_PORT_REGS*(wIndex & 0xff); temp = xhci_readl(xhci, addr); xhci_dbg(xhci, "get port status, actual port %d status = 0x%x\n", wIndex, temp); /* wPortChange bits */ if (temp & PORT_CSC) status |= 1 << USB_PORT_FEAT_C_CONNECTION; if (temp & PORT_PEC) status |= 1 << USB_PORT_FEAT_C_ENABLE; if ((temp & PORT_OCC)) status |= 1 << USB_PORT_FEAT_C_OVER_CURRENT; /* * FIXME ignoring suspend, reset, and USB 2.1/3.0 specific * changes */ if (temp & PORT_CONNECT) { status |= 1 << USB_PORT_FEAT_CONNECTION; status |= xhci_port_speed(temp); } if (temp & PORT_PE) status |= 1 << USB_PORT_FEAT_ENABLE; if (temp & PORT_OC) status |= 1 << USB_PORT_FEAT_OVER_CURRENT; if (temp & PORT_RESET) status |= 1 << USB_PORT_FEAT_RESET; if (temp & PORT_POWER) status |= 1 << USB_PORT_FEAT_POWER; xhci_dbg(xhci, "Get port status returned 0x%x\n", status); put_unaligned(cpu_to_le32(status), (__le32 *) buf); break; case SetPortFeature: wIndex &= 0xff; if (!wIndex || wIndex > ports) goto error; wIndex--; addr = &xhci->op_regs->port_status_base + NUM_PORT_REGS*(wIndex & 0xff); temp = xhci_readl(xhci, addr); temp = xhci_port_state_to_neutral(temp); switch (wValue) { case USB_PORT_FEAT_POWER: /* * 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 * the roothub is registered. */ xhci_writel(xhci, temp | PORT_POWER, addr); temp = xhci_readl(xhci, addr); xhci_dbg(xhci, "set port power, actual port %d status = 0x%x\n", wIndex, temp); break; case USB_PORT_FEAT_RESET: temp = (temp | PORT_RESET); xhci_writel(xhci, temp, addr); temp = xhci_readl(xhci, addr); xhci_dbg(xhci, "set port reset, actual port %d status = 0x%x\n", wIndex, temp); break; default: goto error; } temp = xhci_readl(xhci, addr); /* unblock any posted writes */ break; case ClearPortFeature: if (!wIndex || wIndex > ports) goto error; wIndex--; addr = &xhci->op_regs->port_status_base + NUM_PORT_REGS*(wIndex & 0xff); temp = xhci_readl(xhci, addr); temp = xhci_port_state_to_neutral(temp); switch (wValue) { case USB_PORT_FEAT_C_RESET: status = PORT_RC; port_change_bit = "reset"; break; case USB_PORT_FEAT_C_CONNECTION: status = PORT_CSC; port_change_bit = "connect"; break; case USB_PORT_FEAT_C_OVER_CURRENT: status = PORT_OCC; port_change_bit = "over-current"; break; default: goto error; } /* Change bits are all write 1 to clear */ xhci_writel(xhci, temp | status, addr); temp = xhci_readl(xhci, addr); xhci_dbg(xhci, "clear port %s change, actual port %d status = 0x%x\n", port_change_bit, wIndex, temp); temp = xhci_readl(xhci, addr); /* unblock any posted writes */ break; default: error: /* "stall" on error */ retval = -EPIPE; } spin_unlock_irqrestore(&xhci->lock, flags); return retval; }
int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, u16 wIndex, char *buf, u16 wLength) { struct xhci_hcd *xhci = hcd_to_xhci(hcd); int max_ports; unsigned long flags; u32 temp, status; int retval = 0; __le32 __iomem **port_array; int slot_id; struct xhci_bus_state *bus_state; u16 link_state = 0; u16 wake_mask = 0; u32 __iomem *status_reg = NULL; u32 i, command, num_ports, selector; max_ports = xhci_get_ports(hcd, &port_array); bus_state = &xhci->bus_state[hcd_index(hcd)]; spin_lock_irqsave(&xhci->lock, flags); switch (typeReq) { case GetHubStatus: /* No power source, over-current reported per port */ memset(buf, 0, 4); break; case GetHubDescriptor: /* Check to make sure userspace is asking for the USB 3.0 hub * descriptor for the USB 3.0 roothub. If not, we stall the * endpoint, like external hubs do. */ 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 " "USB 3.0 roothub.\n"); goto error; } xhci_hub_descriptor(hcd, xhci, (struct usb_hub_descriptor *) buf); break; case DeviceRequest | USB_REQ_GET_DESCRIPTOR: if ((wValue & 0xff00) != (USB_DT_BOS << 8)) goto error; if (hcd->speed != HCD_USB3) goto error; 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]); spin_unlock_irqrestore(&xhci->lock, flags); return USB_DT_BOS_SIZE + USB_DT_USB_SS_CAP_SIZE; case GetPortStatus: if (!wIndex || wIndex > max_ports) goto error; wIndex--; status = 0; temp = xhci_readl(xhci, port_array[wIndex]); if (temp == 0xffffffff) { retval = -ENODEV; break; } xhci_dbg(xhci, "get port status, actual port %d status = 0x%x\n", wIndex, temp); /* 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; } 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; } } 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(&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) link_state = (wIndex & 0xff00) >> 3; if (wValue == USB_PORT_FEAT_REMOTE_WAKE_MASK) wake_mask = wIndex & 0xff00; selector = wIndex >> 8; wIndex &= 0xff; if (!wIndex || wIndex > max_ports) goto error; wIndex--; status_reg = &xhci->op_regs->port_power_base + NUM_PORT_REGS*wIndex; temp = xhci_readl(xhci, port_array[wIndex]); if (temp == 0xffffffff) { retval = -ENODEV; break; } temp = xhci_port_state_to_neutral(temp); /* FIXME: What new port features do we need to support? */ switch (wValue) { case USB_PORT_FEAT_SUSPEND: temp = xhci_readl(xhci, port_array[wIndex]); if ((temp & PORT_PLS_MASK) != XDEV_U0) { /* Resume the port to U0 first */ xhci_set_link_state(xhci, port_array, wIndex, XDEV_U0); spin_unlock_irqrestore(&xhci->lock, flags); msleep(10); spin_lock_irqsave(&xhci->lock, flags); } /* In spec software should not attempt to suspend * a port unless the port reports that it is in the * enabled (PED = ‘1’,PLS < ‘3’) state. */ temp = xhci_readl(xhci, port_array[wIndex]); if ((temp & PORT_PE) == 0 || (temp & PORT_RESET) || (temp & PORT_PLS_MASK) >= XDEV_U3) { xhci_warn(xhci, "USB core suspending device " "not in U0/U1/U2.\n"); goto error; } slot_id = xhci_find_slot_id_by_port(hcd, xhci, wIndex + 1); if (!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); spin_lock_irqsave(&xhci->lock, flags); xhci_set_link_state(xhci, port_array, wIndex, XDEV_U3); spin_unlock_irqrestore(&xhci->lock, flags); msleep(10); /* wait device to enter */ spin_lock_irqsave(&xhci->lock, flags); temp = xhci_readl(xhci, port_array[wIndex]); bus_state->suspended_ports |= 1 << wIndex; break; case USB_PORT_FEAT_LINK_STATE: temp = xhci_readl(xhci, port_array[wIndex]); /* Disable port */ if (link_state == USB_SS_PORT_LS_SS_DISABLED) { xhci_dbg(xhci, "Disable port %d\n", wIndex); temp = xhci_port_state_to_neutral(temp); /* * Clear all change bits, so that we get a new * connection event. */ 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]); break; } /* Put link in RxDetect (enable port) */ if (link_state == USB_SS_PORT_LS_RX_DETECT) { 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]); break; } /* Software should not attempt to set * port link state above '3' (U3) and the port * must be enabled. */ if ((temp & PORT_PE) == 0 || (link_state > USB_SS_PORT_LS_U3)) { xhci_warn(xhci, "Cannot set link state.\n"); goto error; } if (link_state == USB_SS_PORT_LS_U3) { slot_id = xhci_find_slot_id_by_port(hcd, xhci, wIndex + 1); if (slot_id) { /* unlock to execute stop endpoint * commands */ spin_unlock_irqrestore(&xhci->lock, flags); xhci_stop_device(xhci, slot_id, 1); spin_lock_irqsave(&xhci->lock, flags); } } xhci_set_link_state(xhci, port_array, wIndex, link_state); spin_unlock_irqrestore(&xhci->lock, flags); msleep(20); /* wait device to enter */ spin_lock_irqsave(&xhci->lock, flags); temp = xhci_readl(xhci, port_array[wIndex]); if (link_state == USB_SS_PORT_LS_U3) bus_state->suspended_ports |= 1 << wIndex; break; case USB_PORT_FEAT_POWER: /* FIXME Do not turn on BYT XHCI port 6 power, * Disable this port's power to disable HSIC hub */ if ((xhci->quirks & XHCI_PORT_DISABLE_QUIRK) && (wIndex == 5)) { temp = xhci_readl(xhci, port_array[wIndex]); temp &= ~PORT_POWER; xhci_writel(xhci, temp, port_array[wIndex]); break; } /* * 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 * the roothub is registered. */ xhci_writel(xhci, temp | PORT_POWER, port_array[wIndex]); temp = xhci_readl(xhci, port_array[wIndex]); xhci_dbg(xhci, "set port power, actual port %d status = 0x%x\n", wIndex, temp); break; case USB_PORT_FEAT_RESET: temp = (temp | PORT_RESET); xhci_writel(xhci, temp, port_array[wIndex]); temp = xhci_readl(xhci, 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]); 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]); temp = xhci_readl(xhci, port_array[wIndex]); break; case USB_PORT_FEAT_TEST: if (!selector || selector >= 5 || !status_reg) goto error; /* * Disable all Device Slots. */ for (i = 0; i < MAX_HC_SLOTS; i++) { if (xhci->dcbaa->dev_context_ptrs[i]) { if (xhci_queue_slot_control(xhci, TRB_DISABLE_SLOT, i)) { xhci_err(xhci, "Disable slot[%d] failed!\n", i); goto error; } xhci_dbg(xhci, "Disable Slot[%d].\n", i); } } /* * All ports shall be in the Disable state (PP = 0) */ xhci_dbg(xhci, "Disable all port (PP = 0)\n"); num_ports = HCS_MAX_PORTS(xhci->hcs_params1); for (i = 0; i < num_ports; i++) { u32 __iomem *sreg = &xhci->op_regs->port_status_base + NUM_PORT_REGS*i; temp = xhci_readl(xhci, sreg); temp &= ~PORT_POWER; xhci_writel(xhci, temp, sreg); } /* Set the Run/Stop (R/S) bit in the USBCMD * register to a '0' and wait for HCHalted(HCH) bit * in the USBSTS register, to transition to a '1'. */ xhci_dbg(xhci, "Stop controller\n"); command = xhci_readl(xhci, &xhci->op_regs->command); command &= ~CMD_RUN; xhci_writel(xhci, command, &xhci->op_regs->command); if (handshake(xhci, &xhci->op_regs->status, STS_HALT, STS_HALT, 100*100)) { xhci_warn(xhci, "WARN: xHC CMD_RUN timeout\n"); return -ETIMEDOUT; } /* * start to test */ xhci_dbg(xhci, "test case:"); switch (selector) { case 1: xhci_dbg(xhci, "TEST_J\n"); break; case 2: xhci_dbg(xhci, "TEST_K\n"); break; case 3: xhci_dbg(xhci, "TEST_SE0_NAK\n"); break; case 4: xhci_dbg(xhci, "TEST_PACKET\n"); break; default: xhci_dbg(xhci, "Invalide test case!\n"); goto error; } temp = xhci_readl(xhci, status_reg); temp |= selector << 28; xhci_writel(xhci, temp, status_reg); break; default: goto error; } /* unblock any posted writes */ temp = xhci_readl(xhci, port_array[wIndex]); break; case ClearPortFeature: if (!wIndex || wIndex > max_ports) goto error; wIndex--; temp = xhci_readl(xhci, port_array[wIndex]); if (temp == 0xffffffff) { retval = -ENODEV; break; } /* FIXME: What new port features do we need to support? */ temp = xhci_port_state_to_neutral(temp); switch (wValue) { case USB_PORT_FEAT_SUSPEND: temp = xhci_readl(xhci, port_array[wIndex]); xhci_dbg(xhci, "clear USB_PORT_FEAT_SUSPEND\n"); xhci_dbg(xhci, "PORTSC %04x\n", temp); if (temp & PORT_RESET) goto error; if ((temp & PORT_PLS_MASK) == XDEV_U3) { if ((temp & PORT_PE) == 0) goto error; xhci_set_link_state(xhci, port_array, wIndex, XDEV_RESUME); spin_unlock_irqrestore(&xhci->lock, flags); msleep(20); spin_lock_irqsave(&xhci->lock, flags); xhci_set_link_state(xhci, port_array, wIndex, XDEV_U0); } bus_state->port_c_suspend |= 1 << wIndex; 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); break; case USB_PORT_FEAT_POWER: /* * Turn off ports even if there isn't per-port * swithing. HC will report connect events even * before this is set. However, khubd will ignore * the roothub events until the roothub is registered. */ xhci_writel(xhci, temp & ~PORT_POWER, port_array[wIndex]); temp = xhci_readl(xhci, port_array[wIndex]); xhci_dbg(xhci, "clear PP, port %d status = 0x%x\n", wIndex, temp); break; case USB_PORT_FEAT_C_SUSPEND: bus_state->port_c_suspend &= ~(1 << wIndex); case USB_PORT_FEAT_C_RESET: case USB_PORT_FEAT_C_BH_PORT_RESET: case USB_PORT_FEAT_C_CONNECTION: case USB_PORT_FEAT_C_OVER_CURRENT: case USB_PORT_FEAT_C_ENABLE: case USB_PORT_FEAT_C_PORT_LINK_STATE: xhci_clear_port_change_bit(xhci, wValue, wIndex, port_array[wIndex], temp); break; case USB_PORT_FEAT_ENABLE: xhci_disable_port(hcd, xhci, wIndex, port_array[wIndex], temp); break; default: goto error; } break; default: error: /* "stall" on error */ retval = -EPIPE; } spin_unlock_irqrestore(&xhci->lock, flags); return retval; }
int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, u16 wIndex, char *buf, u16 wLength) { struct xhci_hcd *xhci = hcd_to_xhci(hcd); int ports; unsigned long flags; u32 temp, status; int retval = 0; u32 __iomem *addr; ports = HCS_MAX_PORTS(xhci->hcs_params1); spin_lock_irqsave(&xhci->lock, flags); switch (typeReq) { case GetHubStatus: /* No power source, over-current reported per port */ memset(buf, 0, 4); break; case GetHubDescriptor: xhci_hub_descriptor(xhci, (struct usb_hub_descriptor *) buf); break; case GetPortStatus: if (!wIndex || wIndex > ports) goto error; wIndex--; status = 0; addr = &xhci->op_regs->port_status_base + NUM_PORT_REGS*(wIndex & 0xff); temp = xhci_readl(xhci, addr); xhci_dbg(xhci, "get port status, actual port %d status = 0x%x\n", wIndex, temp); /* 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_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) status |= USB_PORT_STAT_POWER; xhci_dbg(xhci, "Get port status returned 0x%x\n", status); put_unaligned(cpu_to_le32(status), (__le32 *) buf); break; case SetPortFeature: wIndex &= 0xff; if (!wIndex || wIndex > ports) goto error; wIndex--; addr = &xhci->op_regs->port_status_base + NUM_PORT_REGS*(wIndex & 0xff); temp = xhci_readl(xhci, addr); temp = xhci_port_state_to_neutral(temp); switch (wValue) { case USB_PORT_FEAT_POWER: printk(KERN_INFO "[xhci-hub] usb2mode:[%d]\n", usb2mode); /* * 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 * the roothub is registered. */ if(usb2mode==1) { printk(KERN_INFO "[xhci] USB3 port power off\n"); xhci_writel(xhci, (temp | PORT_PE), addr); xhci_writel(xhci, (temp & ~(PORT_POWER)), addr); xhci_writel(xhci, ((temp & ~PORT_PLS_MASK) | PORT_WR | PORT_PLC | XDEV_U0 | PORT_RESET | PORT_LINK_STROBE), addr); } else if(usb2mode==2) { printk(KERN_INFO "[xhci] USB3 port power off\n"); xhci_writel(xhci, (temp | PORT_PE), addr); xhci_writel(xhci, (temp | PORT_POWER), addr); xhci_writel(xhci, ((temp & ~PORT_PLS_MASK) | PORT_WR | PORT_PLC | XDEV_U0 | PORT_RESET | PORT_LINK_STROBE), addr); } else { xhci_writel(xhci, temp | PORT_POWER, addr); } temp = xhci_readl(xhci, addr); xhci_dbg(xhci, "set port power, actual port %d status = 0x%x\n", wIndex, temp); break; case USB_PORT_FEAT_RESET: temp = (temp | PORT_RESET); xhci_writel(xhci, temp, addr); temp = xhci_readl(xhci, addr); xhci_dbg(xhci, "set port reset, actual port %d status = 0x%x\n", wIndex, temp); break; default: goto error; } temp = xhci_readl(xhci, addr); /* unblock any posted writes */ break; case ClearPortFeature: if (!wIndex || wIndex > ports) goto error; wIndex--; addr = &xhci->op_regs->port_status_base + NUM_PORT_REGS*(wIndex & 0xff); temp = xhci_readl(xhci, addr); temp = xhci_port_state_to_neutral(temp); switch (wValue) { case USB_PORT_FEAT_C_RESET: case USB_PORT_FEAT_C_CONNECTION: case USB_PORT_FEAT_C_OVER_CURRENT: case USB_PORT_FEAT_C_ENABLE: xhci_clear_port_change_bit(xhci, wValue, wIndex, addr, temp); break; case USB_PORT_FEAT_ENABLE: xhci_disable_port(xhci, wIndex, addr, temp); break; default: goto error; } break; default: error: /* "stall" on error */ retval = -EPIPE; } spin_unlock_irqrestore(&xhci->lock, flags); return retval; }
static void xhci_print_cap_regs(struct xhci_hcd *xhci) { u32 temp; u32 hci_version; xhci_dbg(xhci, "xHCI capability registers at %p:\n", xhci->cap_regs); temp = readl(&xhci->cap_regs->hc_capbase); hci_version = HC_VERSION(temp); xhci_dbg(xhci, "CAPLENGTH AND HCIVERSION 0x%x:\n", (unsigned int) temp); xhci_dbg(xhci, "CAPLENGTH: 0x%x\n", (unsigned int) HC_LENGTH(temp)); xhci_dbg(xhci, "HCIVERSION: 0x%x\n", hci_version); temp = readl(&xhci->cap_regs->hcs_params1); xhci_dbg(xhci, "HCSPARAMS 1: 0x%x\n", (unsigned int) temp); xhci_dbg(xhci, " Max device slots: %u\n", (unsigned int) HCS_MAX_SLOTS(temp)); xhci_dbg(xhci, " Max interrupters: %u\n", (unsigned int) HCS_MAX_INTRS(temp)); xhci_dbg(xhci, " Max ports: %u\n", (unsigned int) HCS_MAX_PORTS(temp)); temp = readl(&xhci->cap_regs->hcs_params2); xhci_dbg(xhci, "HCSPARAMS 2: 0x%x\n", (unsigned int) temp); xhci_dbg(xhci, " Isoc scheduling threshold: %u\n", (unsigned int) HCS_IST(temp)); xhci_dbg(xhci, " Maximum allowed segments in event ring: %u\n", (unsigned int) HCS_ERST_MAX(temp)); temp = readl(&xhci->cap_regs->hcs_params3); xhci_dbg(xhci, "HCSPARAMS 3 0x%x:\n", (unsigned int) temp); xhci_dbg(xhci, " Worst case U1 device exit latency: %u\n", (unsigned int) HCS_U1_LATENCY(temp)); xhci_dbg(xhci, " Worst case U2 device exit latency: %u\n", (unsigned int) HCS_U2_LATENCY(temp)); temp = readl(&xhci->cap_regs->hcc_params); xhci_dbg(xhci, "HCC PARAMS 0x%x:\n", (unsigned int) temp); xhci_dbg(xhci, " HC generates %s bit addresses\n", HCC_64BIT_ADDR(temp) ? "64" : "32"); xhci_dbg(xhci, " HC %s Contiguous Frame ID Capability\n", HCC_CFC(temp) ? "has" : "hasn't"); xhci_dbg(xhci, " HC %s generate Stopped - Short Package event\n", HCC_SPC(temp) ? "can" : "can't"); /* FIXME */ xhci_dbg(xhci, " FIXME: more HCCPARAMS debugging\n"); temp = readl(&xhci->cap_regs->run_regs_off); xhci_dbg(xhci, "RTSOFF 0x%x:\n", temp & RTSOFF_MASK); /* xhci 1.1 controllers have the HCCPARAMS2 register */ if (hci_version > 0x100) { temp = readl(&xhci->cap_regs->hcc_params2); xhci_dbg(xhci, "HCC PARAMS2 0x%x:\n", (unsigned int) temp); xhci_dbg(xhci, " HC %s Force save context capability", HCC2_FSC(temp) ? "supports" : "doesn't support"); xhci_dbg(xhci, " HC %s Large ESIT Payload Capability", HCC2_LEC(temp) ? "supports" : "doesn't support"); xhci_dbg(xhci, " HC %s Extended TBC capability", HCC2_ETC(temp) ? "supports" : "doesn't support"); } }