Skip to content

Commit

Permalink
---
Browse files Browse the repository at this point in the history
yaml
---
r: 36475
b: refs/heads/master
c: 26f953f
h: refs/heads/master
i:
  36473: 92410cb
  36471: 2933486
v: v3
  • Loading branch information
David Brownell authored and Greg Kroah-Hartman committed Sep 27, 2006
1 parent e31425a commit 25f563e
Show file tree
Hide file tree
Showing 7 changed files with 71 additions and 40 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: 353a4098c61272b33a02ec5802fb3859fec91a0e
refs/heads/master: 26f953fd884ea4879585287917f855c63c6b2666
4 changes: 1 addition & 3 deletions trunk/drivers/usb/host/ehci-dbg.c
Original file line number Diff line number Diff line change
Expand Up @@ -754,9 +754,7 @@ show_registers (struct class_device *class_dev, char *buf)
}

if (ehci->reclaim) {
temp = scnprintf (next, size, "reclaim qh %p%s\n",
ehci->reclaim,
ehci->reclaim_ready ? " ready" : "");
temp = scnprintf (next, size, "reclaim qh %p\n", ehci->reclaim);
size -= temp;
next += temp;
}
Expand Down
73 changes: 50 additions & 23 deletions trunk/drivers/usb/host/ehci-hcd.c
Original file line number Diff line number Diff line change
Expand Up @@ -111,7 +111,7 @@ static const char hcd_name [] = "ehci_hcd";
#define EHCI_TUNE_MULT_TT 1
#define EHCI_TUNE_FLS 2 /* (small) 256 frame schedule */

#define EHCI_IAA_JIFFIES (HZ/100) /* arbitrary; ~10 msec */
#define EHCI_IAA_MSECS 10 /* arbitrary */
#define EHCI_IO_JIFFIES (HZ/10) /* io watchdog > irq_thresh */
#define EHCI_ASYNC_JIFFIES (HZ/20) /* async idle timeout */
#define EHCI_SHRINK_JIFFIES (HZ/200) /* async qh unlink delay */
Expand Down Expand Up @@ -254,6 +254,7 @@ static void ehci_quiesce (struct ehci_hcd *ehci)

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

static void end_unlink_async (struct ehci_hcd *ehci, struct pt_regs *regs);
static void ehci_work(struct ehci_hcd *ehci, struct pt_regs *regs);

#include "ehci-hub.c"
Expand All @@ -263,28 +264,39 @@ static void ehci_work(struct ehci_hcd *ehci, struct pt_regs *regs);

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

static void ehci_watchdog (unsigned long param)
static void ehci_iaa_watchdog (unsigned long param)
{
struct ehci_hcd *ehci = (struct ehci_hcd *) param;
unsigned long flags;
u32 status;

spin_lock_irqsave (&ehci->lock, flags);
WARN_ON(!ehci->reclaim);

/* lost IAA irqs wedge things badly; seen with a vt8235 */
/* lost IAA irqs wedge things badly; seen first with a vt8235 */
if (ehci->reclaim) {
u32 status = readl (&ehci->regs->status);

status = readl (&ehci->regs->status);
if (status & STS_IAA) {
ehci_vdbg (ehci, "lost IAA\n");
COUNT (ehci->stats.lost_iaa);
writel (STS_IAA, &ehci->regs->status);
ehci->reclaim_ready = 1;
end_unlink_async (ehci, NULL);
}
}

/* stop async processing after it's idled a bit */
spin_unlock_irqrestore (&ehci->lock, flags);
}

static void ehci_watchdog (unsigned long param)
{
struct ehci_hcd *ehci = (struct ehci_hcd *) param;
unsigned long flags;

spin_lock_irqsave (&ehci->lock, flags);

/* stop async processing after it's idled a bit */
if (test_bit (TIMER_ASYNC_OFF, &ehci->actions))
start_unlink_async (ehci, ehci->async);
start_unlink_async (ehci, ehci->async);

/* ehci could run by timer, without IRQs ... */
ehci_work (ehci, NULL);
Expand Down Expand Up @@ -333,8 +345,6 @@ static void ehci_port_power (struct ehci_hcd *ehci, int is_on)
static void ehci_work (struct ehci_hcd *ehci, struct pt_regs *regs)
{
timer_action_done (ehci, TIMER_IO_WATCHDOG);
if (ehci->reclaim_ready)
end_unlink_async (ehci, regs);

/* another CPU may drop ehci->lock during a schedule scan while
* it reports urb completions. this flag guards against bogus
Expand Down Expand Up @@ -369,6 +379,7 @@ static void ehci_stop (struct usb_hcd *hcd)

/* no more interrupts ... */
del_timer_sync (&ehci->watchdog);
del_timer_sync (&ehci->iaa_watchdog);

spin_lock_irq(&ehci->lock);
if (HC_IS_RUNNING (hcd->state))
Expand Down Expand Up @@ -415,6 +426,10 @@ static int ehci_init(struct usb_hcd *hcd)
ehci->watchdog.function = ehci_watchdog;
ehci->watchdog.data = (unsigned long) ehci;

init_timer(&ehci->iaa_watchdog);
ehci->iaa_watchdog.function = ehci_iaa_watchdog;
ehci->iaa_watchdog.data = (unsigned long) ehci;

/*
* hw default: 1K periodic list heads, one per frame.
* periodic_size can shrink by USBCMD update if hcc_params allows.
Expand All @@ -431,7 +446,6 @@ static int ehci_init(struct usb_hcd *hcd)
ehci->i_thresh = 2 + HCC_ISOC_THRES(hcc_params);

ehci->reclaim = NULL;
ehci->reclaim_ready = 0;
ehci->next_uframe = -1;

/*
Expand Down Expand Up @@ -605,7 +619,7 @@ static irqreturn_t ehci_irq (struct usb_hcd *hcd, struct pt_regs *regs)
/* complete the unlinking of some qh [4.15.2.3] */
if (status & STS_IAA) {
COUNT (ehci->stats.reclaim);
ehci->reclaim_ready = 1;
end_unlink_async (ehci, regs);
bh = 1;
}

Expand Down Expand Up @@ -709,10 +723,14 @@ static int ehci_urb_enqueue (

static void unlink_async (struct ehci_hcd *ehci, struct ehci_qh *qh)
{
/* if we need to use IAA and it's busy, defer */
if (qh->qh_state == QH_STATE_LINKED
&& ehci->reclaim
&& HC_IS_RUNNING (ehci_to_hcd(ehci)->state)) {
// BUG_ON(qh->qh_state != QH_STATE_LINKED);

/* failfast */
if (!HC_IS_RUNNING (ehci_to_hcd(ehci)->state))
end_unlink_async (ehci, NULL);

/* defer till later if busy */
else if (ehci->reclaim) {
struct ehci_qh *last;

for (last = ehci->reclaim;
Expand All @@ -722,12 +740,8 @@ static void unlink_async (struct ehci_hcd *ehci, struct ehci_qh *qh)
qh->qh_state = QH_STATE_UNLINK_WAIT;
last->reclaim = qh;

/* bypass IAA if the hc can't care */
} else if (!HC_IS_RUNNING (ehci_to_hcd(ehci)->state) && ehci->reclaim)
end_unlink_async (ehci, NULL);

/* something else might have unlinked the qh by now */
if (qh->qh_state == QH_STATE_LINKED)
/* start IAA cycle */
} else
start_unlink_async (ehci, qh);
}

Expand All @@ -749,7 +763,19 @@ static int ehci_urb_dequeue (struct usb_hcd *hcd, struct urb *urb)
qh = (struct ehci_qh *) urb->hcpriv;
if (!qh)
break;
unlink_async (ehci, qh);
switch (qh->qh_state) {
case QH_STATE_LINKED:
case QH_STATE_COMPLETING:
unlink_async (ehci, qh);
break;
case QH_STATE_UNLINK:
case QH_STATE_UNLINK_WAIT:
/* already started */
break;
case QH_STATE_IDLE:
WARN_ON(1);
break;
}
break;

case PIPE_INTERRUPT:
Expand Down Expand Up @@ -841,6 +867,7 @@ ehci_endpoint_disable (struct usb_hcd *hcd, struct usb_host_endpoint *ep)
unlink_async (ehci, qh);
/* FALL THROUGH */
case QH_STATE_UNLINK: /* wait for hw to finish? */
case QH_STATE_UNLINK_WAIT:
idle_timeout:
spin_unlock_irqrestore (&ehci->lock, flags);
schedule_timeout_uninterruptible(1);
Expand Down
2 changes: 1 addition & 1 deletion trunk/drivers/usb/host/ehci-hub.c
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ static int ehci_bus_suspend (struct usb_hcd *hcd)
}
ehci->command = readl (&ehci->regs->command);
if (ehci->reclaim)
ehci->reclaim_ready = 1;
end_unlink_async (ehci, NULL);
ehci_work(ehci, NULL);

/* suspend any active/unsuspended ports, maybe allow wakeup */
Expand Down
2 changes: 1 addition & 1 deletion trunk/drivers/usb/host/ehci-pci.c
Original file line number Diff line number Diff line change
Expand Up @@ -303,7 +303,7 @@ static int ehci_pci_resume(struct usb_hcd *hcd)
/* emptying the schedule aborts any urbs */
spin_lock_irq(&ehci->lock);
if (ehci->reclaim)
ehci->reclaim_ready = 1;
end_unlink_async (ehci, NULL);
ehci_work(ehci, NULL);
spin_unlock_irq(&ehci->lock);

Expand Down
6 changes: 2 additions & 4 deletions trunk/drivers/usb/host/ehci-q.c
Original file line number Diff line number Diff line change
Expand Up @@ -967,7 +967,7 @@ static void end_unlink_async (struct ehci_hcd *ehci, struct pt_regs *regs)
struct ehci_qh *qh = ehci->reclaim;
struct ehci_qh *next;

timer_action_done (ehci, TIMER_IAA_WATCHDOG);
iaa_watchdog_done (ehci);

// qh->hw_next = cpu_to_le32 (qh->qh_dma);
qh->qh_state = QH_STATE_IDLE;
Expand All @@ -977,7 +977,6 @@ static void end_unlink_async (struct ehci_hcd *ehci, struct pt_regs *regs)
/* other unlink(s) may be pending (in QH_STATE_UNLINK_WAIT) */
next = qh->reclaim;
ehci->reclaim = next;
ehci->reclaim_ready = 0;
qh->reclaim = NULL;

qh_completions (ehci, qh, regs);
Expand Down Expand Up @@ -1052,11 +1051,10 @@ static void start_unlink_async (struct ehci_hcd *ehci, struct ehci_qh *qh)
return;
}

ehci->reclaim_ready = 0;
cmd |= CMD_IAAD;
writel (cmd, &ehci->regs->command);
(void) readl (&ehci->regs->command);
timer_action (ehci, TIMER_IAA_WATCHDOG);
iaa_watchdog_start (ehci);
}

/*-------------------------------------------------------------------------*/
Expand Down
22 changes: 15 additions & 7 deletions trunk/drivers/usb/host/ehci.h
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,6 @@ struct ehci_hcd { /* one per controller */
/* async schedule support */
struct ehci_qh *async;
struct ehci_qh *reclaim;
unsigned reclaim_ready : 1;
unsigned scanning : 1;

/* periodic schedule support */
Expand All @@ -81,6 +80,7 @@ struct ehci_hcd { /* one per controller */
struct dma_pool *itd_pool; /* itd per iso urb */
struct dma_pool *sitd_pool; /* sitd per split iso urb */

struct timer_list iaa_watchdog;
struct timer_list watchdog;
unsigned long actions;
unsigned stamp;
Expand Down Expand Up @@ -114,9 +114,21 @@ static inline struct usb_hcd *ehci_to_hcd (struct ehci_hcd *ehci)
}


static inline void
iaa_watchdog_start (struct ehci_hcd *ehci)
{
WARN_ON(timer_pending(&ehci->iaa_watchdog));
mod_timer (&ehci->iaa_watchdog,
jiffies + msecs_to_jiffies(EHCI_IAA_MSECS));
}

static inline void iaa_watchdog_done (struct ehci_hcd *ehci)
{
del_timer (&ehci->iaa_watchdog);
}

enum ehci_timer_action {
TIMER_IO_WATCHDOG,
TIMER_IAA_WATCHDOG,
TIMER_ASYNC_SHRINK,
TIMER_ASYNC_OFF,
};
Expand All @@ -134,9 +146,6 @@ timer_action (struct ehci_hcd *ehci, enum ehci_timer_action action)
unsigned long t;

switch (action) {
case TIMER_IAA_WATCHDOG:
t = EHCI_IAA_JIFFIES;
break;
case TIMER_IO_WATCHDOG:
t = EHCI_IO_JIFFIES;
break;
Expand All @@ -153,8 +162,7 @@ timer_action (struct ehci_hcd *ehci, enum ehci_timer_action action)
// async queue SHRINK often precedes IAA. while it's ready
// to go OFF neither can matter, and afterwards the IO
// watchdog stops unless there's still periodic traffic.
if (action != TIMER_IAA_WATCHDOG
&& t > ehci->watchdog.expires
if (time_before_eq(t, ehci->watchdog.expires)
&& timer_pending (&ehci->watchdog))
return;
mod_timer (&ehci->watchdog, t);
Expand Down

0 comments on commit 25f563e

Please sign in to comment.