Commit a455212d authored by Alan Stern's avatar Alan Stern Committed by Greg Kroah-Hartman

USB: EHCI: update toggle state for linked QHs

This is an update to the "usb-ehci-update-toggle-state-for-linked-qhs"
patch.  Since an HCD's endpoint_reset method can be called in
interrupt context, it mustn't assume that interrupts are enabled or
that it can sleep.

So we revert to the original way of refreshing QHs' toggle bits.  Now
the endpoint_reset method merely clears the toggle flag in the device
structure (as was done before) and starts an async QH unlink.  When the
QH is linked again, after the unlink finishes and an URB is queued,
the qh_refresh() routine will update the QH's toggle bit.
Signed-off-by: default avatarAlan Stern <stern@rowland.harvard.edu>
Tested-by: default avatarDavid <david@unsolicited.net>
CC: David Brownell <david-b@pacbell.net>
Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@suse.de>
parent 9525dcb3
......@@ -1030,12 +1030,14 @@ ehci_endpoint_reset(struct usb_hcd *hcd, struct usb_host_endpoint *ep)
struct ehci_hcd *ehci = hcd_to_ehci(hcd);
struct ehci_qh *qh;
int eptype = usb_endpoint_type(&ep->desc);
int epnum = usb_endpoint_num(&ep->desc);
int is_out = usb_endpoint_dir_out(&ep->desc);
unsigned long flags;
if (eptype != USB_ENDPOINT_XFER_BULK && eptype != USB_ENDPOINT_XFER_INT)
return;
rescan:
spin_lock_irq(&ehci->lock);
spin_lock_irqsave(&ehci->lock, flags);
qh = ep->hcpriv;
/* For Bulk and Interrupt endpoints we maintain the toggle state
......@@ -1044,29 +1046,24 @@ ehci_endpoint_reset(struct usb_hcd *hcd, struct usb_host_endpoint *ep)
* the toggle bit in the QH.
*/
if (qh) {
usb_settoggle(qh->dev, epnum, is_out, 0);
if (!list_empty(&qh->qtd_list)) {
WARN_ONCE(1, "clear_halt for a busy endpoint\n");
} else if (qh->qh_state == QH_STATE_IDLE) {
qh->hw_token &= ~cpu_to_hc32(ehci, QTD_TOGGLE);
} else {
/* It's not safe to write into the overlay area
* while the QH is active. Unlink it first and
* wait for the unlink to complete.
} else if (qh->qh_state == QH_STATE_LINKED) {
/* The toggle value in the QH can't be updated
* while the QH is active. Unlink it now;
* re-linking will call qh_refresh().
*/
if (qh->qh_state == QH_STATE_LINKED) {
if (eptype == USB_ENDPOINT_XFER_BULK) {
unlink_async(ehci, qh);
} else {
intr_deschedule(ehci, qh);
(void) qh_schedule(ehci, qh);
}
if (eptype == USB_ENDPOINT_XFER_BULK) {
unlink_async(ehci, qh);
} else {
intr_deschedule(ehci, qh);
(void) qh_schedule(ehci, qh);
}
spin_unlock_irq(&ehci->lock);
schedule_timeout_uninterruptible(1);
goto rescan;
}
}
spin_unlock_irq(&ehci->lock);
spin_unlock_irqrestore(&ehci->lock, flags);
}
static int ehci_get_frame (struct usb_hcd *hcd)
......
......@@ -93,6 +93,22 @@ qh_update (struct ehci_hcd *ehci, struct ehci_qh *qh, struct ehci_qtd *qtd)
qh->hw_qtd_next = QTD_NEXT(ehci, qtd->qtd_dma);
qh->hw_alt_next = EHCI_LIST_END(ehci);
/* Except for control endpoints, we make hardware maintain data
* toggle (like OHCI) ... here (re)initialize the toggle in the QH,
* and set the pseudo-toggle in udev. Only usb_clear_halt() will
* ever clear it.
*/
if (!(qh->hw_info1 & cpu_to_hc32(ehci, 1 << 14))) {
unsigned is_out, epnum;
is_out = !(qtd->hw_token & cpu_to_hc32(ehci, 1 << 8));
epnum = (hc32_to_cpup(ehci, &qh->hw_info1) >> 8) & 0x0f;
if (unlikely (!usb_gettoggle (qh->dev, epnum, is_out))) {
qh->hw_token &= ~cpu_to_hc32(ehci, QTD_TOGGLE);
usb_settoggle (qh->dev, epnum, is_out, 1);
}
}
/* HC must see latest qtd and qh data before we clear ACTIVE+HALT */
wmb ();
qh->hw_token &= cpu_to_hc32(ehci, QTD_TOGGLE | QTD_STS_PING);
......@@ -834,6 +850,7 @@ done:
qh->qh_state = QH_STATE_IDLE;
qh->hw_info1 = cpu_to_hc32(ehci, info1);
qh->hw_info2 = cpu_to_hc32(ehci, info2);
usb_settoggle (urb->dev, usb_pipeendpoint (urb->pipe), !is_input, 1);
qh_refresh (ehci, qh);
return qh;
}
......@@ -864,7 +881,7 @@ static void qh_link_async (struct ehci_hcd *ehci, struct ehci_qh *qh)
}
}
/* clear halt and maybe recover from silicon quirk */
/* clear halt and/or toggle; and maybe recover from silicon quirk */
if (qh->qh_state == QH_STATE_IDLE)
qh_refresh (ehci, qh);
......
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