aboutsummaryrefslogtreecommitdiffstats
path: root/usb
diff options
Diffstat (limited to 'usb')
-rw-r--r--usb/usb-core-use-kernel-assigned-address-for-devices-under-xhci.patch108
-rw-r--r--usb/usb-fix-issue-with-usb-3.0-devices-after-system-resume.patch97
-rw-r--r--usb/usb-r8a66597-hcd-change-mistake-of-the-outsw-function.patch34
-rw-r--r--usb/usb-xhci-add-pointer-to-udev-in-struct-xhci_virt_device.patch239
-rw-r--r--usb/usb-xhci-bus-power-management-implementation.patch286
-rw-r--r--usb/usb-xhci-change-xhci_reset_device-to-allocate-new-device.patch128
-rw-r--r--usb/usb-xhci-pci-power-management-implementation.patch356
-rw-r--r--usb/usb-xhci-port-power-management-implementation.patch510
-rw-r--r--usb/usb-xhci-port-remote-wakeup-implementation.patch244
9 files changed, 2002 insertions, 0 deletions
diff --git a/usb/usb-core-use-kernel-assigned-address-for-devices-under-xhci.patch b/usb/usb-core-use-kernel-assigned-address-for-devices-under-xhci.patch
new file mode 100644
index 00000000000000..e8151b9001b031
--- /dev/null
+++ b/usb/usb-core-use-kernel-assigned-address-for-devices-under-xhci.patch
@@ -0,0 +1,108 @@
+From sarah.a.sharp@linux.intel.com Thu Oct 14 11:48:46 2010
+Date: Thu, 14 Oct 2010 07:22:51 -0700
+From: Andiry Xu <andiry.xu@amd.com>
+To: Greg KH <gregkh@suse.de>
+Cc: linux-usb@vger.kernel.org, Andiry Xu <andiry.xu@amd.com>,
+ "Su, Henry" <Henry.Su@amd.com>, "He, Alex" <Alex.He@amd.com>
+Subject: USB: core: use kernel assigned address for devices under xHCI
+Message-ID: <20101014142251.GA2853@xanatos>
+Content-Disposition: inline
+
+From: Andiry Xu <andiry.xu@amd.com>
+
+xHCI driver uses hardware assigned device address. This may cause device
+address conflict in certain cases.
+
+Use kernel assigned address for devices under xHCI. Store the xHC assigned
+address locally in xHCI driver.
+
+Signed-off-by: Andiry Xu <andiry.xu@amd.com>
+Signed-off-by: Sarah Sharp <sarah.a.sharp@linux.intel.com>
+---
+ drivers/usb/core/hub.c | 27 +++++++++++++--------------
+ drivers/usb/host/xhci.c | 8 ++++----
+ drivers/usb/host/xhci.h | 2 ++
+ 3 files changed, 19 insertions(+), 18 deletions(-)
+
+--- a/drivers/usb/core/hub.c
++++ b/drivers/usb/core/hub.c
+@@ -2594,16 +2594,14 @@ static int hub_set_address(struct usb_de
+ return 0;
+ if (udev->state != USB_STATE_DEFAULT)
+ return -EINVAL;
+- if (hcd->driver->address_device) {
++ if (hcd->driver->address_device)
+ retval = hcd->driver->address_device(hcd, udev);
+- } else {
++ else
+ retval = usb_control_msg(udev, usb_sndaddr0pipe(),
+ USB_REQ_SET_ADDRESS, 0, devnum, 0,
+ NULL, 0, USB_CTRL_SET_TIMEOUT);
+- if (retval == 0)
+- update_address(udev, devnum);
+- }
+ if (retval == 0) {
++ update_address(udev, devnum);
+ /* Device now using proper address. */
+ usb_set_device_state(udev, USB_STATE_ADDRESS);
+ usb_ep0_reinit(udev);
+@@ -3097,16 +3095,17 @@ static void hub_port_connect_change(stru
+ udev->speed = USB_SPEED_UNKNOWN;
+
+ /*
+- * xHCI needs to issue an address device command later
+- * in the hub_port_init sequence for SS/HS/FS/LS devices.
++ * Set the address.
++ * Note xHCI needs to issue an address device command later
++ * in the hub_port_init sequence for SS/HS/FS/LS devices,
++ * and xHC will assign an address to the device. But use
++ * kernel assigned address here, to avoid any address conflict
++ * issue.
+ */
+- if (!(hcd->driver->flags & HCD_USB3)) {
+- /* set the address */
+- choose_address(udev);
+- if (udev->devnum <= 0) {
+- status = -ENOTCONN; /* Don't retry */
+- goto loop;
+- }
++ choose_address(udev);
++ if (udev->devnum <= 0) {
++ status = -ENOTCONN; /* Don't retry */
++ goto loop;
+ }
+
+ /* reset (non-USB 3.0 devices) and get descriptor */
+--- a/drivers/usb/host/xhci.c
++++ b/drivers/usb/host/xhci.c
+@@ -2287,15 +2287,15 @@ int xhci_address_device(struct usb_hcd *
+ * address given back to us by the HC.
+ */
+ slot_ctx = xhci_get_slot_ctx(xhci, virt_dev->out_ctx);
+- udev->devnum = (slot_ctx->dev_state & DEV_ADDR_MASK) + 1;
++ /* Use kernel assigned address for devices; store xHC assigned
++ * address locally. */
++ virt_dev->address = (slot_ctx->dev_state & DEV_ADDR_MASK) + 1;
+ /* Zero the input context control for later use */
+ ctrl_ctx = xhci_get_input_control_ctx(xhci, virt_dev->in_ctx);
+ ctrl_ctx->add_flags = 0;
+ ctrl_ctx->drop_flags = 0;
+
+- xhci_dbg(xhci, "Device address = %d\n", udev->devnum);
+- /* XXX Meh, not sure if anyone else but choose_address uses this. */
+- set_bit(udev->devnum, udev->bus->devmap.devicemap);
++ xhci_dbg(xhci, "Internal device address = %d\n", virt_dev->address);
+
+ return 0;
+ }
+--- a/drivers/usb/host/xhci.h
++++ b/drivers/usb/host/xhci.h
+@@ -746,6 +746,8 @@ struct xhci_virt_device {
+ /* Rings saved to ensure old alt settings can be re-instated */
+ struct xhci_ring **ring_cache;
+ int num_rings_cached;
++ /* Store xHC assigned device address */
++ int address;
+ #define XHCI_MAX_RINGS_CACHED 31
+ struct xhci_virt_ep eps[31];
+ struct completion cmd_completion;
diff --git a/usb/usb-fix-issue-with-usb-3.0-devices-after-system-resume.patch b/usb/usb-fix-issue-with-usb-3.0-devices-after-system-resume.patch
new file mode 100644
index 00000000000000..59451e9ac658ae
--- /dev/null
+++ b/usb/usb-fix-issue-with-usb-3.0-devices-after-system-resume.patch
@@ -0,0 +1,97 @@
+From sarah.a.sharp@linux.intel.com Thu Oct 14 11:49:31 2010
+Date: Thu, 14 Oct 2010 07:22:54 -0700
+From: Sarah Sharp <sarah.a.sharp@linux.intel.com>
+To: Greg KH <gregkh@suse.de>
+Cc: linux-usb@vger.kernel.org, "He, Alex" <Alex.He@amd.com>,
+ "Su, Henry" <Henry.Su@amd.com>, Andiry Xu <andiry.xu@amd.com>
+Subject: usb: Fix issue with USB 3.0 devices after system resume
+Message-ID: <20101014142254.GA2928@xanatos>
+Content-Disposition: inline
+
+From: Sarah Sharp <sarah.a.sharp@linux.intel.com>
+
+When the system suspends and a host controller's power is lost, the USB
+core attempts to revive any USB devices that had the persist_enabled flag
+set. For non-SuperSpeed devices, it will disable the port, and then set
+the udev->reset_resume flag. This will cause the USB core to reset the
+device, verify the device descriptors to make sure it's the same device,
+and re-install any non-default configurations or alternate interface
+settings.
+
+However, we can't disable SuperSpeed root hub ports because that turns off
+SuperSpeed terminations, which will inhibit any devices connecting at USB
+3.0 speeds. (Plus external hubs don't allow SuperSpeed ports to be
+disabled.)
+
+Because of this logic in hub_activate():
+ /* We can forget about a "removed" device when there's a
+ * physical disconnect or the connect status changes.
+ */
+ if (!(portstatus & USB_PORT_STAT_CONNECTION) ||
+ (portchange & USB_PORT_STAT_C_CONNECTION))
+ clear_bit(port1, hub->removed_bits);
+
+ if (!udev || udev->state == USB_STATE_NOTATTACHED) {
+ /* Tell khubd to disconnect the device or
+ * check for a new connection
+ */
+ if (udev || (portstatus & USB_PORT_STAT_CONNECTION))
+ set_bit(port1, hub->change_bits);
+
+ } else if (portstatus & USB_PORT_STAT_ENABLE) {
+ /* The power session apparently survived the resume.
+ * If there was an overcurrent or suspend change
+ * (i.e., remote wakeup request), have khubd
+ * take care of it.
+ */
+ if (portchange)
+ set_bit(port1, hub->change_bits);
+
+ } else if (udev->persist_enabled) {
+ udev->reset_resume = 1;
+ set_bit(port1, hub->change_bits);
+
+ } else {
+ /* The power session is gone; tell khubd */
+ usb_set_device_state(udev, USB_STATE_NOTATTACHED);
+ set_bit(port1, hub->change_bits);
+ }
+
+a SuperSpeed device after a resume with a loss of power will never get the
+reset_resume flag set. Instead the core will assume the power session
+survived and that the device still has the same address, configuration,
+and alternate interface settings. The xHCI host controller will have no
+knowledge of the device (since all xhci_virt_devices were destroyed when
+power loss was discovered, and xhci_discover_or_reset_device() has not
+been called), and all URBs to the device will fail.
+
+If the device driver responds by resetting the device, everything will
+continue smoothly. However, if lsusb is used before the device driver
+resets the device (or there is no driver), then all lsusb descriptor
+fetches will fail.
+
+The quick fix is to pretend the port is disabled in hub_activate(), by
+clearing the local variable. But I'm not sure what other parts of the hub
+driver need to be changed because they have assumptions about when ports
+will be disabled.
+
+Signed-off-by: Sarah Sharp <sarah.a.sharp@linux.intel.com>
+Signed-off-by: Andiry Xu <andiry.xu@amd.com>
+Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
+
+---
+ drivers/usb/core/hub.c | 3 +++
+ 1 file changed, 3 insertions(+)
+
+--- a/drivers/usb/core/hub.c
++++ b/drivers/usb/core/hub.c
+@@ -758,6 +758,9 @@ static void hub_activate(struct usb_hub
+ clear_port_feature(hdev, port1,
+ USB_PORT_FEAT_ENABLE);
+ portstatus &= ~USB_PORT_STAT_ENABLE;
++ } else {
++ /* Pretend that power was lost for USB3 devs */
++ portstatus &= ~USB_PORT_STAT_ENABLE;
+ }
+ }
+
diff --git a/usb/usb-r8a66597-hcd-change-mistake-of-the-outsw-function.patch b/usb/usb-r8a66597-hcd-change-mistake-of-the-outsw-function.patch
new file mode 100644
index 00000000000000..400f2dfdd6006a
--- /dev/null
+++ b/usb/usb-r8a66597-hcd-change-mistake-of-the-outsw-function.patch
@@ -0,0 +1,34 @@
+From iwamatsu@nigauri.org Thu Oct 14 11:43:01 2010
+From: Nobuhiro Iwamatsu <nobuhiro.iwamatsu.yj@renesas.com>
+To: linux-usb@vger.kernel.org
+Cc: gregkh@suse.de, lethal@linux-sh.org,
+ yoshihiro.shimoda.uh@renesas.com,
+ Nobuhiro Iwamatsu <nobuhiro.iwamatsu.yj@renesas.com>
+Subject: usb: r8a66597-hcd: Change mistake of the outsw function
+Date: Thu, 14 Oct 2010 14:52:54 +0900
+Message-Id: <1287035574-17972-1-git-send-email-nobuhiro.iwamatsu.yj@renesas.com>
+
+Some functions changed by 1c98347e613bf17ea2f18c9766ce0ab77f65a96d.
+However, There was a change mistake of the function (outsw).
+
+Signed-off-by: Nobuhiro Iwamatsu <nobuhiro.iwamatsu.yj@renesas.com>
+CC: Paul Mundt <lethal@linux-sh.org>
+Cc: stable <stable@kernel.org> [.35 & .36]
+Acked-by: Yoshihiro Shimoda <yoshihiro.shimoda.uh@renesas.com>
+Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
+
+---
+ drivers/usb/host/r8a66597.h | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+--- a/drivers/usb/host/r8a66597.h
++++ b/drivers/usb/host/r8a66597.h
+@@ -227,7 +227,7 @@ static inline void r8a66597_write_fifo(s
+ int odd = len & 0x0001;
+
+ len = len / 2;
+- ioread16_rep(fifoaddr, buf, len);
++ iowrite16_rep(fifoaddr, buf, len);
+ if (unlikely(odd)) {
+ buf = &buf[len];
+ iowrite8((unsigned char)*buf, fifoaddr);
diff --git a/usb/usb-xhci-add-pointer-to-udev-in-struct-xhci_virt_device.patch b/usb/usb-xhci-add-pointer-to-udev-in-struct-xhci_virt_device.patch
new file mode 100644
index 00000000000000..5b1d9085a2b5a1
--- /dev/null
+++ b/usb/usb-xhci-add-pointer-to-udev-in-struct-xhci_virt_device.patch
@@ -0,0 +1,239 @@
+From linux-usb-owner@vger.kernel.org Thu Oct 14 11:48:00 2010
+Date: Thu, 14 Oct 2010 07:22:45 -0700
+From: Andiry Xu <andiry.xu@amd.com>
+To: Greg KH <gregkh@suse.de>
+Cc: linux-usb@vger.kernel.org, Andiry Xu <andiry.xu@amd.com>,
+ "Su, Henry" <Henry.Su@amd.com>, "He, Alex" <Alex.He@amd.com>
+Subject: USB: xHCI: Add pointer to udev in struct xhci_virt_device
+Message-ID: <20101014142245.GA2737@xanatos>
+Content-Disposition: inline
+
+From: Andiry Xu <andiry.xu@amd.com>
+
+Add a pointer to udev in struct xhci_virt_device. When allocate a new
+virt_device, make the pointer point to the corresponding udev.
+
+Modify xhci_check_args(), check if virt_dev->udev matches the target udev,
+to make sure command is issued to the right device.
+
+Signed-off-by: Andiry Xu <andiry.xu@amd.com>
+Signed-off-by: Sarah Sharp <sarah.a.sharp@linux.intel.com>
+Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
+
+---
+ drivers/usb/host/xhci-mem.c | 1
+ drivers/usb/host/xhci.c | 83 ++++++++++++++++++--------------------------
+ drivers/usb/host/xhci.h | 1
+ 3 files changed, 36 insertions(+), 49 deletions(-)
+
+--- a/drivers/usb/host/xhci-mem.c
++++ b/drivers/usb/host/xhci-mem.c
+@@ -778,6 +778,7 @@ int xhci_alloc_virt_device(struct xhci_h
+
+ init_completion(&dev->cmd_completion);
+ INIT_LIST_HEAD(&dev->cmd_list);
++ dev->udev = udev;
+
+ /* Point to output device context in dcbaa. */
+ xhci->dcbaa->dev_context_ptrs[slot_id] = dev->out_ctx->dma;
+--- a/drivers/usb/host/xhci.c
++++ b/drivers/usb/host/xhci.c
+@@ -607,7 +607,11 @@ unsigned int xhci_last_valid_endpoint(u3
+ * returns 0 this is a root hub; returns -EINVAL for NULL pointers.
+ */
+ int xhci_check_args(struct usb_hcd *hcd, struct usb_device *udev,
+- struct usb_host_endpoint *ep, int check_ep, const char *func) {
++ struct usb_host_endpoint *ep, int check_ep, bool check_virt_dev,
++ const char *func) {
++ struct xhci_hcd *xhci;
++ struct xhci_virt_device *virt_dev;
++
+ if (!hcd || (check_ep && !ep) || !udev) {
+ printk(KERN_DEBUG "xHCI %s called with invalid args\n",
+ func);
+@@ -618,11 +622,24 @@ int xhci_check_args(struct usb_hcd *hcd,
+ func);
+ return 0;
+ }
+- if (!udev->slot_id) {
+- printk(KERN_DEBUG "xHCI %s called with unaddressed device\n",
+- func);
+- return -EINVAL;
++
++ if (check_virt_dev) {
++ xhci = hcd_to_xhci(hcd);
++ if (!udev->slot_id || !xhci->devs
++ || !xhci->devs[udev->slot_id]) {
++ printk(KERN_DEBUG "xHCI %s called with unaddressed "
++ "device\n", func);
++ return -EINVAL;
++ }
++
++ virt_dev = xhci->devs[udev->slot_id];
++ if (virt_dev->udev != udev) {
++ printk(KERN_DEBUG "xHCI %s called with udev and "
++ "virt_dev does not match\n", func);
++ return -EINVAL;
++ }
+ }
++
+ return 1;
+ }
+
+@@ -704,18 +721,13 @@ int xhci_urb_enqueue(struct usb_hcd *hcd
+ struct urb_priv *urb_priv;
+ int size, i;
+
+- if (!urb || xhci_check_args(hcd, urb->dev, urb->ep, true, __func__) <= 0)
++ if (!urb || xhci_check_args(hcd, urb->dev, urb->ep,
++ true, true, __func__) <= 0)
+ return -EINVAL;
+
+ slot_id = urb->dev->slot_id;
+ ep_index = xhci_get_endpoint_index(&urb->ep->desc);
+
+- if (!xhci->devs || !xhci->devs[slot_id]) {
+- if (!in_interrupt())
+- dev_warn(&urb->dev->dev, "WARN: urb submitted for dev with no Slot ID\n");
+- ret = -EINVAL;
+- goto exit;
+- }
+ if (!HCD_HW_ACCESSIBLE(hcd)) {
+ if (!in_interrupt())
+ xhci_dbg(xhci, "urb submitted during PCI suspend\n");
+@@ -991,7 +1003,7 @@ int xhci_drop_endpoint(struct usb_hcd *h
+ u32 new_add_flags, new_drop_flags, new_slot_info;
+ int ret;
+
+- ret = xhci_check_args(hcd, udev, ep, 1, __func__);
++ ret = xhci_check_args(hcd, udev, ep, 1, true, __func__);
+ if (ret <= 0)
+ return ret;
+ xhci = hcd_to_xhci(hcd);
+@@ -1004,12 +1016,6 @@ int xhci_drop_endpoint(struct usb_hcd *h
+ return 0;
+ }
+
+- if (!xhci->devs || !xhci->devs[udev->slot_id]) {
+- xhci_warn(xhci, "xHCI %s called with unaddressed device\n",
+- __func__);
+- return -EINVAL;
+- }
+-
+ in_ctx = xhci->devs[udev->slot_id]->in_ctx;
+ out_ctx = xhci->devs[udev->slot_id]->out_ctx;
+ ctrl_ctx = xhci_get_input_control_ctx(xhci, in_ctx);
+@@ -1078,7 +1084,7 @@ int xhci_add_endpoint(struct usb_hcd *hc
+ u32 new_add_flags, new_drop_flags, new_slot_info;
+ int ret = 0;
+
+- ret = xhci_check_args(hcd, udev, ep, 1, __func__);
++ ret = xhci_check_args(hcd, udev, ep, 1, true, __func__);
+ if (ret <= 0) {
+ /* So we won't queue a reset ep command for a root hub */
+ ep->hcpriv = NULL;
+@@ -1098,12 +1104,6 @@ int xhci_add_endpoint(struct usb_hcd *hc
+ return 0;
+ }
+
+- if (!xhci->devs || !xhci->devs[udev->slot_id]) {
+- xhci_warn(xhci, "xHCI %s called with unaddressed device\n",
+- __func__);
+- return -EINVAL;
+- }
+-
+ in_ctx = xhci->devs[udev->slot_id]->in_ctx;
+ out_ctx = xhci->devs[udev->slot_id]->out_ctx;
+ ctrl_ctx = xhci_get_input_control_ctx(xhci, in_ctx);
+@@ -1346,16 +1346,11 @@ int xhci_check_bandwidth(struct usb_hcd
+ struct xhci_input_control_ctx *ctrl_ctx;
+ struct xhci_slot_ctx *slot_ctx;
+
+- ret = xhci_check_args(hcd, udev, NULL, 0, __func__);
++ ret = xhci_check_args(hcd, udev, NULL, 0, true, __func__);
+ if (ret <= 0)
+ return ret;
+ xhci = hcd_to_xhci(hcd);
+
+- if (!udev->slot_id || !xhci->devs || !xhci->devs[udev->slot_id]) {
+- xhci_warn(xhci, "xHCI %s called with unaddressed device\n",
+- __func__);
+- return -EINVAL;
+- }
+ xhci_dbg(xhci, "%s called for udev %p\n", __func__, udev);
+ virt_dev = xhci->devs[udev->slot_id];
+
+@@ -1405,16 +1400,11 @@ void xhci_reset_bandwidth(struct usb_hcd
+ struct xhci_virt_device *virt_dev;
+ int i, ret;
+
+- ret = xhci_check_args(hcd, udev, NULL, 0, __func__);
++ ret = xhci_check_args(hcd, udev, NULL, 0, true, __func__);
+ if (ret <= 0)
+ return;
+ xhci = hcd_to_xhci(hcd);
+
+- if (!xhci->devs || !xhci->devs[udev->slot_id]) {
+- xhci_warn(xhci, "xHCI %s called with unaddressed device\n",
+- __func__);
+- return;
+- }
+ xhci_dbg(xhci, "%s called for udev %p\n", __func__, udev);
+ virt_dev = xhci->devs[udev->slot_id];
+ /* Free any rings allocated for added endpoints */
+@@ -1575,7 +1565,7 @@ static int xhci_check_streams_endpoint(s
+
+ if (!ep)
+ return -EINVAL;
+- ret = xhci_check_args(xhci_to_hcd(xhci), udev, ep, 1, __func__);
++ ret = xhci_check_args(xhci_to_hcd(xhci), udev, ep, 1, true, __func__);
+ if (ret <= 0)
+ return -EINVAL;
+ if (ep->ss_ep_comp.bmAttributes == 0) {
+@@ -1965,17 +1955,12 @@ int xhci_reset_device(struct usb_hcd *hc
+ int timeleft;
+ int last_freed_endpoint;
+
+- ret = xhci_check_args(hcd, udev, NULL, 0, __func__);
++ ret = xhci_check_args(hcd, udev, NULL, 0, true, __func__);
+ if (ret <= 0)
+ return ret;
+ xhci = hcd_to_xhci(hcd);
+ slot_id = udev->slot_id;
+ virt_dev = xhci->devs[slot_id];
+- if (!virt_dev) {
+- xhci_dbg(xhci, "%s called with invalid slot ID %u\n",
+- __func__, slot_id);
+- return -EINVAL;
+- }
+
+ xhci_dbg(xhci, "Resetting device with slot ID %u\n", slot_id);
+ /* Allocate the command structure that holds the struct completion.
+@@ -2077,13 +2062,13 @@ void xhci_free_dev(struct usb_hcd *hcd,
+ struct xhci_virt_device *virt_dev;
+ unsigned long flags;
+ u32 state;
+- int i;
++ int i, ret;
+
+- if (udev->slot_id == 0)
++ ret = xhci_check_args(hcd, udev, NULL, 0, true, __func__);
++ if (ret <= 0)
+ return;
++
+ virt_dev = xhci->devs[udev->slot_id];
+- if (!virt_dev)
+- return;
+
+ /* Stop any wayward timer functions (which may grab the lock) */
+ for (i = 0; i < 31; ++i) {
+--- a/drivers/usb/host/xhci.h
++++ b/drivers/usb/host/xhci.h
+@@ -731,6 +731,7 @@ struct xhci_virt_ep {
+ };
+
+ struct xhci_virt_device {
++ struct usb_device *udev;
+ /*
+ * Commands to the hardware are passed an "input context" that
+ * tells the hardware what to change in its data structures.
diff --git a/usb/usb-xhci-bus-power-management-implementation.patch b/usb/usb-xhci-bus-power-management-implementation.patch
new file mode 100644
index 00000000000000..79fa2dfba5ca56
--- /dev/null
+++ b/usb/usb-xhci-bus-power-management-implementation.patch
@@ -0,0 +1,286 @@
+From linux-usb-owner@vger.kernel.org Thu Oct 14 11:55:11 2010
+Date: Thu, 14 Oct 2010 07:23:03 -0700
+From: Andiry Xu <andiry.xu@amd.com>
+To: Greg KH <gregkh@suse.de>
+Cc: linux-usb@vger.kernel.org, Andiry Xu <andiry.xu@amd.com>,
+ "Su, Henry" <Henry.Su@amd.com>, "He, Alex" <Alex.He@amd.com>
+Subject: USB: xHCI: bus power management implementation
+Message-ID: <20101014142302.GA3027@xanatos>
+Content-Disposition: inline
+
+From: Andiry Xu <andiry.xu@amd.com>
+
+This patch implements xHCI bus suspend/resume function hook.
+
+In the patch it goes through all the ports and suspend/resume
+the ports if needed.
+
+If any port is in remote wakeup, abort bus suspend as what ehci/ohci do.
+
+Signed-off-by: Libin Yang <libin.yang@amd.com>
+Signed-off-by: Crane Cai <crane.cai@amd.com>
+Signed-off-by: Andiry Xu <andiry.xu@amd.com>
+Signed-off-by: Sarah Sharp <sarah.a.sharp@linux.intel.com>
+Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
+
+---
+ drivers/usb/host/xhci-hub.c | 188 ++++++++++++++++++++++++++++++++++++++++++++
+ drivers/usb/host/xhci-mem.c | 1
+ drivers/usb/host/xhci-pci.c | 2
+ drivers/usb/host/xhci.h | 9 ++
+ 4 files changed, 200 insertions(+)
+
+--- a/drivers/usb/host/xhci-hub.c
++++ b/drivers/usb/host/xhci-hub.c
+@@ -24,6 +24,10 @@
+
+ #include "xhci.h"
+
++#define PORT_WAKE_BITS (PORT_WKOC_E | PORT_WKDISC_E | PORT_WKCONN_E)
++#define PORT_RWC_BITS (PORT_CSC | PORT_PEC | PORT_WRC | PORT_OCC | \
++ PORT_RC | PORT_PLC | PORT_PE)
++
+ static void xhci_hub_descriptor(struct xhci_hcd *xhci,
+ struct usb_hub_descriptor *desc)
+ {
+@@ -560,3 +564,187 @@ int xhci_hub_status_data(struct usb_hcd
+ spin_unlock_irqrestore(&xhci->lock, flags);
+ return status ? retval : 0;
+ }
++
++#ifdef CONFIG_PM
++
++int xhci_bus_suspend(struct usb_hcd *hcd)
++{
++ struct xhci_hcd *xhci = hcd_to_xhci(hcd);
++ int port;
++ unsigned long flags;
++
++ xhci_dbg(xhci, "suspend root hub\n");
++
++ spin_lock_irqsave(&xhci->lock, flags);
++
++ if (hcd->self.root_hub->do_remote_wakeup) {
++ port = HCS_MAX_PORTS(xhci->hcs_params1);
++ while (port--) {
++ if (xhci->resume_done[port] != 0) {
++ spin_unlock_irqrestore(&xhci->lock, flags);
++ xhci_dbg(xhci, "suspend failed because "
++ "port %d is resuming\n",
++ port + 1);
++ return -EBUSY;
++ }
++ }
++ }
++
++ port = HCS_MAX_PORTS(xhci->hcs_params1);
++ xhci->bus_suspended = 0;
++ while (port--) {
++ /* suspend the port if the port is not suspended */
++ u32 __iomem *addr;
++ u32 t1, t2;
++ int slot_id;
++
++ addr = &xhci->op_regs->port_status_base +
++ NUM_PORT_REGS * (port & 0xff);
++ t1 = xhci_readl(xhci, addr);
++ t2 = xhci_port_state_to_neutral(t1);
++
++ if ((t1 & PORT_PE) && !(t1 & PORT_PLS_MASK)) {
++ xhci_dbg(xhci, "port %d not suspended\n", port);
++ slot_id = xhci_find_slot_id_by_port(xhci, port + 1);
++ if (slot_id) {
++ spin_unlock_irqrestore(&xhci->lock, flags);
++ xhci_stop_device(xhci, slot_id, 1);
++ spin_lock_irqsave(&xhci->lock, flags);
++ }
++ t2 &= ~PORT_PLS_MASK;
++ t2 |= PORT_LINK_STROBE | XDEV_U3;
++ set_bit(port, &xhci->bus_suspended);
++ }
++ if (hcd->self.root_hub->do_remote_wakeup) {
++ if (t1 & PORT_CONNECT) {
++ t2 |= PORT_WKOC_E | PORT_WKDISC_E;
++ t2 &= ~PORT_WKCONN_E;
++ } else {
++ t2 |= PORT_WKOC_E | PORT_WKCONN_E;
++ t2 &= ~PORT_WKDISC_E;
++ }
++ } else
++ t2 &= ~PORT_WAKE_BITS;
++
++ t1 = xhci_port_state_to_neutral(t1);
++ if (t1 != t2)
++ xhci_writel(xhci, t2, addr);
++
++ if (DEV_HIGHSPEED(t1)) {
++ /* enable remote wake up for USB 2.0 */
++ u32 __iomem *addr;
++ u32 tmp;
++
++ addr = &xhci->op_regs->port_power_base +
++ NUM_PORT_REGS * (port & 0xff);
++ tmp = xhci_readl(xhci, addr);
++ tmp |= PORT_RWE;
++ xhci_writel(xhci, tmp, addr);
++ }
++ }
++ hcd->state = HC_STATE_SUSPENDED;
++ xhci->next_statechange = jiffies + msecs_to_jiffies(10);
++ spin_unlock_irqrestore(&xhci->lock, flags);
++ return 0;
++}
++
++int xhci_bus_resume(struct usb_hcd *hcd)
++{
++ struct xhci_hcd *xhci = hcd_to_xhci(hcd);
++ int port;
++ u32 temp;
++ unsigned long flags;
++
++ xhci_dbg(xhci, "resume root hub\n");
++
++ if (time_before(jiffies, xhci->next_statechange))
++ msleep(5);
++
++ spin_lock_irqsave(&xhci->lock, flags);
++ if (!HCD_HW_ACCESSIBLE(hcd)) {
++ spin_unlock_irqrestore(&xhci->lock, flags);
++ return -ESHUTDOWN;
++ }
++
++ /* delay the irqs */
++ temp = xhci_readl(xhci, &xhci->op_regs->command);
++ temp &= ~CMD_EIE;
++ xhci_writel(xhci, temp, &xhci->op_regs->command);
++
++ port = HCS_MAX_PORTS(xhci->hcs_params1);
++ while (port--) {
++ /* Check whether need resume ports. If needed
++ resume port and disable remote wakeup */
++ u32 __iomem *addr;
++ u32 temp;
++ int slot_id;
++
++ addr = &xhci->op_regs->port_status_base +
++ NUM_PORT_REGS * (port & 0xff);
++ temp = xhci_readl(xhci, addr);
++ if (DEV_SUPERSPEED(temp))
++ temp &= ~(PORT_RWC_BITS | PORT_CEC | PORT_WAKE_BITS);
++ else
++ temp &= ~(PORT_RWC_BITS | PORT_WAKE_BITS);
++ if (test_bit(port, &xhci->bus_suspended) &&
++ (temp & PORT_PLS_MASK)) {
++ if (DEV_SUPERSPEED(temp)) {
++ temp = xhci_port_state_to_neutral(temp);
++ temp &= ~PORT_PLS_MASK;
++ temp |= PORT_LINK_STROBE | XDEV_U0;
++ xhci_writel(xhci, temp, addr);
++ } else {
++ temp = xhci_port_state_to_neutral(temp);
++ temp &= ~PORT_PLS_MASK;
++ temp |= PORT_LINK_STROBE | XDEV_RESUME;
++ xhci_writel(xhci, temp, addr);
++
++ spin_unlock_irqrestore(&xhci->lock, flags);
++ msleep(20);
++ spin_lock_irqsave(&xhci->lock, flags);
++
++ temp = xhci_readl(xhci, addr);
++ temp = xhci_port_state_to_neutral(temp);
++ temp &= ~PORT_PLS_MASK;
++ temp |= PORT_LINK_STROBE | XDEV_U0;
++ xhci_writel(xhci, temp, addr);
++ }
++ slot_id = xhci_find_slot_id_by_port(xhci, port + 1);
++ if (slot_id)
++ xhci_ring_device(xhci, slot_id);
++ } else
++ xhci_writel(xhci, temp, addr);
++
++ if (DEV_HIGHSPEED(temp)) {
++ /* disable remote wake up for USB 2.0 */
++ u32 __iomem *addr;
++ u32 tmp;
++
++ addr = &xhci->op_regs->port_power_base +
++ NUM_PORT_REGS * (port & 0xff);
++ tmp = xhci_readl(xhci, addr);
++ tmp &= ~PORT_RWE;
++ xhci_writel(xhci, tmp, addr);
++ }
++ }
++
++ (void) xhci_readl(xhci, &xhci->op_regs->command);
++
++ xhci->next_statechange = jiffies + msecs_to_jiffies(5);
++ hcd->state = HC_STATE_RUNNING;
++ /* re-enable irqs */
++ temp = xhci_readl(xhci, &xhci->op_regs->command);
++ temp |= CMD_EIE;
++ xhci_writel(xhci, temp, &xhci->op_regs->command);
++ temp = xhci_readl(xhci, &xhci->op_regs->command);
++
++ spin_unlock_irqrestore(&xhci->lock, flags);
++ return 0;
++}
++
++#else
++
++#define xhci_bus_suspend NULL
++#define xhci_bus_resume NULL
++
++#endif
+--- a/drivers/usb/host/xhci-mem.c
++++ b/drivers/usb/host/xhci-mem.c
+@@ -1445,6 +1445,7 @@ void xhci_mem_cleanup(struct xhci_hcd *x
+ scratchpad_free(xhci);
+ xhci->page_size = 0;
+ xhci->page_shift = 0;
++ xhci->bus_suspended = 0;
+ }
+
+ static int xhci_test_trb_in_td(struct xhci_hcd *xhci,
+--- a/drivers/usb/host/xhci-pci.c
++++ b/drivers/usb/host/xhci-pci.c
+@@ -162,6 +162,8 @@ static const struct hc_driver xhci_pci_h
+ /* Root hub support */
+ .hub_control = xhci_hub_control,
+ .hub_status_data = xhci_hub_status_data,
++ .bus_suspend = xhci_bus_suspend,
++ .bus_resume = xhci_bus_resume,
+ };
+
+ /*-------------------------------------------------------------------------*/
+--- a/drivers/usb/host/xhci.h
++++ b/drivers/usb/host/xhci.h
+@@ -357,6 +357,8 @@ struct xhci_op_regs {
+ #define PORT_U2_TIMEOUT(p) (((p) & 0xff) << 8)
+ /* Bits 24:31 for port testing */
+
++/* USB2 Protocol PORTSPMSC */
++#define PORT_RWE (1 << 0x3)
+
+ /**
+ * struct xhci_intr_reg - Interrupt Register Set
+@@ -1191,6 +1193,11 @@ struct xhci_hcd {
+ #endif
+ /* Host controller watchdog timer structures */
+ unsigned int xhc_state;
++
++ unsigned long bus_suspended;
++ unsigned long next_statechange;
++
++ u32 command;
+ /* Host controller is dying - not responding to commands. "I'm not dead yet!"
+ *
+ * xHC interrupts have been disabled and a watchdog timer will (or has already)
+@@ -1460,6 +1467,8 @@ void xhci_ring_ep_doorbell(struct xhci_h
+ int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, u16 wIndex,
+ char *buf, u16 wLength);
+ int xhci_hub_status_data(struct usb_hcd *hcd, char *buf);
++int xhci_bus_suspend(struct usb_hcd *hcd);
++int xhci_bus_resume(struct usb_hcd *hcd);
+ u32 xhci_port_state_to_neutral(u32 state);
+ int xhci_find_slot_id_by_port(struct xhci_hcd *xhci, u16 port);
+ void xhci_ring_device(struct xhci_hcd *xhci, int slot_id);
diff --git a/usb/usb-xhci-change-xhci_reset_device-to-allocate-new-device.patch b/usb/usb-xhci-change-xhci_reset_device-to-allocate-new-device.patch
new file mode 100644
index 00000000000000..867484767f9ee7
--- /dev/null
+++ b/usb/usb-xhci-change-xhci_reset_device-to-allocate-new-device.patch
@@ -0,0 +1,128 @@
+From linux-usb-owner@vger.kernel.org Thu Oct 14 11:48:23 2010
+Date: Thu, 14 Oct 2010 07:22:48 -0700
+From: Andiry Xu <andiry.xu@amd.com>
+To: Greg KH <gregkh@suse.de>
+Cc: linux-usb@vger.kernel.org, "He, Alex" <Alex.He@amd.com>,
+ "Su, Henry" <Henry.Su@amd.com>, Andiry Xu <andiry.xu@amd.com>
+Subject: USB: xHCI: change xhci_reset_device() to allocate new device
+Message-ID: <20101014142248.GA2774@xanatos>
+Content-Disposition: inline
+
+From: Andiry Xu <andiry.xu@amd.com>
+
+Rename xhci_reset_device() to xhci_discover_or_reset_device().
+If xhci_discover_or_reset_device() is called to reset a device which does
+not exist or does not match the udev, it calls xhci_alloc_dev() to
+re-allocate the device.
+
+This would prevent the reset device failure, possibly due to the xHC restore
+error during S3/S4 resume.
+
+Signed-off-by: Andiry Xu <andiry.xu@amd.com>
+Signed-off-by: Sarah Sharp <sarah.a.sharp@linux.intel.com>
+Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
+
+---
+ drivers/usb/host/xhci-pci.c | 2 +-
+ drivers/usb/host/xhci.c | 44 +++++++++++++++++++++++++++++++++++++++-----
+ drivers/usb/host/xhci.h | 2 +-
+ 3 files changed, 41 insertions(+), 7 deletions(-)
+
+--- a/drivers/usb/host/xhci-pci.c
++++ b/drivers/usb/host/xhci-pci.c
+@@ -152,7 +152,7 @@ static const struct hc_driver xhci_pci_h
+ .reset_bandwidth = xhci_reset_bandwidth,
+ .address_device = xhci_address_device,
+ .update_hub_device = xhci_update_hub_device,
+- .reset_device = xhci_reset_device,
++ .reset_device = xhci_discover_or_reset_device,
+
+ /*
+ * scheduling support
+--- a/drivers/usb/host/xhci.c
++++ b/drivers/usb/host/xhci.c
+@@ -1943,8 +1943,13 @@ int xhci_free_streams(struct usb_hcd *hc
+ * Wait for the Reset Device command to finish. Remove all structures
+ * associated with the endpoints that were disabled. Clear the input device
+ * structure? Cache the rings? Reset the control endpoint 0 max packet size?
++ *
++ * If the virt_dev to be reset does not exist or does not match the udev,
++ * it means the device is lost, possibly due to the xHC restore error and
++ * re-initialization during S3/S4. In this case, call xhci_alloc_dev() to
++ * re-allocate the device.
+ */
+-int xhci_reset_device(struct usb_hcd *hcd, struct usb_device *udev)
++int xhci_discover_or_reset_device(struct usb_hcd *hcd, struct usb_device *udev)
+ {
+ int ret, i;
+ unsigned long flags;
+@@ -1955,12 +1960,36 @@ int xhci_reset_device(struct usb_hcd *hc
+ int timeleft;
+ int last_freed_endpoint;
+
+- ret = xhci_check_args(hcd, udev, NULL, 0, true, __func__);
++ ret = xhci_check_args(hcd, udev, NULL, 0, false, __func__);
+ if (ret <= 0)
+ return ret;
+ xhci = hcd_to_xhci(hcd);
+ slot_id = udev->slot_id;
+ virt_dev = xhci->devs[slot_id];
++ if (!virt_dev) {
++ xhci_dbg(xhci, "The device to be reset with slot ID %u does "
++ "not exist. Re-allocate the device\n", slot_id);
++ ret = xhci_alloc_dev(hcd, udev);
++ if (ret == 1)
++ return 0;
++ else
++ return -EINVAL;
++ }
++
++ if (virt_dev->udev != udev) {
++ /* If the virt_dev and the udev does not match, this virt_dev
++ * may belong to another udev.
++ * Re-allocate the device.
++ */
++ xhci_dbg(xhci, "The device to be reset with slot ID %u does "
++ "not match the udev. Re-allocate the device\n",
++ slot_id);
++ ret = xhci_alloc_dev(hcd, udev);
++ if (ret == 1)
++ return 0;
++ else
++ return -EINVAL;
++ }
+
+ xhci_dbg(xhci, "Resetting device with slot ID %u\n", slot_id);
+ /* Allocate the command structure that holds the struct completion.
+@@ -2176,12 +2205,17 @@ int xhci_address_device(struct usb_hcd *
+
+ virt_dev = xhci->devs[udev->slot_id];
+
+- /* If this is a Set Address to an unconfigured device, setup ep 0 */
+- if (!udev->config)
++ slot_ctx = xhci_get_slot_ctx(xhci, virt_dev->in_ctx);
++ /*
++ * If this is the first Set Address since device plug-in or
++ * virt_device realloaction after a resume with an xHCI power loss,
++ * then set up the slot context.
++ */
++ if (!slot_ctx->dev_info)
+ xhci_setup_addressable_virt_dev(xhci, udev);
++ /* Otherwise, update the control endpoint ring enqueue pointer. */
+ else
+ xhci_copy_ep0_dequeue_into_input_ctx(xhci, udev);
+- /* Otherwise, assume the core has the device configured how it wants */
+ xhci_dbg(xhci, "Slot ID %d Input Context:\n", udev->slot_id);
+ xhci_dbg_ctx(xhci, virt_dev->in_ctx, 2);
+
+--- a/drivers/usb/host/xhci.h
++++ b/drivers/usb/host/xhci.h
+@@ -1389,7 +1389,7 @@ int xhci_urb_dequeue(struct usb_hcd *hcd
+ int xhci_add_endpoint(struct usb_hcd *hcd, struct usb_device *udev, struct usb_host_endpoint *ep);
+ int xhci_drop_endpoint(struct usb_hcd *hcd, struct usb_device *udev, struct usb_host_endpoint *ep);
+ void xhci_endpoint_reset(struct usb_hcd *hcd, struct usb_host_endpoint *ep);
+-int xhci_reset_device(struct usb_hcd *hcd, struct usb_device *udev);
++int xhci_discover_or_reset_device(struct usb_hcd *hcd, struct usb_device *udev);
+ int xhci_check_bandwidth(struct usb_hcd *hcd, struct usb_device *udev);
+ void xhci_reset_bandwidth(struct usb_hcd *hcd, struct usb_device *udev);
+
diff --git a/usb/usb-xhci-pci-power-management-implementation.patch b/usb/usb-xhci-pci-power-management-implementation.patch
new file mode 100644
index 00000000000000..d2cc88b2357b55
--- /dev/null
+++ b/usb/usb-xhci-pci-power-management-implementation.patch
@@ -0,0 +1,356 @@
+From linux-usb-owner@vger.kernel.org Thu Oct 14 11:55:27 2010
+Date: Thu, 14 Oct 2010 07:23:06 -0700
+From: Andiry Xu <andiry.xu@amd.com>
+To: Greg KH <gregkh@suse.de>
+Cc: linux-usb@vger.kernel.org, "He, Alex" <Alex.He@amd.com>,
+ "Su, Henry" <Henry.Su@amd.com>,
+ "Nguyen, Dong" <Dong.Nguyen@amd.com>, Andiry Xu <andiry.xu@amd.com>
+Subject: USB: xHCI: PCI power management implementation
+Message-ID: <20101014142306.GA3060@xanatos>
+Content-Disposition: inline
+
+From: Andiry Xu <andiry.xu@amd.com>
+
+This patch implements the PCI suspend/resume.
+
+Please refer to xHCI spec for doing the suspend/resume operation.
+
+For S3, CSS/SRS in USBCMD is used to save/restore the internal state.
+However, an error maybe occurs while restoring the internal state.
+In this case, it means that HC internal state is wrong and HC will be
+re-initialized.
+
+Signed-off-by: Libin Yang <libin.yang@amd.com>
+Signed-off-by: Dong Nguyen <dong.nguyen@amd.com>
+Signed-off-by: Andiry Xu <andiry.xu@amd.com>
+Signed-off-by: Sarah Sharp <sarah.a.sharp@linux.intel.com>
+Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
+
+---
+ drivers/usb/host/xhci-pci.c | 34 ++++++-
+ drivers/usb/host/xhci.c | 210 ++++++++++++++++++++++++++++++++++++++++++++
+ drivers/usb/host/xhci.h | 16 +++
+ 3 files changed, 258 insertions(+), 2 deletions(-)
+
+--- a/drivers/usb/host/xhci-pci.c
++++ b/drivers/usb/host/xhci-pci.c
+@@ -116,6 +116,30 @@ static int xhci_pci_setup(struct usb_hcd
+ return xhci_pci_reinit(xhci, pdev);
+ }
+
++#ifdef CONFIG_PM
++static int xhci_pci_suspend(struct usb_hcd *hcd, bool do_wakeup)
++{
++ struct xhci_hcd *xhci = hcd_to_xhci(hcd);
++ int retval = 0;
++
++ if (hcd->state != HC_STATE_SUSPENDED)
++ return -EINVAL;
++
++ retval = xhci_suspend(xhci);
++
++ return retval;
++}
++
++static int xhci_pci_resume(struct usb_hcd *hcd, bool hibernated)
++{
++ struct xhci_hcd *xhci = hcd_to_xhci(hcd);
++ int retval = 0;
++
++ retval = xhci_resume(xhci, hibernated);
++ return retval;
++}
++#endif /* CONFIG_PM */
++
+ static const struct hc_driver xhci_pci_hc_driver = {
+ .description = hcd_name,
+ .product_desc = "xHCI Host Controller",
+@@ -132,7 +156,10 @@ static const struct hc_driver xhci_pci_h
+ */
+ .reset = xhci_pci_setup,
+ .start = xhci_run,
+- /* suspend and resume implemented later */
++#ifdef CONFIG_PM
++ .pci_suspend = xhci_pci_suspend,
++ .pci_resume = xhci_pci_resume,
++#endif
+ .stop = xhci_stop,
+ .shutdown = xhci_shutdown,
+
+@@ -188,6 +215,11 @@ static struct pci_driver xhci_pci_driver
+ /* suspend and resume implemented later */
+
+ .shutdown = usb_hcd_pci_shutdown,
++#ifdef CONFIG_PM_SLEEP
++ .driver = {
++ .pm = &usb_hcd_pci_pm_ops
++ },
++#endif
+ };
+
+ int xhci_register_pci(void)
+--- a/drivers/usb/host/xhci.c
++++ b/drivers/usb/host/xhci.c
+@@ -551,6 +551,216 @@ void xhci_shutdown(struct usb_hcd *hcd)
+ xhci_readl(xhci, &xhci->op_regs->status));
+ }
+
++static void xhci_save_registers(struct xhci_hcd *xhci)
++{
++ xhci->s3.command = xhci_readl(xhci, &xhci->op_regs->command);
++ xhci->s3.dev_nt = xhci_readl(xhci, &xhci->op_regs->dev_notification);
++ xhci->s3.dcbaa_ptr = xhci_read_64(xhci, &xhci->op_regs->dcbaa_ptr);
++ xhci->s3.config_reg = xhci_readl(xhci, &xhci->op_regs->config_reg);
++ xhci->s3.irq_pending = xhci_readl(xhci, &xhci->ir_set->irq_pending);
++ xhci->s3.irq_control = xhci_readl(xhci, &xhci->ir_set->irq_control);
++ xhci->s3.erst_size = xhci_readl(xhci, &xhci->ir_set->erst_size);
++ xhci->s3.erst_base = xhci_read_64(xhci, &xhci->ir_set->erst_base);
++ xhci->s3.erst_dequeue = xhci_read_64(xhci, &xhci->ir_set->erst_dequeue);
++}
++
++static void xhci_restore_registers(struct xhci_hcd *xhci)
++{
++ xhci_writel(xhci, xhci->s3.command, &xhci->op_regs->command);
++ xhci_writel(xhci, xhci->s3.dev_nt, &xhci->op_regs->dev_notification);
++ xhci_write_64(xhci, xhci->s3.dcbaa_ptr, &xhci->op_regs->dcbaa_ptr);
++ xhci_writel(xhci, xhci->s3.config_reg, &xhci->op_regs->config_reg);
++ xhci_writel(xhci, xhci->s3.irq_pending, &xhci->ir_set->irq_pending);
++ xhci_writel(xhci, xhci->s3.irq_control, &xhci->ir_set->irq_control);
++ xhci_writel(xhci, xhci->s3.erst_size, &xhci->ir_set->erst_size);
++ xhci_write_64(xhci, xhci->s3.erst_base, &xhci->ir_set->erst_base);
++}
++
++/*
++ * Stop HC (not bus-specific)
++ *
++ * This is called when the machine transition into S3/S4 mode.
++ *
++ */
++int xhci_suspend(struct xhci_hcd *xhci)
++{
++ int rc = 0;
++ struct usb_hcd *hcd = xhci_to_hcd(xhci);
++ u32 command;
++
++ spin_lock_irq(&xhci->lock);
++ clear_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags);
++ /* step 1: stop endpoint */
++ /* skipped assuming that port suspend has done */
++
++ /* step 2: clear Run/Stop bit */
++ command = xhci_readl(xhci, &xhci->op_regs->command);
++ command &= ~CMD_RUN;
++ xhci_writel(xhci, command, &xhci->op_regs->command);
++ if (handshake(xhci, &xhci->op_regs->status,
++ STS_HALT, STS_HALT, 100*100)) {
++ xhci_warn(xhci, "WARN: xHC CMD_RUN timeout\n");
++ spin_unlock_irq(&xhci->lock);
++ return -ETIMEDOUT;
++ }
++
++ /* step 3: save registers */
++ xhci_save_registers(xhci);
++
++ /* step 4: set CSS flag */
++ command = xhci_readl(xhci, &xhci->op_regs->command);
++ command |= CMD_CSS;
++ xhci_writel(xhci, command, &xhci->op_regs->command);
++ if (handshake(xhci, &xhci->op_regs->status, STS_SAVE, 0, 10*100)) {
++ xhci_warn(xhci, "WARN: xHC CMD_CSS timeout\n");
++ spin_unlock_irq(&xhci->lock);
++ return -ETIMEDOUT;
++ }
++ /* step 5: remove core well power */
++ xhci_cleanup_msix(xhci);
++ spin_unlock_irq(&xhci->lock);
++
++ return rc;
++}
++
++/*
++ * start xHC (not bus-specific)
++ *
++ * This is called when the machine transition from S3/S4 mode.
++ *
++ */
++int xhci_resume(struct xhci_hcd *xhci, bool hibernated)
++{
++ u32 command, temp = 0;
++ struct usb_hcd *hcd = xhci_to_hcd(xhci);
++ struct pci_dev *pdev = to_pci_dev(hcd->self.controller);
++ u64 val_64;
++ int old_state, retval;
++
++ old_state = hcd->state;
++ if (time_before(jiffies, xhci->next_statechange))
++ msleep(100);
++
++ spin_lock_irq(&xhci->lock);
++
++ if (!hibernated) {
++ /* step 1: restore register */
++ xhci_restore_registers(xhci);
++ /* step 2: initialize command ring buffer */
++ val_64 = xhci_read_64(xhci, &xhci->op_regs->cmd_ring);
++ val_64 = (val_64 & (u64) CMD_RING_RSVD_BITS) |
++ (xhci_trb_virt_to_dma(xhci->cmd_ring->deq_seg,
++ xhci->cmd_ring->dequeue) &
++ (u64) ~CMD_RING_RSVD_BITS) |
++ xhci->cmd_ring->cycle_state;
++ xhci_dbg(xhci, "// Setting command ring address to 0x%llx\n",
++ (long unsigned long) val_64);
++ xhci_write_64(xhci, val_64, &xhci->op_regs->cmd_ring);
++ /* step 3: restore state and start state*/
++ /* step 3: set CRS flag */
++ command = xhci_readl(xhci, &xhci->op_regs->command);
++ command |= CMD_CRS;
++ xhci_writel(xhci, command, &xhci->op_regs->command);
++ if (handshake(xhci, &xhci->op_regs->status,
++ STS_RESTORE, 0, 10*100)) {
++ xhci_dbg(xhci, "WARN: xHC CMD_CSS timeout\n");
++ spin_unlock_irq(&xhci->lock);
++ return -ETIMEDOUT;
++ }
++ temp = xhci_readl(xhci, &xhci->op_regs->status);
++ }
++
++ /* If restore operation fails, re-initialize the HC during resume */
++ if ((temp & STS_SRE) || hibernated) {
++ usb_root_hub_lost_power(hcd->self.root_hub);
++
++ xhci_dbg(xhci, "Stop HCD\n");
++ xhci_halt(xhci);
++ xhci_reset(xhci);
++ if (hibernated)
++ xhci_cleanup_msix(xhci);
++ spin_unlock_irq(&xhci->lock);
++
++#ifdef CONFIG_USB_XHCI_HCD_DEBUGGING
++ /* Tell the event ring poll function not to reschedule */
++ xhci->zombie = 1;
++ del_timer_sync(&xhci->event_ring_timer);
++#endif
++
++ xhci_dbg(xhci, "// Disabling event ring interrupts\n");
++ temp = xhci_readl(xhci, &xhci->op_regs->status);
++ xhci_writel(xhci, temp & ~STS_EINT, &xhci->op_regs->status);
++ temp = xhci_readl(xhci, &xhci->ir_set->irq_pending);
++ xhci_writel(xhci, ER_IRQ_DISABLE(temp),
++ &xhci->ir_set->irq_pending);
++ xhci_print_ir_set(xhci, xhci->ir_set, 0);
++
++ xhci_dbg(xhci, "cleaning up memory\n");
++ xhci_mem_cleanup(xhci);
++ xhci_dbg(xhci, "xhci_stop completed - status = %x\n",
++ xhci_readl(xhci, &xhci->op_regs->status));
++
++ xhci_dbg(xhci, "Initialize the HCD\n");
++ retval = xhci_init(hcd);
++ if (retval)
++ return retval;
++
++ xhci_dbg(xhci, "Start the HCD\n");
++ retval = xhci_run(hcd);
++ if (!retval)
++ set_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags);
++ hcd->state = HC_STATE_SUSPENDED;
++ return retval;
++ }
++
++ /* Re-setup MSI-X */
++ if (hcd->irq)
++ free_irq(hcd->irq, hcd);
++ hcd->irq = -1;
++
++ retval = xhci_setup_msix(xhci);
++ if (retval)
++ /* fall back to msi*/
++ retval = xhci_setup_msi(xhci);
++
++ if (retval) {
++ /* fall back to legacy interrupt*/
++ retval = request_irq(pdev->irq, &usb_hcd_irq, IRQF_SHARED,
++ hcd->irq_descr, hcd);
++ if (retval) {
++ xhci_err(xhci, "request interrupt %d failed\n",
++ pdev->irq);
++ return retval;
++ }
++ hcd->irq = pdev->irq;
++ }
++
++ /* step 4: set Run/Stop bit */
++ command = xhci_readl(xhci, &xhci->op_regs->command);
++ command |= CMD_RUN;
++ xhci_writel(xhci, command, &xhci->op_regs->command);
++ handshake(xhci, &xhci->op_regs->status, STS_HALT,
++ 0, 250 * 1000);
++
++ /* step 5: walk topology and initialize portsc,
++ * portpmsc and portli
++ */
++ /* this is done in bus_resume */
++
++ /* step 6: restart each of the previously
++ * Running endpoints by ringing their doorbells
++ */
++
++ set_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags);
++ if (!hibernated)
++ hcd->state = old_state;
++ else
++ hcd->state = HC_STATE_SUSPENDED;
++
++ spin_unlock_irq(&xhci->lock);
++ return 0;
++}
++
+ /*-------------------------------------------------------------------------*/
+
+ /**
+--- a/drivers/usb/host/xhci.h
++++ b/drivers/usb/host/xhci.h
+@@ -191,7 +191,7 @@ struct xhci_op_regs {
+ /* bits 4:6 are reserved (and should be preserved on writes). */
+ /* light reset (port status stays unchanged) - reset completed when this is 0 */
+ #define CMD_LRESET (1 << 7)
+-/* FIXME: ignoring host controller save/restore state for now. */
++/* host controller save/restore state. */
+ #define CMD_CSS (1 << 8)
+ #define CMD_CRS (1 << 9)
+ /* Enable Wrap Event - '1' means xHC generates an event when MFINDEX wraps. */
+@@ -1130,6 +1130,17 @@ struct urb_priv {
+ #define XHCI_STOP_EP_CMD_TIMEOUT 5
+ /* XXX: Make these module parameters */
+
++struct s3_save {
++ u32 command;
++ u32 dev_nt;
++ u64 dcbaa_ptr;
++ u32 config_reg;
++ u32 irq_pending;
++ u32 irq_control;
++ u32 erst_size;
++ u64 erst_base;
++ u64 erst_dequeue;
++};
+
+ /* There is one ehci_hci structure per controller */
+ struct xhci_hcd {
+@@ -1198,6 +1209,7 @@ struct xhci_hcd {
+ unsigned long next_statechange;
+
+ u32 command;
++ struct s3_save s3;
+ /* Host controller is dying - not responding to commands. "I'm not dead yet!"
+ *
+ * xHC interrupts have been disabled and a watchdog timer will (or has already)
+@@ -1393,6 +1405,8 @@ int xhci_init(struct usb_hcd *hcd);
+ int xhci_run(struct usb_hcd *hcd);
+ void xhci_stop(struct usb_hcd *hcd);
+ void xhci_shutdown(struct usb_hcd *hcd);
++int xhci_suspend(struct xhci_hcd *xhci);
++int xhci_resume(struct xhci_hcd *xhci, bool hibernated);
+ int xhci_get_frame(struct usb_hcd *hcd);
+ irqreturn_t xhci_irq(struct usb_hcd *hcd);
+ irqreturn_t xhci_msi_irq(int irq, struct usb_hcd *hcd);
diff --git a/usb/usb-xhci-port-power-management-implementation.patch b/usb/usb-xhci-port-power-management-implementation.patch
new file mode 100644
index 00000000000000..ee65bbaed76a99
--- /dev/null
+++ b/usb/usb-xhci-port-power-management-implementation.patch
@@ -0,0 +1,510 @@
+From linux-usb-owner@vger.kernel.org Thu Oct 14 11:54:19 2010
+Date: Thu, 14 Oct 2010 07:22:57 -0700
+From: Andiry Xu <andiry.xu@amd.com>
+To: Greg KH <gregkh@suse.de>
+Cc: linux-usb@vger.kernel.org, Andiry Xu <andiry.xu@amd.com>,
+ "Su, Henry" <Henry.Su@amd.com>, "He, Alex" <Alex.He@amd.com>
+Subject: USB: xHCI: port power management implementation
+Message-ID: <20101014142257.GA2961@xanatos>
+
+From: Andiry Xu <andiry.xu@amd.com>
+
+Add software trigger USB device suspend resume function hook.
+Do port suspend & resume in terms of xHCI spec.
+
+Port Suspend:
+Stop all endpoints via Stop Endpoint Command with Suspend (SP) flag set.
+Place individual ports into suspend mode by writing '3' for Port Link State
+(PLS) field into PORTSC register. This can only be done when the port is in
+Enabled state. When writing, the Port Link State Write Strobe (LWS) bit shall
+be set to '1'.
+Allocate an xhci_command and stash it in xhci_virt_device to wait completion for
+the last Stop Endpoint Command. Use the Suspend bit in TRB to indicate the Stop
+Endpoint Command is for port suspend. Based on Sarah's suggestion.
+
+Port Resume:
+Write '0' in PLS field, device will transition to running state.
+Ring an endpoints' doorbell to restart it.
+
+Ref: USB device remote wake need another patch to implement. For details of
+how USB subsystem do power management, please see:
+ Documentation/usb/power-management.txt
+
+Signed-off-by: Crane Cai <crane.cai@amd.com>
+Signed-off-by: Libin Yang <libin.yang@amd.com>
+Signed-off-by: Andiry Xu <andiry.xu@amd.com>
+Signed-off-by: Sarah Sharp <sarah.a.sharp@linux.intel.com>
+Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
+
+---
+ drivers/usb/host/xhci-hub.c | 196 ++++++++++++++++++++++++++++++++++++++++++-
+ drivers/usb/host/xhci-mem.c | 1
+ drivers/usb/host/xhci-ring.c | 44 +++++++--
+ drivers/usb/host/xhci.c | 2
+ drivers/usb/host/xhci.h | 17 +++
+ 5 files changed, 248 insertions(+), 12 deletions(-)
+
+--- a/drivers/usb/host/xhci-hub.c
++++ b/drivers/usb/host/xhci-hub.c
+@@ -129,6 +129,99 @@ static u32 xhci_port_state_to_neutral(u3
+ return (state & XHCI_PORT_RO) | (state & XHCI_PORT_RWS);
+ }
+
++/*
++ * find slot id based on port number.
++ */
++static int xhci_find_slot_id_by_port(struct xhci_hcd *xhci, u16 port)
++{
++ int slot_id;
++ int i;
++
++ slot_id = 0;
++ for (i = 0; i < MAX_HC_SLOTS; i++) {
++ if (!xhci->devs[i])
++ continue;
++ if (xhci->devs[i]->port == port) {
++ slot_id = i;
++ break;
++ }
++ }
++
++ return slot_id;
++}
++
++/*
++ * Stop device
++ * It issues stop endpoint command for EP 0 to 30. And wait the last command
++ * to complete.
++ * suspend will set to 1, if suspend bit need to set in command.
++ */
++static int xhci_stop_device(struct xhci_hcd *xhci, int slot_id, int suspend)
++{
++ struct xhci_virt_device *virt_dev;
++ struct xhci_command *cmd;
++ unsigned long flags;
++ int timeleft;
++ int ret;
++ int i;
++
++ ret = 0;
++ virt_dev = xhci->devs[slot_id];
++ cmd = xhci_alloc_command(xhci, false, true, GFP_NOIO);
++ if (!cmd) {
++ xhci_dbg(xhci, "Couldn't allocate command structure.\n");
++ return -ENOMEM;
++ }
++
++ spin_lock_irqsave(&xhci->lock, flags);
++ for (i = LAST_EP_INDEX; i > 0; i--) {
++ if (virt_dev->eps[i].ring && virt_dev->eps[i].ring->dequeue)
++ xhci_queue_stop_endpoint(xhci, slot_id, i, suspend);
++ }
++ cmd->command_trb = xhci->cmd_ring->enqueue;
++ list_add_tail(&cmd->cmd_list, &virt_dev->cmd_list);
++ xhci_queue_stop_endpoint(xhci, slot_id, 0, suspend);
++ xhci_ring_cmd_db(xhci);
++ spin_unlock_irqrestore(&xhci->lock, flags);
++
++ /* Wait for last stop endpoint command to finish */
++ timeleft = wait_for_completion_interruptible_timeout(
++ cmd->completion,
++ USB_CTRL_SET_TIMEOUT);
++ if (timeleft <= 0) {
++ xhci_warn(xhci, "%s while waiting for stop endpoint command\n",
++ timeleft == 0 ? "Timeout" : "Signal");
++ spin_lock_irqsave(&xhci->lock, flags);
++ /* The timeout might have raced with the event ring handler, so
++ * only delete from the list if the item isn't poisoned.
++ */
++ if (cmd->cmd_list.next != LIST_POISON1)
++ list_del(&cmd->cmd_list);
++ spin_unlock_irqrestore(&xhci->lock, flags);
++ ret = -ETIME;
++ goto command_cleanup;
++ }
++
++command_cleanup:
++ xhci_free_command(xhci, cmd);
++ return ret;
++}
++
++/*
++ * Ring device, it rings the all doorbells unconditionally.
++ */
++static void xhci_ring_device(struct xhci_hcd *xhci, int slot_id)
++{
++ int i;
++
++ for (i = 0; i < LAST_EP_INDEX + 1; i++)
++ if (xhci->devs[slot_id]->eps[i].ring &&
++ xhci->devs[slot_id]->eps[i].ring->dequeue)
++ xhci_ring_ep_doorbell(xhci, slot_id, i, 0);
++
++ return;
++}
++
+ static void xhci_disable_port(struct xhci_hcd *xhci, u16 wIndex,
+ u32 __iomem *addr, u32 port_status)
+ {
+@@ -162,6 +255,10 @@ static void xhci_clear_port_change_bit(s
+ status = PORT_PEC;
+ port_change_bit = "enable/disable";
+ break;
++ case USB_PORT_FEAT_C_SUSPEND:
++ status = PORT_PLC;
++ port_change_bit = "suspend/resume";
++ break;
+ default:
+ /* Should never happen */
+ return;
+@@ -182,6 +279,7 @@ int xhci_hub_control(struct usb_hcd *hcd
+ u32 temp, status;
+ int retval = 0;
+ u32 __iomem *addr;
++ int slot_id;
+
+ ports = HCS_MAX_PORTS(xhci->hcs_params1);
+
+@@ -211,9 +309,21 @@ int xhci_hub_control(struct usb_hcd *hcd
+ if ((temp & PORT_OCC))
+ status |= USB_PORT_STAT_C_OVERCURRENT << 16;
+ /*
+- * FIXME ignoring suspend, reset, and USB 2.1/3.0 specific
++ * FIXME ignoring reset and USB 2.1/3.0 specific
+ * changes
+ */
++ if ((temp & PORT_PLS_MASK) == XDEV_U3
++ && (temp & PORT_POWER))
++ status |= 1 << USB_PORT_FEAT_SUSPEND;
++ if ((temp & PORT_PLS_MASK) == XDEV_U0
++ && (temp & PORT_POWER)
++ && (xhci->suspended_ports[wIndex >> 5] &
++ (1 << (wIndex & 31)))) {
++ xhci->suspended_ports[wIndex >> 5] &=
++ ~(1 << (wIndex & 31));
++ xhci->port_c_suspend[wIndex >> 5] |=
++ 1 << (wIndex & 31);
++ }
+ if (temp & PORT_CONNECT) {
+ status |= USB_PORT_STAT_CONNECTION;
+ status |= xhci_port_speed(temp);
+@@ -226,6 +336,8 @@ int xhci_hub_control(struct usb_hcd *hcd
+ status |= USB_PORT_STAT_RESET;
+ if (temp & PORT_POWER)
+ status |= USB_PORT_STAT_POWER;
++ if (xhci->port_c_suspend[wIndex >> 5] & (1 << (wIndex & 31)))
++ status |= 1 << USB_PORT_FEAT_C_SUSPEND;
+ xhci_dbg(xhci, "Get port status returned 0x%x\n", status);
+ put_unaligned(cpu_to_le32(status), (__le32 *) buf);
+ break;
+@@ -238,6 +350,42 @@ int xhci_hub_control(struct usb_hcd *hcd
+ temp = xhci_readl(xhci, addr);
+ temp = xhci_port_state_to_neutral(temp);
+ switch (wValue) {
++ case USB_PORT_FEAT_SUSPEND:
++ temp = xhci_readl(xhci, addr);
++ /* In spec software should not attempt to suspend
++ * a port unless the port reports that it is in the
++ * enabled (PED = ‘1’,PLS < ‘3’) state.
++ */
++ if ((temp & PORT_PE) == 0 || (temp & PORT_RESET)
++ || (temp & PORT_PLS_MASK) >= XDEV_U3) {
++ xhci_warn(xhci, "USB core suspending device "
++ "not in U0/U1/U2.\n");
++ goto error;
++ }
++
++ slot_id = xhci_find_slot_id_by_port(xhci, wIndex + 1);
++ if (!slot_id) {
++ xhci_warn(xhci, "slot_id is zero\n");
++ goto error;
++ }
++ /* unlock to execute stop endpoint commands */
++ spin_unlock_irqrestore(&xhci->lock, flags);
++ xhci_stop_device(xhci, slot_id, 1);
++ spin_lock_irqsave(&xhci->lock, flags);
++
++ temp = xhci_port_state_to_neutral(temp);
++ temp &= ~PORT_PLS_MASK;
++ temp |= PORT_LINK_STROBE | XDEV_U3;
++ xhci_writel(xhci, temp, addr);
++
++ spin_unlock_irqrestore(&xhci->lock, flags);
++ msleep(10); /* wait device to enter */
++ spin_lock_irqsave(&xhci->lock, flags);
++
++ temp = xhci_readl(xhci, addr);
++ xhci->suspended_ports[wIndex >> 5] |=
++ 1 << (wIndex & (31));
++ break;
+ case USB_PORT_FEAT_POWER:
+ /*
+ * Turn on ports, even if there isn't per-port switching.
+@@ -271,6 +419,52 @@ int xhci_hub_control(struct usb_hcd *hcd
+ temp = xhci_readl(xhci, addr);
+ temp = xhci_port_state_to_neutral(temp);
+ switch (wValue) {
++ case USB_PORT_FEAT_SUSPEND:
++ temp = xhci_readl(xhci, addr);
++ xhci_dbg(xhci, "clear USB_PORT_FEAT_SUSPEND\n");
++ xhci_dbg(xhci, "PORTSC %04x\n", temp);
++ if (temp & PORT_RESET)
++ goto error;
++ if (temp & XDEV_U3) {
++ if ((temp & PORT_PE) == 0)
++ goto error;
++ if (DEV_SUPERSPEED(temp)) {
++ temp = xhci_port_state_to_neutral(temp);
++ temp &= ~PORT_PLS_MASK;
++ temp |= PORT_LINK_STROBE | XDEV_U0;
++ xhci_writel(xhci, temp, addr);
++ xhci_readl(xhci, addr);
++ } else {
++ temp = xhci_port_state_to_neutral(temp);
++ temp &= ~PORT_PLS_MASK;
++ temp |= PORT_LINK_STROBE | XDEV_RESUME;
++ xhci_writel(xhci, temp, addr);
++
++ spin_unlock_irqrestore(&xhci->lock,
++ flags);
++ msleep(20);
++ spin_lock_irqsave(&xhci->lock, flags);
++
++ temp = xhci_readl(xhci, addr);
++ temp = xhci_port_state_to_neutral(temp);
++ temp &= ~PORT_PLS_MASK;
++ temp |= PORT_LINK_STROBE | XDEV_U0;
++ xhci_writel(xhci, temp, addr);
++ }
++ xhci->port_c_suspend[wIndex >> 5] |=
++ 1 << (wIndex & 31);
++ }
++
++ slot_id = xhci_find_slot_id_by_port(xhci, wIndex + 1);
++ if (!slot_id) {
++ xhci_dbg(xhci, "slot_id is zero\n");
++ goto error;
++ }
++ xhci_ring_device(xhci, slot_id);
++ break;
++ case USB_PORT_FEAT_C_SUSPEND:
++ xhci->port_c_suspend[wIndex >> 5] &=
++ ~(1 << (wIndex & 31));
+ case USB_PORT_FEAT_C_RESET:
+ case USB_PORT_FEAT_C_CONNECTION:
+ case USB_PORT_FEAT_C_OVER_CURRENT:
+--- a/drivers/usb/host/xhci-mem.c
++++ b/drivers/usb/host/xhci-mem.c
+@@ -867,6 +867,7 @@ int xhci_setup_addressable_virt_dev(stru
+ top_dev = top_dev->parent)
+ /* Found device below root hub */;
+ slot_ctx->dev_info2 |= (u32) ROOT_HUB_PORT(top_dev->portnum);
++ dev->port = top_dev->portnum;
+ xhci_dbg(xhci, "Set root hub portnum to %d\n", top_dev->portnum);
+
+ /* Is this a LS/FS device under a HS hub? */
+--- a/drivers/usb/host/xhci-ring.c
++++ b/drivers/usb/host/xhci-ring.c
+@@ -68,6 +68,10 @@
+ #include <linux/slab.h>
+ #include "xhci.h"
+
++static int handle_cmd_in_cmd_wait_list(struct xhci_hcd *xhci,
++ struct xhci_virt_device *virt_dev,
++ struct xhci_event_cmd *event);
++
+ /*
+ * Returns zero if the TRB isn't in this segment, otherwise it returns the DMA
+ * address of the TRB.
+@@ -313,7 +317,7 @@ void xhci_ring_cmd_db(struct xhci_hcd *x
+ xhci_readl(xhci, &xhci->dba->doorbell[0]);
+ }
+
+-static void ring_ep_doorbell(struct xhci_hcd *xhci,
++void xhci_ring_ep_doorbell(struct xhci_hcd *xhci,
+ unsigned int slot_id,
+ unsigned int ep_index,
+ unsigned int stream_id)
+@@ -353,7 +357,7 @@ static void ring_doorbell_for_active_rin
+ /* A ring has pending URBs if its TD list is not empty */
+ if (!(ep->ep_state & EP_HAS_STREAMS)) {
+ if (!(list_empty(&ep->ring->td_list)))
+- ring_ep_doorbell(xhci, slot_id, ep_index, 0);
++ xhci_ring_ep_doorbell(xhci, slot_id, ep_index, 0);
+ return;
+ }
+
+@@ -361,7 +365,8 @@ static void ring_doorbell_for_active_rin
+ stream_id++) {
+ struct xhci_stream_info *stream_info = ep->stream_info;
+ if (!list_empty(&stream_info->stream_rings[stream_id]->td_list))
+- ring_ep_doorbell(xhci, slot_id, ep_index, stream_id);
++ xhci_ring_ep_doorbell(xhci, slot_id, ep_index,
++ stream_id);
+ }
+ }
+
+@@ -626,10 +631,11 @@ static void xhci_giveback_urb_in_irq(str
+ * bit cleared) so that the HW will skip over them.
+ */
+ static void handle_stopped_endpoint(struct xhci_hcd *xhci,
+- union xhci_trb *trb)
++ union xhci_trb *trb, struct xhci_event_cmd *event)
+ {
+ unsigned int slot_id;
+ unsigned int ep_index;
++ struct xhci_virt_device *virt_dev;
+ struct xhci_ring *ep_ring;
+ struct xhci_virt_ep *ep;
+ struct list_head *entry;
+@@ -638,6 +644,21 @@ static void handle_stopped_endpoint(stru
+
+ struct xhci_dequeue_state deq_state;
+
++ if (unlikely(TRB_TO_SUSPEND_PORT(
++ xhci->cmd_ring->dequeue->generic.field[3]))) {
++ slot_id = TRB_TO_SLOT_ID(
++ xhci->cmd_ring->dequeue->generic.field[3]);
++ virt_dev = xhci->devs[slot_id];
++ if (virt_dev)
++ handle_cmd_in_cmd_wait_list(xhci, virt_dev,
++ event);
++ else
++ xhci_warn(xhci, "Stop endpoint command "
++ "completion for disabled slot %u\n",
++ slot_id);
++ return;
++ }
++
+ memset(&deq_state, 0, sizeof(deq_state));
+ slot_id = TRB_TO_SLOT_ID(trb->generic.field[3]);
+ ep_index = TRB_TO_EP_INDEX(trb->generic.field[3]);
+@@ -1091,7 +1112,7 @@ bandwidth_change:
+ complete(&xhci->addr_dev);
+ break;
+ case TRB_TYPE(TRB_STOP_RING):
+- handle_stopped_endpoint(xhci, xhci->cmd_ring->dequeue);
++ handle_stopped_endpoint(xhci, xhci->cmd_ring->dequeue, event);
+ break;
+ case TRB_TYPE(TRB_SET_DEQ):
+ handle_set_deq_completion(xhci, event, xhci->cmd_ring->dequeue);
+@@ -2347,7 +2368,7 @@ static void giveback_first_trb(struct xh
+ */
+ wmb();
+ start_trb->field[3] |= start_cycle;
+- ring_ep_doorbell(xhci, slot_id, ep_index, stream_id);
++ xhci_ring_ep_doorbell(xhci, slot_id, ep_index, stream_id);
+ }
+
+ /*
+@@ -2931,7 +2952,7 @@ static int xhci_queue_isoc_tx(struct xhc
+ wmb();
+ start_trb->field[3] |= start_cycle;
+
+- ring_ep_doorbell(xhci, slot_id, ep_index, urb->stream_id);
++ xhci_ring_ep_doorbell(xhci, slot_id, ep_index, urb->stream_id);
+ return 0;
+ }
+
+@@ -3108,15 +3129,20 @@ int xhci_queue_evaluate_context(struct x
+ false);
+ }
+
++/*
++ * Suspend is set to indicate "Stop Endpoint Command" is being issued to stop
++ * activity on an endpoint that is about to be suspended.
++ */
+ int xhci_queue_stop_endpoint(struct xhci_hcd *xhci, int slot_id,
+- unsigned int ep_index)
++ unsigned int ep_index, int suspend)
+ {
+ u32 trb_slot_id = SLOT_ID_FOR_TRB(slot_id);
+ u32 trb_ep_index = EP_ID_FOR_TRB(ep_index);
+ u32 type = TRB_TYPE(TRB_STOP_RING);
++ u32 trb_suspend = SUSPEND_PORT_FOR_TRB(suspend);
+
+ return queue_command(xhci, 0, 0, 0,
+- trb_slot_id | trb_ep_index | type, false);
++ trb_slot_id | trb_ep_index | type | trb_suspend, false);
+ }
+
+ /* Set Transfer Ring Dequeue Pointer command.
+--- a/drivers/usb/host/xhci.c
++++ b/drivers/usb/host/xhci.c
+@@ -968,7 +968,7 @@ int xhci_urb_dequeue(struct usb_hcd *hcd
+ ep->stop_cmd_timer.expires = jiffies +
+ XHCI_STOP_EP_CMD_TIMEOUT * HZ;
+ add_timer(&ep->stop_cmd_timer);
+- xhci_queue_stop_endpoint(xhci, urb->dev->slot_id, ep_index);
++ xhci_queue_stop_endpoint(xhci, urb->dev->slot_id, ep_index, 0);
+ xhci_ring_cmd_db(xhci);
+ }
+ done:
+--- a/drivers/usb/host/xhci.h
++++ b/drivers/usb/host/xhci.h
+@@ -269,6 +269,10 @@ struct xhci_op_regs {
+ * A read gives the current link PM state of the port,
+ * a write with Link State Write Strobe set sets the link state.
+ */
++#define PORT_PLS_MASK (0xf << 5)
++#define XDEV_U0 (0x0 << 5)
++#define XDEV_U3 (0x3 << 5)
++#define XDEV_RESUME (0xf << 5)
+ /* true: port has power (see HCC_PPC) */
+ #define PORT_POWER (1 << 9)
+ /* bits 10:13 indicate device speed:
+@@ -510,6 +514,7 @@ struct xhci_slot_ctx {
+ #define MAX_EXIT (0xffff)
+ /* Root hub port number that is needed to access the USB device */
+ #define ROOT_HUB_PORT(p) (((p) & 0xff) << 16)
++#define DEVINFO_TO_ROOT_HUB_PORT(p) (((p) >> 16) & 0xff)
+ /* Maximum number of ports under a hub device */
+ #define XHCI_MAX_PORTS(p) (((p) & 0xff) << 24)
+
+@@ -754,6 +759,7 @@ struct xhci_virt_device {
+ /* Status of the last command issued for this device */
+ u32 cmd_status;
+ struct list_head cmd_list;
++ u8 port;
+ };
+
+
+@@ -884,6 +890,10 @@ struct xhci_event_cmd {
+ #define TRB_TO_EP_INDEX(p) ((((p) & (0x1f << 16)) >> 16) - 1)
+ #define EP_ID_FOR_TRB(p) ((((p) + 1) & 0x1f) << 16)
+
++#define SUSPEND_PORT_FOR_TRB(p) (((p) & 1) << 23)
++#define TRB_TO_SUSPEND_PORT(p) (((p) & (1 << 23)) >> 23)
++#define LAST_EP_INDEX 30
++
+ /* Set TR Dequeue Pointer command TRB fields */
+ #define TRB_TO_STREAM_ID(p) ((((p) & (0xffff << 16)) >> 16))
+ #define STREAM_ID_FOR_TRB(p) ((((p)) & 0xffff) << 16)
+@@ -1202,6 +1212,9 @@ struct xhci_hcd {
+ #define XHCI_LINK_TRB_QUIRK (1 << 0)
+ #define XHCI_RESET_EP_QUIRK (1 << 1)
+ #define XHCI_NEC_HOST (1 << 2)
++ u32 port_c_suspend[8]; /* port suspend change*/
++ u32 suspended_ports[8]; /* which ports are
++ suspended */
+ };
+
+ /* For testing purposes */
+@@ -1409,7 +1422,7 @@ int xhci_queue_address_device(struct xhc
+ int xhci_queue_vendor_command(struct xhci_hcd *xhci,
+ u32 field1, u32 field2, u32 field3, u32 field4);
+ int xhci_queue_stop_endpoint(struct xhci_hcd *xhci, int slot_id,
+- unsigned int ep_index);
++ unsigned int ep_index, int suspend);
+ int xhci_queue_ctrl_tx(struct xhci_hcd *xhci, gfp_t mem_flags, struct urb *urb,
+ int slot_id, unsigned int ep_index);
+ int xhci_queue_bulk_tx(struct xhci_hcd *xhci, gfp_t mem_flags, struct urb *urb,
+@@ -1439,6 +1452,8 @@ void xhci_queue_config_ep_quirk(struct x
+ unsigned int slot_id, unsigned int ep_index,
+ struct xhci_dequeue_state *deq_state);
+ void xhci_stop_endpoint_command_watchdog(unsigned long arg);
++void xhci_ring_ep_doorbell(struct xhci_hcd *xhci, unsigned int slot_id,
++ unsigned int ep_index, unsigned int stream_id);
+
+ /* xHCI roothub code */
+ int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, u16 wIndex,
diff --git a/usb/usb-xhci-port-remote-wakeup-implementation.patch b/usb/usb-xhci-port-remote-wakeup-implementation.patch
new file mode 100644
index 00000000000000..4aee93363e43ff
--- /dev/null
+++ b/usb/usb-xhci-port-remote-wakeup-implementation.patch
@@ -0,0 +1,244 @@
+From linux-usb-owner@vger.kernel.org Thu Oct 14 11:54:49 2010
+Date: Thu, 14 Oct 2010 07:23:00 -0700
+From: Andiry Xu <andiry.xu@amd.com>
+To: Greg KH <gregkh@suse.de>
+Cc: linux-usb@vger.kernel.org, "He, Alex" <Alex.He@amd.com>,
+ "Su, Henry" <Henry.Su@amd.com>, Andiry Xu <andiry.xu@amd.com>
+Subject: USB: xHCI: port remote wakeup implementation
+Message-ID: <20101014142300.GA2994@xanatos>
+Content-Disposition: inline
+
+From: Andiry Xu <andiry.xu@amd.com>
+
+This commit implements port remote wakeup.
+
+When a port is in U3 state and resume signaling is detected from a device,
+the port transitions to the Resume state, and the xHC generates a Port Status
+Change Event.
+
+For USB3 port, software write a '0' to the PLS field to complete the resume
+signaling. For USB2 port, the resume should be signaling for at least 20ms,
+irq handler set a timer for port remote wakeup, and then finishes process in
+hub_control GetPortStatus.
+
+Some codes are borrowed from EHCI code.
+
+Signed-off-by: Andiry Xu <andiry.xu@amd.com>
+Signed-off-by: Sarah Sharp <sarah.a.sharp@linux.intel.com>
+Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
+
+---
+ drivers/usb/host/xhci-hub.c | 44 +++++++++++++++++++++++++++++----
+ drivers/usb/host/xhci-mem.c | 2 +
+ drivers/usb/host/xhci-ring.c | 57 ++++++++++++++++++++++++++++++++++++++++++-
+ drivers/usb/host/xhci.h | 4 +++
+ 4 files changed, 101 insertions(+), 6 deletions(-)
+
+--- a/drivers/usb/host/xhci-hub.c
++++ b/drivers/usb/host/xhci-hub.c
+@@ -123,7 +123,7 @@ static unsigned int xhci_port_speed(unsi
+ * writing a 0 clears the bit and writing a 1 sets the bit (RWS).
+ * For all other types (RW1S, RW1CS, RW, and RZ), writing a '0' has no effect.
+ */
+-static u32 xhci_port_state_to_neutral(u32 state)
++u32 xhci_port_state_to_neutral(u32 state)
+ {
+ /* Save read-only status and port state */
+ return (state & XHCI_PORT_RO) | (state & XHCI_PORT_RWS);
+@@ -132,7 +132,7 @@ static u32 xhci_port_state_to_neutral(u3
+ /*
+ * find slot id based on port number.
+ */
+-static int xhci_find_slot_id_by_port(struct xhci_hcd *xhci, u16 port)
++int xhci_find_slot_id_by_port(struct xhci_hcd *xhci, u16 port)
+ {
+ int slot_id;
+ int i;
+@@ -210,7 +210,7 @@ command_cleanup:
+ /*
+ * Ring device, it rings the all doorbells unconditionally.
+ */
+-static void xhci_ring_device(struct xhci_hcd *xhci, int slot_id)
++void xhci_ring_device(struct xhci_hcd *xhci, int slot_id)
+ {
+ int i;
+
+@@ -276,7 +276,7 @@ int xhci_hub_control(struct usb_hcd *hcd
+ struct xhci_hcd *xhci = hcd_to_xhci(hcd);
+ int ports;
+ unsigned long flags;
+- u32 temp, status;
++ u32 temp, temp1, status;
+ int retval = 0;
+ u32 __iomem *addr;
+ int slot_id;
+@@ -315,6 +315,34 @@ int xhci_hub_control(struct usb_hcd *hcd
+ if ((temp & PORT_PLS_MASK) == XDEV_U3
+ && (temp & PORT_POWER))
+ status |= 1 << USB_PORT_FEAT_SUSPEND;
++ if ((temp & PORT_PLS_MASK) == XDEV_RESUME) {
++ if ((temp & PORT_RESET) || !(temp & PORT_PE))
++ goto error;
++ if (!DEV_SUPERSPEED(temp) && time_after_eq(jiffies,
++ xhci->resume_done[wIndex])) {
++ xhci_dbg(xhci, "Resume USB2 port %d\n",
++ wIndex + 1);
++ xhci->resume_done[wIndex] = 0;
++ temp1 = xhci_port_state_to_neutral(temp);
++ temp1 &= ~PORT_PLS_MASK;
++ temp1 |= PORT_LINK_STROBE | XDEV_U0;
++ xhci_writel(xhci, temp1, addr);
++
++ xhci_dbg(xhci, "set port %d resume\n",
++ wIndex + 1);
++ slot_id = xhci_find_slot_id_by_port(xhci,
++ wIndex + 1);
++ if (!slot_id) {
++ xhci_dbg(xhci, "slot_id is zero\n");
++ goto error;
++ }
++ xhci_ring_device(xhci, slot_id);
++ xhci->port_c_suspend[wIndex >> 5] |=
++ 1 << (wIndex & 31);
++ xhci->suspended_ports[wIndex >> 5] &=
++ ~(1 << (wIndex & 31));
++ }
++ }
+ if ((temp & PORT_PLS_MASK) == XDEV_U0
+ && (temp & PORT_POWER)
+ && (xhci->suspended_ports[wIndex >> 5] &
+@@ -500,6 +528,7 @@ int xhci_hub_status_data(struct usb_hcd
+ {
+ unsigned long flags;
+ u32 temp, status;
++ u32 mask;
+ int i, retval;
+ struct xhci_hcd *xhci = hcd_to_xhci(hcd);
+ int ports;
+@@ -512,13 +541,18 @@ int xhci_hub_status_data(struct usb_hcd
+ memset(buf, 0, retval);
+ status = 0;
+
++ mask = PORT_CSC | PORT_PEC | PORT_OCC;
++
+ spin_lock_irqsave(&xhci->lock, flags);
+ /* For each port, did anything change? If so, set that bit in buf. */
+ for (i = 0; i < ports; i++) {
+ addr = &xhci->op_regs->port_status_base +
+ NUM_PORT_REGS*i;
+ temp = xhci_readl(xhci, addr);
+- if (temp & (PORT_CSC | PORT_PEC | PORT_OCC)) {
++ if ((temp & mask) != 0 ||
++ (xhci->port_c_suspend[i >> 5] & 1 << (i & 31)) ||
++ (xhci->resume_done[i] && time_after_eq(
++ jiffies, xhci->resume_done[i]))) {
+ buf[(i + 1) / 8] |= 1 << (i + 1) % 8;
+ status = 1;
+ }
+--- a/drivers/usb/host/xhci-mem.c
++++ b/drivers/usb/host/xhci-mem.c
+@@ -1803,6 +1803,8 @@ int xhci_mem_init(struct xhci_hcd *xhci,
+ init_completion(&xhci->addr_dev);
+ for (i = 0; i < MAX_HC_SLOTS; ++i)
+ xhci->devs[i] = NULL;
++ for (i = 0; i < MAX_HC_PORTS; ++i)
++ xhci->resume_done[i] = 0;
+
+ if (scratchpad_alloc(xhci, flags))
+ goto fail;
+--- a/drivers/usb/host/xhci-ring.c
++++ b/drivers/usb/host/xhci-ring.c
+@@ -1165,17 +1165,72 @@ static void handle_vendor_event(struct x
+ static void handle_port_status(struct xhci_hcd *xhci,
+ union xhci_trb *event)
+ {
++ struct usb_hcd *hcd = xhci_to_hcd(xhci);
+ u32 port_id;
++ u32 temp, temp1;
++ u32 __iomem *addr;
++ int ports;
++ int slot_id;
+
+ /* Port status change events always have a successful completion code */
+ if (GET_COMP_CODE(event->generic.field[2]) != COMP_SUCCESS) {
+ xhci_warn(xhci, "WARN: xHC returned failed port status event\n");
+ xhci->error_bitmask |= 1 << 8;
+ }
+- /* FIXME: core doesn't care about all port link state changes yet */
+ port_id = GET_PORT_ID(event->generic.field[0]);
+ xhci_dbg(xhci, "Port Status Change Event for port %d\n", port_id);
+
++ ports = HCS_MAX_PORTS(xhci->hcs_params1);
++ if ((port_id <= 0) || (port_id > ports)) {
++ xhci_warn(xhci, "Invalid port id %d\n", port_id);
++ goto cleanup;
++ }
++
++ addr = &xhci->op_regs->port_status_base + NUM_PORT_REGS * (port_id - 1);
++ temp = xhci_readl(xhci, addr);
++ if ((temp & PORT_CONNECT) && (hcd->state == HC_STATE_SUSPENDED)) {
++ xhci_dbg(xhci, "resume root hub\n");
++ usb_hcd_resume_root_hub(hcd);
++ }
++
++ if ((temp & PORT_PLC) && (temp & PORT_PLS_MASK) == XDEV_RESUME) {
++ xhci_dbg(xhci, "port resume event for port %d\n", port_id);
++
++ temp1 = xhci_readl(xhci, &xhci->op_regs->command);
++ if (!(temp1 & CMD_RUN)) {
++ xhci_warn(xhci, "xHC is not running.\n");
++ goto cleanup;
++ }
++
++ if (DEV_SUPERSPEED(temp)) {
++ xhci_dbg(xhci, "resume SS port %d\n", port_id);
++ temp = xhci_port_state_to_neutral(temp);
++ temp &= ~PORT_PLS_MASK;
++ temp |= PORT_LINK_STROBE | XDEV_U0;
++ xhci_writel(xhci, temp, addr);
++ slot_id = xhci_find_slot_id_by_port(xhci, port_id);
++ if (!slot_id) {
++ xhci_dbg(xhci, "slot_id is zero\n");
++ goto cleanup;
++ }
++ xhci_ring_device(xhci, slot_id);
++ xhci_dbg(xhci, "resume SS port %d finished\n", port_id);
++ /* Clear PORT_PLC */
++ temp = xhci_readl(xhci, addr);
++ temp = xhci_port_state_to_neutral(temp);
++ temp |= PORT_PLC;
++ xhci_writel(xhci, temp, addr);
++ } else {
++ xhci_dbg(xhci, "resume HS port %d\n", port_id);
++ xhci->resume_done[port_id - 1] = jiffies +
++ msecs_to_jiffies(20);
++ mod_timer(&hcd->rh_timer,
++ xhci->resume_done[port_id - 1]);
++ /* Do the rest in GetPortStatus */
++ }
++ }
++
++cleanup:
+ /* Update event ring dequeue pointer before dropping the lock */
+ inc_deq(xhci, xhci->event_ring, true);
+
+--- a/drivers/usb/host/xhci.h
++++ b/drivers/usb/host/xhci.h
+@@ -1215,6 +1215,7 @@ struct xhci_hcd {
+ u32 port_c_suspend[8]; /* port suspend change*/
+ u32 suspended_ports[8]; /* which ports are
+ suspended */
++ unsigned long resume_done[MAX_HC_PORTS];
+ };
+
+ /* For testing purposes */
+@@ -1459,6 +1460,9 @@ void xhci_ring_ep_doorbell(struct xhci_h
+ int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, u16 wIndex,
+ char *buf, u16 wLength);
+ int xhci_hub_status_data(struct usb_hcd *hcd, char *buf);
++u32 xhci_port_state_to_neutral(u32 state);
++int xhci_find_slot_id_by_port(struct xhci_hcd *xhci, u16 port);
++void xhci_ring_device(struct xhci_hcd *xhci, int slot_id);
+
+ /* xHCI contexts */
+ struct xhci_input_control_ctx *xhci_get_input_control_ctx(struct xhci_hcd *xhci, struct xhci_container_ctx *ctx);