2.6.35-longterm review patch.  If anyone has any objections, please let me know.

------------------
From: Sebastian Andrzej Siewior <[email protected]>

The document says:
|2.1 Problem description
|    When at least two USB devices are simultaneously running, it is observed 
that
|    sometimes the INT corresponding to one of the USB devices stops occurring. 
This may
|    be observed sometimes with USB-to-serial or USB-to-network devices.
|    The problem is not noticed when only USB mass storage devices are running.
|2.2 Implication
|    This issue is because of the clearing of the respective Done Map bit on 
reading the ATL
|    PTD Done Map register when an INT is generated by another PTD completion, 
but is not
|    found set on that read access. In this situation, the respective Done Map 
bit will remain
|    reset and no further INT will be asserted so the data transfer 
corresponding to that USB
|    device will stop.
|2.3 Workaround
|    An SOF INT can be used instead of an ATL INT with polling on Done bits. A 
time-out can
|    be implemented and if a certain Done bit is never set, verification of the 
PTD completion
|    can be done by reading PTD contents (valid bit).
|    This is a proven workaround implemented in software.

Russell King run into this with an USB-to-serial converter. This patch
implements his suggestion to enable the high frequent SOF interrupt only
at the time we have ATL packages queued. It goes even one step further
and enables the SOF interrupt only if we have more than one ATL packet
queued at the same time.

Cc: <[email protected]> # [2.6.35.x, 2.6.36.x, 2.6.37.x]
Tested-by: Russell King <[email protected]>
Signed-off-by: Sebastian Andrzej Siewior <[email protected]>
Signed-off-by: Greg Kroah-Hartman <[email protected]>
Signed-off-by: Andi Kleen <[email protected]>

---
 drivers/usb/host/isp1760-hcd.c |   22 ++++++++++++++++------
 drivers/usb/host/isp1760-hcd.h |    1 +
 2 files changed, 17 insertions(+), 6 deletions(-)

Index: linux-2.6.35.y/drivers/usb/host/isp1760-hcd.c
===================================================================
--- linux-2.6.35.y.orig/drivers/usb/host/isp1760-hcd.c  2011-01-13 
09:47:51.000000000 -0800
+++ linux-2.6.35.y/drivers/usb/host/isp1760-hcd.c       2011-03-29 
23:21:23.469065159 -0700
@@ -33,6 +33,7 @@
        struct inter_packet_info atl_ints[32];
        struct inter_packet_info int_ints[32];
        struct memory_chunk memory_pool[BLOCKS];
+       u32 atl_queued;
 
        /* periodic schedule support */
 #define        DEFAULT_I_TDPS          1024
@@ -850,6 +851,11 @@
        skip_map &= ~queue_entry;
        isp1760_writel(skip_map, hcd->regs + HC_ATL_PTD_SKIPMAP_REG);
 
+       priv->atl_queued++;
+       if (priv->atl_queued == 2)
+               isp1760_writel(INTERRUPT_ENABLE_SOT_MASK,
+                               hcd->regs + HC_INTERRUPT_ENABLE);
+
        buffstatus = isp1760_readl(hcd->regs + HC_BUFFER_STATUS_REG);
        buffstatus |= ATL_BUFFER;
        isp1760_writel(buffstatus, hcd->regs + HC_BUFFER_STATUS_REG);
@@ -991,6 +997,7 @@
                u32 dw3;
 
                status = 0;
+               priv->atl_queued--;
 
                queue_entry = __ffs(done_map);
                done_map &= ~(1 << queue_entry);
@@ -1053,11 +1060,6 @@
                         * device is not able to send data fast enough.
                         * This happens mostly on slower hardware.
                         */
-                       printk(KERN_NOTICE "Reloading ptd %p/%p... qh %p read: "
-                                       "%d of %zu done: %08x cur: %08x\n", qtd,
-                                       urb, qh, PTD_XFERRED_LENGTH(dw3),
-                                       qtd->length, done_map,
-                                       (1 << queue_entry));
 
                        /* RL counter = ERR counter */
                        dw3 &= ~(0xf << 19);
@@ -1085,6 +1087,11 @@
                        priv_write_copy(priv, (u32 *)&ptd, usb_hcd->regs +
                                        atl_regs, sizeof(ptd));
 
+                       priv->atl_queued++;
+                       if (priv->atl_queued == 2)
+                               isp1760_writel(INTERRUPT_ENABLE_SOT_MASK,
+                                   usb_hcd->regs + HC_INTERRUPT_ENABLE);
+
                        buffstatus = isp1760_readl(usb_hcd->regs +
                                        HC_BUFFER_STATUS_REG);
                        buffstatus |= ATL_BUFFER;
@@ -1190,6 +1197,9 @@
                skip_map = isp1760_readl(usb_hcd->regs +
                                HC_ATL_PTD_SKIPMAP_REG);
        }
+       if (priv->atl_queued <= 1)
+               isp1760_writel(INTERRUPT_ENABLE_MASK,
+                               usb_hcd->regs + HC_INTERRUPT_ENABLE);
 }
 
 static void do_intl_int(struct usb_hcd *usb_hcd)
@@ -1769,7 +1779,7 @@
                goto leave;
 
        isp1760_writel(imask, usb_hcd->regs + HC_INTERRUPT_REG);
-       if (imask & HC_ATL_INT)
+       if (imask & (HC_ATL_INT | HC_SOT_INT))
                do_atl_int(usb_hcd);
 
        if (imask & HC_INTL_INT)
Index: linux-2.6.35.y/drivers/usb/host/isp1760-hcd.h
===================================================================
--- linux-2.6.35.y.orig/drivers/usb/host/isp1760-hcd.h  2011-01-13 
09:47:51.000000000 -0800
+++ linux-2.6.35.y/drivers/usb/host/isp1760-hcd.h       2011-03-29 
23:21:23.480064878 -0700
@@ -69,6 +69,7 @@
 
 #define HC_INTERRUPT_ENABLE    0x314
 #define INTERRUPT_ENABLE_MASK  (HC_INTL_INT | HC_ATL_INT | HC_EOT_INT)
+#define INTERRUPT_ENABLE_SOT_MASK      (HC_INTL_INT | HC_SOT_INT | HC_EOT_INT)
 
 #define HC_ISO_INT             (1 << 9)
 #define HC_ATL_INT             (1 << 8)

_______________________________________________
stable mailing list
[email protected]
http://linux.kernel.org/mailman/listinfo/stable

Reply via email to