xhci: Features for 3.12
In the spirit of "let's stop gossiping around the water cooler and get to work", here's some xHCI patches for 3.12. They include a patch for suspend/resume support for xhci platform hosts, two patches to support showing USB 2.1 link status, and a patch to future-proof the Intel EHCI to xHCI port switchover. Sarah Sharp -----BEGIN PGP SIGNATURE----- Version: GnuPG v1.4.11 (GNU/Linux) iQIcBAABAgAGBQJR7vxeAAoJEBMGWMLi1Gc56VoQAI9RwDPAjx5aqj2Gg1uAZmh6 x965vdIjrEd+ND5rLS/G1khkTWW7o0SW2rwwuGhMOsrPve6R+Dr+rXoFxvPSpyTZ 1F+eiSNX+lmPtbeSrdNo5u+787yNR0UuvfDP5uIrqcrA3lo6Xc5Sk3qQ7fqEd9rg iSAQ7WwAKgpO40QMOFwTLi257mDfGJPg5d8cwwa9OAe3a7DVFHYfxv1vxmDFlR/w KPgq38hjR5S8Npcl9mC2RpjQoj1e3oO+4kZJX2CPmrRWx7GWAGfg/alDSGzl3zwk A8juRlKQGiFb9LmFwtvtHqolJuBGtTdSj0jPe1MObCw6LWzQcF2RppnTFlt/JHxp mpBRG94QC0ssHkUFhBKIlQLpL1KQiyUiWsBUyjaxtiVUMzZSt482Wnhnwr5lr1sn /WHnVY5MeWuAyFVx79+2KgRRbEaL0OnEGqEaIf/tfZL7D7dbDMkcOsOALguAvI4a 33KKSeiIyNqWcRXhQ9lVVxlsfS6ZFHl9MZvqIbhfO3Uzd4HIW+EdXOo2zx7GIlUO Zds5bpiV8wpDXzVHkY6NMr8HJWrD7pmD22o8tm8wY+LzO4Vxjdyxbvi5waSfPzYv 5FGVd9qN9tAif0xlhDj7GN63cF0rjgoFfBkfZcP+Y0Tvk2Po9mobzLoQwerDI7uJ v5BIoDuGBaDWIycJMYO2 =K3xk -----END PGP SIGNATURE----- Merge tag 'for-usb-next-2013-07-23' of git://git.kernel.org/pub/scm/linux/kernel/git/sarah/xhci into usb-next Sarah writes: xhci: Features for 3.12 In the spirit of "let's stop gossiping around the water cooler and get to work", here's some xHCI patches for 3.12. They include a patch for suspend/resume support for xhci platform hosts, two patches to support showing USB 2.1 link status, and a patch to future-proof the Intel EHCI to xHCI port switchover. Sarah Sharp
This commit is contained in:
commit
7603dee3bd
7 changed files with 187 additions and 177 deletions
|
@ -315,53 +315,11 @@ done:
|
||||||
* Also they depend on separate root hub suspend/resume.
|
* Also they depend on separate root hub suspend/resume.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
static bool usb_is_intel_switchable_ehci(struct pci_dev *pdev)
|
|
||||||
{
|
|
||||||
return pdev->class == PCI_CLASS_SERIAL_USB_EHCI &&
|
|
||||||
pdev->vendor == PCI_VENDOR_ID_INTEL &&
|
|
||||||
(pdev->device == 0x1E26 ||
|
|
||||||
pdev->device == 0x8C2D ||
|
|
||||||
pdev->device == 0x8C26 ||
|
|
||||||
pdev->device == 0x9C26);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void ehci_enable_xhci_companion(void)
|
|
||||||
{
|
|
||||||
struct pci_dev *companion = NULL;
|
|
||||||
|
|
||||||
/* The xHCI and EHCI controllers are not on the same PCI slot */
|
|
||||||
for_each_pci_dev(companion) {
|
|
||||||
if (!usb_is_intel_switchable_xhci(companion))
|
|
||||||
continue;
|
|
||||||
usb_enable_xhci_ports(companion);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static int ehci_pci_resume(struct usb_hcd *hcd, bool hibernated)
|
static int ehci_pci_resume(struct usb_hcd *hcd, bool hibernated)
|
||||||
{
|
{
|
||||||
struct ehci_hcd *ehci = hcd_to_ehci(hcd);
|
struct ehci_hcd *ehci = hcd_to_ehci(hcd);
|
||||||
struct pci_dev *pdev = to_pci_dev(hcd->self.controller);
|
struct pci_dev *pdev = to_pci_dev(hcd->self.controller);
|
||||||
|
|
||||||
/* The BIOS on systems with the Intel Panther Point chipset may or may
|
|
||||||
* not support xHCI natively. That means that during system resume, it
|
|
||||||
* may switch the ports back to EHCI so that users can use their
|
|
||||||
* keyboard to select a kernel from GRUB after resume from hibernate.
|
|
||||||
*
|
|
||||||
* The BIOS is supposed to remember whether the OS had xHCI ports
|
|
||||||
* enabled before resume, and switch the ports back to xHCI when the
|
|
||||||
* BIOS/OS semaphore is written, but we all know we can't trust BIOS
|
|
||||||
* writers.
|
|
||||||
*
|
|
||||||
* Unconditionally switch the ports back to xHCI after a system resume.
|
|
||||||
* We can't tell whether the EHCI or xHCI controller will be resumed
|
|
||||||
* first, so we have to do the port switchover in both drivers. Writing
|
|
||||||
* a '1' to the port switchover registers should have no effect if the
|
|
||||||
* port was already switched over.
|
|
||||||
*/
|
|
||||||
if (usb_is_intel_switchable_ehci(pdev))
|
|
||||||
ehci_enable_xhci_companion();
|
|
||||||
|
|
||||||
if (ehci_resume(hcd, hibernated) != 0)
|
if (ehci_resume(hcd, hibernated) != 0)
|
||||||
(void) ehci_pci_reinit(ehci, pdev);
|
(void) ehci_pci_reinit(ehci, pdev);
|
||||||
return 0;
|
return 0;
|
||||||
|
|
|
@ -735,32 +735,6 @@ static int handshake(void __iomem *ptr, u32 mask, u32 done,
|
||||||
return -ETIMEDOUT;
|
return -ETIMEDOUT;
|
||||||
}
|
}
|
||||||
|
|
||||||
#define PCI_DEVICE_ID_INTEL_LYNX_POINT_XHCI 0x8C31
|
|
||||||
#define PCI_DEVICE_ID_INTEL_LYNX_POINT_LP_XHCI 0x9C31
|
|
||||||
|
|
||||||
bool usb_is_intel_ppt_switchable_xhci(struct pci_dev *pdev)
|
|
||||||
{
|
|
||||||
return pdev->class == PCI_CLASS_SERIAL_USB_XHCI &&
|
|
||||||
pdev->vendor == PCI_VENDOR_ID_INTEL &&
|
|
||||||
pdev->device == PCI_DEVICE_ID_INTEL_PANTHERPOINT_XHCI;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* The Intel Lynx Point chipset also has switchable ports. */
|
|
||||||
bool usb_is_intel_lpt_switchable_xhci(struct pci_dev *pdev)
|
|
||||||
{
|
|
||||||
return pdev->class == PCI_CLASS_SERIAL_USB_XHCI &&
|
|
||||||
pdev->vendor == PCI_VENDOR_ID_INTEL &&
|
|
||||||
(pdev->device == PCI_DEVICE_ID_INTEL_LYNX_POINT_XHCI ||
|
|
||||||
pdev->device == PCI_DEVICE_ID_INTEL_LYNX_POINT_LP_XHCI);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool usb_is_intel_switchable_xhci(struct pci_dev *pdev)
|
|
||||||
{
|
|
||||||
return usb_is_intel_ppt_switchable_xhci(pdev) ||
|
|
||||||
usb_is_intel_lpt_switchable_xhci(pdev);
|
|
||||||
}
|
|
||||||
EXPORT_SYMBOL_GPL(usb_is_intel_switchable_xhci);
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Intel's Panther Point chipset has two host controllers (EHCI and xHCI) that
|
* Intel's Panther Point chipset has two host controllers (EHCI and xHCI) that
|
||||||
* share some number of ports. These ports can be switched between either
|
* share some number of ports. These ports can be switched between either
|
||||||
|
@ -779,9 +753,23 @@ EXPORT_SYMBOL_GPL(usb_is_intel_switchable_xhci);
|
||||||
* terminations before switching the USB 2.0 wires over, so that USB 3.0
|
* terminations before switching the USB 2.0 wires over, so that USB 3.0
|
||||||
* devices connect at SuperSpeed, rather than at USB 2.0 speeds.
|
* devices connect at SuperSpeed, rather than at USB 2.0 speeds.
|
||||||
*/
|
*/
|
||||||
void usb_enable_xhci_ports(struct pci_dev *xhci_pdev)
|
void usb_enable_intel_xhci_ports(struct pci_dev *xhci_pdev)
|
||||||
{
|
{
|
||||||
u32 ports_available;
|
u32 ports_available;
|
||||||
|
bool ehci_found = false;
|
||||||
|
struct pci_dev *companion = NULL;
|
||||||
|
|
||||||
|
/* make sure an intel EHCI controller exists */
|
||||||
|
for_each_pci_dev(companion) {
|
||||||
|
if (companion->class == PCI_CLASS_SERIAL_USB_EHCI &&
|
||||||
|
companion->vendor == PCI_VENDOR_ID_INTEL) {
|
||||||
|
ehci_found = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!ehci_found)
|
||||||
|
return;
|
||||||
|
|
||||||
/* Don't switchover the ports if the user hasn't compiled the xHCI
|
/* Don't switchover the ports if the user hasn't compiled the xHCI
|
||||||
* driver. Otherwise they will see "dead" USB ports that don't power
|
* driver. Otherwise they will see "dead" USB ports that don't power
|
||||||
|
@ -840,7 +828,7 @@ void usb_enable_xhci_ports(struct pci_dev *xhci_pdev)
|
||||||
dev_dbg(&xhci_pdev->dev, "USB 2.0 ports that are now switched over "
|
dev_dbg(&xhci_pdev->dev, "USB 2.0 ports that are now switched over "
|
||||||
"to xHCI: 0x%x\n", ports_available);
|
"to xHCI: 0x%x\n", ports_available);
|
||||||
}
|
}
|
||||||
EXPORT_SYMBOL_GPL(usb_enable_xhci_ports);
|
EXPORT_SYMBOL_GPL(usb_enable_intel_xhci_ports);
|
||||||
|
|
||||||
void usb_disable_xhci_ports(struct pci_dev *xhci_pdev)
|
void usb_disable_xhci_ports(struct pci_dev *xhci_pdev)
|
||||||
{
|
{
|
||||||
|
@ -921,8 +909,8 @@ static void quirk_usb_handoff_xhci(struct pci_dev *pdev)
|
||||||
writel(val, base + ext_cap_offset + XHCI_LEGACY_CONTROL_OFFSET);
|
writel(val, base + ext_cap_offset + XHCI_LEGACY_CONTROL_OFFSET);
|
||||||
|
|
||||||
hc_init:
|
hc_init:
|
||||||
if (usb_is_intel_switchable_xhci(pdev))
|
if (pdev->vendor == PCI_VENDOR_ID_INTEL)
|
||||||
usb_enable_xhci_ports(pdev);
|
usb_enable_intel_xhci_ports(pdev);
|
||||||
|
|
||||||
op_reg_base = base + XHCI_HC_LENGTH(readl(base));
|
op_reg_base = base + XHCI_HC_LENGTH(readl(base));
|
||||||
|
|
||||||
|
|
|
@ -8,8 +8,7 @@ int usb_amd_find_chipset_info(void);
|
||||||
void usb_amd_dev_put(void);
|
void usb_amd_dev_put(void);
|
||||||
void usb_amd_quirk_pll_disable(void);
|
void usb_amd_quirk_pll_disable(void);
|
||||||
void usb_amd_quirk_pll_enable(void);
|
void usb_amd_quirk_pll_enable(void);
|
||||||
bool usb_is_intel_switchable_xhci(struct pci_dev *pdev);
|
void usb_enable_intel_xhci_ports(struct pci_dev *xhci_pdev);
|
||||||
void usb_enable_xhci_ports(struct pci_dev *xhci_pdev);
|
|
||||||
void usb_disable_xhci_ports(struct pci_dev *xhci_pdev);
|
void usb_disable_xhci_ports(struct pci_dev *xhci_pdev);
|
||||||
void sb800_prefetch(struct device *dev, int on);
|
void sb800_prefetch(struct device *dev, int on);
|
||||||
#else
|
#else
|
||||||
|
|
|
@ -461,8 +461,15 @@ void xhci_test_and_clear_bit(struct xhci_hcd *xhci, __le32 __iomem **port_array,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* 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 */
|
/* Updates Link Status for super Speed port */
|
||||||
static void xhci_hub_report_link_state(u32 *status, u32 status_reg)
|
static void xhci_hub_report_usb3_link_state(u32 *status, u32 status_reg)
|
||||||
{
|
{
|
||||||
u32 pls = status_reg & PORT_PLS_MASK;
|
u32 pls = status_reg & PORT_PLS_MASK;
|
||||||
|
|
||||||
|
@ -534,6 +541,120 @@ void xhci_del_comp_mod_timer(struct xhci_hcd *xhci, u32 status, u16 wIndex)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
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)
|
||||||
|
{
|
||||||
|
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) {
|
||||||
|
if ((raw_port_status & PORT_PLC))
|
||||||
|
status |= USB_PORT_STAT_C_LINK_STATE << 16;
|
||||||
|
if ((raw_port_status & PORT_WRC))
|
||||||
|
status |= USB_PORT_STAT_C_BH_RESET << 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(raw_port_status)) {
|
||||||
|
if ((raw_port_status & PORT_RESET) ||
|
||||||
|
!(raw_port_status & PORT_PE))
|
||||||
|
return 0xffffffff;
|
||||||
|
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");
|
||||||
|
return 0xffffffff;
|
||||||
|
}
|
||||||
|
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 ((raw_port_status & PORT_PLS_MASK) == XDEV_U0
|
||||||
|
&& (raw_port_status & 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 (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(&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,
|
int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue,
|
||||||
u16 wIndex, char *buf, u16 wLength)
|
u16 wIndex, char *buf, u16 wLength)
|
||||||
{
|
{
|
||||||
|
@ -598,104 +719,20 @@ int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue,
|
||||||
if (!wIndex || wIndex > max_ports)
|
if (!wIndex || wIndex > max_ports)
|
||||||
goto error;
|
goto error;
|
||||||
wIndex--;
|
wIndex--;
|
||||||
status = 0;
|
|
||||||
temp = xhci_readl(xhci, port_array[wIndex]);
|
temp = xhci_readl(xhci, port_array[wIndex]);
|
||||||
if (temp == 0xffffffff) {
|
if (temp == 0xffffffff) {
|
||||||
retval = -ENODEV;
|
retval = -ENODEV;
|
||||||
break;
|
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);
|
||||||
/* wPortChange bits */
|
if (status == 0xffffffff)
|
||||||
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;
|
goto error;
|
||||||
if (time_after_eq(jiffies,
|
|
||||||
bus_state->resume_done[wIndex])) {
|
xhci_dbg(xhci, "get port status, actual port %d status = 0x%x\n",
|
||||||
xhci_dbg(xhci, "Resume USB2 port %d\n",
|
wIndex, temp);
|
||||||
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);
|
xhci_dbg(xhci, "Get port status returned 0x%x\n", status);
|
||||||
|
|
||||||
put_unaligned(cpu_to_le32(status), (__le32 *) buf);
|
put_unaligned(cpu_to_le32(status), (__le32 *) buf);
|
||||||
break;
|
break;
|
||||||
case SetPortFeature:
|
case SetPortFeature:
|
||||||
|
|
|
@ -250,13 +250,15 @@ static int xhci_pci_resume(struct usb_hcd *hcd, bool hibernated)
|
||||||
* writers.
|
* writers.
|
||||||
*
|
*
|
||||||
* Unconditionally switch the ports back to xHCI after a system resume.
|
* Unconditionally switch the ports back to xHCI after a system resume.
|
||||||
* We can't tell whether the EHCI or xHCI controller will be resumed
|
* It should not matter whether the EHCI or xHCI controller is
|
||||||
* first, so we have to do the port switchover in both drivers. Writing
|
* resumed first. It's enough to do the switchover in xHCI because
|
||||||
* a '1' to the port switchover registers should have no effect if the
|
* USB core won't notice anything as the hub driver doesn't start
|
||||||
* port was already switched over.
|
* running again until after all the devices (including both EHCI and
|
||||||
|
* xHCI host controllers) have been resumed.
|
||||||
*/
|
*/
|
||||||
if (usb_is_intel_switchable_xhci(pdev))
|
|
||||||
usb_enable_xhci_ports(pdev);
|
if (pdev->vendor == PCI_VENDOR_ID_INTEL)
|
||||||
|
usb_enable_intel_xhci_ports(pdev);
|
||||||
|
|
||||||
retval = xhci_resume(xhci, hibernated);
|
retval = xhci_resume(xhci, hibernated);
|
||||||
return retval;
|
return retval;
|
||||||
|
|
|
@ -186,11 +186,37 @@ static int xhci_plat_remove(struct platform_device *dev)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef CONFIG_PM
|
||||||
|
static int xhci_plat_suspend(struct device *dev)
|
||||||
|
{
|
||||||
|
struct usb_hcd *hcd = dev_get_drvdata(dev);
|
||||||
|
struct xhci_hcd *xhci = hcd_to_xhci(hcd);
|
||||||
|
|
||||||
|
return xhci_suspend(xhci);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int xhci_plat_resume(struct device *dev)
|
||||||
|
{
|
||||||
|
struct usb_hcd *hcd = dev_get_drvdata(dev);
|
||||||
|
struct xhci_hcd *xhci = hcd_to_xhci(hcd);
|
||||||
|
|
||||||
|
return xhci_resume(xhci, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
static const struct dev_pm_ops xhci_plat_pm_ops = {
|
||||||
|
SET_SYSTEM_SLEEP_PM_OPS(xhci_plat_suspend, xhci_plat_resume)
|
||||||
|
};
|
||||||
|
#define DEV_PM_OPS (&xhci_plat_pm_ops)
|
||||||
|
#else
|
||||||
|
#define DEV_PM_OPS NULL
|
||||||
|
#endif /* CONFIG_PM */
|
||||||
|
|
||||||
static struct platform_driver usb_xhci_driver = {
|
static struct platform_driver usb_xhci_driver = {
|
||||||
.probe = xhci_plat_probe,
|
.probe = xhci_plat_probe,
|
||||||
.remove = xhci_plat_remove,
|
.remove = xhci_plat_remove,
|
||||||
.driver = {
|
.driver = {
|
||||||
.name = "xhci-hcd",
|
.name = "xhci-hcd",
|
||||||
|
.pm = DEV_PM_OPS,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
MODULE_ALIAS("platform:xhci-hcd");
|
MODULE_ALIAS("platform:xhci-hcd");
|
||||||
|
|
|
@ -3075,8 +3075,8 @@ static u32 xhci_calculate_no_streams_bitmask(struct xhci_hcd *xhci,
|
||||||
/* Are streams already being freed for the endpoint? */
|
/* Are streams already being freed for the endpoint? */
|
||||||
if (ep_state & EP_GETTING_NO_STREAMS) {
|
if (ep_state & EP_GETTING_NO_STREAMS) {
|
||||||
xhci_warn(xhci, "WARN Can't disable streams for "
|
xhci_warn(xhci, "WARN Can't disable streams for "
|
||||||
"endpoint 0x%x\n, "
|
"endpoint 0x%x, "
|
||||||
"streams are being disabled already.",
|
"streams are being disabled already\n",
|
||||||
eps[i]->desc.bEndpointAddress);
|
eps[i]->desc.bEndpointAddress);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -3084,8 +3084,8 @@ static u32 xhci_calculate_no_streams_bitmask(struct xhci_hcd *xhci,
|
||||||
if (!(ep_state & EP_HAS_STREAMS) &&
|
if (!(ep_state & EP_HAS_STREAMS) &&
|
||||||
!(ep_state & EP_GETTING_STREAMS)) {
|
!(ep_state & EP_GETTING_STREAMS)) {
|
||||||
xhci_warn(xhci, "WARN Can't disable streams for "
|
xhci_warn(xhci, "WARN Can't disable streams for "
|
||||||
"endpoint 0x%x\n, "
|
"endpoint 0x%x, "
|
||||||
"streams are already disabled!",
|
"streams are already disabled!\n",
|
||||||
eps[i]->desc.bEndpointAddress);
|
eps[i]->desc.bEndpointAddress);
|
||||||
xhci_warn(xhci, "WARN xhci_free_streams() called "
|
xhci_warn(xhci, "WARN xhci_free_streams() called "
|
||||||
"with non-streams endpoint\n");
|
"with non-streams endpoint\n");
|
||||||
|
@ -4353,7 +4353,7 @@ static u16 xhci_get_timeout_no_hub_lpm(struct usb_device *udev,
|
||||||
state_name, sel);
|
state_name, sel);
|
||||||
else
|
else
|
||||||
dev_dbg(&udev->dev, "Device-initiated %s disabled "
|
dev_dbg(&udev->dev, "Device-initiated %s disabled "
|
||||||
"due to long PEL %llu\n ms",
|
"due to long PEL %llu ms\n",
|
||||||
state_name, pel);
|
state_name, pel);
|
||||||
return USB3_LPM_DISABLED;
|
return USB3_LPM_DISABLED;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Reference in a new issue