mirror of
https://github.com/rd-stuffs/msm-4.14.git
synced 2025-03-24 20:28:07 +08:00
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 -----BEGIN PGP SIGNATURE----- Version: GnuPG v1.4.11 (GNU/Linux) iQIcBAABAgAGBQJQ5gVdAAoJEBMGWMLi1Gc5DZMQAKBwA07JWHONfJVfoPU/+Iz5 rxstace/JP4HWerUbvuArkd7aEn+14JO2nU7czLZHxsjm1F7PiNp+f/rPTKxdoG5 Qm6x5oASeGfFrihG8BVh4WDeT2SraCNOtD6miqZwkjVW73mfYSEjyPFxJYlnpXOI u7ZBQVqWC5ZTJGupR4CHs38utYFuBlS1brR+f4D4+4p/YardBM0aD2u/o5xdqQJV AHK1HAL9UttVubQkV7B7tQwB1hk8RQ/24TrATceQpehsqqRvWqAz2dzSivk1/tqb Vf8abYURxKWNUYYQ1L7iV6n+t5oiF3wK3dI+D8xwlkb4xheVwcu1lvXC0XmGdyrf 2JqYs6quJk6755flM5qGtftdf3Bwdj8p4pqpcizxjeCDCCqIGpy+32cnzli5JfBy /w0pa2qbUzHZWNqGVlrqO3f8Bjb+Qsx2mqdvJVfhsbt2OsP+k/SYUwQ4UK/6ZDRO /mZxW0MEWVUd7CdGoFlGFam0vD/XWrPCDh32V5Oqt4kNKPHxXMjsrexR5XyRm7MV Kd2pA+L+XAbUPF2wJQXtyFXb4h6jtSZe6xvWujI3rtsqpeb5XyX/70uqOQ4y4ZYG HEWmieCCtuP+SfUQyR/xvi/WKRihQT+Pz00BlCmXEsuD5h5IBnK7i/CMBN85HUPj bb4GuYIrzKPADIP0ReLh =VBlC -----END PGP SIGNATURE----- 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
This commit is contained in:
commit
102ee00191
@ -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;
|
||||
if (!(portstatus & USB_PORT_STAT_RESET))
|
||||
break;
|
||||
|
||||
/*
|
||||
* 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;
|
||||
}
|
||||
|
||||
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 @@ delay:
|
||||
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)
|
||||
|
Loading…
x
Reference in New Issue
Block a user