Kyle McMartin 9ecbc0
commit 5e5563661948c57f72cc16b3a0cc5dc205ed4900
Kyle McMartin 9ecbc0
Author: Andiry Xu <andiry.xu@amd.com>
Kyle McMartin 9ecbc0
Date:   Thu Oct 14 07:23:06 2010 -0700
Kyle McMartin 9ecbc0
Kyle McMartin 9ecbc0
    USB: xHCI: PCI power management implementation
Kyle McMartin 9ecbc0
    
Kyle McMartin 9ecbc0
    This patch implements the PCI suspend/resume.
Kyle McMartin 9ecbc0
    
Kyle McMartin 9ecbc0
    Please refer to xHCI spec for doing the suspend/resume operation.
Kyle McMartin 9ecbc0
    
Kyle McMartin 9ecbc0
    For S3, CSS/SRS in USBCMD is used to save/restore the internal state.
Kyle McMartin 9ecbc0
    However, an error maybe occurs while restoring the internal state.
Kyle McMartin 9ecbc0
    In this case, it means that HC internal state is wrong and HC will be
Kyle McMartin 9ecbc0
    re-initialized.
Kyle McMartin 9ecbc0
    
Kyle McMartin 9ecbc0
    Signed-off-by: Libin Yang <libin.yang@amd.com>
Kyle McMartin 9ecbc0
    Signed-off-by: Dong Nguyen <dong.nguyen@amd.com>
Kyle McMartin 9ecbc0
    Signed-off-by: Andiry Xu <andiry.xu@amd.com>
Kyle McMartin 9ecbc0
    Signed-off-by: Sarah Sharp <sarah.a.sharp@linux.intel.com>
Kyle McMartin 9ecbc0
    Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
Kyle McMartin 9ecbc0
Kyle McMartin 9ecbc0
commit 96520f33d383c9a3ba1ca571cac5fa75325728f5
Kyle McMartin 9ecbc0
Author: Andiry Xu <andiry.xu@amd.com>
Kyle McMartin 9ecbc0
Date:   Thu Oct 14 07:23:03 2010 -0700
Kyle McMartin 9ecbc0
Kyle McMartin 9ecbc0
    USB: xHCI: bus power management implementation
Kyle McMartin 9ecbc0
    
Kyle McMartin 9ecbc0
    This patch implements xHCI bus suspend/resume function hook.
Kyle McMartin 9ecbc0
    
Kyle McMartin 9ecbc0
    In the patch it goes through all the ports and suspend/resume
Kyle McMartin 9ecbc0
    the ports if needed.
Kyle McMartin 9ecbc0
    
Kyle McMartin 9ecbc0
    If any port is in remote wakeup, abort bus suspend as what ehci/ohci do.
Kyle McMartin 9ecbc0
    
Kyle McMartin 9ecbc0
    Signed-off-by: Libin Yang <libin.yang@amd.com>
Kyle McMartin 9ecbc0
    Signed-off-by: Crane Cai <crane.cai@amd.com>
Kyle McMartin 9ecbc0
    Signed-off-by: Andiry Xu <andiry.xu@amd.com>
Kyle McMartin 9ecbc0
    Signed-off-by: Sarah Sharp <sarah.a.sharp@linux.intel.com>
Kyle McMartin 9ecbc0
    Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
Kyle McMartin 9ecbc0
Kyle McMartin 9ecbc0
commit 7b29198e193ab6f5e8bfcd48c59340b8c7689f5c
Kyle McMartin 9ecbc0
Author: Andiry Xu <andiry.xu@amd.com>
Kyle McMartin 9ecbc0
Date:   Thu Oct 14 07:23:00 2010 -0700
Kyle McMartin 9ecbc0
Kyle McMartin 9ecbc0
    USB: xHCI: port remote wakeup implementation
Kyle McMartin 9ecbc0
    
Kyle McMartin 9ecbc0
    This commit implements port remote wakeup.
Kyle McMartin 9ecbc0
    
Kyle McMartin 9ecbc0
    When a port is in U3 state and resume signaling is detected from a device,
Kyle McMartin 9ecbc0
    the port transitions to the Resume state, and the xHC generates a Port Status
Kyle McMartin 9ecbc0
    Change Event.
Kyle McMartin 9ecbc0
    
Kyle McMartin 9ecbc0
    For USB3 port, software write a '0' to the PLS field to complete the resume
Kyle McMartin 9ecbc0
    signaling. For USB2 port, the resume should be signaling for at least 20ms,
Kyle McMartin 9ecbc0
    irq handler set a timer for port remote wakeup, and then finishes process in
Kyle McMartin 9ecbc0
    hub_control GetPortStatus.
Kyle McMartin 9ecbc0
    
Kyle McMartin 9ecbc0
    Some codes are borrowed from EHCI code.
Kyle McMartin 9ecbc0
    
Kyle McMartin 9ecbc0
    Signed-off-by: Andiry Xu <andiry.xu@amd.com>
Kyle McMartin 9ecbc0
    Signed-off-by: Sarah Sharp <sarah.a.sharp@linux.intel.com>
Kyle McMartin 9ecbc0
    Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
Kyle McMartin 9ecbc0
Kyle McMartin 9ecbc0
commit 9ada0dec259dfe796a757ff2c9b63a05e6408e5f
Kyle McMartin 9ecbc0
Author: Andiry Xu <andiry.xu@amd.com>
Kyle McMartin 9ecbc0
Date:   Thu Oct 14 07:22:57 2010 -0700
Kyle McMartin 9ecbc0
Kyle McMartin 9ecbc0
    USB: xHCI: port power management implementation
Kyle McMartin 9ecbc0
    
Kyle McMartin 9ecbc0
    Add software trigger USB device suspend resume function hook.
Kyle McMartin 9ecbc0
    Do port suspend & resume in terms of xHCI spec.
Kyle McMartin 9ecbc0
    
Kyle McMartin 9ecbc0
    Port Suspend:
Kyle McMartin 9ecbc0
    Stop all endpoints via Stop Endpoint Command with Suspend (SP) flag set.
Kyle McMartin 9ecbc0
    Place individual ports into suspend mode by writing '3' for Port Link State
Kyle McMartin 9ecbc0
    (PLS) field into PORTSC register. This can only be done when the port is in
Kyle McMartin 9ecbc0
    Enabled state. When writing, the Port Link State Write Strobe (LWS) bit shall
Kyle McMartin 9ecbc0
    be set to '1'.
Kyle McMartin 9ecbc0
    Allocate an xhci_command and stash it in xhci_virt_device to wait completion for
Kyle McMartin 9ecbc0
    the last Stop Endpoint Command.  Use the Suspend bit in TRB to indicate the Stop
Kyle McMartin 9ecbc0
    Endpoint Command is for port suspend. Based on Sarah's suggestion.
Kyle McMartin 9ecbc0
    
Kyle McMartin 9ecbc0
    Port Resume:
Kyle McMartin 9ecbc0
    Write '0' in PLS field, device will transition to running state.
Kyle McMartin 9ecbc0
    Ring an endpoints' doorbell to restart it.
Kyle McMartin 9ecbc0
    
Kyle McMartin 9ecbc0
    Ref: USB device remote wake need another patch to implement. For details of
Kyle McMartin 9ecbc0
    how USB subsystem do power management, please see:
Kyle McMartin 9ecbc0
        Documentation/usb/power-management.txt
Kyle McMartin 9ecbc0
    
Kyle McMartin 9ecbc0
    Signed-off-by: Crane Cai <crane.cai@amd.com>
Kyle McMartin 9ecbc0
    Signed-off-by: Libin Yang <libin.yang@amd.com>
Kyle McMartin 9ecbc0
    Signed-off-by: Andiry Xu <andiry.xu@amd.com>
Kyle McMartin 9ecbc0
    Signed-off-by: Sarah Sharp <sarah.a.sharp@linux.intel.com>
Kyle McMartin 9ecbc0
    Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
Kyle McMartin 9ecbc0
Kyle McMartin 9ecbc0
 drivers/usb/host/xhci-hub.c  |  424 +++++++++++++++++++++++++++++++++++++++++-
Kyle McMartin 9ecbc0
 drivers/usb/host/xhci-mem.c  |    4 +
Kyle McMartin 9ecbc0
 drivers/usb/host/xhci-pci.c  |   36 ++++-
Kyle McMartin 9ecbc0
 drivers/usb/host/xhci-ring.c |  101 +++++++++-
Kyle McMartin 9ecbc0
 drivers/usb/host/xhci.c      |  212 +++++++++++++++++++++-
Kyle McMartin 9ecbc0
 drivers/usb/host/xhci.h      |   46 +++++-
Kyle McMartin 9ecbc0
 6 files changed, 805 insertions(+), 18 deletions(-)
Kyle McMartin 9ecbc0
Kyle McMartin 9ecbc0
diff --git a/drivers/usb/host/xhci-hub.c b/drivers/usb/host/xhci-hub.c
Kyle McMartin 9ecbc0
index a1a7a97..7f2f63c 100644
Kyle McMartin 9ecbc0
--- a/drivers/usb/host/xhci-hub.c
Kyle McMartin 9ecbc0
+++ b/drivers/usb/host/xhci-hub.c
Kyle McMartin 9ecbc0
@@ -24,6 +24,10 @@
Kyle McMartin 9ecbc0
 
Kyle McMartin 9ecbc0
 #include "xhci.h"
Kyle McMartin 9ecbc0
 
Kyle McMartin 9ecbc0
+#define	PORT_WAKE_BITS	(PORT_WKOC_E | PORT_WKDISC_E | PORT_WKCONN_E)
Kyle McMartin 9ecbc0
+#define	PORT_RWC_BITS	(PORT_CSC | PORT_PEC | PORT_WRC | PORT_OCC | \
Kyle McMartin 9ecbc0
+			 PORT_RC | PORT_PLC | PORT_PE)
Kyle McMartin 9ecbc0
+
Kyle McMartin 9ecbc0
 static void xhci_hub_descriptor(struct xhci_hcd *xhci,
Kyle McMartin 9ecbc0
 		struct usb_hub_descriptor *desc)
Kyle McMartin 9ecbc0
 {
Kyle McMartin 9ecbc0
@@ -123,12 +127,105 @@ static unsigned int xhci_port_speed(unsigned int port_status)
Kyle McMartin 9ecbc0
  * writing a 0 clears the bit and writing a 1 sets the bit (RWS).
Kyle McMartin 9ecbc0
  * For all other types (RW1S, RW1CS, RW, and RZ), writing a '0' has no effect.
Kyle McMartin 9ecbc0
  */
Kyle McMartin 9ecbc0
-static u32 xhci_port_state_to_neutral(u32 state)
Kyle McMartin 9ecbc0
+u32 xhci_port_state_to_neutral(u32 state)
Kyle McMartin 9ecbc0
 {
Kyle McMartin 9ecbc0
 	/* Save read-only status and port state */
Kyle McMartin 9ecbc0
 	return (state & XHCI_PORT_RO) | (state & XHCI_PORT_RWS);
Kyle McMartin 9ecbc0
 }
Kyle McMartin 9ecbc0
 
Kyle McMartin 9ecbc0
+/*
Kyle McMartin 9ecbc0
+ * find slot id based on port number.
Kyle McMartin 9ecbc0
+ */
Kyle McMartin 9ecbc0
+int xhci_find_slot_id_by_port(struct xhci_hcd *xhci, u16 port)
Kyle McMartin 9ecbc0
+{
Kyle McMartin 9ecbc0
+	int slot_id;
Kyle McMartin 9ecbc0
+	int i;
Kyle McMartin 9ecbc0
+
Kyle McMartin 9ecbc0
+	slot_id = 0;
Kyle McMartin 9ecbc0
+	for (i = 0; i < MAX_HC_SLOTS; i++) {
Kyle McMartin 9ecbc0
+		if (!xhci->devs[i])
Kyle McMartin 9ecbc0
+			continue;
Kyle McMartin 9ecbc0
+		if (xhci->devs[i]->port == port) {
Kyle McMartin 9ecbc0
+			slot_id = i;
Kyle McMartin 9ecbc0
+			break;
Kyle McMartin 9ecbc0
+		}
Kyle McMartin 9ecbc0
+	}
Kyle McMartin 9ecbc0
+
Kyle McMartin 9ecbc0
+	return slot_id;
Kyle McMartin 9ecbc0
+}
Kyle McMartin 9ecbc0
+
Kyle McMartin 9ecbc0
+/*
Kyle McMartin 9ecbc0
+ * Stop device
Kyle McMartin 9ecbc0
+ * It issues stop endpoint command for EP 0 to 30. And wait the last command
Kyle McMartin 9ecbc0
+ * to complete.
Kyle McMartin 9ecbc0
+ * suspend will set to 1, if suspend bit need to set in command.
Kyle McMartin 9ecbc0
+ */
Kyle McMartin 9ecbc0
+static int xhci_stop_device(struct xhci_hcd *xhci, int slot_id, int suspend)
Kyle McMartin 9ecbc0
+{
Kyle McMartin 9ecbc0
+	struct xhci_virt_device *virt_dev;
Kyle McMartin 9ecbc0
+	struct xhci_command *cmd;
Kyle McMartin 9ecbc0
+	unsigned long flags;
Kyle McMartin 9ecbc0
+	int timeleft;
Kyle McMartin 9ecbc0
+	int ret;
Kyle McMartin 9ecbc0
+	int i;
Kyle McMartin 9ecbc0
+
Kyle McMartin 9ecbc0
+	ret = 0;
Kyle McMartin 9ecbc0
+	virt_dev = xhci->devs[slot_id];
Kyle McMartin 9ecbc0
+	cmd = xhci_alloc_command(xhci, false, true, GFP_NOIO);
Kyle McMartin 9ecbc0
+	if (!cmd) {
Kyle McMartin 9ecbc0
+		xhci_dbg(xhci, "Couldn't allocate command structure.\n");
Kyle McMartin 9ecbc0
+		return -ENOMEM;
Kyle McMartin 9ecbc0
+	}
Kyle McMartin 9ecbc0
+
Kyle McMartin 9ecbc0
+	spin_lock_irqsave(&xhci->lock, flags);
Kyle McMartin 9ecbc0
+	for (i = LAST_EP_INDEX; i > 0; i--) {
Kyle McMartin 9ecbc0
+		if (virt_dev->eps[i].ring && virt_dev->eps[i].ring->dequeue)
Kyle McMartin 9ecbc0
+			xhci_queue_stop_endpoint(xhci, slot_id, i, suspend);
Kyle McMartin 9ecbc0
+	}
Kyle McMartin 9ecbc0
+	cmd->command_trb = xhci->cmd_ring->enqueue;
Kyle McMartin 9ecbc0
+	list_add_tail(&cmd->cmd_list, &virt_dev->cmd_list);
Kyle McMartin 9ecbc0
+	xhci_queue_stop_endpoint(xhci, slot_id, 0, suspend);
Kyle McMartin 9ecbc0
+	xhci_ring_cmd_db(xhci);
Kyle McMartin 9ecbc0
+	spin_unlock_irqrestore(&xhci->lock, flags);
Kyle McMartin 9ecbc0
+
Kyle McMartin 9ecbc0
+	/* Wait for last stop endpoint command to finish */
Kyle McMartin 9ecbc0
+	timeleft = wait_for_completion_interruptible_timeout(
Kyle McMartin 9ecbc0
+			cmd->completion,
Kyle McMartin 9ecbc0
+			USB_CTRL_SET_TIMEOUT);
Kyle McMartin 9ecbc0
+	if (timeleft <= 0) {
Kyle McMartin 9ecbc0
+		xhci_warn(xhci, "%s while waiting for stop endpoint command\n",
Kyle McMartin 9ecbc0
+				timeleft == 0 ? "Timeout" : "Signal");
Kyle McMartin 9ecbc0
+		spin_lock_irqsave(&xhci->lock, flags);
Kyle McMartin 9ecbc0
+		/* The timeout might have raced with the event ring handler, so
Kyle McMartin 9ecbc0
+		 * only delete from the list if the item isn't poisoned.
Kyle McMartin 9ecbc0
+		 */
Kyle McMartin 9ecbc0
+		if (cmd->cmd_list.next != LIST_POISON1)
Kyle McMartin 9ecbc0
+			list_del(&cmd->cmd_list);
Kyle McMartin 9ecbc0
+		spin_unlock_irqrestore(&xhci->lock, flags);
Kyle McMartin 9ecbc0
+		ret = -ETIME;
Kyle McMartin 9ecbc0
+		goto command_cleanup;
Kyle McMartin 9ecbc0
+	}
Kyle McMartin 9ecbc0
+
Kyle McMartin 9ecbc0
+command_cleanup:
Kyle McMartin 9ecbc0
+	xhci_free_command(xhci, cmd);
Kyle McMartin 9ecbc0
+	return ret;
Kyle McMartin 9ecbc0
+}
Kyle McMartin 9ecbc0
+
Kyle McMartin 9ecbc0
+/*
Kyle McMartin 9ecbc0
+ * Ring device, it rings the all doorbells unconditionally.
Kyle McMartin 9ecbc0
+ */
Kyle McMartin 9ecbc0
+void xhci_ring_device(struct xhci_hcd *xhci, int slot_id)
Kyle McMartin 9ecbc0
+{
Kyle McMartin 9ecbc0
+	int i;
Kyle McMartin 9ecbc0
+
Kyle McMartin 9ecbc0
+	for (i = 0; i < LAST_EP_INDEX + 1; i++)
Kyle McMartin 9ecbc0
+		if (xhci->devs[slot_id]->eps[i].ring &&
Kyle McMartin 9ecbc0
+		    xhci->devs[slot_id]->eps[i].ring->dequeue)
Kyle McMartin 9ecbc0
+			xhci_ring_ep_doorbell(xhci, slot_id, i, 0);
Kyle McMartin 9ecbc0
+
Kyle McMartin 9ecbc0
+	return;
Kyle McMartin 9ecbc0
+}
Kyle McMartin 9ecbc0
+
Kyle McMartin 9ecbc0
 static void xhci_disable_port(struct xhci_hcd *xhci, u16 wIndex,
Kyle McMartin 9ecbc0
 		u32 __iomem *addr, u32 port_status)
Kyle McMartin 9ecbc0
 {
Kyle McMartin 9ecbc0
@@ -162,6 +259,10 @@ static void xhci_clear_port_change_bit(struct xhci_hcd *xhci, u16 wValue,
Kyle McMartin 9ecbc0
 		status = PORT_PEC;
Kyle McMartin 9ecbc0
 		port_change_bit = "enable/disable";
Kyle McMartin 9ecbc0
 		break;
Kyle McMartin 9ecbc0
+	case USB_PORT_FEAT_C_SUSPEND:
Kyle McMartin 9ecbc0
+		status = PORT_PLC;
Kyle McMartin 9ecbc0
+		port_change_bit = "suspend/resume";
Kyle McMartin 9ecbc0
+		break;
Kyle McMartin 9ecbc0
 	default:
Kyle McMartin 9ecbc0
 		/* Should never happen */
Kyle McMartin 9ecbc0
 		return;
Kyle McMartin 9ecbc0
@@ -179,9 +280,10 @@ int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue,
Kyle McMartin 9ecbc0
 	struct xhci_hcd	*xhci = hcd_to_xhci(hcd);
Kyle McMartin 9ecbc0
 	int ports;
Kyle McMartin 9ecbc0
 	unsigned long flags;
Kyle McMartin 9ecbc0
-	u32 temp, status;
Kyle McMartin 9ecbc0
+	u32 temp, temp1, status;
Kyle McMartin 9ecbc0
 	int retval = 0;
Kyle McMartin 9ecbc0
 	u32 __iomem *addr;
Kyle McMartin 9ecbc0
+	int slot_id;
Kyle McMartin 9ecbc0
 
Kyle McMartin 9ecbc0
 	ports = HCS_MAX_PORTS(xhci->hcs_params1);
Kyle McMartin 9ecbc0
 
Kyle McMartin 9ecbc0
@@ -211,9 +313,49 @@ int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue,
Kyle McMartin 9ecbc0
 		if ((temp & PORT_OCC))
Kyle McMartin 9ecbc0
 			status |= USB_PORT_STAT_C_OVERCURRENT << 16;
Kyle McMartin 9ecbc0
 		/*
Kyle McMartin 9ecbc0
-		 * FIXME ignoring suspend, reset, and USB 2.1/3.0 specific
Kyle McMartin 9ecbc0
+		 * FIXME ignoring reset and USB 2.1/3.0 specific
Kyle McMartin 9ecbc0
 		 * changes
Kyle McMartin 9ecbc0
 		 */
Kyle McMartin 9ecbc0
+		if ((temp & PORT_PLS_MASK) == XDEV_U3
Kyle McMartin 9ecbc0
+			&& (temp & PORT_POWER))
Kyle McMartin 9ecbc0
+			status |= 1 << USB_PORT_FEAT_SUSPEND;
Kyle McMartin 9ecbc0
+		if ((temp & PORT_PLS_MASK) == XDEV_RESUME) {
Kyle McMartin 9ecbc0
+			if ((temp & PORT_RESET) || !(temp & PORT_PE))
Kyle McMartin 9ecbc0
+				goto error;
Kyle McMartin 9ecbc0
+			if (!DEV_SUPERSPEED(temp) && time_after_eq(jiffies,
Kyle McMartin 9ecbc0
+						xhci->resume_done[wIndex])) {
Kyle McMartin 9ecbc0
+				xhci_dbg(xhci, "Resume USB2 port %d\n",
Kyle McMartin 9ecbc0
+					wIndex + 1);
Kyle McMartin 9ecbc0
+				xhci->resume_done[wIndex] = 0;
Kyle McMartin 9ecbc0
+				temp1 = xhci_port_state_to_neutral(temp);
Kyle McMartin 9ecbc0
+				temp1 &= ~PORT_PLS_MASK;
Kyle McMartin 9ecbc0
+				temp1 |= PORT_LINK_STROBE | XDEV_U0;
Kyle McMartin 9ecbc0
+				xhci_writel(xhci, temp1, addr);
Kyle McMartin 9ecbc0
+
Kyle McMartin 9ecbc0
+				xhci_dbg(xhci, "set port %d resume\n",
Kyle McMartin 9ecbc0
+					wIndex + 1);
Kyle McMartin 9ecbc0
+				slot_id = xhci_find_slot_id_by_port(xhci,
Kyle McMartin 9ecbc0
+								 wIndex + 1);
Kyle McMartin 9ecbc0
+				if (!slot_id) {
Kyle McMartin 9ecbc0
+					xhci_dbg(xhci, "slot_id is zero\n");
Kyle McMartin 9ecbc0
+					goto error;
Kyle McMartin 9ecbc0
+				}
Kyle McMartin 9ecbc0
+				xhci_ring_device(xhci, slot_id);
Kyle McMartin 9ecbc0
+				xhci->port_c_suspend[wIndex >> 5] |=
Kyle McMartin 9ecbc0
+						1 << (wIndex & 31);
Kyle McMartin 9ecbc0
+				xhci->suspended_ports[wIndex >> 5] &=
Kyle McMartin 9ecbc0
+						~(1 << (wIndex & 31));
Kyle McMartin 9ecbc0
+			}
Kyle McMartin 9ecbc0
+		}
Kyle McMartin 9ecbc0
+		if ((temp & PORT_PLS_MASK) == XDEV_U0
Kyle McMartin 9ecbc0
+			&& (temp & PORT_POWER)
Kyle McMartin 9ecbc0
+			&& (xhci->suspended_ports[wIndex >> 5] &
Kyle McMartin 9ecbc0
+			    (1 << (wIndex & 31)))) {
Kyle McMartin 9ecbc0
+			xhci->suspended_ports[wIndex >> 5] &=
Kyle McMartin 9ecbc0
+					~(1 << (wIndex & 31));
Kyle McMartin 9ecbc0
+			xhci->port_c_suspend[wIndex >> 5] |=
Kyle McMartin 9ecbc0
+					1 << (wIndex & 31);
Kyle McMartin 9ecbc0
+		}
Kyle McMartin 9ecbc0
 		if (temp & PORT_CONNECT) {
Kyle McMartin 9ecbc0
 			status |= USB_PORT_STAT_CONNECTION;
Kyle McMartin 9ecbc0
 			status |= xhci_port_speed(temp);
Kyle McMartin 9ecbc0
@@ -226,6 +368,8 @@ int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue,
Kyle McMartin 9ecbc0
 			status |= USB_PORT_STAT_RESET;
Kyle McMartin 9ecbc0
 		if (temp & PORT_POWER)
Kyle McMartin 9ecbc0
 			status |= USB_PORT_STAT_POWER;
Kyle McMartin 9ecbc0
+		if (xhci->port_c_suspend[wIndex >> 5] & (1 << (wIndex & 31)))
Kyle McMartin 9ecbc0
+			status |= 1 << USB_PORT_FEAT_C_SUSPEND;
Kyle McMartin 9ecbc0
 		xhci_dbg(xhci, "Get port status returned 0x%x\n", status);
Kyle McMartin 9ecbc0
 		put_unaligned(cpu_to_le32(status), (__le32 *) buf);
Kyle McMartin 9ecbc0
 		break;
Kyle McMartin 9ecbc0
@@ -238,6 +382,42 @@ int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue,
Kyle McMartin 9ecbc0
 		temp = xhci_readl(xhci, addr);
Kyle McMartin 9ecbc0
 		temp = xhci_port_state_to_neutral(temp);
Kyle McMartin 9ecbc0
 		switch (wValue) {
Kyle McMartin 9ecbc0
+		case USB_PORT_FEAT_SUSPEND:
Kyle McMartin 9ecbc0
+			temp = xhci_readl(xhci, addr);
Kyle McMartin 9ecbc0
+			/* In spec software should not attempt to suspend
Kyle McMartin 9ecbc0
+			 * a port unless the port reports that it is in the
Kyle McMartin 9ecbc0
+			 * enabled (PED = ‘1’,PLS < ‘3’) state.
Kyle McMartin 9ecbc0
+			 */
Kyle McMartin 9ecbc0
+			if ((temp & PORT_PE) == 0 || (temp & PORT_RESET)
Kyle McMartin 9ecbc0
+				|| (temp & PORT_PLS_MASK) >= XDEV_U3) {
Kyle McMartin 9ecbc0
+				xhci_warn(xhci, "USB core suspending device "
Kyle McMartin 9ecbc0
+					  "not in U0/U1/U2.\n");
Kyle McMartin 9ecbc0
+				goto error;
Kyle McMartin 9ecbc0
+			}
Kyle McMartin 9ecbc0
+
Kyle McMartin 9ecbc0
+			slot_id = xhci_find_slot_id_by_port(xhci, wIndex + 1);
Kyle McMartin 9ecbc0
+			if (!slot_id) {
Kyle McMartin 9ecbc0
+				xhci_warn(xhci, "slot_id is zero\n");
Kyle McMartin 9ecbc0
+				goto error;
Kyle McMartin 9ecbc0
+			}
Kyle McMartin 9ecbc0
+			/* unlock to execute stop endpoint commands */
Kyle McMartin 9ecbc0
+			spin_unlock_irqrestore(&xhci->lock, flags);
Kyle McMartin 9ecbc0
+			xhci_stop_device(xhci, slot_id, 1);
Kyle McMartin 9ecbc0
+			spin_lock_irqsave(&xhci->lock, flags);
Kyle McMartin 9ecbc0
+
Kyle McMartin 9ecbc0
+			temp = xhci_port_state_to_neutral(temp);
Kyle McMartin 9ecbc0
+			temp &= ~PORT_PLS_MASK;
Kyle McMartin 9ecbc0
+			temp |= PORT_LINK_STROBE | XDEV_U3;
Kyle McMartin 9ecbc0
+			xhci_writel(xhci, temp, addr);
Kyle McMartin 9ecbc0
+
Kyle McMartin 9ecbc0
+			spin_unlock_irqrestore(&xhci->lock, flags);
Kyle McMartin 9ecbc0
+			msleep(10); /* wait device to enter */
Kyle McMartin 9ecbc0
+			spin_lock_irqsave(&xhci->lock, flags);
Kyle McMartin 9ecbc0
+
Kyle McMartin 9ecbc0
+			temp = xhci_readl(xhci, addr);
Kyle McMartin 9ecbc0
+			xhci->suspended_ports[wIndex >> 5] |=
Kyle McMartin 9ecbc0
+					1 << (wIndex & (31));
Kyle McMartin 9ecbc0
+			break;
Kyle McMartin 9ecbc0
 		case USB_PORT_FEAT_POWER:
Kyle McMartin 9ecbc0
 			/*
Kyle McMartin 9ecbc0
 			 * Turn on ports, even if there isn't per-port switching.
Kyle McMartin 9ecbc0
@@ -271,6 +451,52 @@ int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue,
Kyle McMartin 9ecbc0
 		temp = xhci_readl(xhci, addr);
Kyle McMartin 9ecbc0
 		temp = xhci_port_state_to_neutral(temp);
Kyle McMartin 9ecbc0
 		switch (wValue) {
Kyle McMartin 9ecbc0
+		case USB_PORT_FEAT_SUSPEND:
Kyle McMartin 9ecbc0
+			temp = xhci_readl(xhci, addr);
Kyle McMartin 9ecbc0
+			xhci_dbg(xhci, "clear USB_PORT_FEAT_SUSPEND\n");
Kyle McMartin 9ecbc0
+			xhci_dbg(xhci, "PORTSC %04x\n", temp);
Kyle McMartin 9ecbc0
+			if (temp & PORT_RESET)
Kyle McMartin 9ecbc0
+				goto error;
Kyle McMartin 9ecbc0
+			if (temp & XDEV_U3) {
Kyle McMartin 9ecbc0
+				if ((temp & PORT_PE) == 0)
Kyle McMartin 9ecbc0
+					goto error;
Kyle McMartin 9ecbc0
+				if (DEV_SUPERSPEED(temp)) {
Kyle McMartin 9ecbc0
+					temp = xhci_port_state_to_neutral(temp);
Kyle McMartin 9ecbc0
+					temp &= ~PORT_PLS_MASK;
Kyle McMartin 9ecbc0
+					temp |= PORT_LINK_STROBE | XDEV_U0;
Kyle McMartin 9ecbc0
+					xhci_writel(xhci, temp, addr);
Kyle McMartin 9ecbc0
+					xhci_readl(xhci, addr);
Kyle McMartin 9ecbc0
+				} else {
Kyle McMartin 9ecbc0
+					temp = xhci_port_state_to_neutral(temp);
Kyle McMartin 9ecbc0
+					temp &= ~PORT_PLS_MASK;
Kyle McMartin 9ecbc0
+					temp |= PORT_LINK_STROBE | XDEV_RESUME;
Kyle McMartin 9ecbc0
+					xhci_writel(xhci, temp, addr);
Kyle McMartin 9ecbc0
+
Kyle McMartin 9ecbc0
+					spin_unlock_irqrestore(&xhci->lock,
Kyle McMartin 9ecbc0
+							       flags);
Kyle McMartin 9ecbc0
+					msleep(20);
Kyle McMartin 9ecbc0
+					spin_lock_irqsave(&xhci->lock, flags);
Kyle McMartin 9ecbc0
+
Kyle McMartin 9ecbc0
+					temp = xhci_readl(xhci, addr);
Kyle McMartin 9ecbc0
+					temp = xhci_port_state_to_neutral(temp);
Kyle McMartin 9ecbc0
+					temp &= ~PORT_PLS_MASK;
Kyle McMartin 9ecbc0
+					temp |= PORT_LINK_STROBE | XDEV_U0;
Kyle McMartin 9ecbc0
+					xhci_writel(xhci, temp, addr);
Kyle McMartin 9ecbc0
+				}
Kyle McMartin 9ecbc0
+				xhci->port_c_suspend[wIndex >> 5] |=
Kyle McMartin 9ecbc0
+						1 << (wIndex & 31);
Kyle McMartin 9ecbc0
+			}
Kyle McMartin 9ecbc0
+
Kyle McMartin 9ecbc0
+			slot_id = xhci_find_slot_id_by_port(xhci, wIndex + 1);
Kyle McMartin 9ecbc0
+			if (!slot_id) {
Kyle McMartin 9ecbc0
+				xhci_dbg(xhci, "slot_id is zero\n");
Kyle McMartin 9ecbc0
+				goto error;
Kyle McMartin 9ecbc0
+			}
Kyle McMartin 9ecbc0
+			xhci_ring_device(xhci, slot_id);
Kyle McMartin 9ecbc0
+			break;
Kyle McMartin 9ecbc0
+		case USB_PORT_FEAT_C_SUSPEND:
Kyle McMartin 9ecbc0
+			xhci->port_c_suspend[wIndex >> 5] &=
Kyle McMartin 9ecbc0
+					~(1 << (wIndex & 31));
Kyle McMartin 9ecbc0
 		case USB_PORT_FEAT_C_RESET:
Kyle McMartin 9ecbc0
 		case USB_PORT_FEAT_C_CONNECTION:
Kyle McMartin 9ecbc0
 		case USB_PORT_FEAT_C_OVER_CURRENT:
Kyle McMartin 9ecbc0
@@ -306,6 +532,7 @@ int xhci_hub_status_data(struct usb_hcd *hcd, char *buf)
Kyle McMartin 9ecbc0
 {
Kyle McMartin 9ecbc0
 	unsigned long flags;
Kyle McMartin 9ecbc0
 	u32 temp, status;
Kyle McMartin 9ecbc0
+	u32 mask;
Kyle McMartin 9ecbc0
 	int i, retval;
Kyle McMartin 9ecbc0
 	struct xhci_hcd	*xhci = hcd_to_xhci(hcd);
Kyle McMartin 9ecbc0
 	int ports;
Kyle McMartin 9ecbc0
@@ -318,13 +545,18 @@ int xhci_hub_status_data(struct usb_hcd *hcd, char *buf)
Kyle McMartin 9ecbc0
 	memset(buf, 0, retval);
Kyle McMartin 9ecbc0
 	status = 0;
Kyle McMartin 9ecbc0
 
Kyle McMartin 9ecbc0
+	mask = PORT_CSC | PORT_PEC | PORT_OCC;
Kyle McMartin 9ecbc0
+
Kyle McMartin 9ecbc0
 	spin_lock_irqsave(&xhci->lock, flags);
Kyle McMartin 9ecbc0
 	/* For each port, did anything change?  If so, set that bit in buf. */
Kyle McMartin 9ecbc0
 	for (i = 0; i < ports; i++) {
Kyle McMartin 9ecbc0
 		addr = &xhci->op_regs->port_status_base +
Kyle McMartin 9ecbc0
 			NUM_PORT_REGS*i;
Kyle McMartin 9ecbc0
 		temp = xhci_readl(xhci, addr);
Kyle McMartin 9ecbc0
-		if (temp & (PORT_CSC | PORT_PEC | PORT_OCC)) {
Kyle McMartin 9ecbc0
+		if ((temp & mask) != 0 ||
Kyle McMartin 9ecbc0
+			(xhci->port_c_suspend[i >> 5] &	1 << (i & 31)) ||
Kyle McMartin 9ecbc0
+			(xhci->resume_done[i] && time_after_eq(
Kyle McMartin 9ecbc0
+			    jiffies, xhci->resume_done[i]))) {
Kyle McMartin 9ecbc0
 			buf[(i + 1) / 8] |= 1 << (i + 1) % 8;
Kyle McMartin 9ecbc0
 			status = 1;
Kyle McMartin 9ecbc0
 		}
Kyle McMartin 9ecbc0
@@ -332,3 +564,187 @@ int xhci_hub_status_data(struct usb_hcd *hcd, char *buf)
Kyle McMartin 9ecbc0
 	spin_unlock_irqrestore(&xhci->lock, flags);
Kyle McMartin 9ecbc0
 	return status ? retval : 0;
Kyle McMartin 9ecbc0
 }
Kyle McMartin 9ecbc0
+
Kyle McMartin 9ecbc0
+#ifdef CONFIG_PM
Kyle McMartin 9ecbc0
+
Kyle McMartin 9ecbc0
+int xhci_bus_suspend(struct usb_hcd *hcd)
Kyle McMartin 9ecbc0
+{
Kyle McMartin 9ecbc0
+	struct xhci_hcd	*xhci = hcd_to_xhci(hcd);
Kyle McMartin 9ecbc0
+	int port;
Kyle McMartin 9ecbc0
+	unsigned long flags;
Kyle McMartin 9ecbc0
+
Kyle McMartin 9ecbc0
+	xhci_dbg(xhci, "suspend root hub\n");
Kyle McMartin 9ecbc0
+
Kyle McMartin 9ecbc0
+	spin_lock_irqsave(&xhci->lock, flags);
Kyle McMartin 9ecbc0
+
Kyle McMartin 9ecbc0
+	if (hcd->self.root_hub->do_remote_wakeup) {
Kyle McMartin 9ecbc0
+		port = HCS_MAX_PORTS(xhci->hcs_params1);
Kyle McMartin 9ecbc0
+		while (port--) {
Kyle McMartin 9ecbc0
+			if (xhci->resume_done[port] != 0) {
Kyle McMartin 9ecbc0
+				spin_unlock_irqrestore(&xhci->lock, flags);
Kyle McMartin 9ecbc0
+				xhci_dbg(xhci, "suspend failed because "
Kyle McMartin 9ecbc0
+						"port %d is resuming\n",
Kyle McMartin 9ecbc0
+						port + 1);
Kyle McMartin 9ecbc0
+				return -EBUSY;
Kyle McMartin 9ecbc0
+			}
Kyle McMartin 9ecbc0
+		}
Kyle McMartin 9ecbc0
+	}
Kyle McMartin 9ecbc0
+
Kyle McMartin 9ecbc0
+	port = HCS_MAX_PORTS(xhci->hcs_params1);
Kyle McMartin 9ecbc0
+	xhci->bus_suspended = 0;
Kyle McMartin 9ecbc0
+	while (port--) {
Kyle McMartin 9ecbc0
+		/* suspend the port if the port is not suspended */
Kyle McMartin 9ecbc0
+		u32 __iomem *addr;
Kyle McMartin 9ecbc0
+		u32 t1, t2;
Kyle McMartin 9ecbc0
+		int slot_id;
Kyle McMartin 9ecbc0
+
Kyle McMartin 9ecbc0
+		addr = &xhci->op_regs->port_status_base +
Kyle McMartin 9ecbc0
+			NUM_PORT_REGS * (port & 0xff);
Kyle McMartin 9ecbc0
+		t1 = xhci_readl(xhci, addr);
Kyle McMartin 9ecbc0
+		t2 = xhci_port_state_to_neutral(t1);
Kyle McMartin 9ecbc0
+
Kyle McMartin 9ecbc0
+		if ((t1 & PORT_PE) && !(t1 & PORT_PLS_MASK)) {
Kyle McMartin 9ecbc0
+			xhci_dbg(xhci, "port %d not suspended\n", port);
Kyle McMartin 9ecbc0
+			slot_id = xhci_find_slot_id_by_port(xhci, port + 1);
Kyle McMartin 9ecbc0
+			if (slot_id) {
Kyle McMartin 9ecbc0
+				spin_unlock_irqrestore(&xhci->lock, flags);
Kyle McMartin 9ecbc0
+				xhci_stop_device(xhci, slot_id, 1);
Kyle McMartin 9ecbc0
+				spin_lock_irqsave(&xhci->lock, flags);
Kyle McMartin 9ecbc0
+			}
Kyle McMartin 9ecbc0
+			t2 &= ~PORT_PLS_MASK;
Kyle McMartin 9ecbc0
+			t2 |= PORT_LINK_STROBE | XDEV_U3;
Kyle McMartin 9ecbc0
+			set_bit(port, &xhci->bus_suspended);
Kyle McMartin 9ecbc0
+		}
Kyle McMartin 9ecbc0
+		if (hcd->self.root_hub->do_remote_wakeup) {
Kyle McMartin 9ecbc0
+			if (t1 & PORT_CONNECT) {
Kyle McMartin 9ecbc0
+				t2 |= PORT_WKOC_E | PORT_WKDISC_E;
Kyle McMartin 9ecbc0
+				t2 &= ~PORT_WKCONN_E;
Kyle McMartin 9ecbc0
+			} else {
Kyle McMartin 9ecbc0
+				t2 |= PORT_WKOC_E | PORT_WKCONN_E;
Kyle McMartin 9ecbc0
+				t2 &= ~PORT_WKDISC_E;
Kyle McMartin 9ecbc0
+			}
Kyle McMartin 9ecbc0
+		} else
Kyle McMartin 9ecbc0
+			t2 &= ~PORT_WAKE_BITS;
Kyle McMartin 9ecbc0
+
Kyle McMartin 9ecbc0
+		t1 = xhci_port_state_to_neutral(t1);
Kyle McMartin 9ecbc0
+		if (t1 != t2)
Kyle McMartin 9ecbc0
+			xhci_writel(xhci, t2, addr);
Kyle McMartin 9ecbc0
+
Kyle McMartin 9ecbc0
+		if (DEV_HIGHSPEED(t1)) {
Kyle McMartin 9ecbc0
+			/* enable remote wake up for USB 2.0 */
Kyle McMartin 9ecbc0
+			u32 __iomem *addr;
Kyle McMartin 9ecbc0
+			u32 tmp;
Kyle McMartin 9ecbc0
+
Kyle McMartin 9ecbc0
+			addr = &xhci->op_regs->port_power_base +
Kyle McMartin 9ecbc0
+				NUM_PORT_REGS * (port & 0xff);
Kyle McMartin 9ecbc0
+			tmp = xhci_readl(xhci, addr);
Kyle McMartin 9ecbc0
+			tmp |= PORT_RWE;
Kyle McMartin 9ecbc0
+			xhci_writel(xhci, tmp, addr);
Kyle McMartin 9ecbc0
+		}
Kyle McMartin 9ecbc0
+	}
Kyle McMartin 9ecbc0
+	hcd->state = HC_STATE_SUSPENDED;
Kyle McMartin 9ecbc0
+	xhci->next_statechange = jiffies + msecs_to_jiffies(10);
Kyle McMartin 9ecbc0
+	spin_unlock_irqrestore(&xhci->lock, flags);
Kyle McMartin 9ecbc0
+	return 0;
Kyle McMartin 9ecbc0
+}
Kyle McMartin 9ecbc0
+
Kyle McMartin 9ecbc0
+int xhci_bus_resume(struct usb_hcd *hcd)
Kyle McMartin 9ecbc0
+{
Kyle McMartin 9ecbc0
+	struct xhci_hcd	*xhci = hcd_to_xhci(hcd);
Kyle McMartin 9ecbc0
+	int port;
Kyle McMartin 9ecbc0
+	u32 temp;
Kyle McMartin 9ecbc0
+	unsigned long flags;
Kyle McMartin 9ecbc0
+
Kyle McMartin 9ecbc0
+	xhci_dbg(xhci, "resume root hub\n");
Kyle McMartin 9ecbc0
+
Kyle McMartin 9ecbc0
+	if (time_before(jiffies, xhci->next_statechange))
Kyle McMartin 9ecbc0
+		msleep(5);
Kyle McMartin 9ecbc0
+
Kyle McMartin 9ecbc0
+	spin_lock_irqsave(&xhci->lock, flags);
Kyle McMartin 9ecbc0
+	if (!HCD_HW_ACCESSIBLE(hcd)) {
Kyle McMartin 9ecbc0
+		spin_unlock_irqrestore(&xhci->lock, flags);
Kyle McMartin 9ecbc0
+		return -ESHUTDOWN;
Kyle McMartin 9ecbc0
+	}
Kyle McMartin 9ecbc0
+
Kyle McMartin 9ecbc0
+	/* delay the irqs */
Kyle McMartin 9ecbc0
+	temp = xhci_readl(xhci, &xhci->op_regs->command);
Kyle McMartin 9ecbc0
+	temp &= ~CMD_EIE;
Kyle McMartin 9ecbc0
+	xhci_writel(xhci, temp, &xhci->op_regs->command);
Kyle McMartin 9ecbc0
+
Kyle McMartin 9ecbc0
+	port = HCS_MAX_PORTS(xhci->hcs_params1);
Kyle McMartin 9ecbc0
+	while (port--) {
Kyle McMartin 9ecbc0
+		/* Check whether need resume ports. If needed
Kyle McMartin 9ecbc0
+		   resume port and disable remote wakeup */
Kyle McMartin 9ecbc0
+		u32 __iomem *addr;
Kyle McMartin 9ecbc0
+		u32 temp;
Kyle McMartin 9ecbc0
+		int slot_id;
Kyle McMartin 9ecbc0
+
Kyle McMartin 9ecbc0
+		addr = &xhci->op_regs->port_status_base +
Kyle McMartin 9ecbc0
+			NUM_PORT_REGS * (port & 0xff);
Kyle McMartin 9ecbc0
+		temp = xhci_readl(xhci, addr);
Kyle McMartin 9ecbc0
+		if (DEV_SUPERSPEED(temp))
Kyle McMartin 9ecbc0
+			temp &= ~(PORT_RWC_BITS | PORT_CEC | PORT_WAKE_BITS);
Kyle McMartin 9ecbc0
+		else
Kyle McMartin 9ecbc0
+			temp &= ~(PORT_RWC_BITS | PORT_WAKE_BITS);
Kyle McMartin 9ecbc0
+		if (test_bit(port, &xhci->bus_suspended) &&
Kyle McMartin 9ecbc0
+		    (temp & PORT_PLS_MASK)) {
Kyle McMartin 9ecbc0
+			if (DEV_SUPERSPEED(temp)) {
Kyle McMartin 9ecbc0
+				temp = xhci_port_state_to_neutral(temp);
Kyle McMartin 9ecbc0
+				temp &= ~PORT_PLS_MASK;
Kyle McMartin 9ecbc0
+				temp |= PORT_LINK_STROBE | XDEV_U0;
Kyle McMartin 9ecbc0
+				xhci_writel(xhci, temp, addr);
Kyle McMartin 9ecbc0
+			} else {
Kyle McMartin 9ecbc0
+				temp = xhci_port_state_to_neutral(temp);
Kyle McMartin 9ecbc0
+				temp &= ~PORT_PLS_MASK;
Kyle McMartin 9ecbc0
+				temp |= PORT_LINK_STROBE | XDEV_RESUME;
Kyle McMartin 9ecbc0
+				xhci_writel(xhci, temp, addr);
Kyle McMartin 9ecbc0
+
Kyle McMartin 9ecbc0
+				spin_unlock_irqrestore(&xhci->lock, flags);
Kyle McMartin 9ecbc0
+				msleep(20);
Kyle McMartin 9ecbc0
+				spin_lock_irqsave(&xhci->lock, flags);
Kyle McMartin 9ecbc0
+
Kyle McMartin 9ecbc0
+				temp = xhci_readl(xhci, addr);
Kyle McMartin 9ecbc0
+				temp = xhci_port_state_to_neutral(temp);
Kyle McMartin 9ecbc0
+				temp &= ~PORT_PLS_MASK;
Kyle McMartin 9ecbc0
+				temp |= PORT_LINK_STROBE | XDEV_U0;
Kyle McMartin 9ecbc0
+				xhci_writel(xhci, temp, addr);
Kyle McMartin 9ecbc0
+			}
Kyle McMartin 9ecbc0
+			slot_id = xhci_find_slot_id_by_port(xhci, port + 1);
Kyle McMartin 9ecbc0
+			if (slot_id)
Kyle McMartin 9ecbc0
+				xhci_ring_device(xhci, slot_id);
Kyle McMartin 9ecbc0
+		} else
Kyle McMartin 9ecbc0
+			xhci_writel(xhci, temp, addr);
Kyle McMartin 9ecbc0
+
Kyle McMartin 9ecbc0
+		if (DEV_HIGHSPEED(temp)) {
Kyle McMartin 9ecbc0
+			/* disable remote wake up for USB 2.0 */
Kyle McMartin 9ecbc0
+			u32 __iomem *addr;
Kyle McMartin 9ecbc0
+			u32 tmp;
Kyle McMartin 9ecbc0
+
Kyle McMartin 9ecbc0
+			addr = &xhci->op_regs->port_power_base +
Kyle McMartin 9ecbc0
+				NUM_PORT_REGS * (port & 0xff);
Kyle McMartin 9ecbc0
+			tmp = xhci_readl(xhci, addr);
Kyle McMartin 9ecbc0
+			tmp &= ~PORT_RWE;
Kyle McMartin 9ecbc0
+			xhci_writel(xhci, tmp, addr);
Kyle McMartin 9ecbc0
+		}
Kyle McMartin 9ecbc0
+	}
Kyle McMartin 9ecbc0
+
Kyle McMartin 9ecbc0
+	(void) xhci_readl(xhci, &xhci->op_regs->command);
Kyle McMartin 9ecbc0
+
Kyle McMartin 9ecbc0
+	xhci->next_statechange = jiffies + msecs_to_jiffies(5);
Kyle McMartin 9ecbc0
+	hcd->state = HC_STATE_RUNNING;
Kyle McMartin 9ecbc0
+	/* re-enable irqs */
Kyle McMartin 9ecbc0
+	temp = xhci_readl(xhci, &xhci->op_regs->command);
Kyle McMartin 9ecbc0
+	temp |= CMD_EIE;
Kyle McMartin 9ecbc0
+	xhci_writel(xhci, temp, &xhci->op_regs->command);
Kyle McMartin 9ecbc0
+	temp = xhci_readl(xhci, &xhci->op_regs->command);
Kyle McMartin 9ecbc0
+
Kyle McMartin 9ecbc0
+	spin_unlock_irqrestore(&xhci->lock, flags);
Kyle McMartin 9ecbc0
+	return 0;
Kyle McMartin 9ecbc0
+}
Kyle McMartin 9ecbc0
+
Kyle McMartin 9ecbc0
+#else
Kyle McMartin 9ecbc0
+
Kyle McMartin 9ecbc0
+#define	xhci_bus_suspend	NULL
Kyle McMartin 9ecbc0
+#define	xhci_bus_resume		NULL
Kyle McMartin 9ecbc0
+
Kyle McMartin 9ecbc0
+#endif
Kyle McMartin 9ecbc0
diff --git a/drivers/usb/host/xhci-mem.c b/drivers/usb/host/xhci-mem.c
Kyle McMartin 9ecbc0
index 4e51343..cef8d81 100644
Kyle McMartin 9ecbc0
--- a/drivers/usb/host/xhci-mem.c
Kyle McMartin 9ecbc0
+++ b/drivers/usb/host/xhci-mem.c
Kyle McMartin 9ecbc0
@@ -866,6 +866,7 @@ int xhci_setup_addressable_virt_dev(struct xhci_hcd *xhci, struct usb_device *ud
Kyle McMartin 9ecbc0
 			top_dev = top_dev->parent)
Kyle McMartin 9ecbc0
 		/* Found device below root hub */;
Kyle McMartin 9ecbc0
 	slot_ctx->dev_info2 |= (u32) ROOT_HUB_PORT(top_dev->portnum);
Kyle McMartin 9ecbc0
+	dev->port = top_dev->portnum;
Kyle McMartin 9ecbc0
 	xhci_dbg(xhci, "Set root hub portnum to %d\n", top_dev->portnum);
Kyle McMartin 9ecbc0
 
Kyle McMartin 9ecbc0
 	/* Is this a LS/FS device under a HS hub? */
Kyle McMartin 9ecbc0
@@ -1443,6 +1444,7 @@ void xhci_mem_cleanup(struct xhci_hcd *xhci)
Kyle McMartin 9ecbc0
 	scratchpad_free(xhci);
Kyle McMartin 9ecbc0
 	xhci->page_size = 0;
Kyle McMartin 9ecbc0
 	xhci->page_shift = 0;
Kyle McMartin 9ecbc0
+	xhci->bus_suspended = 0;
Kyle McMartin 9ecbc0
 }
Kyle McMartin 9ecbc0
 
Kyle McMartin 9ecbc0
 static int xhci_test_trb_in_td(struct xhci_hcd *xhci,
Kyle McMartin 9ecbc0
@@ -1801,6 +1803,8 @@ int xhci_mem_init(struct xhci_hcd *xhci, gfp_t flags)
Kyle McMartin 9ecbc0
 	init_completion(&xhci->addr_dev);
Kyle McMartin 9ecbc0
 	for (i = 0; i < MAX_HC_SLOTS; ++i)
Kyle McMartin 9ecbc0
 		xhci->devs[i] = NULL;
Kyle McMartin 9ecbc0
+	for (i = 0; i < MAX_HC_PORTS; ++i)
Kyle McMartin 9ecbc0
+		xhci->resume_done[i] = 0;
Kyle McMartin 9ecbc0
 
Kyle McMartin 9ecbc0
 	if (scratchpad_alloc(xhci, flags))
Kyle McMartin 9ecbc0
 		goto fail;
Kyle McMartin 9ecbc0
diff --git a/drivers/usb/host/xhci-pci.c b/drivers/usb/host/xhci-pci.c
Kyle McMartin 9ecbc0
index f7efe02..e3a5924 100644
Kyle McMartin 9ecbc0
--- a/drivers/usb/host/xhci-pci.c
Kyle McMartin 9ecbc0
+++ b/drivers/usb/host/xhci-pci.c
Kyle McMartin 9ecbc0
@@ -116,6 +116,30 @@ static int xhci_pci_setup(struct usb_hcd *hcd)
Kyle McMartin 9ecbc0
 	return xhci_pci_reinit(xhci, pdev);
Kyle McMartin 9ecbc0
 }
Kyle McMartin 9ecbc0
 
Kyle McMartin 9ecbc0
+#ifdef CONFIG_PM
Kyle McMartin 9ecbc0
+static int xhci_pci_suspend(struct usb_hcd *hcd, bool do_wakeup)
Kyle McMartin 9ecbc0
+{
Kyle McMartin 9ecbc0
+	struct xhci_hcd	*xhci = hcd_to_xhci(hcd);
Kyle McMartin 9ecbc0
+	int	retval = 0;
Kyle McMartin 9ecbc0
+
Kyle McMartin 9ecbc0
+	if (hcd->state != HC_STATE_SUSPENDED)
Kyle McMartin 9ecbc0
+		return -EINVAL;
Kyle McMartin 9ecbc0
+
Kyle McMartin 9ecbc0
+	retval = xhci_suspend(xhci);
Kyle McMartin 9ecbc0
+
Kyle McMartin 9ecbc0
+	return retval;
Kyle McMartin 9ecbc0
+}
Kyle McMartin 9ecbc0
+
Kyle McMartin 9ecbc0
+static int xhci_pci_resume(struct usb_hcd *hcd, bool hibernated)
Kyle McMartin 9ecbc0
+{
Kyle McMartin 9ecbc0
+	struct xhci_hcd		*xhci = hcd_to_xhci(hcd);
Kyle McMartin 9ecbc0
+	int			retval = 0;
Kyle McMartin 9ecbc0
+
Kyle McMartin 9ecbc0
+	retval = xhci_resume(xhci, hibernated);
Kyle McMartin 9ecbc0
+	return retval;
Kyle McMartin 9ecbc0
+}
Kyle McMartin 9ecbc0
+#endif /* CONFIG_PM */
Kyle McMartin 9ecbc0
+
Kyle McMartin 9ecbc0
 static const struct hc_driver xhci_pci_hc_driver = {
Kyle McMartin 9ecbc0
 	.description =		hcd_name,
Kyle McMartin 9ecbc0
 	.product_desc =		"xHCI Host Controller",
Kyle McMartin 9ecbc0
@@ -132,7 +156,10 @@ static const struct hc_driver xhci_pci_hc_driver = {
Kyle McMartin 9ecbc0
 	 */
Kyle McMartin 9ecbc0
 	.reset =		xhci_pci_setup,
Kyle McMartin 9ecbc0
 	.start =		xhci_run,
Kyle McMartin 9ecbc0
-	/* suspend and resume implemented later */
Kyle McMartin 9ecbc0
+#ifdef CONFIG_PM
Kyle McMartin 9ecbc0
+	.pci_suspend =          xhci_pci_suspend,
Kyle McMartin 9ecbc0
+	.pci_resume =           xhci_pci_resume,
Kyle McMartin 9ecbc0
+#endif
Kyle McMartin 9ecbc0
 	.stop =			xhci_stop,
Kyle McMartin 9ecbc0
 	.shutdown =		xhci_shutdown,
Kyle McMartin 9ecbc0
 
Kyle McMartin 9ecbc0
@@ -162,6 +189,8 @@ static const struct hc_driver xhci_pci_hc_driver = {
Kyle McMartin 9ecbc0
 	/* Root hub support */
Kyle McMartin 9ecbc0
 	.hub_control =		xhci_hub_control,
Kyle McMartin 9ecbc0
 	.hub_status_data =	xhci_hub_status_data,
Kyle McMartin 9ecbc0
+	.bus_suspend =		xhci_bus_suspend,
Kyle McMartin 9ecbc0
+	.bus_resume =		xhci_bus_resume,
Kyle McMartin 9ecbc0
 };
Kyle McMartin 9ecbc0
 
Kyle McMartin 9ecbc0
 /*-------------------------------------------------------------------------*/
Kyle McMartin 9ecbc0
@@ -186,6 +215,11 @@ static struct pci_driver xhci_pci_driver = {
Kyle McMartin 9ecbc0
 	/* suspend and resume implemented later */
Kyle McMartin 9ecbc0
 
Kyle McMartin 9ecbc0
 	.shutdown = 	usb_hcd_pci_shutdown,
Kyle McMartin 9ecbc0
+#ifdef CONFIG_PM_SLEEP
Kyle McMartin 9ecbc0
+	.driver = {
Kyle McMartin 9ecbc0
+		.pm = &usb_hcd_pci_pm_ops
Kyle McMartin 9ecbc0
+	},
Kyle McMartin 9ecbc0
+#endif
Kyle McMartin 9ecbc0
 };
Kyle McMartin 9ecbc0
 
Kyle McMartin 9ecbc0
 int xhci_register_pci(void)
Kyle McMartin 9ecbc0
diff --git a/drivers/usb/host/xhci-ring.c b/drivers/usb/host/xhci-ring.c
Kyle McMartin 9ecbc0
index 48e60d1..9f3115e 100644
Kyle McMartin 9ecbc0
--- a/drivers/usb/host/xhci-ring.c
Kyle McMartin 9ecbc0
+++ b/drivers/usb/host/xhci-ring.c
Kyle McMartin 9ecbc0
@@ -68,6 +68,10 @@
Kyle McMartin 9ecbc0
 #include <linux slab.h="">
Kyle McMartin 9ecbc0
 #include "xhci.h"
Kyle McMartin 9ecbc0
 
Kyle McMartin 9ecbc0
+static int handle_cmd_in_cmd_wait_list(struct xhci_hcd *xhci,
Kyle McMartin 9ecbc0
+		struct xhci_virt_device *virt_dev,
Kyle McMartin 9ecbc0
+		struct xhci_event_cmd *event);
Kyle McMartin 9ecbc0
+
Kyle McMartin 9ecbc0
 /*
Kyle McMartin 9ecbc0
  * Returns zero if the TRB isn't in this segment, otherwise it returns the DMA
Kyle McMartin 9ecbc0
  * address of the TRB.
Kyle McMartin 9ecbc0
@@ -313,7 +317,7 @@ void xhci_ring_cmd_db(struct xhci_hcd *xhci)
Kyle McMartin 9ecbc0
 	xhci_readl(xhci, &xhci->dba->doorbell[0]);
Kyle McMartin 9ecbc0
 }
Kyle McMartin 9ecbc0
 
Kyle McMartin 9ecbc0
-static void ring_ep_doorbell(struct xhci_hcd *xhci,
Kyle McMartin 9ecbc0
+void xhci_ring_ep_doorbell(struct xhci_hcd *xhci,
Kyle McMartin 9ecbc0
 		unsigned int slot_id,
Kyle McMartin 9ecbc0
 		unsigned int ep_index,
Kyle McMartin 9ecbc0
 		unsigned int stream_id)
Kyle McMartin 9ecbc0
@@ -353,7 +357,7 @@ static void ring_doorbell_for_active_rings(struct xhci_hcd *xhci,
Kyle McMartin 9ecbc0
 	/* A ring has pending URBs if its TD list is not empty */
Kyle McMartin 9ecbc0
 	if (!(ep->ep_state & EP_HAS_STREAMS)) {
Kyle McMartin 9ecbc0
 		if (!(list_empty(&ep->ring->td_list)))
Kyle McMartin 9ecbc0
-			ring_ep_doorbell(xhci, slot_id, ep_index, 0);
Kyle McMartin 9ecbc0
+			xhci_ring_ep_doorbell(xhci, slot_id, ep_index, 0);
Kyle McMartin 9ecbc0
 		return;
Kyle McMartin 9ecbc0
 	}
Kyle McMartin 9ecbc0
 
Kyle McMartin 9ecbc0
@@ -361,7 +365,8 @@ static void ring_doorbell_for_active_rings(struct xhci_hcd *xhci,
Kyle McMartin 9ecbc0
 			stream_id++) {
Kyle McMartin 9ecbc0
 		struct xhci_stream_info *stream_info = ep->stream_info;
Kyle McMartin 9ecbc0
 		if (!list_empty(&stream_info->stream_rings[stream_id]->td_list))
Kyle McMartin 9ecbc0
-			ring_ep_doorbell(xhci, slot_id, ep_index, stream_id);
Kyle McMartin 9ecbc0
+			xhci_ring_ep_doorbell(xhci, slot_id, ep_index,
Kyle McMartin 9ecbc0
+						stream_id);
Kyle McMartin 9ecbc0
 	}
Kyle McMartin 9ecbc0
 }
Kyle McMartin 9ecbc0
 
Kyle McMartin 9ecbc0
@@ -626,10 +631,11 @@ static void xhci_giveback_urb_in_irq(struct xhci_hcd *xhci,
Kyle McMartin 9ecbc0
  *     bit cleared) so that the HW will skip over them.
Kyle McMartin 9ecbc0
  */
Kyle McMartin 9ecbc0
 static void handle_stopped_endpoint(struct xhci_hcd *xhci,
Kyle McMartin 9ecbc0
-		union xhci_trb *trb)
Kyle McMartin 9ecbc0
+		union xhci_trb *trb, struct xhci_event_cmd *event)
Kyle McMartin 9ecbc0
 {
Kyle McMartin 9ecbc0
 	unsigned int slot_id;
Kyle McMartin 9ecbc0
 	unsigned int ep_index;
Kyle McMartin 9ecbc0
+	struct xhci_virt_device *virt_dev;
Kyle McMartin 9ecbc0
 	struct xhci_ring *ep_ring;
Kyle McMartin 9ecbc0
 	struct xhci_virt_ep *ep;
Kyle McMartin 9ecbc0
 	struct list_head *entry;
Kyle McMartin 9ecbc0
@@ -638,6 +644,21 @@ static void handle_stopped_endpoint(struct xhci_hcd *xhci,
Kyle McMartin 9ecbc0
 
Kyle McMartin 9ecbc0
 	struct xhci_dequeue_state deq_state;
Kyle McMartin 9ecbc0
 
Kyle McMartin 9ecbc0
+	if (unlikely(TRB_TO_SUSPEND_PORT(
Kyle McMartin 9ecbc0
+			xhci->cmd_ring->dequeue->generic.field[3]))) {
Kyle McMartin 9ecbc0
+		slot_id = TRB_TO_SLOT_ID(
Kyle McMartin 9ecbc0
+			xhci->cmd_ring->dequeue->generic.field[3]);
Kyle McMartin 9ecbc0
+		virt_dev = xhci->devs[slot_id];
Kyle McMartin 9ecbc0
+		if (virt_dev)
Kyle McMartin 9ecbc0
+			handle_cmd_in_cmd_wait_list(xhci, virt_dev,
Kyle McMartin 9ecbc0
+				event);
Kyle McMartin 9ecbc0
+		else
Kyle McMartin 9ecbc0
+			xhci_warn(xhci, "Stop endpoint command "
Kyle McMartin 9ecbc0
+				"completion for disabled slot %u\n",
Kyle McMartin 9ecbc0
+				slot_id);
Kyle McMartin 9ecbc0
+		return;
Kyle McMartin 9ecbc0
+	}
Kyle McMartin 9ecbc0
+
Kyle McMartin 9ecbc0
 	memset(&deq_state, 0, sizeof(deq_state));
Kyle McMartin 9ecbc0
 	slot_id = TRB_TO_SLOT_ID(trb->generic.field[3]);
Kyle McMartin 9ecbc0
 	ep_index = TRB_TO_EP_INDEX(trb->generic.field[3]);
Kyle McMartin 9ecbc0
@@ -1091,7 +1112,7 @@ bandwidth_change:
Kyle McMartin 9ecbc0
 		complete(&xhci->addr_dev);
Kyle McMartin 9ecbc0
 		break;
Kyle McMartin 9ecbc0
 	case TRB_TYPE(TRB_STOP_RING):
Kyle McMartin 9ecbc0
-		handle_stopped_endpoint(xhci, xhci->cmd_ring->dequeue);
Kyle McMartin 9ecbc0
+		handle_stopped_endpoint(xhci, xhci->cmd_ring->dequeue, event);
Kyle McMartin 9ecbc0
 		break;
Kyle McMartin 9ecbc0
 	case TRB_TYPE(TRB_SET_DEQ):
Kyle McMartin 9ecbc0
 		handle_set_deq_completion(xhci, event, xhci->cmd_ring->dequeue);
Kyle McMartin 9ecbc0
@@ -1144,17 +1165,72 @@ static void handle_vendor_event(struct xhci_hcd *xhci,
Kyle McMartin 9ecbc0
 static void handle_port_status(struct xhci_hcd *xhci,
Kyle McMartin 9ecbc0
 		union xhci_trb *event)
Kyle McMartin 9ecbc0
 {
Kyle McMartin 9ecbc0
+	struct usb_hcd *hcd = xhci_to_hcd(xhci);
Kyle McMartin 9ecbc0
 	u32 port_id;
Kyle McMartin 9ecbc0
+	u32 temp, temp1;
Kyle McMartin 9ecbc0
+	u32 __iomem *addr;
Kyle McMartin 9ecbc0
+	int ports;
Kyle McMartin 9ecbc0
+	int slot_id;
Kyle McMartin 9ecbc0
 
Kyle McMartin 9ecbc0
 	/* Port status change events always have a successful completion code */
Kyle McMartin 9ecbc0
 	if (GET_COMP_CODE(event->generic.field[2]) != COMP_SUCCESS) {
Kyle McMartin 9ecbc0
 		xhci_warn(xhci, "WARN: xHC returned failed port status event\n");
Kyle McMartin 9ecbc0
 		xhci->error_bitmask |= 1 << 8;
Kyle McMartin 9ecbc0
 	}
Kyle McMartin 9ecbc0
-	/* FIXME: core doesn't care about all port link state changes yet */
Kyle McMartin 9ecbc0
 	port_id = GET_PORT_ID(event->generic.field[0]);
Kyle McMartin 9ecbc0
 	xhci_dbg(xhci, "Port Status Change Event for port %d\n", port_id);
Kyle McMartin 9ecbc0
 
Kyle McMartin 9ecbc0
+	ports = HCS_MAX_PORTS(xhci->hcs_params1);
Kyle McMartin 9ecbc0
+	if ((port_id <= 0) || (port_id > ports)) {
Kyle McMartin 9ecbc0
+		xhci_warn(xhci, "Invalid port id %d\n", port_id);
Kyle McMartin 9ecbc0
+		goto cleanup;
Kyle McMartin 9ecbc0
+	}
Kyle McMartin 9ecbc0
+
Kyle McMartin 9ecbc0
+	addr = &xhci->op_regs->port_status_base + NUM_PORT_REGS * (port_id - 1);
Kyle McMartin 9ecbc0
+	temp = xhci_readl(xhci, addr);
Kyle McMartin 9ecbc0
+	if ((temp & PORT_CONNECT) && (hcd->state == HC_STATE_SUSPENDED)) {
Kyle McMartin 9ecbc0
+		xhci_dbg(xhci, "resume root hub\n");
Kyle McMartin 9ecbc0
+		usb_hcd_resume_root_hub(hcd);
Kyle McMartin 9ecbc0
+	}
Kyle McMartin 9ecbc0
+
Kyle McMartin 9ecbc0
+	if ((temp & PORT_PLC) && (temp & PORT_PLS_MASK) == XDEV_RESUME) {
Kyle McMartin 9ecbc0
+		xhci_dbg(xhci, "port resume event for port %d\n", port_id);
Kyle McMartin 9ecbc0
+
Kyle McMartin 9ecbc0
+		temp1 = xhci_readl(xhci, &xhci->op_regs->command);
Kyle McMartin 9ecbc0
+		if (!(temp1 & CMD_RUN)) {
Kyle McMartin 9ecbc0
+			xhci_warn(xhci, "xHC is not running.\n");
Kyle McMartin 9ecbc0
+			goto cleanup;
Kyle McMartin 9ecbc0
+		}
Kyle McMartin 9ecbc0
+
Kyle McMartin 9ecbc0
+		if (DEV_SUPERSPEED(temp)) {
Kyle McMartin 9ecbc0
+			xhci_dbg(xhci, "resume SS port %d\n", port_id);
Kyle McMartin 9ecbc0
+			temp = xhci_port_state_to_neutral(temp);
Kyle McMartin 9ecbc0
+			temp &= ~PORT_PLS_MASK;
Kyle McMartin 9ecbc0
+			temp |= PORT_LINK_STROBE | XDEV_U0;
Kyle McMartin 9ecbc0
+			xhci_writel(xhci, temp, addr);
Kyle McMartin 9ecbc0
+			slot_id = xhci_find_slot_id_by_port(xhci, port_id);
Kyle McMartin 9ecbc0
+			if (!slot_id) {
Kyle McMartin 9ecbc0
+				xhci_dbg(xhci, "slot_id is zero\n");
Kyle McMartin 9ecbc0
+				goto cleanup;
Kyle McMartin 9ecbc0
+			}
Kyle McMartin 9ecbc0
+			xhci_ring_device(xhci, slot_id);
Kyle McMartin 9ecbc0
+			xhci_dbg(xhci, "resume SS port %d finished\n", port_id);
Kyle McMartin 9ecbc0
+			/* Clear PORT_PLC */
Kyle McMartin 9ecbc0
+			temp = xhci_readl(xhci, addr);
Kyle McMartin 9ecbc0
+			temp = xhci_port_state_to_neutral(temp);
Kyle McMartin 9ecbc0
+			temp |= PORT_PLC;
Kyle McMartin 9ecbc0
+			xhci_writel(xhci, temp, addr);
Kyle McMartin 9ecbc0
+		} else {
Kyle McMartin 9ecbc0
+			xhci_dbg(xhci, "resume HS port %d\n", port_id);
Kyle McMartin 9ecbc0
+			xhci->resume_done[port_id - 1] = jiffies +
Kyle McMartin 9ecbc0
+				msecs_to_jiffies(20);
Kyle McMartin 9ecbc0
+			mod_timer(&hcd->rh_timer,
Kyle McMartin 9ecbc0
+				  xhci->resume_done[port_id - 1]);
Kyle McMartin 9ecbc0
+			/* Do the rest in GetPortStatus */
Kyle McMartin 9ecbc0
+		}
Kyle McMartin 9ecbc0
+	}
Kyle McMartin 9ecbc0
+
Kyle McMartin 9ecbc0
+cleanup:
Kyle McMartin 9ecbc0
 	/* Update event ring dequeue pointer before dropping the lock */
Kyle McMartin 9ecbc0
 	inc_deq(xhci, xhci->event_ring, true);
Kyle McMartin 9ecbc0
 
Kyle McMartin 9ecbc0
@@ -2347,7 +2423,7 @@ static void giveback_first_trb(struct xhci_hcd *xhci, int slot_id,
Kyle McMartin 9ecbc0
 	 */
Kyle McMartin 9ecbc0
 	wmb();
Kyle McMartin 9ecbc0
 	start_trb->field[3] |= start_cycle;
Kyle McMartin 9ecbc0
-	ring_ep_doorbell(xhci, slot_id, ep_index, stream_id);
Kyle McMartin 9ecbc0
+	xhci_ring_ep_doorbell(xhci, slot_id, ep_index, stream_id);
Kyle McMartin 9ecbc0
 }
Kyle McMartin 9ecbc0
 
Kyle McMartin 9ecbc0
 /*
Kyle McMartin 9ecbc0
@@ -2931,7 +3007,7 @@ static int xhci_queue_isoc_tx(struct xhci_hcd *xhci, gfp_t mem_flags,
Kyle McMartin 9ecbc0
 	wmb();
Kyle McMartin 9ecbc0
 	start_trb->field[3] |= start_cycle;
Kyle McMartin 9ecbc0
 
Kyle McMartin 9ecbc0
-	ring_ep_doorbell(xhci, slot_id, ep_index, urb->stream_id);
Kyle McMartin 9ecbc0
+	xhci_ring_ep_doorbell(xhci, slot_id, ep_index, urb->stream_id);
Kyle McMartin 9ecbc0
 	return 0;
Kyle McMartin 9ecbc0
 }
Kyle McMartin 9ecbc0
 
Kyle McMartin 9ecbc0
@@ -3108,15 +3184,20 @@ int xhci_queue_evaluate_context(struct xhci_hcd *xhci, dma_addr_t in_ctx_ptr,
Kyle McMartin 9ecbc0
 			false);
Kyle McMartin 9ecbc0
 }
Kyle McMartin 9ecbc0
 
Kyle McMartin 9ecbc0
+/*
Kyle McMartin 9ecbc0
+ * Suspend is set to indicate "Stop Endpoint Command" is being issued to stop
Kyle McMartin 9ecbc0
+ * activity on an endpoint that is about to be suspended.
Kyle McMartin 9ecbc0
+ */
Kyle McMartin 9ecbc0
 int xhci_queue_stop_endpoint(struct xhci_hcd *xhci, int slot_id,
Kyle McMartin 9ecbc0
-		unsigned int ep_index)
Kyle McMartin 9ecbc0
+		unsigned int ep_index, int suspend)
Kyle McMartin 9ecbc0
 {
Kyle McMartin 9ecbc0
 	u32 trb_slot_id = SLOT_ID_FOR_TRB(slot_id);
Kyle McMartin 9ecbc0
 	u32 trb_ep_index = EP_ID_FOR_TRB(ep_index);
Kyle McMartin 9ecbc0
 	u32 type = TRB_TYPE(TRB_STOP_RING);
Kyle McMartin 9ecbc0
+	u32 trb_suspend = SUSPEND_PORT_FOR_TRB(suspend);
Kyle McMartin 9ecbc0
 
Kyle McMartin 9ecbc0
 	return queue_command(xhci, 0, 0, 0,
Kyle McMartin 9ecbc0
-			trb_slot_id | trb_ep_index | type, false);
Kyle McMartin 9ecbc0
+			trb_slot_id | trb_ep_index | type | trb_suspend, false);
Kyle McMartin 9ecbc0
 }
Kyle McMartin 9ecbc0
 
Kyle McMartin 9ecbc0
 /* Set Transfer Ring Dequeue Pointer command.
Kyle McMartin 9ecbc0
diff --git a/drivers/usb/host/xhci.c b/drivers/usb/host/xhci.c
Kyle McMartin 9ecbc0
index d5c550e..34f1b3b 100644
Kyle McMartin 9ecbc0
--- a/drivers/usb/host/xhci.c
Kyle McMartin 9ecbc0
+++ b/drivers/usb/host/xhci.c
Kyle McMartin 9ecbc0
@@ -551,6 +551,216 @@ void xhci_shutdown(struct usb_hcd *hcd)
Kyle McMartin 9ecbc0
 		    xhci_readl(xhci, &xhci->op_regs->status));
Kyle McMartin 9ecbc0
 }
Kyle McMartin 9ecbc0
 
Kyle McMartin 9ecbc0
+static void xhci_save_registers(struct xhci_hcd *xhci)
Kyle McMartin 9ecbc0
+{
Kyle McMartin 9ecbc0
+	xhci->s3.command = xhci_readl(xhci, &xhci->op_regs->command);
Kyle McMartin 9ecbc0
+	xhci->s3.dev_nt = xhci_readl(xhci, &xhci->op_regs->dev_notification);
Kyle McMartin 9ecbc0
+	xhci->s3.dcbaa_ptr = xhci_read_64(xhci, &xhci->op_regs->dcbaa_ptr);
Kyle McMartin 9ecbc0
+	xhci->s3.config_reg = xhci_readl(xhci, &xhci->op_regs->config_reg);
Kyle McMartin 9ecbc0
+	xhci->s3.irq_pending = xhci_readl(xhci, &xhci->ir_set->irq_pending);
Kyle McMartin 9ecbc0
+	xhci->s3.irq_control = xhci_readl(xhci, &xhci->ir_set->irq_control);
Kyle McMartin 9ecbc0
+	xhci->s3.erst_size = xhci_readl(xhci, &xhci->ir_set->erst_size);
Kyle McMartin 9ecbc0
+	xhci->s3.erst_base = xhci_read_64(xhci, &xhci->ir_set->erst_base);
Kyle McMartin 9ecbc0
+	xhci->s3.erst_dequeue = xhci_read_64(xhci, &xhci->ir_set->erst_dequeue);
Kyle McMartin 9ecbc0
+}
Kyle McMartin 9ecbc0
+
Kyle McMartin 9ecbc0
+static void xhci_restore_registers(struct xhci_hcd *xhci)
Kyle McMartin 9ecbc0
+{
Kyle McMartin 9ecbc0
+	xhci_writel(xhci, xhci->s3.command, &xhci->op_regs->command);
Kyle McMartin 9ecbc0
+	xhci_writel(xhci, xhci->s3.dev_nt, &xhci->op_regs->dev_notification);
Kyle McMartin 9ecbc0
+	xhci_write_64(xhci, xhci->s3.dcbaa_ptr, &xhci->op_regs->dcbaa_ptr);
Kyle McMartin 9ecbc0
+	xhci_writel(xhci, xhci->s3.config_reg, &xhci->op_regs->config_reg);
Kyle McMartin 9ecbc0
+	xhci_writel(xhci, xhci->s3.irq_pending, &xhci->ir_set->irq_pending);
Kyle McMartin 9ecbc0
+	xhci_writel(xhci, xhci->s3.irq_control, &xhci->ir_set->irq_control);
Kyle McMartin 9ecbc0
+	xhci_writel(xhci, xhci->s3.erst_size, &xhci->ir_set->erst_size);
Kyle McMartin 9ecbc0
+	xhci_write_64(xhci, xhci->s3.erst_base, &xhci->ir_set->erst_base);
Kyle McMartin 9ecbc0
+}
Kyle McMartin 9ecbc0
+
Kyle McMartin 9ecbc0
+/*
Kyle McMartin 9ecbc0
+ * Stop HC (not bus-specific)
Kyle McMartin 9ecbc0
+ *
Kyle McMartin 9ecbc0
+ * This is called when the machine transition into S3/S4 mode.
Kyle McMartin 9ecbc0
+ *
Kyle McMartin 9ecbc0
+ */
Kyle McMartin 9ecbc0
+int xhci_suspend(struct xhci_hcd *xhci)
Kyle McMartin 9ecbc0
+{
Kyle McMartin 9ecbc0
+	int			rc = 0;
Kyle McMartin 9ecbc0
+	struct usb_hcd		*hcd = xhci_to_hcd(xhci);
Kyle McMartin 9ecbc0
+	u32			command;
Kyle McMartin 9ecbc0
+
Kyle McMartin 9ecbc0
+	spin_lock_irq(&xhci->lock);
Kyle McMartin 9ecbc0
+	clear_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags);
Kyle McMartin 9ecbc0
+	/* step 1: stop endpoint */
Kyle McMartin 9ecbc0
+	/* skipped assuming that port suspend has done */
Kyle McMartin 9ecbc0
+
Kyle McMartin 9ecbc0
+	/* step 2: clear Run/Stop bit */
Kyle McMartin 9ecbc0
+	command = xhci_readl(xhci, &xhci->op_regs->command);
Kyle McMartin 9ecbc0
+	command &= ~CMD_RUN;
Kyle McMartin 9ecbc0
+	xhci_writel(xhci, command, &xhci->op_regs->command);
Kyle McMartin 9ecbc0
+	if (handshake(xhci, &xhci->op_regs->status,
Kyle McMartin 9ecbc0
+		      STS_HALT, STS_HALT, 100*100)) {
Kyle McMartin 9ecbc0
+		xhci_warn(xhci, "WARN: xHC CMD_RUN timeout\n");
Kyle McMartin 9ecbc0
+		spin_unlock_irq(&xhci->lock);
Kyle McMartin 9ecbc0
+		return -ETIMEDOUT;
Kyle McMartin 9ecbc0
+	}
Kyle McMartin 9ecbc0
+
Kyle McMartin 9ecbc0
+	/* step 3: save registers */
Kyle McMartin 9ecbc0
+	xhci_save_registers(xhci);
Kyle McMartin 9ecbc0
+
Kyle McMartin 9ecbc0
+	/* step 4: set CSS flag */
Kyle McMartin 9ecbc0
+	command = xhci_readl(xhci, &xhci->op_regs->command);
Kyle McMartin 9ecbc0
+	command |= CMD_CSS;
Kyle McMartin 9ecbc0
+	xhci_writel(xhci, command, &xhci->op_regs->command);
Kyle McMartin 9ecbc0
+	if (handshake(xhci, &xhci->op_regs->status, STS_SAVE, 0, 10*100)) {
Kyle McMartin 9ecbc0
+		xhci_warn(xhci, "WARN: xHC CMD_CSS timeout\n");
Kyle McMartin 9ecbc0
+		spin_unlock_irq(&xhci->lock);
Kyle McMartin 9ecbc0
+		return -ETIMEDOUT;
Kyle McMartin 9ecbc0
+	}
Kyle McMartin 9ecbc0
+	/* step 5: remove core well power */
Kyle McMartin 9ecbc0
+	xhci_cleanup_msix(xhci);
Kyle McMartin 9ecbc0
+	spin_unlock_irq(&xhci->lock);
Kyle McMartin 9ecbc0
+
Kyle McMartin 9ecbc0
+	return rc;
Kyle McMartin 9ecbc0
+}
Kyle McMartin 9ecbc0
+
Kyle McMartin 9ecbc0
+/*
Kyle McMartin 9ecbc0
+ * start xHC (not bus-specific)
Kyle McMartin 9ecbc0
+ *
Kyle McMartin 9ecbc0
+ * This is called when the machine transition from S3/S4 mode.
Kyle McMartin 9ecbc0
+ *
Kyle McMartin 9ecbc0
+ */
Kyle McMartin 9ecbc0
+int xhci_resume(struct xhci_hcd *xhci, bool hibernated)
Kyle McMartin 9ecbc0
+{
Kyle McMartin 9ecbc0
+	u32			command, temp = 0;
Kyle McMartin 9ecbc0
+	struct usb_hcd		*hcd = xhci_to_hcd(xhci);
Kyle McMartin 9ecbc0
+	struct pci_dev		*pdev = to_pci_dev(hcd->self.controller);
Kyle McMartin 9ecbc0
+	u64	val_64;
Kyle McMartin 9ecbc0
+	int	old_state, retval;
Kyle McMartin 9ecbc0
+
Kyle McMartin 9ecbc0
+	old_state = hcd->state;
Kyle McMartin 9ecbc0
+	if (time_before(jiffies, xhci->next_statechange))
Kyle McMartin 9ecbc0
+		msleep(100);
Kyle McMartin 9ecbc0
+
Kyle McMartin 9ecbc0
+	spin_lock_irq(&xhci->lock);
Kyle McMartin 9ecbc0
+
Kyle McMartin 9ecbc0
+	if (!hibernated) {
Kyle McMartin 9ecbc0
+		/* step 1: restore register */
Kyle McMartin 9ecbc0
+		xhci_restore_registers(xhci);
Kyle McMartin 9ecbc0
+		/* step 2: initialize command ring buffer */
Kyle McMartin 9ecbc0
+		val_64 = xhci_read_64(xhci, &xhci->op_regs->cmd_ring);
Kyle McMartin 9ecbc0
+		val_64 = (val_64 & (u64) CMD_RING_RSVD_BITS) |
Kyle McMartin 9ecbc0
+			 (xhci_trb_virt_to_dma(xhci->cmd_ring->deq_seg,
Kyle McMartin 9ecbc0
+					       xhci->cmd_ring->dequeue) &
Kyle McMartin 9ecbc0
+			 (u64) ~CMD_RING_RSVD_BITS) |
Kyle McMartin 9ecbc0
+			 xhci->cmd_ring->cycle_state;
Kyle McMartin 9ecbc0
+		xhci_dbg(xhci, "// Setting command ring address to 0x%llx\n",
Kyle McMartin 9ecbc0
+				(long unsigned long) val_64);
Kyle McMartin 9ecbc0
+		xhci_write_64(xhci, val_64, &xhci->op_regs->cmd_ring);
Kyle McMartin 9ecbc0
+		/* step 3: restore state and start state*/
Kyle McMartin 9ecbc0
+		/* step 3: set CRS flag */
Kyle McMartin 9ecbc0
+		command = xhci_readl(xhci, &xhci->op_regs->command);
Kyle McMartin 9ecbc0
+		command |= CMD_CRS;
Kyle McMartin 9ecbc0
+		xhci_writel(xhci, command, &xhci->op_regs->command);
Kyle McMartin 9ecbc0
+		if (handshake(xhci, &xhci->op_regs->status,
Kyle McMartin 9ecbc0
+			      STS_RESTORE, 0, 10*100)) {
Kyle McMartin 9ecbc0
+			xhci_dbg(xhci, "WARN: xHC CMD_CSS timeout\n");
Kyle McMartin 9ecbc0
+			spin_unlock_irq(&xhci->lock);
Kyle McMartin 9ecbc0
+			return -ETIMEDOUT;
Kyle McMartin 9ecbc0
+		}
Kyle McMartin 9ecbc0
+		temp = xhci_readl(xhci, &xhci->op_regs->status);
Kyle McMartin 9ecbc0
+	}
Kyle McMartin 9ecbc0
+
Kyle McMartin 9ecbc0
+	/* If restore operation fails, re-initialize the HC during resume */
Kyle McMartin 9ecbc0
+	if ((temp & STS_SRE) || hibernated) {
Kyle McMartin 9ecbc0
+		usb_root_hub_lost_power(hcd->self.root_hub);
Kyle McMartin 9ecbc0
+
Kyle McMartin 9ecbc0
+		xhci_dbg(xhci, "Stop HCD\n");
Kyle McMartin 9ecbc0
+		xhci_halt(xhci);
Kyle McMartin 9ecbc0
+		xhci_reset(xhci);
Kyle McMartin 9ecbc0
+		if (hibernated)
Kyle McMartin 9ecbc0
+			xhci_cleanup_msix(xhci);
Kyle McMartin 9ecbc0
+		spin_unlock_irq(&xhci->lock);
Kyle McMartin 9ecbc0
+
Kyle McMartin 9ecbc0
+#ifdef CONFIG_USB_XHCI_HCD_DEBUGGING
Kyle McMartin 9ecbc0
+		/* Tell the event ring poll function not to reschedule */
Kyle McMartin 9ecbc0
+		xhci->zombie = 1;
Kyle McMartin 9ecbc0
+		del_timer_sync(&xhci->event_ring_timer);
Kyle McMartin 9ecbc0
+#endif
Kyle McMartin 9ecbc0
+
Kyle McMartin 9ecbc0
+		xhci_dbg(xhci, "// Disabling event ring interrupts\n");
Kyle McMartin 9ecbc0
+		temp = xhci_readl(xhci, &xhci->op_regs->status);
Kyle McMartin 9ecbc0
+		xhci_writel(xhci, temp & ~STS_EINT, &xhci->op_regs->status);
Kyle McMartin 9ecbc0
+		temp = xhci_readl(xhci, &xhci->ir_set->irq_pending);
Kyle McMartin 9ecbc0
+		xhci_writel(xhci, ER_IRQ_DISABLE(temp),
Kyle McMartin 9ecbc0
+				&xhci->ir_set->irq_pending);
Kyle McMartin 9ecbc0
+		xhci_print_ir_set(xhci, xhci->ir_set, 0);
Kyle McMartin 9ecbc0
+
Kyle McMartin 9ecbc0
+		xhci_dbg(xhci, "cleaning up memory\n");
Kyle McMartin 9ecbc0
+		xhci_mem_cleanup(xhci);
Kyle McMartin 9ecbc0
+		xhci_dbg(xhci, "xhci_stop completed - status = %x\n",
Kyle McMartin 9ecbc0
+			    xhci_readl(xhci, &xhci->op_regs->status));
Kyle McMartin 9ecbc0
+
Kyle McMartin 9ecbc0
+		xhci_dbg(xhci, "Initialize the HCD\n");
Kyle McMartin 9ecbc0
+		retval = xhci_init(hcd);
Kyle McMartin 9ecbc0
+		if (retval)
Kyle McMartin 9ecbc0
+			return retval;
Kyle McMartin 9ecbc0
+
Kyle McMartin 9ecbc0
+		xhci_dbg(xhci, "Start the HCD\n");
Kyle McMartin 9ecbc0
+		retval = xhci_run(hcd);
Kyle McMartin 9ecbc0
+		if (!retval)
Kyle McMartin 9ecbc0
+			set_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags);
Kyle McMartin 9ecbc0
+		hcd->state = HC_STATE_SUSPENDED;
Kyle McMartin 9ecbc0
+		return retval;
Kyle McMartin 9ecbc0
+	}
Kyle McMartin 9ecbc0
+
Kyle McMartin 9ecbc0
+	/* Re-setup MSI-X */
Kyle McMartin 9ecbc0
+	if (hcd->irq)
Kyle McMartin 9ecbc0
+		free_irq(hcd->irq, hcd);
Kyle McMartin 9ecbc0
+	hcd->irq = -1;
Kyle McMartin 9ecbc0
+
Kyle McMartin 9ecbc0
+	retval = xhci_setup_msix(xhci);
Kyle McMartin 9ecbc0
+	if (retval)
Kyle McMartin 9ecbc0
+		/* fall back to msi*/
Kyle McMartin 9ecbc0
+		retval = xhci_setup_msi(xhci);
Kyle McMartin 9ecbc0
+
Kyle McMartin 9ecbc0
+	if (retval) {
Kyle McMartin 9ecbc0
+		/* fall back to legacy interrupt*/
Kyle McMartin 9ecbc0
+		retval = request_irq(pdev->irq, &usb_hcd_irq, IRQF_SHARED,
Kyle McMartin 9ecbc0
+					hcd->irq_descr, hcd);
Kyle McMartin 9ecbc0
+		if (retval) {
Kyle McMartin 9ecbc0
+			xhci_err(xhci, "request interrupt %d failed\n",
Kyle McMartin 9ecbc0
+					pdev->irq);
Kyle McMartin 9ecbc0
+			return retval;
Kyle McMartin 9ecbc0
+		}
Kyle McMartin 9ecbc0
+		hcd->irq = pdev->irq;
Kyle McMartin 9ecbc0
+	}
Kyle McMartin 9ecbc0
+
Kyle McMartin 9ecbc0
+	/* step 4: set Run/Stop bit */
Kyle McMartin 9ecbc0
+	command = xhci_readl(xhci, &xhci->op_regs->command);
Kyle McMartin 9ecbc0
+	command |= CMD_RUN;
Kyle McMartin 9ecbc0
+	xhci_writel(xhci, command, &xhci->op_regs->command);
Kyle McMartin 9ecbc0
+	handshake(xhci, &xhci->op_regs->status, STS_HALT,
Kyle McMartin 9ecbc0
+		  0, 250 * 1000);
Kyle McMartin 9ecbc0
+
Kyle McMartin 9ecbc0
+	/* step 5: walk topology and initialize portsc,
Kyle McMartin 9ecbc0
+	 * portpmsc and portli
Kyle McMartin 9ecbc0
+	 */
Kyle McMartin 9ecbc0
+	/* this is done in bus_resume */
Kyle McMartin 9ecbc0
+
Kyle McMartin 9ecbc0
+	/* step 6: restart each of the previously
Kyle McMartin 9ecbc0
+	 * Running endpoints by ringing their doorbells
Kyle McMartin 9ecbc0
+	 */
Kyle McMartin 9ecbc0
+
Kyle McMartin 9ecbc0
+	set_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags);
Kyle McMartin 9ecbc0
+	if (!hibernated)
Kyle McMartin 9ecbc0
+		hcd->state = old_state;
Kyle McMartin 9ecbc0
+	else
Kyle McMartin 9ecbc0
+		hcd->state = HC_STATE_SUSPENDED;
Kyle McMartin 9ecbc0
+
Kyle McMartin 9ecbc0
+	spin_unlock_irq(&xhci->lock);
Kyle McMartin 9ecbc0
+	return 0;
Kyle McMartin 9ecbc0
+}
Kyle McMartin 9ecbc0
+
Kyle McMartin 9ecbc0
 /*-------------------------------------------------------------------------*/
Kyle McMartin 9ecbc0
 
Kyle McMartin 9ecbc0
 /**
Kyle McMartin 9ecbc0
@@ -956,7 +1166,7 @@ int xhci_urb_dequeue(struct usb_hcd *hcd, struct urb *urb, int status)
Kyle McMartin 9ecbc0
 		ep->stop_cmd_timer.expires = jiffies +
Kyle McMartin 9ecbc0
 			XHCI_STOP_EP_CMD_TIMEOUT * HZ;
Kyle McMartin 9ecbc0
 		add_timer(&ep->stop_cmd_timer);
Kyle McMartin 9ecbc0
-		xhci_queue_stop_endpoint(xhci, urb->dev->slot_id, ep_index);
Kyle McMartin 9ecbc0
+		xhci_queue_stop_endpoint(xhci, urb->dev->slot_id, ep_index, 0);
Kyle McMartin 9ecbc0
 		xhci_ring_cmd_db(xhci);
Kyle McMartin 9ecbc0
 	}
Kyle McMartin 9ecbc0
 done:
Kyle McMartin 9ecbc0
diff --git a/drivers/usb/host/xhci.h b/drivers/usb/host/xhci.h
Kyle McMartin 9ecbc0
index 34a60d9..b6d8033 100644
Kyle McMartin 9ecbc0
--- a/drivers/usb/host/xhci.h
Kyle McMartin 9ecbc0
+++ b/drivers/usb/host/xhci.h
Kyle McMartin 9ecbc0
@@ -191,7 +191,7 @@ struct xhci_op_regs {
Kyle McMartin 9ecbc0
 /* bits 4:6 are reserved (and should be preserved on writes). */
Kyle McMartin 9ecbc0
 /* light reset (port status stays unchanged) - reset completed when this is 0 */
Kyle McMartin 9ecbc0
 #define CMD_LRESET	(1 << 7)
Kyle McMartin 9ecbc0
-/* FIXME: ignoring host controller save/restore state for now. */
Kyle McMartin 9ecbc0
+/* host controller save/restore state. */
Kyle McMartin 9ecbc0
 #define CMD_CSS		(1 << 8)
Kyle McMartin 9ecbc0
 #define CMD_CRS		(1 << 9)
Kyle McMartin 9ecbc0
 /* Enable Wrap Event - '1' means xHC generates an event when MFINDEX wraps. */
Kyle McMartin 9ecbc0
@@ -269,6 +269,10 @@ struct xhci_op_regs {
Kyle McMartin 9ecbc0
  * A read gives the current link PM state of the port,
Kyle McMartin 9ecbc0
  * a write with Link State Write Strobe set sets the link state.
Kyle McMartin 9ecbc0
  */
Kyle McMartin 9ecbc0
+#define PORT_PLS_MASK	(0xf << 5)
Kyle McMartin 9ecbc0
+#define XDEV_U0		(0x0 << 5)
Kyle McMartin 9ecbc0
+#define XDEV_U3		(0x3 << 5)
Kyle McMartin 9ecbc0
+#define XDEV_RESUME	(0xf << 5)
Kyle McMartin 9ecbc0
 /* true: port has power (see HCC_PPC) */
Kyle McMartin 9ecbc0
 #define PORT_POWER	(1 << 9)
Kyle McMartin 9ecbc0
 /* bits 10:13 indicate device speed:
Kyle McMartin 9ecbc0
@@ -353,6 +357,8 @@ struct xhci_op_regs {
Kyle McMartin 9ecbc0
 #define PORT_U2_TIMEOUT(p)	(((p) & 0xff) << 8)
Kyle McMartin 9ecbc0
 /* Bits 24:31 for port testing */
Kyle McMartin 9ecbc0
 
Kyle McMartin 9ecbc0
+/* USB2 Protocol PORTSPMSC */
Kyle McMartin 9ecbc0
+#define PORT_RWE	(1 << 0x3)
Kyle McMartin 9ecbc0
 
Kyle McMartin 9ecbc0
 /**
Kyle McMartin 9ecbc0
  * struct xhci_intr_reg - Interrupt Register Set
Kyle McMartin 9ecbc0
@@ -510,6 +516,7 @@ struct xhci_slot_ctx {
Kyle McMartin 9ecbc0
 #define MAX_EXIT	(0xffff)
Kyle McMartin 9ecbc0
 /* Root hub port number that is needed to access the USB device */
Kyle McMartin 9ecbc0
 #define ROOT_HUB_PORT(p)	(((p) & 0xff) << 16)
Kyle McMartin 9ecbc0
+#define DEVINFO_TO_ROOT_HUB_PORT(p)	(((p) >> 16) & 0xff)
Kyle McMartin 9ecbc0
 /* Maximum number of ports under a hub device */
Kyle McMartin 9ecbc0
 #define XHCI_MAX_PORTS(p)	(((p) & 0xff) << 24)
Kyle McMartin 9ecbc0
 
Kyle McMartin 9ecbc0
@@ -751,6 +758,7 @@ struct xhci_virt_device {
Kyle McMartin 9ecbc0
 	/* Status of the last command issued for this device */
Kyle McMartin 9ecbc0
 	u32				cmd_status;
Kyle McMartin 9ecbc0
 	struct list_head		cmd_list;
Kyle McMartin 9ecbc0
+	u8				port;
Kyle McMartin 9ecbc0
 };
Kyle McMartin 9ecbc0
 
Kyle McMartin 9ecbc0
 
Kyle McMartin 9ecbc0
@@ -881,6 +889,10 @@ struct xhci_event_cmd {
Kyle McMartin 9ecbc0
 #define TRB_TO_EP_INDEX(p)		((((p) & (0x1f << 16)) >> 16) - 1)
Kyle McMartin 9ecbc0
 #define	EP_ID_FOR_TRB(p)		((((p) + 1) & 0x1f) << 16)
Kyle McMartin 9ecbc0
 
Kyle McMartin 9ecbc0
+#define SUSPEND_PORT_FOR_TRB(p)		(((p) & 1) << 23)
Kyle McMartin 9ecbc0
+#define TRB_TO_SUSPEND_PORT(p)		(((p) & (1 << 23)) >> 23)
Kyle McMartin 9ecbc0
+#define LAST_EP_INDEX			30
Kyle McMartin 9ecbc0
+
Kyle McMartin 9ecbc0
 /* Set TR Dequeue Pointer command TRB fields */
Kyle McMartin 9ecbc0
 #define TRB_TO_STREAM_ID(p)		((((p) & (0xffff << 16)) >> 16))
Kyle McMartin 9ecbc0
 #define STREAM_ID_FOR_TRB(p)		((((p)) & 0xffff) << 16)
Kyle McMartin 9ecbc0
@@ -1115,6 +1127,17 @@ struct urb_priv {
Kyle McMartin 9ecbc0
 #define XHCI_STOP_EP_CMD_TIMEOUT	5
Kyle McMartin 9ecbc0
 /* XXX: Make these module parameters */
Kyle McMartin 9ecbc0
 
Kyle McMartin 9ecbc0
+struct s3_save {
Kyle McMartin 9ecbc0
+	u32	command;
Kyle McMartin 9ecbc0
+	u32	dev_nt;
Kyle McMartin 9ecbc0
+	u64	dcbaa_ptr;
Kyle McMartin 9ecbc0
+	u32	config_reg;
Kyle McMartin 9ecbc0
+	u32	irq_pending;
Kyle McMartin 9ecbc0
+	u32	irq_control;
Kyle McMartin 9ecbc0
+	u32	erst_size;
Kyle McMartin 9ecbc0
+	u64	erst_base;
Kyle McMartin 9ecbc0
+	u64	erst_dequeue;
Kyle McMartin 9ecbc0
+};
Kyle McMartin 9ecbc0
 
Kyle McMartin 9ecbc0
 /* There is one ehci_hci structure per controller */
Kyle McMartin 9ecbc0
 struct xhci_hcd {
Kyle McMartin 9ecbc0
@@ -1178,6 +1201,12 @@ struct xhci_hcd {
Kyle McMartin 9ecbc0
 #endif
Kyle McMartin 9ecbc0
 	/* Host controller watchdog timer structures */
Kyle McMartin 9ecbc0
 	unsigned int		xhc_state;
Kyle McMartin 9ecbc0
+
Kyle McMartin 9ecbc0
+	unsigned long		bus_suspended;
Kyle McMartin 9ecbc0
+	unsigned long		next_statechange;
Kyle McMartin 9ecbc0
+
Kyle McMartin 9ecbc0
+	u32			command;
Kyle McMartin 9ecbc0
+	struct s3_save		s3;
Kyle McMartin 9ecbc0
 /* Host controller is dying - not responding to commands. "I'm not dead yet!"
Kyle McMartin 9ecbc0
  *
Kyle McMartin 9ecbc0
  * xHC interrupts have been disabled and a watchdog timer will (or has already)
Kyle McMartin 9ecbc0
@@ -1199,6 +1228,10 @@ struct xhci_hcd {
Kyle McMartin ee99a6
	/* Array of pointers to USB 2.0 PORTSC registers */
Kyle McMartin ee99a6
	u32 __iomem		**usb2_ports;
Kyle McMartin ee99a6
	unsigned int		num_usb2_ports;
Kyle McMartin 9ecbc0
+	u32			port_c_suspend[8];	/* port suspend change*/
Kyle McMartin 9ecbc0
+	u32			suspended_ports[8];	/* which ports are
Kyle McMartin 9ecbc0
+							   suspended */
Kyle McMartin 9ecbc0
+	unsigned long		resume_done[MAX_HC_PORTS];
Kyle McMartin 9ecbc0
 };
Kyle McMartin 9ecbc0
 
Kyle McMartin 9ecbc0
 /* For testing purposes */
Kyle McMartin 9ecbc0
@@ -1369,6 +1402,8 @@ int xhci_init(struct usb_hcd *hcd);
Kyle McMartin 9ecbc0
 int xhci_run(struct usb_hcd *hcd);
Kyle McMartin 9ecbc0
 void xhci_stop(struct usb_hcd *hcd);
Kyle McMartin 9ecbc0
 void xhci_shutdown(struct usb_hcd *hcd);
Kyle McMartin 9ecbc0
+int xhci_suspend(struct xhci_hcd *xhci);
Kyle McMartin 9ecbc0
+int xhci_resume(struct xhci_hcd *xhci, bool hibernated);
Kyle McMartin 9ecbc0
 int xhci_get_frame(struct usb_hcd *hcd);
Kyle McMartin 9ecbc0
 irqreturn_t xhci_irq(struct usb_hcd *hcd);
Kyle McMartin 9ecbc0
 irqreturn_t xhci_msi_irq(int irq, struct usb_hcd *hcd);
Kyle McMartin 9ecbc0
@@ -1406,7 +1441,7 @@ int xhci_queue_address_device(struct xhci_hcd *xhci, dma_addr_t in_ctx_ptr,
Kyle McMartin 9ecbc0
 int xhci_queue_vendor_command(struct xhci_hcd *xhci,
Kyle McMartin 9ecbc0
 		u32 field1, u32 field2, u32 field3, u32 field4);
Kyle McMartin 9ecbc0
 int xhci_queue_stop_endpoint(struct xhci_hcd *xhci, int slot_id,
Kyle McMartin 9ecbc0
-		unsigned int ep_index);
Kyle McMartin 9ecbc0
+		unsigned int ep_index, int suspend);
Kyle McMartin 9ecbc0
 int xhci_queue_ctrl_tx(struct xhci_hcd *xhci, gfp_t mem_flags, struct urb *urb,
Kyle McMartin 9ecbc0
 		int slot_id, unsigned int ep_index);
Kyle McMartin 9ecbc0
 int xhci_queue_bulk_tx(struct xhci_hcd *xhci, gfp_t mem_flags, struct urb *urb,
Kyle McMartin 9ecbc0
@@ -1436,11 +1471,18 @@ void xhci_queue_config_ep_quirk(struct xhci_hcd *xhci,
Kyle McMartin 9ecbc0
 		unsigned int slot_id, unsigned int ep_index,
Kyle McMartin 9ecbc0
 		struct xhci_dequeue_state *deq_state);
Kyle McMartin 9ecbc0
 void xhci_stop_endpoint_command_watchdog(unsigned long arg);
Kyle McMartin 9ecbc0
+void xhci_ring_ep_doorbell(struct xhci_hcd *xhci, unsigned int slot_id,
Kyle McMartin 9ecbc0
+		unsigned int ep_index, unsigned int stream_id);
Kyle McMartin 9ecbc0
 
Kyle McMartin 9ecbc0
 /* xHCI roothub code */
Kyle McMartin 9ecbc0
 int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, u16 wIndex,
Kyle McMartin 9ecbc0
 		char *buf, u16 wLength);
Kyle McMartin 9ecbc0
 int xhci_hub_status_data(struct usb_hcd *hcd, char *buf);
Kyle McMartin 9ecbc0
+int xhci_bus_suspend(struct usb_hcd *hcd);
Kyle McMartin 9ecbc0
+int xhci_bus_resume(struct usb_hcd *hcd);
Kyle McMartin 9ecbc0
+u32 xhci_port_state_to_neutral(u32 state);
Kyle McMartin 9ecbc0
+int xhci_find_slot_id_by_port(struct xhci_hcd *xhci, u16 port);
Kyle McMartin 9ecbc0
+void xhci_ring_device(struct xhci_hcd *xhci, int slot_id);
Kyle McMartin 9ecbc0
 
Kyle McMartin 9ecbc0
 /* xHCI contexts */
Kyle McMartin 9ecbc0
 struct xhci_input_control_ctx *xhci_get_input_control_ctx(struct xhci_hcd *xhci, struct xhci_container_ctx *ctx);