Commit 102ee001 authored by Greg Kroah-Hartman's avatar Greg Kroah-Hartman

Merge tag 'for-usb-next-2013-01-03' of...

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

Sarah writes:
	usb-next: Further warm reset improvements

	Hi Greg,

	Here's some patches for 3.9.  They further improve the warm reset
	error handling, but they're too big to go into stable.  There's also a
	patch to remove an unused variable in the xHCI driver.

	As I mentioned, you'll need to merge usb-linus into usb-next before
	applying these patches.

	Sarah Sharp
parents 962426e0 026630d0
......@@ -2535,77 +2535,9 @@ static int hub_port_wait_reset(struct usb_hub *hub, int port1,
return ret;
/* The port state is unknown until the reset completes. */
if ((portstatus & USB_PORT_STAT_RESET))
goto delay;
/*
* Some buggy devices require a warm reset to be issued even
* when the port appears not to be connected.
*/
if (!warm) {
/*
* Some buggy devices can cause an NEC host controller
* to transition to the "Error" state after a hot port
* reset. This will show up as the port state in
* "Inactive", and the port may also report a
* disconnect. Forcing a warm port reset seems to make
* the device work.
*
* See https://bugzilla.kernel.org/show_bug.cgi?id=41752
*/
if (hub_port_warm_reset_required(hub, portstatus)) {
int ret;
if ((portchange & USB_PORT_STAT_C_CONNECTION))
clear_port_feature(hub->hdev, port1,
USB_PORT_FEAT_C_CONNECTION);
if (portchange & USB_PORT_STAT_C_LINK_STATE)
clear_port_feature(hub->hdev, port1,
USB_PORT_FEAT_C_PORT_LINK_STATE);
if (portchange & USB_PORT_STAT_C_RESET)
clear_port_feature(hub->hdev, port1,
USB_PORT_FEAT_C_RESET);
dev_dbg(hub->intfdev, "hot reset failed, warm reset port %d\n",
port1);
ret = hub_port_reset(hub, port1,
udev, HUB_BH_RESET_TIME,
true);
if ((portchange & USB_PORT_STAT_C_CONNECTION))
clear_port_feature(hub->hdev, port1,
USB_PORT_FEAT_C_CONNECTION);
return ret;
}
/* Device went away? */
if (!(portstatus & USB_PORT_STAT_CONNECTION))
return -ENOTCONN;
/* bomb out completely if the connection bounced */
if ((portchange & USB_PORT_STAT_C_CONNECTION))
return -ENOTCONN;
if ((portstatus & USB_PORT_STAT_ENABLE)) {
if (hub_is_wusb(hub))
udev->speed = USB_SPEED_WIRELESS;
else if (hub_is_superspeed(hub->hdev))
udev->speed = USB_SPEED_SUPER;
else if (portstatus & USB_PORT_STAT_HIGH_SPEED)
udev->speed = USB_SPEED_HIGH;
else if (portstatus & USB_PORT_STAT_LOW_SPEED)
udev->speed = USB_SPEED_LOW;
else
udev->speed = USB_SPEED_FULL;
return 0;
}
} else {
if (!(portstatus & USB_PORT_STAT_CONNECTION) ||
hub_port_warm_reset_required(hub,
portstatus))
return -ENOTCONN;
return 0;
}
if (!(portstatus & USB_PORT_STAT_RESET))
break;
delay:
/* switch to the long delay after two short delay failures */
if (delay_time >= 2 * HUB_SHORT_RESET_TIME)
delay = HUB_LONG_RESET_TIME;
......@@ -2615,20 +2547,54 @@ static int hub_port_wait_reset(struct usb_hub *hub, int port1,
port1, warm ? "warm " : "", delay);
}
return -EBUSY;
if ((portstatus & USB_PORT_STAT_RESET))
return -EBUSY;
if (hub_port_warm_reset_required(hub, portstatus))
return -ENOTCONN;
/* Device went away? */
if (!(portstatus & USB_PORT_STAT_CONNECTION))
return -ENOTCONN;
/* bomb out completely if the connection bounced. A USB 3.0
* connection may bounce if multiple warm resets were issued,
* but the device may have successfully re-connected. Ignore it.
*/
if (!hub_is_superspeed(hub->hdev) &&
(portchange & USB_PORT_STAT_C_CONNECTION))
return -ENOTCONN;
if (!(portstatus & USB_PORT_STAT_ENABLE))
return -EBUSY;
if (!udev)
return 0;
if (hub_is_wusb(hub))
udev->speed = USB_SPEED_WIRELESS;
else if (hub_is_superspeed(hub->hdev))
udev->speed = USB_SPEED_SUPER;
else if (portstatus & USB_PORT_STAT_HIGH_SPEED)
udev->speed = USB_SPEED_HIGH;
else if (portstatus & USB_PORT_STAT_LOW_SPEED)
udev->speed = USB_SPEED_LOW;
else
udev->speed = USB_SPEED_FULL;
return 0;
}
static void hub_port_finish_reset(struct usb_hub *hub, int port1,
struct usb_device *udev, int *status, bool warm)
struct usb_device *udev, int *status)
{
switch (*status) {
case 0:
if (!warm) {
struct usb_hcd *hcd;
/* TRSTRCY = 10 ms; plus some extra */
msleep(10 + 40);
/* TRSTRCY = 10 ms; plus some extra */
msleep(10 + 40);
if (udev) {
struct usb_hcd *hcd = bus_to_hcd(udev->bus);
update_devnum(udev, 0);
hcd = bus_to_hcd(udev->bus);
/* The xHC may think the device is already reset,
* so ignore the status.
*/
......@@ -2640,14 +2606,15 @@ static void hub_port_finish_reset(struct usb_hub *hub, int port1,
case -ENODEV:
clear_port_feature(hub->hdev,
port1, USB_PORT_FEAT_C_RESET);
/* FIXME need disconnect() for NOTATTACHED device */
if (hub_is_superspeed(hub->hdev)) {
clear_port_feature(hub->hdev, port1,
USB_PORT_FEAT_C_BH_PORT_RESET);
clear_port_feature(hub->hdev, port1,
USB_PORT_FEAT_C_PORT_LINK_STATE);
clear_port_feature(hub->hdev, port1,
USB_PORT_FEAT_C_CONNECTION);
}
if (!warm)
if (udev)
usb_set_device_state(udev, *status
? USB_STATE_NOTATTACHED
: USB_STATE_DEFAULT);
......@@ -2660,18 +2627,30 @@ static int hub_port_reset(struct usb_hub *hub, int port1,
struct usb_device *udev, unsigned int delay, bool warm)
{
int i, status;
u16 portchange, portstatus;
if (!warm) {
/* Block EHCI CF initialization during the port reset.
* Some companion controllers don't like it when they mix.
*/
down_read(&ehci_cf_port_reset_rwsem);
} else {
if (!hub_is_superspeed(hub->hdev)) {
if (!hub_is_superspeed(hub->hdev)) {
if (warm) {
dev_err(hub->intfdev, "only USB3 hub support "
"warm reset\n");
return -EINVAL;
}
/* Block EHCI CF initialization during the port reset.
* Some companion controllers don't like it when they mix.
*/
down_read(&ehci_cf_port_reset_rwsem);
} else if (!warm) {
/*
* If the caller hasn't explicitly requested a warm reset,
* double check and see if one is needed.
*/
status = hub_port_status(hub, port1,
&portstatus, &portchange);
if (status < 0)
goto done;
if (hub_port_warm_reset_required(hub, portstatus))
warm = true;
}
/* Reset the port */
......@@ -2692,10 +2671,33 @@ static int hub_port_reset(struct usb_hub *hub, int port1,
status);
}
/* return on disconnect or reset */
/* Check for disconnect or reset */
if (status == 0 || status == -ENOTCONN || status == -ENODEV) {
hub_port_finish_reset(hub, port1, udev, &status, warm);
goto done;
hub_port_finish_reset(hub, port1, udev, &status);
if (!hub_is_superspeed(hub->hdev))
goto done;
/*
* If a USB 3.0 device migrates from reset to an error
* state, re-issue the warm reset.
*/
if (hub_port_status(hub, port1,
&portstatus, &portchange) < 0)
goto done;
if (!hub_port_warm_reset_required(hub, portstatus))
goto done;
/*
* If the port is in SS.Inactive or Compliance Mode, the
* hot or warm reset failed. Try another warm reset.
*/
if (!warm) {
dev_dbg(hub->intfdev, "hot reset failed, warm reset port %d\n",
port1);
warm = true;
}
}
dev_dbg (hub->intfdev,
......@@ -2709,7 +2711,7 @@ static int hub_port_reset(struct usb_hub *hub, int port1,
port1);
done:
if (!warm)
if (!hub_is_superspeed(hub->hdev))
up_read(&ehci_cf_port_reset_rwsem);
return status;
......@@ -2945,9 +2947,7 @@ int usb_port_suspend(struct usb_device *udev, pm_message_t msg)
/* see 7.1.7.6 */
if (hub_is_superspeed(hub->hdev))
status = set_port_feature(hub->hdev,
port1 | (USB_SS_PORT_LS_U3 << 3),
USB_PORT_FEAT_LINK_STATE);
status = hub_set_port_link_state(hub, port1, USB_SS_PORT_LS_U3);
else
status = set_port_feature(hub->hdev, port1,
USB_PORT_FEAT_SUSPEND);
......@@ -3117,9 +3117,7 @@ int usb_port_resume(struct usb_device *udev, pm_message_t msg)
/* see 7.1.7.7; affects power usage, but not budgeting */
if (hub_is_superspeed(hub->hdev))
status = set_port_feature(hub->hdev,
port1 | (USB_SS_PORT_LS_U0 << 3),
USB_PORT_FEAT_LINK_STATE);
status = hub_set_port_link_state(hub, port1, USB_SS_PORT_LS_U0);
else
status = clear_port_feature(hub->hdev,
port1, USB_PORT_FEAT_SUSPEND);
......@@ -4700,12 +4698,21 @@ static void hub_events(void)
*/
if (hub_port_warm_reset_required(hub, portstatus)) {
int status;
struct usb_device *udev =
hub->ports[i - 1]->child;
dev_dbg(hub_dev, "warm reset port %d\n", i);
status = hub_port_reset(hub, i, NULL,
HUB_BH_RESET_TIME, true);
if (status < 0)
hub_port_disable(hub, i, 1);
if (!udev) {
status = hub_port_reset(hub, i,
NULL, HUB_BH_RESET_TIME,
true);
if (status < 0)
hub_port_disable(hub, i, 1);
} else {
usb_lock_device(udev);
status = usb_reset_device(udev);
usb_unlock_device(udev);
}
connect_change = 0;
}
......
......@@ -2706,13 +2706,11 @@ irqreturn_t xhci_irq(struct usb_hcd *hcd)
{
struct xhci_hcd *xhci = hcd_to_xhci(hcd);
u32 status;
union xhci_trb *trb;
u64 temp_64;
union xhci_trb *event_ring_deq;
dma_addr_t deq;
spin_lock(&xhci->lock);
trb = xhci->event_ring->dequeue;
/* Check if the xHC generated the interrupt, or the irq is shared */
status = xhci_readl(xhci, &xhci->op_regs->status);
if (status == 0xffffffff)
......
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