ChangeSet 1.1305.7.3, 2003/06/13 16:41:08-07:00, david-b@pacbell.net [PATCH] USB: ehci-hcd: short reads, chip workaround, cleanup This is a minor update to the patch I posted the other day: - Updates processing for completed QTDs, fixing a regression I've been chasing for a while. - Works around a bug seen in some EHCI silicon (like NEC), which the previous problem was covering up. - Cleanup: updates the debug support a bit, removes a now-fixed FIXME comment, etc; and a version ID change. drivers/usb/host/ehci-dbg.c | 48 ++++++++++++++++++++++++++++---------------- drivers/usb/host/ehci-hcd.c | 14 +++++------- drivers/usb/host/ehci-q.c | 28 +++++++++++-------------- drivers/usb/host/ehci.h | 5 +++- 4 files changed, 53 insertions(+), 42 deletions(-) diff -Nru a/drivers/usb/host/ehci-dbg.c b/drivers/usb/host/ehci-dbg.c --- a/drivers/usb/host/ehci-dbg.c Wed Jun 18 11:16:05 2003 +++ b/drivers/usb/host/ehci-dbg.c Wed Jun 18 11:16:05 2003 @@ -115,19 +115,28 @@ #ifdef DEBUG static void __attribute__((__unused__)) +dbg_qtd (char *label, struct ehci_hcd *ehci, struct ehci_qtd *qtd) +{ + ehci_dbg (ehci, "%s td %p n%08x %08x t%08x p0=%08x\n", label, qtd, + cpu_to_le32p (&qtd->hw_next), + cpu_to_le32p (&qtd->hw_alt_next), + cpu_to_le32p (&qtd->hw_token), + cpu_to_le32p (&qtd->hw_buf [0])); + if (qtd->hw_buf [1]) + ehci_dbg (ehci, " p1=%08x p2=%08x p3=%08x p4=%08x\n", + cpu_to_le32p (&qtd->hw_buf [1]), + cpu_to_le32p (&qtd->hw_buf [2]), + cpu_to_le32p (&qtd->hw_buf [3]), + cpu_to_le32p (&qtd->hw_buf [4])); +} + +static void __attribute__((__unused__)) dbg_qh (char *label, struct ehci_hcd *ehci, struct ehci_qh *qh) { - dbg ("%s %p n%08x info1 %x info2 %x hw_curr %x qtd_next %x", label, + ehci_dbg (ehci, "%s qh %p n%08x info %x %x qtd %x\n", label, qh, qh->hw_next, qh->hw_info1, qh->hw_info2, - qh->hw_current, qh->hw_qtd_next); - dbg (" alt+nak+t= %x, token= %x, page0= %x, page1= %x", - qh->hw_alt_next, qh->hw_token, - qh->hw_buf [0], qh->hw_buf [1]); - if (qh->hw_buf [2]) { - dbg (" page2= %x, page3= %x, page4= %x", - qh->hw_buf [2], qh->hw_buf [3], - qh->hw_buf [4]); - } + qh->hw_current); + dbg_qtd ("overlay", ehci, (struct ehci_qtd *) &qh->hw_qtd_next); } static int __attribute__((__unused__)) @@ -284,8 +293,7 @@ return '*'; if (token & QTD_STS_HALT) return '-'; - if (QTD_PID (token) != 1 /* not IN: OUT or SETUP */ - || QTD_LENGTH (token) == 0) + if (!IS_SHORT_READ (token)) return ' '; /* tries to advance through hw_alt_next */ return '/'; @@ -307,11 +315,14 @@ char *next = *nextp; char mark; - mark = token_mark (qh->hw_token); + if (qh->hw_qtd_next == EHCI_LIST_END) /* NEC does this */ + mark = '@'; + else + mark = token_mark (qh->hw_token); if (mark == '/') { /* qh_alt_next controls qh advance? */ if ((qh->hw_alt_next & QTD_MASK) == ehci->async->hw_alt_next) mark = '#'; /* blocked */ - else if (qh->hw_alt_next & cpu_to_le32 (0x01)) + else if (qh->hw_alt_next == EHCI_LIST_END) mark = '.'; /* use hw_qtd_next */ /* else alt_next points to some other qtd */ } @@ -324,7 +335,7 @@ (scratch >> 8) & 0x000f, scratch, cpu_to_le32p (&qh->hw_info2), cpu_to_le32p (&qh->hw_token), mark, - (cpu_to_le32 (0x8000000) & qh->hw_token) + (__constant_cpu_to_le32 (QTD_TOGGLE) & qh->hw_token) ? "data0" : "data1", (cpu_to_le32p (&qh->hw_alt_next) >> 1) & 0x0f); size -= temp; @@ -390,6 +401,8 @@ char *next; struct ehci_qh *qh; + *buf = 0; + pdev = container_of (dev, struct pci_dev, dev); ehci = container_of (pci_get_drvdata (pdev), struct ehci_hcd, hcd); next = buf; @@ -412,7 +425,7 @@ } spin_unlock_irqrestore (&ehci->lock, flags); - return PAGE_SIZE - size; + return strlen (buf); } static DEVICE_ATTR (async, S_IRUGO, show_async, NULL); @@ -548,7 +561,8 @@ /* Capability Registers */ i = readw (&ehci->caps->hci_version); temp = snprintf (next, size, - "EHCI %x.%02x, hcd state %d (version " DRIVER_VERSION ")\n", + "%s\nEHCI %x.%02x, hcd state %d (driver " DRIVER_VERSION ")\n", + pdev->dev.name, i >> 8, i & 0x0ff, ehci->hcd.state); size -= temp; next += temp; diff -Nru a/drivers/usb/host/ehci-hcd.c b/drivers/usb/host/ehci-hcd.c --- a/drivers/usb/host/ehci-hcd.c Wed Jun 18 11:16:05 2003 +++ b/drivers/usb/host/ehci-hcd.c Wed Jun 18 11:16:05 2003 @@ -39,13 +39,10 @@ #include #include #include +#include #include -#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,32) -#include "../hcd.h" -#else #include "../core/hcd.h" -#endif #include #include @@ -94,11 +91,11 @@ * 2001-June Works with usb-storage and NEC EHCI on 2.4 */ -#define DRIVER_VERSION "2003-Jan-22" +#define DRIVER_VERSION "2003-Jun-12" #define DRIVER_AUTHOR "David Brownell" #define DRIVER_DESC "USB 2.0 'Enhanced' Host Controller (EHCI) Driver" -static const char hcd_name [] = "ehci-hcd"; +static const char hcd_name [] = "ehci_hcd"; // #define EHCI_VERBOSE_DEBUG @@ -123,7 +120,7 @@ /* Initial IRQ latency: lower than default */ static int log2_irq_thresh = 0; // 0 to 6 -MODULE_PARM (log2_irq_thresh, "i"); +module_param (log2_irq_thresh, int, S_IRUGO); MODULE_PARM_DESC (log2_irq_thresh, "log2 IRQ latency, 1-64 microframes"); #define INTR_MASK (STS_IAA | STS_FATAL | STS_ERR | STS_INT) @@ -1020,7 +1017,8 @@ if (usb_disabled()) return -ENODEV; - dbg ("block sizes: qh %Zd qtd %Zd itd %Zd sitd %Zd", + pr_debug ("%s: block sizes: qh %Zd qtd %Zd itd %Zd sitd %Zd\n", + hcd_name, sizeof (struct ehci_qh), sizeof (struct ehci_qtd), sizeof (struct ehci_itd), sizeof (struct ehci_sitd)); diff -Nru a/drivers/usb/host/ehci-q.c b/drivers/usb/host/ehci-q.c --- a/drivers/usb/host/ehci-q.c Wed Jun 18 11:16:05 2003 +++ b/drivers/usb/host/ehci-q.c Wed Jun 18 11:16:05 2003 @@ -88,7 +88,6 @@ static inline void qh_update (struct ehci_hcd *ehci, struct ehci_qh *qh, struct ehci_qtd *qtd) { - qh->hw_current = 0; qh->hw_qtd_next = QTD_NEXT (qtd->qtd_dma); qh->hw_alt_next = EHCI_LIST_END; @@ -99,8 +98,6 @@ /*-------------------------------------------------------------------------*/ -#define IS_SHORT_READ(token) (QTD_LENGTH (token) != 0 && QTD_PID (token) == 1) - static void qtd_copy_status ( struct ehci_hcd *ehci, struct urb *urb, @@ -279,16 +276,15 @@ /* hardware copies qtd out of qh overlay */ rmb (); token = le32_to_cpu (qtd->hw_token); - stopped = stopped - || (HALT_BIT & qh->hw_token) != 0 - || (ehci->hcd.state == USB_STATE_HALT); /* always clean up qtds the hc de-activated */ if ((token & QTD_STS_ACTIVE) == 0) { - /* magic dummy for short reads; won't advance */ - if (IS_SHORT_READ (token) - && !(token & QTD_STS_HALT) + if ((token & QTD_STS_HALT) != 0) { + stopped = 1; + + /* magic dummy for some short reads; qh won't advance */ + } else if (IS_SHORT_READ (token) && (qh->hw_alt_next & QTD_MASK) == ehci->async->hw_alt_next) { stopped = 1; @@ -296,10 +292,13 @@ } /* stop scanning when we reach qtds the hc is using */ - } else if (likely (!stopped)) { + } else if (likely (!stopped + || HCD_IS_RUNNING (ehci->hcd.state))) { break; } else { + stopped = 1; + /* ignore active urbs unless some previous qtd * for the urb faulted (including short read) or * its urb was canceled. we may patch qh or qtds. @@ -358,7 +357,9 @@ qh->qh_state = state; /* update qh after fault cleanup */ - if (unlikely ((HALT_BIT & qh->hw_token) != 0)) { + if (unlikely (stopped != 0) + /* some EHCI 0.95 impls will overlay dummy qtds */ + || qh->hw_qtd_next == EHCI_LIST_END) { qh_update (ehci, qh, list_empty (&qh->qtd_list) ? qh->dummy @@ -787,11 +788,6 @@ } } } - - /* FIXME: changing config or interface setting is not - * supported yet. preferred fix is for usbcore to tell - * us to clear out each endpoint's state, but... - */ /* usb_clear_halt() means qh data toggle gets reset */ if (unlikely (!usb_gettoggle (urb->dev, diff -Nru a/drivers/usb/host/ehci.h b/drivers/usb/host/ehci.h --- a/drivers/usb/host/ehci.h Wed Jun 18 11:16:05 2003 +++ b/drivers/usb/host/ehci.h Wed Jun 18 11:16:05 2003 @@ -290,7 +290,10 @@ size_t length; /* length of buffer */ } __attribute__ ((aligned (32))); -#define QTD_MASK cpu_to_le32 (~0x1f) /* mask NakCnt+T in qh->hw_alt_next */ +/* mask NakCnt+T in qh->hw_alt_next */ +#define QTD_MASK __constant_cpu_to_le32 (~0x1f) + +#define IS_SHORT_READ(token) (QTD_LENGTH (token) != 0 && QTD_PID (token) == 1) /*-------------------------------------------------------------------------*/