diff options
13 files changed, 2153 insertions, 56 deletions
@@ -109,7 +109,8 @@ tty/altera_uart-don-t-use-plain-integer-as-null-pointer.patch tty/8250-allow-platforms-to-override-pm-hook.patch tty/alchemy-add-uart-pm-methods.patch tty/jsm-remove-the-uart-port-on-errors.patch -tty/serial-imx-serial-driver-fix-resume.patch +tty/serial-mfd-add-more-baud-rates-support.patch +tty/serial-imx-check-that-the-buffer-is-non-empty-before-sending-it-out.patch ################################### # USB stuff for after 2.6.36 is out @@ -231,6 +232,15 @@ usb/usb-opticon-add-opticon-opn2001-write-support.patch usb/usb-opticon-whitespace-fixes-in-opticon-driver.patch usb/usb-otg-ulpi-improve-ulpi-phy-detection.patch usb/usb-otg-ulpi-extend-id-s-table.patch +usb/usb-r8a66597-hcd-change-mistake-of-the-outsw-function.patch +usb/usb-xhci-add-pointer-to-udev-in-struct-xhci_virt_device.patch +usb/usb-xhci-change-xhci_reset_device-to-allocate-new-device.patch +usb/usb-core-use-kernel-assigned-address-for-devices-under-xhci.patch +usb/usb-fix-issue-with-usb-3.0-devices-after-system-resume.patch +usb/usb-xhci-port-power-management-implementation.patch +usb/usb-xhci-port-remote-wakeup-implementation.patch +usb/usb-xhci-bus-power-management-implementation.patch +usb/usb-xhci-pci-power-management-implementation.patch # staging stuff for next is now in the staging-next tree on git.kernel.org diff --git a/tty/serial-imx-check-that-the-buffer-is-non-empty-before-sending-it-out.patch b/tty/serial-imx-check-that-the-buffer-is-non-empty-before-sending-it-out.patch new file mode 100644 index 00000000000000..ddc4f517626dd3 --- /dev/null +++ b/tty/serial-imx-check-that-the-buffer-is-non-empty-before-sending-it-out.patch @@ -0,0 +1,51 @@ +From ukl@pengutronix.de Thu Oct 14 11:45:36 2010 +From: Volker Ernst <volker.ernst@txtr.com> +To: Daniel Mack <daniel@caiaq.de>, + Volker Ernst <volker.ernst@txtr.com> +Cc: Sascha Hauer <s.hauer@pengutronix.de>, + linux-arm-kernel@lists.infradead.org, akpm@linux-foundation.org, + kernel@pengutronix.de, gregkh@suse.de, + Volker Ernst <volker.ernst@txtr.com>, Daniel Mack <daniel@caiaq.de>, + Andy Green <andy@warmcat.com> +Subject: serial/imx: check that the buffer is non-empty before sending it out +Date: Wed, 13 Oct 2010 11:03:57 +0200 +Message-Id: <1286960637-11600-1-git-send-email-u.kleine-koenig@pengutronix.de> + +From: Volker Ernst <volker.ernst@txtr.com> + +The .start_tx callback (imx_start_tx here) isn't only called when the +buffer is non-empty. E.g. after resume or when handshaking is enabled +and the other side starts to signal being ready. + +So check for an empty puffer already before sending the first character. +This prevents sending out stale (or uninitialised) data. + +Signed-off-by: Volker Ernst <volker.ernst@txtr.com> +Signed-off-by: Daniel Mack <daniel@caiaq.de> +Cc: Andy Green <andy@warmcat.com> +[ukl: reword commit log, put check in while condition] +Signed-off-by: Uwe Kleine-König <u.kleine-koenig@pengutronix.de> +Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de> + +--- a/drivers/serial/imx.c ++++ b/drivers/serial/imx.c +@@ -327,14 +327,13 @@ static inline void imx_transmit_buffer(struct imx_port *sport) + { + struct circ_buf *xmit = &sport->port.state->xmit; + +- while (!(readl(sport->port.membase + UTS) & UTS_TXFULL)) { ++ while (!uart_circ_empty(xmit) && ++ !(readl(sport->port.membase + UTS) & UTS_TXFULL)) { + /* send xmit->buf[xmit->tail] + * out the port here */ + writel(xmit->buf[xmit->tail], sport->port.membase + URTX0); + xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1); + sport->port.icount.tx++; +- if (uart_circ_empty(xmit)) +- break; + } + + if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) +-- +1.7.2.3 + diff --git a/tty/serial-imx-serial-driver-fix-resume.patch b/tty/serial-imx-serial-driver-fix-resume.patch deleted file mode 100644 index b68bb284517808..00000000000000 --- a/tty/serial-imx-serial-driver-fix-resume.patch +++ /dev/null @@ -1,55 +0,0 @@ -From daniel@caiaq.de Thu Oct 7 10:49:08 2010 -From: Volker Ernst <volker.ernst@txtr.com> -To: linux-arm-kernel@lists.infradead.org -Cc: gregkh@suse.de, akpm@linux-foundation.org, - s.hauer@pengutronix.de, Volker Ernst <volker.ernst@txtr.com>, - Daniel Mack <daniel@caiaq.de>, Andy Green <andy@warmcat.com> -Subject: serial: imx serial driver: fix resume -Date: Wed, 6 Oct 2010 18:57:16 +0200 -Message-Id: <1286384236-4241-1-git-send-email-daniel@caiaq.de> - -From: Volker Ernst <volker.ernst@txtr.com> - -I just came across a bug in the IMX31 serial driver which is still -present in the newest kernels and which prevents successful -resume-operation for the IMX31 serial ports. - -What happens is that in "drivers/serial/imx.c" on resume function -"serial_imx_resume" gets called. This function in turn calls -"uart_resume_port" (in the generic serial driver "serial_core.c"), -which in turn calls "imx_start_tx" in "imx.c" (in case the SIO-port -was really suspended) which in turn calls "imx_transmit_buffer". - -However calling "imx_transmit_buffer" with an empty TX-fifo (as is -usually the case) will result in the serial port starting to transmit -(actually the old [already sent] tx-buffer), as there is no check if -the tx-buffer is empty before starting to feed tx-fifo-data to the -serial port hardware. - -Signed-off-by: Volker Ernst <volker.ernst@txtr.com> -Cc: Daniel Mack <daniel@caiaq.de> -Cc: Andy Green <andy@warmcat.com> -Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de> - ---- - drivers/serial/imx.c | 4 ++-- - 1 file changed, 2 insertions(+), 2 deletions(-) - ---- a/drivers/serial/imx.c -+++ b/drivers/serial/imx.c -@@ -328,13 +328,13 @@ static inline void imx_transmit_buffer(s - struct circ_buf *xmit = &sport->port.state->xmit; - - while (!(readl(sport->port.membase + UTS) & UTS_TXFULL)) { -+ if (uart_circ_empty(xmit)) -+ break; - /* send xmit->buf[xmit->tail] - * out the port here */ - writel(xmit->buf[xmit->tail], sport->port.membase + URTX0); - xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1); - sport->port.icount.tx++; -- if (uart_circ_empty(xmit)) -- break; - } - - if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) diff --git a/tty/serial-mfd-add-more-baud-rates-support.patch b/tty/serial-mfd-add-more-baud-rates-support.patch new file mode 100644 index 00000000000000..024fa94150ace2 --- /dev/null +++ b/tty/serial-mfd-add-more-baud-rates-support.patch @@ -0,0 +1,89 @@ +From feng.tang@intel.com Thu Oct 14 11:44:00 2010 +From: Feng Tang <feng.tang@intel.com> +To: meego-kernel@meego.com +Cc: alan@linux.intel.com, gregkh@suse.de, + Feng Tang <feng.tang@intel.com> +Subject: serial: mfd: add more baud rates support +Date: Thu, 14 Oct 2010 17:47:35 +0800 +Message-Id: <1287049655-15395-1-git-send-email-feng.tang@intel.com> + +Add more baud rates support referring the baud_table[] defined +in drivers/char/tty_ioctl.c: 3000000/2000000/1000000/500000 + +Signed-off-by: Feng Tang <feng.tang@intel.com> +Acked-by: Alan Cox <alan.cox@linux.intel.com> +Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de> + +--- + drivers/serial/mfd.c | 41 +++++++++++++++++++++++++++-------------- + 1 file changed, 27 insertions(+), 14 deletions(-) + +--- a/drivers/serial/mfd.c ++++ b/drivers/serial/mfd.c +@@ -929,39 +929,52 @@ serial_hsu_set_termios(struct uart_port + cval |= UART_LCR_EPAR; + + /* ++ * The base clk is 50Mhz, and the baud rate come from: ++ * baud = 50M * MUL / (DIV * PS * DLAB) ++ * + * For those basic low baud rate we can get the direct +- * scalar from 2746800, like 115200 = 2746800/24, for those +- * higher baud rate, we have to handle them case by case, +- * but DIV reg is never touched as its default value 0x3d09 ++ * scalar from 2746800, like 115200 = 2746800/24. For those ++ * higher baud rate, we handle them case by case, mainly by ++ * adjusting the MUL/PS registers, and DIV register is kept ++ * as default value 0x3d09 to make things simple + */ + baud = uart_get_baud_rate(port, termios, old, 0, 4000000); +- quot = uart_get_divisor(port, baud); + ++ quot = 1; + switch (baud) { + case 3500000: + mul = 0x3345; + ps = 0xC; +- quot = 1; ++ break; ++ case 3000000: ++ mul = 0x2EE0; + break; + case 2500000: + mul = 0x2710; +- ps = 0x10; +- quot = 1; + break; +- case 18432000: ++ case 2000000: ++ mul = 0x1F40; ++ break; ++ case 1843200: + mul = 0x2400; +- ps = 0x10; +- quot = 1; + break; + case 1500000: +- mul = 0x1D4C; +- ps = 0xc; +- quot = 1; ++ mul = 0x1770; ++ break; ++ case 1000000: ++ mul = 0xFA0; ++ break; ++ case 500000: ++ mul = 0x7D0; + break; + default: +- ; ++ /* Use uart_get_divisor to get quot for other baud rates */ ++ quot = 0; + } + ++ if (!quot) ++ quot = uart_get_divisor(port, baud); ++ + if ((up->port.uartclk / quot) < (2400 * 16)) + fcr = UART_FCR_ENABLE_FIFO | UART_FCR_HSU_64_1B; + else if ((up->port.uartclk / quot) < (230400 * 16)) 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); |
