Commit 6d10b0c3 authored by David Brownell's avatar David Brownell Committed by Greg Kroah-Hartman

[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.
parent bc32f3fb
...@@ -114,20 +114,29 @@ static inline void dbg_hcc_params (struct ehci_hcd *ehci, char *label) {} ...@@ -114,20 +114,29 @@ static inline void dbg_hcc_params (struct ehci_hcd *ehci, char *label) {}
#ifdef DEBUG #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__)) static void __attribute__((__unused__))
dbg_qh (char *label, struct ehci_hcd *ehci, struct ehci_qh *qh) 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, qh->hw_next, qh->hw_info1, qh->hw_info2,
qh->hw_current, qh->hw_qtd_next); qh->hw_current);
dbg (" alt+nak+t= %x, token= %x, page0= %x, page1= %x", dbg_qtd ("overlay", ehci, (struct ehci_qtd *) &qh->hw_qtd_next);
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]);
}
} }
static int __attribute__((__unused__)) static int __attribute__((__unused__))
...@@ -284,8 +293,7 @@ static inline char token_mark (u32 token) ...@@ -284,8 +293,7 @@ static inline char token_mark (u32 token)
return '*'; return '*';
if (token & QTD_STS_HALT) if (token & QTD_STS_HALT)
return '-'; return '-';
if (QTD_PID (token) != 1 /* not IN: OUT or SETUP */ if (!IS_SHORT_READ (token))
|| QTD_LENGTH (token) == 0)
return ' '; return ' ';
/* tries to advance through hw_alt_next */ /* tries to advance through hw_alt_next */
return '/'; return '/';
...@@ -307,11 +315,14 @@ static void qh_lines ( ...@@ -307,11 +315,14 @@ static void qh_lines (
char *next = *nextp; char *next = *nextp;
char mark; 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 (mark == '/') { /* qh_alt_next controls qh advance? */
if ((qh->hw_alt_next & QTD_MASK) == ehci->async->hw_alt_next) if ((qh->hw_alt_next & QTD_MASK) == ehci->async->hw_alt_next)
mark = '#'; /* blocked */ 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 */ mark = '.'; /* use hw_qtd_next */
/* else alt_next points to some other qtd */ /* else alt_next points to some other qtd */
} }
...@@ -324,7 +335,7 @@ static void qh_lines ( ...@@ -324,7 +335,7 @@ static void qh_lines (
(scratch >> 8) & 0x000f, (scratch >> 8) & 0x000f,
scratch, cpu_to_le32p (&qh->hw_info2), scratch, cpu_to_le32p (&qh->hw_info2),
cpu_to_le32p (&qh->hw_token), mark, 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", ? "data0" : "data1",
(cpu_to_le32p (&qh->hw_alt_next) >> 1) & 0x0f); (cpu_to_le32p (&qh->hw_alt_next) >> 1) & 0x0f);
size -= temp; size -= temp;
...@@ -390,6 +401,8 @@ show_async (struct device *dev, char *buf) ...@@ -390,6 +401,8 @@ show_async (struct device *dev, char *buf)
char *next; char *next;
struct ehci_qh *qh; struct ehci_qh *qh;
*buf = 0;
pdev = container_of (dev, struct pci_dev, dev); pdev = container_of (dev, struct pci_dev, dev);
ehci = container_of (pci_get_drvdata (pdev), struct ehci_hcd, hcd); ehci = container_of (pci_get_drvdata (pdev), struct ehci_hcd, hcd);
next = buf; next = buf;
...@@ -412,7 +425,7 @@ show_async (struct device *dev, char *buf) ...@@ -412,7 +425,7 @@ show_async (struct device *dev, char *buf)
} }
spin_unlock_irqrestore (&ehci->lock, flags); spin_unlock_irqrestore (&ehci->lock, flags);
return PAGE_SIZE - size; return strlen (buf);
} }
static DEVICE_ATTR (async, S_IRUGO, show_async, NULL); static DEVICE_ATTR (async, S_IRUGO, show_async, NULL);
...@@ -548,7 +561,8 @@ show_registers (struct device *dev, char *buf) ...@@ -548,7 +561,8 @@ show_registers (struct device *dev, char *buf)
/* Capability Registers */ /* Capability Registers */
i = readw (&ehci->caps->hci_version); i = readw (&ehci->caps->hci_version);
temp = snprintf (next, size, 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); i >> 8, i & 0x0ff, ehci->hcd.state);
size -= temp; size -= temp;
next += temp; next += temp;
......
...@@ -39,13 +39,10 @@ ...@@ -39,13 +39,10 @@
#include <linux/interrupt.h> #include <linux/interrupt.h>
#include <linux/reboot.h> #include <linux/reboot.h>
#include <linux/usb.h> #include <linux/usb.h>
#include <linux/moduleparam.h>
#include <linux/version.h> #include <linux/version.h>
#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,32)
#include "../hcd.h"
#else
#include "../core/hcd.h" #include "../core/hcd.h"
#endif
#include <asm/byteorder.h> #include <asm/byteorder.h>
#include <asm/io.h> #include <asm/io.h>
...@@ -94,11 +91,11 @@ ...@@ -94,11 +91,11 @@
* 2001-June Works with usb-storage and NEC EHCI on 2.4 * 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_AUTHOR "David Brownell"
#define DRIVER_DESC "USB 2.0 'Enhanced' Host Controller (EHCI) Driver" #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 // #define EHCI_VERBOSE_DEBUG
...@@ -123,7 +120,7 @@ static const char hcd_name [] = "ehci-hcd"; ...@@ -123,7 +120,7 @@ static const char hcd_name [] = "ehci-hcd";
/* Initial IRQ latency: lower than default */ /* Initial IRQ latency: lower than default */
static int log2_irq_thresh = 0; // 0 to 6 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"); MODULE_PARM_DESC (log2_irq_thresh, "log2 IRQ latency, 1-64 microframes");
#define INTR_MASK (STS_IAA | STS_FATAL | STS_ERR | STS_INT) #define INTR_MASK (STS_IAA | STS_FATAL | STS_ERR | STS_INT)
...@@ -1020,7 +1017,8 @@ static int __init init (void) ...@@ -1020,7 +1017,8 @@ static int __init init (void)
if (usb_disabled()) if (usb_disabled())
return -ENODEV; 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_qh), sizeof (struct ehci_qtd),
sizeof (struct ehci_itd), sizeof (struct ehci_sitd)); sizeof (struct ehci_itd), sizeof (struct ehci_sitd));
......
...@@ -88,7 +88,6 @@ qtd_fill (struct ehci_qtd *qtd, dma_addr_t buf, size_t len, ...@@ -88,7 +88,6 @@ qtd_fill (struct ehci_qtd *qtd, dma_addr_t buf, size_t len,
static inline void static inline void
qh_update (struct ehci_hcd *ehci, struct ehci_qh *qh, struct ehci_qtd *qtd) 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_qtd_next = QTD_NEXT (qtd->qtd_dma);
qh->hw_alt_next = EHCI_LIST_END; qh->hw_alt_next = EHCI_LIST_END;
...@@ -99,8 +98,6 @@ qh_update (struct ehci_hcd *ehci, struct ehci_qh *qh, struct ehci_qtd *qtd) ...@@ -99,8 +98,6 @@ qh_update (struct ehci_hcd *ehci, struct ehci_qh *qh, struct ehci_qtd *qtd)
/*-------------------------------------------------------------------------*/ /*-------------------------------------------------------------------------*/
#define IS_SHORT_READ(token) (QTD_LENGTH (token) != 0 && QTD_PID (token) == 1)
static void qtd_copy_status ( static void qtd_copy_status (
struct ehci_hcd *ehci, struct ehci_hcd *ehci,
struct urb *urb, struct urb *urb,
...@@ -279,16 +276,15 @@ qh_completions (struct ehci_hcd *ehci, struct ehci_qh *qh, struct pt_regs *regs) ...@@ -279,16 +276,15 @@ qh_completions (struct ehci_hcd *ehci, struct ehci_qh *qh, struct pt_regs *regs)
/* hardware copies qtd out of qh overlay */ /* hardware copies qtd out of qh overlay */
rmb (); rmb ();
token = le32_to_cpu (qtd->hw_token); 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 */ /* always clean up qtds the hc de-activated */
if ((token & QTD_STS_ACTIVE) == 0) { if ((token & QTD_STS_ACTIVE) == 0) {
/* magic dummy for short reads; won't advance */ if ((token & QTD_STS_HALT) != 0) {
if (IS_SHORT_READ (token) stopped = 1;
&& !(token & QTD_STS_HALT)
/* magic dummy for some short reads; qh won't advance */
} else if (IS_SHORT_READ (token)
&& (qh->hw_alt_next & QTD_MASK) && (qh->hw_alt_next & QTD_MASK)
== ehci->async->hw_alt_next) { == ehci->async->hw_alt_next) {
stopped = 1; stopped = 1;
...@@ -296,10 +292,13 @@ qh_completions (struct ehci_hcd *ehci, struct ehci_qh *qh, struct pt_regs *regs) ...@@ -296,10 +292,13 @@ qh_completions (struct ehci_hcd *ehci, struct ehci_qh *qh, struct pt_regs *regs)
} }
/* stop scanning when we reach qtds the hc is using */ /* 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; break;
} else { } else {
stopped = 1;
/* ignore active urbs unless some previous qtd /* ignore active urbs unless some previous qtd
* for the urb faulted (including short read) or * for the urb faulted (including short read) or
* its urb was canceled. we may patch qh or qtds. * its urb was canceled. we may patch qh or qtds.
...@@ -358,7 +357,9 @@ qh_completions (struct ehci_hcd *ehci, struct ehci_qh *qh, struct pt_regs *regs) ...@@ -358,7 +357,9 @@ qh_completions (struct ehci_hcd *ehci, struct ehci_qh *qh, struct pt_regs *regs)
qh->qh_state = state; qh->qh_state = state;
/* update qh after fault cleanup */ /* 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, qh_update (ehci, qh,
list_empty (&qh->qtd_list) list_empty (&qh->qtd_list)
? qh->dummy ? qh->dummy
...@@ -788,11 +789,6 @@ static struct ehci_qh *qh_append_tds ( ...@@ -788,11 +789,6 @@ static struct ehci_qh *qh_append_tds (
} }
} }
/* 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 */ /* usb_clear_halt() means qh data toggle gets reset */
if (unlikely (!usb_gettoggle (urb->dev, if (unlikely (!usb_gettoggle (urb->dev,
(epnum & 0x0f), !(epnum & 0x10))) (epnum & 0x0f), !(epnum & 0x10)))
......
...@@ -290,7 +290,10 @@ struct ehci_qtd { ...@@ -290,7 +290,10 @@ struct ehci_qtd {
size_t length; /* length of buffer */ size_t length; /* length of buffer */
} __attribute__ ((aligned (32))); } __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)
/*-------------------------------------------------------------------------*/ /*-------------------------------------------------------------------------*/
......
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