Commit cbad67e3 authored by Greg Kroah-Hartman's avatar Greg Kroah-Hartman

Merge tag 'for-usb-next-2012-05-03' of...

Merge tag 'for-usb-next-2012-05-03' of git://git.kernel.org/pub/scm/linux/kernel/git/sarah/xhci into usb-next

xhci: isoc, Intel xHCI, and suspend races.

Hi Greg,

Here's some xHCI fixes that should be queued for 3.5.

The first patch builds on Alan Stern's 3.4 patch to close the suspend and port
event race conditions.  It's marked for 3.4 stable, since that's where Alan's
patch landed.  The second patch fixes an incorrect error code that the xHCI
driver would return when an isochronous transfer error occurred.

The third and fourth patches fix issues seen on Intel xHCI host controllers.
The third patch fixes a dead port issue that was seen on the Panther Point
EHCI/xHCI host when CONFIG_USB_XHCI_HCD was turned off.  The ports were being
switched over to xHCI, even though the xHCI driver was never even built.  The
fourth patch adds support for the EHCI to xHCI port switchover for the upcoming
Intel Lynx Point chipset.

As I said, there's nothing here that's terribly urgent, and these patches can
wait a couple weeks for the 3.5 merge window.

Thanks,
Sarah Sharp
parents 32445605 1c12443a
...@@ -360,7 +360,9 @@ static bool usb_is_intel_switchable_ehci(struct pci_dev *pdev) ...@@ -360,7 +360,9 @@ static bool usb_is_intel_switchable_ehci(struct pci_dev *pdev)
{ {
return pdev->class == PCI_CLASS_SERIAL_USB_EHCI && return pdev->class == PCI_CLASS_SERIAL_USB_EHCI &&
pdev->vendor == PCI_VENDOR_ID_INTEL && pdev->vendor == PCI_VENDOR_ID_INTEL &&
pdev->device == 0x1E26; (pdev->device == 0x1E26 ||
pdev->device == 0x8C2D ||
pdev->device == 0x8C26);
} }
static void ehci_enable_xhci_companion(void) static void ehci_enable_xhci_companion(void)
......
...@@ -9,6 +9,7 @@ ...@@ -9,6 +9,7 @@
*/ */
#include <linux/types.h> #include <linux/types.h>
#include <linux/kconfig.h>
#include <linux/kernel.h> #include <linux/kernel.h>
#include <linux/pci.h> #include <linux/pci.h>
#include <linux/init.h> #include <linux/init.h>
...@@ -712,12 +713,28 @@ static int handshake(void __iomem *ptr, u32 mask, u32 done, ...@@ -712,12 +713,28 @@ static int handshake(void __iomem *ptr, u32 mask, u32 done,
return -ETIMEDOUT; return -ETIMEDOUT;
} }
bool usb_is_intel_switchable_xhci(struct pci_dev *pdev) #define PCI_DEVICE_ID_INTEL_LYNX_POINT_XHCI 0x8C31
bool usb_is_intel_ppt_switchable_xhci(struct pci_dev *pdev)
{ {
return pdev->class == PCI_CLASS_SERIAL_USB_XHCI && return pdev->class == PCI_CLASS_SERIAL_USB_XHCI &&
pdev->vendor == PCI_VENDOR_ID_INTEL && pdev->vendor == PCI_VENDOR_ID_INTEL &&
pdev->device == PCI_DEVICE_ID_INTEL_PANTHERPOINT_XHCI; pdev->device == PCI_DEVICE_ID_INTEL_PANTHERPOINT_XHCI;
} }
/* The Intel Lynx Point chipset also has switchable ports. */
bool usb_is_intel_lpt_switchable_xhci(struct pci_dev *pdev)
{
return pdev->class == PCI_CLASS_SERIAL_USB_XHCI &&
pdev->vendor == PCI_VENDOR_ID_INTEL &&
pdev->device == PCI_DEVICE_ID_INTEL_LYNX_POINT_XHCI;
}
bool usb_is_intel_switchable_xhci(struct pci_dev *pdev)
{
return usb_is_intel_ppt_switchable_xhci(pdev) ||
usb_is_intel_lpt_switchable_xhci(pdev);
}
EXPORT_SYMBOL_GPL(usb_is_intel_switchable_xhci); EXPORT_SYMBOL_GPL(usb_is_intel_switchable_xhci);
/* /*
...@@ -742,6 +759,19 @@ void usb_enable_xhci_ports(struct pci_dev *xhci_pdev) ...@@ -742,6 +759,19 @@ void usb_enable_xhci_ports(struct pci_dev *xhci_pdev)
{ {
u32 ports_available; u32 ports_available;
/* Don't switchover the ports if the user hasn't compiled the xHCI
* driver. Otherwise they will see "dead" USB ports that don't power
* the devices.
*/
if (!IS_ENABLED(CONFIG_USB_XHCI_HCD)) {
dev_warn(&xhci_pdev->dev,
"CONFIG_USB_XHCI_HCD is turned off, "
"defaulting to EHCI.\n");
dev_warn(&xhci_pdev->dev,
"USB 3.0 devices will work at USB 2.0 speeds.\n");
return;
}
ports_available = 0xffffffff; ports_available = 0xffffffff;
/* Write USB3_PSSEN, the USB 3.0 Port SuperSpeed Enable /* Write USB3_PSSEN, the USB 3.0 Port SuperSpeed Enable
* Register, to turn on SuperSpeed terminations for all * Register, to turn on SuperSpeed terminations for all
......
...@@ -558,6 +558,7 @@ int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, ...@@ -558,6 +558,7 @@ int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue,
xhci_dbg(xhci, "Resume USB2 port %d\n", xhci_dbg(xhci, "Resume USB2 port %d\n",
wIndex + 1); wIndex + 1);
bus_state->resume_done[wIndex] = 0; bus_state->resume_done[wIndex] = 0;
clear_bit(wIndex, &bus_state->resuming_ports);
xhci_set_link_state(xhci, port_array, wIndex, xhci_set_link_state(xhci, port_array, wIndex,
XDEV_U0); XDEV_U0);
xhci_dbg(xhci, "set port %d resume\n", xhci_dbg(xhci, "set port %d resume\n",
...@@ -845,7 +846,12 @@ int xhci_hub_status_data(struct usb_hcd *hcd, char *buf) ...@@ -845,7 +846,12 @@ int xhci_hub_status_data(struct usb_hcd *hcd, char *buf)
/* Initial status is no changes */ /* Initial status is no changes */
retval = (max_ports + 8) / 8; retval = (max_ports + 8) / 8;
memset(buf, 0, retval); memset(buf, 0, retval);
status = 0;
/*
* Inform the usbcore about resume-in-progress by returning
* a non-zero value even if there are no status changes.
*/
status = bus_state->resuming_ports;
mask = PORT_CSC | PORT_PEC | PORT_OCC | PORT_PLC | PORT_WRC; mask = PORT_CSC | PORT_PEC | PORT_OCC | PORT_PLC | PORT_WRC;
...@@ -885,17 +891,13 @@ int xhci_bus_suspend(struct usb_hcd *hcd) ...@@ -885,17 +891,13 @@ int xhci_bus_suspend(struct usb_hcd *hcd)
spin_lock_irqsave(&xhci->lock, flags); spin_lock_irqsave(&xhci->lock, flags);
if (hcd->self.root_hub->do_remote_wakeup) { if (hcd->self.root_hub->do_remote_wakeup) {
port_index = max_ports; if (bus_state->resuming_ports) {
while (port_index--) {
if (bus_state->resume_done[port_index] != 0) {
spin_unlock_irqrestore(&xhci->lock, flags); spin_unlock_irqrestore(&xhci->lock, flags);
xhci_dbg(xhci, "suspend failed because " xhci_dbg(xhci, "suspend failed because "
"port %d is resuming\n", "a port is resuming\n");
port_index + 1);
return -EBUSY; return -EBUSY;
} }
} }
}
port_index = max_ports; port_index = max_ports;
bus_state->bus_suspended = 0; bus_state->bus_suspended = 0;
......
...@@ -1377,6 +1377,7 @@ static void handle_port_status(struct xhci_hcd *xhci, ...@@ -1377,6 +1377,7 @@ static void handle_port_status(struct xhci_hcd *xhci,
xhci_dbg(xhci, "resume HS port %d\n", port_id); xhci_dbg(xhci, "resume HS port %d\n", port_id);
bus_state->resume_done[faked_port_index] = jiffies + bus_state->resume_done[faked_port_index] = jiffies +
msecs_to_jiffies(20); msecs_to_jiffies(20);
set_bit(faked_port_index, &bus_state->resuming_ports);
mod_timer(&hcd->rh_timer, mod_timer(&hcd->rh_timer,
bus_state->resume_done[faked_port_index]); bus_state->resume_done[faked_port_index]);
/* Do the rest in GetPortStatus */ /* Do the rest in GetPortStatus */
...@@ -1803,6 +1804,7 @@ static int process_isoc_td(struct xhci_hcd *xhci, struct xhci_td *td, ...@@ -1803,6 +1804,7 @@ static int process_isoc_td(struct xhci_hcd *xhci, struct xhci_td *td,
break; break;
case COMP_DEV_ERR: case COMP_DEV_ERR:
case COMP_STALL: case COMP_STALL:
case COMP_TX_ERR:
frame->status = -EPROTO; frame->status = -EPROTO;
skip_td = true; skip_td = true;
break; break;
......
...@@ -152,7 +152,7 @@ int xhci_reset(struct xhci_hcd *xhci) ...@@ -152,7 +152,7 @@ int xhci_reset(struct xhci_hcd *xhci)
{ {
u32 command; u32 command;
u32 state; u32 state;
int ret; int ret, i;
state = xhci_readl(xhci, &xhci->op_regs->status); state = xhci_readl(xhci, &xhci->op_regs->status);
if ((state & STS_HALT) == 0) { if ((state & STS_HALT) == 0) {
...@@ -175,7 +175,15 @@ int xhci_reset(struct xhci_hcd *xhci) ...@@ -175,7 +175,15 @@ int xhci_reset(struct xhci_hcd *xhci)
* xHCI cannot write to any doorbells or operational registers other * xHCI cannot write to any doorbells or operational registers other
* than status until the "Controller Not Ready" flag is cleared. * than status until the "Controller Not Ready" flag is cleared.
*/ */
return handshake(xhci, &xhci->op_regs->status, STS_CNR, 0, 250 * 1000); ret = handshake(xhci, &xhci->op_regs->status, STS_CNR, 0, 250 * 1000);
for (i = 0; i < 2; ++i) {
xhci->bus_state[i].port_c_suspend = 0;
xhci->bus_state[i].suspended_ports = 0;
xhci->bus_state[i].resuming_ports = 0;
}
return ret;
} }
#ifdef CONFIG_PCI #ifdef CONFIG_PCI
......
...@@ -1362,6 +1362,8 @@ struct xhci_bus_state { ...@@ -1362,6 +1362,8 @@ struct xhci_bus_state {
u32 suspended_ports; u32 suspended_ports;
u32 port_remote_wakeup; u32 port_remote_wakeup;
unsigned long resume_done[USB_MAXCHILDREN]; unsigned long resume_done[USB_MAXCHILDREN];
/* which ports have started to resume */
unsigned long resuming_ports;
}; };
static inline unsigned int hcd_index(struct usb_hcd *hcd) static inline unsigned int hcd_index(struct usb_hcd *hcd)
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment