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

Merge tag 'for-usb-linus-2012-07-02' of...

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
parents 6887a413 0d9f78a9
......@@ -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) */
......
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