Skip to content

Commit

Permalink
USB: EHCI: use the new clear_tt_buffer interface
Browse files Browse the repository at this point in the history
This patch (as1256) changes ehci-hcd and all the other drivers in the
EHCI family to make use of the new clear_tt_buffer callbacks.  When a
Clear-TT-Buffer request is in progress for a QH, the QH is not allowed
to be linked into the async schedule until the request is finished.
At that time, if there are any URBs queued for the QH, it is linked
into the async schedule.

Signed-off-by: Alan Stern <stern@rowland.harvard.edu>
Cc: stable <stable@kernel.org>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
  • Loading branch information
Alan Stern authored and Greg Kroah-Hartman committed Jul 12, 2009
1 parent cb88a1b commit 914b701
Show file tree
Hide file tree
Showing 10 changed files with 86 additions and 23 deletions.
2 changes: 2 additions & 0 deletions drivers/usb/host/ehci-au1xxx.c
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,8 @@ static const struct hc_driver ehci_au1xxx_hc_driver = {
.bus_resume = ehci_bus_resume,
.relinquish_port = ehci_relinquish_port,
.port_handed_over = ehci_port_handed_over,

.clear_tt_buffer_complete = ehci_clear_tt_buffer_complete,
};

static int ehci_hcd_au1xxx_drv_probe(struct platform_device *pdev)
Expand Down
2 changes: 2 additions & 0 deletions drivers/usb/host/ehci-fsl.c
Original file line number Diff line number Diff line change
Expand Up @@ -325,6 +325,8 @@ static const struct hc_driver ehci_fsl_hc_driver = {
.bus_resume = ehci_bus_resume,
.relinquish_port = ehci_relinquish_port,
.port_handed_over = ehci_port_handed_over,

.clear_tt_buffer_complete = ehci_clear_tt_buffer_complete,
};

static int ehci_fsl_drv_probe(struct platform_device *pdev)
Expand Down
2 changes: 2 additions & 0 deletions drivers/usb/host/ehci-hcd.c
Original file line number Diff line number Diff line change
Expand Up @@ -1003,6 +1003,8 @@ ehci_endpoint_disable (struct usb_hcd *hcd, struct usb_host_endpoint *ep)
schedule_timeout_uninterruptible(1);
goto rescan;
case QH_STATE_IDLE: /* fully unlinked */
if (qh->clearing_tt)
goto idle_timeout;
if (list_empty (&qh->qtd_list)) {
qh_put (qh);
break;
Expand Down
2 changes: 2 additions & 0 deletions drivers/usb/host/ehci-ixp4xx.c
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,8 @@ static const struct hc_driver ixp4xx_ehci_hc_driver = {
#endif
.relinquish_port = ehci_relinquish_port,
.port_handed_over = ehci_port_handed_over,

.clear_tt_buffer_complete = ehci_clear_tt_buffer_complete,
};

static int ixp4xx_ehci_probe(struct platform_device *pdev)
Expand Down
2 changes: 2 additions & 0 deletions drivers/usb/host/ehci-orion.c
Original file line number Diff line number Diff line change
Expand Up @@ -165,6 +165,8 @@ static const struct hc_driver ehci_orion_hc_driver = {
.bus_resume = ehci_bus_resume,
.relinquish_port = ehci_relinquish_port,
.port_handed_over = ehci_port_handed_over,

.clear_tt_buffer_complete = ehci_clear_tt_buffer_complete,
};

static void __init
Expand Down
2 changes: 2 additions & 0 deletions drivers/usb/host/ehci-pci.c
Original file line number Diff line number Diff line change
Expand Up @@ -404,6 +404,8 @@ static const struct hc_driver ehci_pci_hc_driver = {
.bus_resume = ehci_bus_resume,
.relinquish_port = ehci_relinquish_port,
.port_handed_over = ehci_port_handed_over,

.clear_tt_buffer_complete = ehci_clear_tt_buffer_complete,
};

/*-------------------------------------------------------------------------*/
Expand Down
2 changes: 2 additions & 0 deletions drivers/usb/host/ehci-ppc-of.c
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,8 @@ static const struct hc_driver ehci_ppc_of_hc_driver = {
#endif
.relinquish_port = ehci_relinquish_port,
.port_handed_over = ehci_port_handed_over,

.clear_tt_buffer_complete = ehci_clear_tt_buffer_complete,
};


Expand Down
2 changes: 2 additions & 0 deletions drivers/usb/host/ehci-ps3.c
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,8 @@ static const struct hc_driver ps3_ehci_hc_driver = {
#endif
.relinquish_port = ehci_relinquish_port,
.port_handed_over = ehci_port_handed_over,

.clear_tt_buffer_complete = ehci_clear_tt_buffer_complete,
};

static int __devinit ps3_ehci_probe(struct ps3_system_bus_device *dev)
Expand Down
91 changes: 68 additions & 23 deletions drivers/usb/host/ehci-q.c
Original file line number Diff line number Diff line change
Expand Up @@ -139,6 +139,55 @@ qh_refresh (struct ehci_hcd *ehci, struct ehci_qh *qh)

/*-------------------------------------------------------------------------*/

static void qh_link_async(struct ehci_hcd *ehci, struct ehci_qh *qh);

static void ehci_clear_tt_buffer_complete(struct usb_hcd *hcd,
struct usb_host_endpoint *ep)
{
struct ehci_hcd *ehci = hcd_to_ehci(hcd);
struct ehci_qh *qh = ep->hcpriv;
unsigned long flags;

spin_lock_irqsave(&ehci->lock, flags);
qh->clearing_tt = 0;
if (qh->qh_state == QH_STATE_IDLE && !list_empty(&qh->qtd_list)
&& HC_IS_RUNNING(hcd->state))
qh_link_async(ehci, qh);
spin_unlock_irqrestore(&ehci->lock, flags);
}

static void ehci_clear_tt_buffer(struct ehci_hcd *ehci, struct ehci_qh *qh,
struct urb *urb, u32 token)
{

/* If an async split transaction gets an error or is unlinked,
* the TT buffer may be left in an indeterminate state. We
* have to clear the TT buffer.
*
* Note: this routine is never called for Isochronous transfers.
*/
if (urb->dev->tt && !usb_pipeint(urb->pipe) && !qh->clearing_tt) {
#ifdef DEBUG
struct usb_device *tt = urb->dev->tt->hub;
dev_dbg(&tt->dev,
"clear tt buffer port %d, a%d ep%d t%08x\n",
urb->dev->ttport, urb->dev->devnum,
usb_pipeendpoint(urb->pipe), token);
#endif /* DEBUG */
if (!ehci_is_TDI(ehci)
|| urb->dev->tt->hub !=
ehci_to_hcd(ehci)->self.root_hub) {
if (usb_hub_clear_tt_buffer(urb) == 0)
qh->clearing_tt = 1;
} else {

/* REVISIT ARC-derived cores don't clear the root
* hub TT buffer in this way...
*/
}
}
}

static int qtd_copy_status (
struct ehci_hcd *ehci,
struct urb *urb,
Expand Down Expand Up @@ -195,28 +244,6 @@ static int qtd_copy_status (
usb_pipeendpoint (urb->pipe),
usb_pipein (urb->pipe) ? "in" : "out",
token, status);

/* if async CSPLIT failed, try cleaning out the TT buffer */
if (status != -EPIPE
&& urb->dev->tt
&& !usb_pipeint(urb->pipe)
&& ((token & QTD_STS_MMF) != 0
|| QTD_CERR(token) == 0)
&& (!ehci_is_TDI(ehci)
|| urb->dev->tt->hub !=
ehci_to_hcd(ehci)->self.root_hub)) {
#ifdef DEBUG
struct usb_device *tt = urb->dev->tt->hub;
dev_dbg (&tt->dev,
"clear tt buffer port %d, a%d ep%d t%08x\n",
urb->dev->ttport, urb->dev->devnum,
usb_pipeendpoint (urb->pipe), token);
#endif /* DEBUG */
/* REVISIT ARC-derived cores don't clear the root
* hub TT buffer in this way...
*/
usb_hub_clear_tt_buffer(urb);
}
}

return status;
Expand Down Expand Up @@ -407,9 +434,16 @@ qh_completions (struct ehci_hcd *ehci, struct ehci_qh *qh)
/* qh unlinked; token in overlay may be most current */
if (state == QH_STATE_IDLE
&& cpu_to_hc32(ehci, qtd->qtd_dma)
== qh->hw_current)
== qh->hw_current) {
token = hc32_to_cpu(ehci, qh->hw_token);

/* An unlink may leave an incomplete
* async transaction in the TT buffer.
* We have to clear it.
*/
ehci_clear_tt_buffer(ehci, qh, urb, token);
}

/* force halt for unlinked or blocked qh, so we'll
* patch the qh later and so that completions can't
* activate it while we "know" it's stopped.
Expand All @@ -435,6 +469,13 @@ qh_completions (struct ehci_hcd *ehci, struct ehci_qh *qh)
&& (qtd->hw_alt_next
& EHCI_LIST_END(ehci)))
last_status = -EINPROGRESS;

/* As part of low/full-speed endpoint-halt processing
* we must clear the TT buffer (11.17.5).
*/
if (unlikely(last_status != -EINPROGRESS &&
last_status != -EREMOTEIO))
ehci_clear_tt_buffer(ehci, qh, urb, token);
}

/* if we're removing something not at the queue head,
Expand Down Expand Up @@ -864,6 +905,10 @@ static void qh_link_async (struct ehci_hcd *ehci, struct ehci_qh *qh)
__hc32 dma = QH_NEXT(ehci, qh->qh_dma);
struct ehci_qh *head;

/* Don't link a QH if there's a Clear-TT-Buffer pending */
if (unlikely(qh->clearing_tt))
return;

/* (re)start the async schedule? */
head = ehci->async;
timer_action_done (ehci, TIMER_ASYNC_OFF);
Expand Down
2 changes: 2 additions & 0 deletions drivers/usb/host/ehci.h
Original file line number Diff line number Diff line change
Expand Up @@ -354,7 +354,9 @@ struct ehci_qh {
unsigned short period; /* polling interval */
unsigned short start; /* where polling starts */
#define NO_FRAME ((unsigned short)~0) /* pick new start */

struct usb_device *dev; /* access to TT */
unsigned clearing_tt:1; /* Clear-TT-Buf in progress */
} __attribute__ ((aligned (32)));

/*-------------------------------------------------------------------------*/
Expand Down

0 comments on commit 914b701

Please sign in to comment.