Skip to content

Commit

Permalink
---
Browse files Browse the repository at this point in the history
yaml
---
r: 317030
b: refs/heads/master
c: 3c273a0
h: refs/heads/master
v: v3
  • Loading branch information
Alan Stern authored and Greg Kroah-Hartman committed Jul 16, 2012
1 parent f5e7969 commit afc91c6
Show file tree
Hide file tree
Showing 6 changed files with 99 additions and 95 deletions.
2 changes: 1 addition & 1 deletion [refs]
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
---
refs/heads/master: 9d9387475af261949f61a5ec465e1f762d7be08a
refs/heads/master: 3c273a056bf46167f0a1309c2ba72282a17d2541
35 changes: 4 additions & 31 deletions trunk/drivers/usb/host/ehci-hcd.c
Original file line number Diff line number Diff line change
Expand Up @@ -795,7 +795,7 @@ static irqreturn_t ehci_irq (struct usb_hcd *hcd)
/* guard against (alleged) silicon errata */
if (cmd & CMD_IAAD)
ehci_dbg(ehci, "IAA with IAAD still set?\n");
if (ehci->async_unlink) {
if (ehci->async_iaa) {
COUNT(ehci->stats.iaa);
end_unlink_async(ehci);
} else
Expand Down Expand Up @@ -926,33 +926,6 @@ static int ehci_urb_enqueue (
}
}

static void unlink_async (struct ehci_hcd *ehci, struct ehci_qh *qh)
{
/* failfast */
if (ehci->rh_state < EHCI_RH_RUNNING && ehci->async_unlink)
end_unlink_async(ehci);

/* If the QH isn't linked then there's nothing we can do
* unless we were called during a giveback, in which case
* qh_completions() has to deal with it.
*/
if (qh->qh_state != QH_STATE_LINKED) {
if (qh->qh_state == QH_STATE_COMPLETING)
qh->needs_rescan = 1;
return;
}

/* defer till later if busy */
if (ehci->async_unlink) {
qh->qh_state = QH_STATE_UNLINK_WAIT;
ehci->async_unlink_last->unlink_next = qh;
ehci->async_unlink_last = qh;

/* start IAA cycle */
} else
start_unlink_async (ehci, qh);
}

/* remove from hardware lists
* completions normally happen asynchronously
*/
Expand All @@ -979,7 +952,7 @@ static int ehci_urb_dequeue(struct usb_hcd *hcd, struct urb *urb, int status)
switch (qh->qh_state) {
case QH_STATE_LINKED:
case QH_STATE_COMPLETING:
unlink_async(ehci, qh);
start_unlink_async(ehci, qh);
break;
case QH_STATE_UNLINK:
case QH_STATE_UNLINK_WAIT:
Expand Down Expand Up @@ -1070,7 +1043,7 @@ ehci_endpoint_disable (struct usb_hcd *hcd, struct usb_host_endpoint *ep)
* may already be unlinked.
*/
if (tmp)
unlink_async(ehci, qh);
start_unlink_async(ehci, qh);
/* FALL THROUGH */
case QH_STATE_UNLINK: /* wait for hw to finish? */
case QH_STATE_UNLINK_WAIT:
Expand Down Expand Up @@ -1133,7 +1106,7 @@ ehci_endpoint_reset(struct usb_hcd *hcd, struct usb_host_endpoint *ep)
* re-linking will call qh_refresh().
*/
if (eptype == USB_ENDPOINT_XFER_BULK)
unlink_async(ehci, qh);
start_unlink_async(ehci, qh);
else
start_unlink_intr(ehci, qh);
}
Expand Down
3 changes: 1 addition & 2 deletions trunk/drivers/usb/host/ehci-hub.c
Original file line number Diff line number Diff line change
Expand Up @@ -299,8 +299,7 @@ static int ehci_bus_suspend (struct usb_hcd *hcd)
ehci_halt (ehci);
ehci->rh_state = EHCI_RH_SUSPENDED;

if (ehci->async_unlink)
end_unlink_async(ehci);
end_unlink_async(ehci);
ehci_handle_intr_unlinks(ehci);
end_free_itds(ehci);

Expand Down
149 changes: 89 additions & 60 deletions trunk/drivers/usb/host/ehci-q.c
Original file line number Diff line number Diff line change
Expand Up @@ -293,9 +293,6 @@ __acquires(ehci->lock)
spin_lock (&ehci->lock);
}

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

static int qh_schedule (struct ehci_hcd *ehci, struct ehci_qh *qh);

/*
Expand Down Expand Up @@ -1166,82 +1163,114 @@ submit_async (

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

/* the async qh for the qtds being unlinked are now gone from the HC */

static void end_unlink_async (struct ehci_hcd *ehci)
static void single_unlink_async(struct ehci_hcd *ehci, struct ehci_qh *qh)
{
struct ehci_qh *qh = ehci->async_unlink;
struct ehci_qh *next;
struct ehci_qh *prev;

// qh->hw_next = cpu_to_hc32(qh->qh_dma);
qh->qh_state = QH_STATE_IDLE;
qh->qh_next.qh = NULL;
/* Add to the end of the list of QHs waiting for the next IAAD */
qh->qh_state = QH_STATE_UNLINK;
if (ehci->async_unlink)
ehci->async_unlink_last->unlink_next = qh;
else
ehci->async_unlink = qh;
ehci->async_unlink_last = qh;

/* Unlink it from the schedule */
prev = ehci->async;
while (prev->qh_next.qh != qh)
prev = prev->qh_next.qh;

prev->hw->hw_next = qh->hw->hw_next;
prev->qh_next = qh->qh_next;
if (ehci->qh_scan_next == qh)
ehci->qh_scan_next = qh->qh_next.qh;
}

/* other unlink(s) may be pending (in QH_STATE_UNLINK_WAIT) */
next = qh->unlink_next;
ehci->async_unlink = next;
qh->unlink_next = NULL;
static void start_iaa_cycle(struct ehci_hcd *ehci, bool nested)
{
/*
* Do nothing if an IAA cycle is already running or
* if one will be started shortly.
*/
if (ehci->async_iaa || ehci->async_unlinking)
return;

qh_completions (ehci, qh);
/* Do all the waiting QHs at once */
ehci->async_iaa = ehci->async_unlink;
ehci->async_unlink = NULL;

if (!list_empty(&qh->qtd_list) && ehci->rh_state == EHCI_RH_RUNNING)
qh_link_async (ehci, qh);
/* If the controller isn't running, we don't have to wait for it */
if (unlikely(ehci->rh_state < EHCI_RH_RUNNING)) {
if (!nested) /* Avoid recursion */
end_unlink_async(ehci);

disable_async(ehci);
/* Otherwise start a new IAA cycle */
} else {
/* Make sure the unlinks are all visible to the hardware */
wmb();

if (next) {
ehci->async_unlink = NULL;
start_unlink_async (ehci, next);
ehci_writel(ehci, ehci->command | CMD_IAAD,
&ehci->regs->command);
ehci_readl(ehci, &ehci->regs->command);
ehci_enable_event(ehci, EHCI_HRTIMER_IAA_WATCHDOG, true);
}
}

/* the async qh for the qtds being unlinked are now gone from the HC */

static void end_unlink_async(struct ehci_hcd *ehci)
{
struct ehci_qh *qh;

if (ehci->has_synopsys_hc_bug)
ehci_writel(ehci, (u32) ehci->async->qh_dma,
&ehci->regs->async_next);

/* Process the idle QHs */
restart:
ehci->async_unlinking = true;
while (ehci->async_iaa) {
qh = ehci->async_iaa;
ehci->async_iaa = qh->unlink_next;
qh->unlink_next = NULL;

qh->qh_state = QH_STATE_IDLE;
qh->qh_next.qh = NULL;

qh_completions(ehci, qh);
if (!list_empty(&qh->qtd_list) &&
ehci->rh_state == EHCI_RH_RUNNING)
qh_link_async(ehci, qh);
disable_async(ehci);
}
ehci->async_unlinking = false;

/* Start a new IAA cycle if any QHs are waiting for it */
if (ehci->async_unlink) {
start_iaa_cycle(ehci, true);
if (unlikely(ehci->rh_state < EHCI_RH_RUNNING))
goto restart;
}
}

/* makes sure the async qh will become idle */
/* caller must own ehci->lock */

static void start_unlink_async (struct ehci_hcd *ehci, struct ehci_qh *qh)
static void start_unlink_async(struct ehci_hcd *ehci, struct ehci_qh *qh)
{
struct ehci_qh *prev;

#ifdef DEBUG
assert_spin_locked(&ehci->lock);
if (ehci->async_unlink
|| (qh->qh_state != QH_STATE_LINKED
&& qh->qh_state != QH_STATE_UNLINK_WAIT)
)
BUG ();
#endif

qh->qh_state = QH_STATE_UNLINK;
ehci->async_unlink = qh;
if (!qh->unlink_next)
ehci->async_unlink_last = qh;

prev = ehci->async;
while (prev->qh_next.qh != qh)
prev = prev->qh_next.qh;

prev->hw->hw_next = qh->hw->hw_next;
prev->qh_next = qh->qh_next;
if (ehci->qh_scan_next == qh)
ehci->qh_scan_next = qh->qh_next.qh;
wmb ();

/* If the controller isn't running, we don't have to wait for it */
if (unlikely(ehci->rh_state < EHCI_RH_RUNNING)) {
/* if (unlikely (qh->unlink_next != 0))
* this will recurse, probably not much
*/
end_unlink_async (ehci);
/*
* If the QH isn't linked then there's nothing we can do
* unless we were called during a giveback, in which case
* qh_completions() has to deal with it.
*/
if (qh->qh_state != QH_STATE_LINKED) {
if (qh->qh_state == QH_STATE_COMPLETING)
qh->needs_rescan = 1;
return;
}

ehci_writel(ehci, ehci->command | CMD_IAAD, &ehci->regs->command);
(void)ehci_readl(ehci, &ehci->regs->command);
ehci_enable_event(ehci, EHCI_HRTIMER_IAA_WATCHDOG, true);
single_unlink_async(ehci, qh);
start_iaa_cycle(ehci, false);
}

/*-------------------------------------------------------------------------*/
Expand Down Expand Up @@ -1269,11 +1298,11 @@ static void scan_async (struct ehci_hcd *ehci)
* drops the lock. That's why ehci->qh_scan_next
* always holds the next qh to scan; if the next qh
* gets unlinked then ehci->qh_scan_next is adjusted
* in start_unlink_async().
* in single_unlink_async().
*/
temp = qh_completions(ehci, qh);
if (qh->needs_rescan)
unlink_async(ehci, qh);
start_unlink_async(ehci, qh);
qh->unlink_time = jiffies + EHCI_SHRINK_JIFFIES;
if (temp != 0)
goto rescan;
Expand Down
3 changes: 2 additions & 1 deletion trunk/drivers/usb/host/ehci-timer.c
Original file line number Diff line number Diff line change
Expand Up @@ -211,6 +211,7 @@ static void ehci_handle_controller_death(struct ehci_hcd *ehci)
ehci_writel(ehci, 0, &ehci->regs->configured_flag);
ehci_writel(ehci, 0, &ehci->regs->intr_enable);
ehci_work(ehci);
end_unlink_async(ehci);

/* Not in process context, so don't try to reset the controller */
}
Expand Down Expand Up @@ -304,7 +305,7 @@ static void ehci_iaa_watchdog(struct ehci_hcd *ehci)
* (a) SMP races against real IAA firing and retriggering, and
* (b) clean HC shutdown, when IAA watchdog was pending.
*/
if (ehci->async_unlink) {
if (ehci->async_iaa) {
u32 cmd, status;

/* If we get here, IAA is *REALLY* late. It's barely
Expand Down
2 changes: 2 additions & 0 deletions trunk/drivers/usb/host/ehci.h
Original file line number Diff line number Diff line change
Expand Up @@ -114,12 +114,14 @@ struct ehci_hcd { /* one per controller */
/* general schedule support */
unsigned scanning:1;
bool intr_unlinking:1;
bool async_unlinking:1;

/* async schedule support */
struct ehci_qh *async;
struct ehci_qh *dummy; /* For AMD quirk use */
struct ehci_qh *async_unlink;
struct ehci_qh *async_unlink_last;
struct ehci_qh *async_iaa;
struct ehci_qh *qh_scan_next;
unsigned async_count; /* async activity count */

Expand Down

0 comments on commit afc91c6

Please sign in to comment.