forked from Minki/linux
xhci: Fix driver hang and resume error path.
Hi Greg, Here's two bug fixes for 3.5. The first fixes an issue with port connections not being reported to the USB core after a system resume. The second fixes a driver hang when there are two back to back stalls on an endpoint. Sarah Sharp -----BEGIN PGP SIGNATURE----- Version: GnuPG v1.4.11 (GNU/Linux) iQIcBAABAgAGBQJP8fwtAAoJEBMGWMLi1Gc5KrwP/0acHiWeflQ+uo1VKsZayh0k jf8TE55Noopoem8R0GVQQILMvvcGZQDdIZ9frSaR+Fm7oQLf/OfuVgehEUfnMzX3 rjSl5yk3pS37dLjYWIROUYGPBxoGCO/WpHxh53eGhgV9gV6ToG6tJo/a8ysj4K90 HmwWYPp/BPmAMYepRdKQSNvTUj8VaLR/F1zPZA+evNYoTFL5xY6aejjrMmI6TiIC N1x7b/KdDbT1XC1VS/ZhAzkdCZ49rdUiE9KCL9TWrSWMGWY408ApSWkusfY0w2Yi /A3PrlACt56N6yfOcqHXLoYpE6GAvCnvBZDztVlauqwFn3szzlMKMhtJgtLAalKQ +HenE0N5x02FX2WYO1zV2Lraesa93U9HwQGnuXnsd1yXvkEDZJziQLrXI6Q0qMvz ImSIcCUbZanPSmXAfOKwXOKba5A/Ep5bwftERfIuKlbJbBSlQ/dCeLaInrlFLcGb Q490pYC9poGx9raGZWgcSPj9JmPd5XYqSjc6wq/IFdrzrKFY1AWHn4m/tlGihzd0 JIst/vP5LAoAlApWAWFLhoSzA/+tFI3JLO7meJcgURJAe+XX7vzZCe3kYHc2HPLT DjYdmgw6K+FVzy7BJ673dDKzFB/B0bj/MeLtv79q0ybrG2dZX7lbS44fOw2Wt4M0 DMO6Kc8dIQdYhl9wCgzj =FB8L -----END PGP SIGNATURE----- Merge tag 'for-usb-linus-2012-07-02' of git://git.kernel.org/pub/scm/linux/kernel/git/sarah/xhci into usb-linus xhci: Fix driver hang and resume error path. Hi Greg, Here's two bug fixes for 3.5. The first fixes an issue with port connections not being reported to the USB core after a system resume. The second fixes a driver hang when there are two back to back stalls on an endpoint. Sarah Sharp
This commit is contained in:
commit
d25ae36934
@ -2324,12 +2324,16 @@ static unsigned hub_is_wusb(struct usb_hub *hub)
|
||||
static int hub_port_reset(struct usb_hub *hub, int port1,
|
||||
struct usb_device *udev, unsigned int delay, bool warm);
|
||||
|
||||
/* Is a USB 3.0 port in the Inactive state? */
|
||||
static bool hub_port_inactive(struct usb_hub *hub, u16 portstatus)
|
||||
/* Is a USB 3.0 port in the Inactive or Complinance Mode state?
|
||||
* Port worm reset is required to recover
|
||||
*/
|
||||
static bool hub_port_warm_reset_required(struct usb_hub *hub, u16 portstatus)
|
||||
{
|
||||
return hub_is_superspeed(hub->hdev) &&
|
||||
(portstatus & USB_PORT_STAT_LINK_STATE) ==
|
||||
USB_SS_PORT_LS_SS_INACTIVE;
|
||||
(((portstatus & USB_PORT_STAT_LINK_STATE) ==
|
||||
USB_SS_PORT_LS_SS_INACTIVE) ||
|
||||
((portstatus & USB_PORT_STAT_LINK_STATE) ==
|
||||
USB_SS_PORT_LS_COMP_MOD)) ;
|
||||
}
|
||||
|
||||
static int hub_port_wait_reset(struct usb_hub *hub, int port1,
|
||||
@ -2365,7 +2369,7 @@ static int hub_port_wait_reset(struct usb_hub *hub, int port1,
|
||||
*
|
||||
* See https://bugzilla.kernel.org/show_bug.cgi?id=41752
|
||||
*/
|
||||
if (hub_port_inactive(hub, portstatus)) {
|
||||
if (hub_port_warm_reset_required(hub, portstatus)) {
|
||||
int ret;
|
||||
|
||||
if ((portchange & USB_PORT_STAT_C_CONNECTION))
|
||||
@ -4408,9 +4412,7 @@ static void hub_events(void)
|
||||
/* Warm reset a USB3 protocol port if it's in
|
||||
* SS.Inactive state.
|
||||
*/
|
||||
if (hub_is_superspeed(hub->hdev) &&
|
||||
(portstatus & USB_PORT_STAT_LINK_STATE)
|
||||
== USB_SS_PORT_LS_SS_INACTIVE) {
|
||||
if (hub_port_warm_reset_required(hub, portstatus)) {
|
||||
dev_dbg(hub_dev, "warm reset port %d\n", i);
|
||||
hub_port_reset(hub, i, NULL,
|
||||
HUB_BH_RESET_TIME, true);
|
||||
|
@ -462,6 +462,42 @@ void xhci_test_and_clear_bit(struct xhci_hcd *xhci, __le32 __iomem **port_array,
|
||||
}
|
||||
}
|
||||
|
||||
/* Updates Link Status for super Speed port */
|
||||
static void xhci_hub_report_link_state(u32 *status, u32 status_reg)
|
||||
{
|
||||
u32 pls = status_reg & PORT_PLS_MASK;
|
||||
|
||||
/* resume state is a xHCI internal state.
|
||||
* Do not report it to usb core.
|
||||
*/
|
||||
if (pls == XDEV_RESUME)
|
||||
return;
|
||||
|
||||
/* When the CAS bit is set then warm reset
|
||||
* should be performed on port
|
||||
*/
|
||||
if (status_reg & PORT_CAS) {
|
||||
/* The CAS bit can be set while the port is
|
||||
* in any link state.
|
||||
* Only roothubs have CAS bit, so we
|
||||
* pretend to be in compliance mode
|
||||
* unless we're already in compliance
|
||||
* or the inactive state.
|
||||
*/
|
||||
if (pls != USB_SS_PORT_LS_COMP_MOD &&
|
||||
pls != USB_SS_PORT_LS_SS_INACTIVE) {
|
||||
pls = USB_SS_PORT_LS_COMP_MOD;
|
||||
}
|
||||
/* Return also connection bit -
|
||||
* hub state machine resets port
|
||||
* when this bit is set.
|
||||
*/
|
||||
pls |= USB_PORT_STAT_CONNECTION;
|
||||
}
|
||||
/* update status field */
|
||||
*status |= pls;
|
||||
}
|
||||
|
||||
int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue,
|
||||
u16 wIndex, char *buf, u16 wLength)
|
||||
{
|
||||
@ -606,13 +642,9 @@ int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue,
|
||||
else
|
||||
status |= USB_PORT_STAT_POWER;
|
||||
}
|
||||
/* Port Link State */
|
||||
/* Update Port Link State for super speed ports*/
|
||||
if (hcd->speed == HCD_USB3) {
|
||||
/* resume state is a xHCI internal state.
|
||||
* Do not report it to usb core.
|
||||
*/
|
||||
if ((temp & PORT_PLS_MASK) != XDEV_RESUME)
|
||||
status |= (temp & PORT_PLS_MASK);
|
||||
xhci_hub_report_link_state(&status, temp);
|
||||
}
|
||||
if (bus_state->port_c_suspend & (1 << wIndex))
|
||||
status |= 1 << USB_PORT_FEAT_C_SUSPEND;
|
||||
|
@ -885,6 +885,17 @@ static void update_ring_for_set_deq_completion(struct xhci_hcd *xhci,
|
||||
num_trbs_free_temp = ep_ring->num_trbs_free;
|
||||
dequeue_temp = ep_ring->dequeue;
|
||||
|
||||
/* If we get two back-to-back stalls, and the first stalled transfer
|
||||
* ends just before a link TRB, the dequeue pointer will be left on
|
||||
* the link TRB by the code in the while loop. So we have to update
|
||||
* the dequeue pointer one segment further, or we'll jump off
|
||||
* the segment into la-la-land.
|
||||
*/
|
||||
if (last_trb(xhci, ep_ring, ep_ring->deq_seg, ep_ring->dequeue)) {
|
||||
ep_ring->deq_seg = ep_ring->deq_seg->next;
|
||||
ep_ring->dequeue = ep_ring->deq_seg->trbs;
|
||||
}
|
||||
|
||||
while (ep_ring->dequeue != dev->eps[ep_index].queued_deq_ptr) {
|
||||
/* We have more usable TRBs */
|
||||
ep_ring->num_trbs_free++;
|
||||
|
@ -341,7 +341,11 @@ struct xhci_op_regs {
|
||||
#define PORT_PLC (1 << 22)
|
||||
/* port configure error change - port failed to configure its link partner */
|
||||
#define PORT_CEC (1 << 23)
|
||||
/* bit 24 reserved */
|
||||
/* Cold Attach Status - xHC can set this bit to report device attached during
|
||||
* Sx state. Warm port reset should be perfomed to clear this bit and move port
|
||||
* to connected state.
|
||||
*/
|
||||
#define PORT_CAS (1 << 24)
|
||||
/* wake on connect (enable) */
|
||||
#define PORT_WKCONN_E (1 << 25)
|
||||
/* wake on disconnect (enable) */
|
||||
|
Loading…
Reference in New Issue
Block a user