aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/usb/host/isp1760-hcd.c
diff options
context:
space:
mode:
authorSebastian Andrzej Siewior <bigeasy@linutronix.de>2011-02-08 15:07:40 -0500
committerGreg Kroah-Hartman <gregkh@suse.de>2011-02-17 13:47:55 -0500
commitb14e840d04dba211fbdc930247e379085623eacd (patch)
tree8b57a59f211b3304fe8e10a7c8a23791b11771d3 /drivers/usb/host/isp1760-hcd.c
parent4f9e6c64d1fc4668f3153f456b41cf40b30c730f (diff)
USB: isp1760: Implement solution for erratum 2
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: <stable@kernel.org> # [2.6.35.x, 2.6.36.x, 2.6.37.x] Tested-by: Russell King <rmk+kernel@arm.linux.org.uk> Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de> Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
Diffstat (limited to 'drivers/usb/host/isp1760-hcd.c')
-rw-r--r--drivers/usb/host/isp1760-hcd.c22
1 files changed, 16 insertions, 6 deletions
diff --git a/drivers/usb/host/isp1760-hcd.c b/drivers/usb/host/isp1760-hcd.c
index bdba8c5d844a..c470cc83dbb0 100644
--- a/drivers/usb/host/isp1760-hcd.c
+++ b/drivers/usb/host/isp1760-hcd.c
@@ -33,6 +33,7 @@ struct isp1760_hcd {
33 struct inter_packet_info atl_ints[32]; 33 struct inter_packet_info atl_ints[32];
34 struct inter_packet_info int_ints[32]; 34 struct inter_packet_info int_ints[32];
35 struct memory_chunk memory_pool[BLOCKS]; 35 struct memory_chunk memory_pool[BLOCKS];
36 u32 atl_queued;
36 37
37 /* periodic schedule support */ 38 /* periodic schedule support */
38#define DEFAULT_I_TDPS 1024 39#define DEFAULT_I_TDPS 1024
@@ -850,6 +851,11 @@ static void enqueue_an_ATL_packet(struct usb_hcd *hcd, struct isp1760_qh *qh,
850 skip_map &= ~queue_entry; 851 skip_map &= ~queue_entry;
851 isp1760_writel(skip_map, hcd->regs + HC_ATL_PTD_SKIPMAP_REG); 852 isp1760_writel(skip_map, hcd->regs + HC_ATL_PTD_SKIPMAP_REG);
852 853
854 priv->atl_queued++;
855 if (priv->atl_queued == 2)
856 isp1760_writel(INTERRUPT_ENABLE_SOT_MASK,
857 hcd->regs + HC_INTERRUPT_ENABLE);
858
853 buffstatus = isp1760_readl(hcd->regs + HC_BUFFER_STATUS_REG); 859 buffstatus = isp1760_readl(hcd->regs + HC_BUFFER_STATUS_REG);
854 buffstatus |= ATL_BUFFER; 860 buffstatus |= ATL_BUFFER;
855 isp1760_writel(buffstatus, hcd->regs + HC_BUFFER_STATUS_REG); 861 isp1760_writel(buffstatus, hcd->regs + HC_BUFFER_STATUS_REG);
@@ -992,6 +998,7 @@ static void do_atl_int(struct usb_hcd *usb_hcd)
992 u32 dw3; 998 u32 dw3;
993 999
994 status = 0; 1000 status = 0;
1001 priv->atl_queued--;
995 1002
996 queue_entry = __ffs(done_map); 1003 queue_entry = __ffs(done_map);
997 done_map &= ~(1 << queue_entry); 1004 done_map &= ~(1 << queue_entry);
@@ -1054,11 +1061,6 @@ static void do_atl_int(struct usb_hcd *usb_hcd)
1054 * device is not able to send data fast enough. 1061 * device is not able to send data fast enough.
1055 * This happens mostly on slower hardware. 1062 * This happens mostly on slower hardware.
1056 */ 1063 */
1057 printk(KERN_NOTICE "Reloading ptd %p/%p... qh %p read: "
1058 "%d of %zu done: %08x cur: %08x\n", qtd,
1059 urb, qh, PTD_XFERRED_LENGTH(dw3),
1060 qtd->length, done_map,
1061 (1 << queue_entry));
1062 1064
1063 /* RL counter = ERR counter */ 1065 /* RL counter = ERR counter */
1064 dw3 &= ~(0xf << 19); 1066 dw3 &= ~(0xf << 19);
@@ -1086,6 +1088,11 @@ static void do_atl_int(struct usb_hcd *usb_hcd)
1086 priv_write_copy(priv, (u32 *)&ptd, usb_hcd->regs + 1088 priv_write_copy(priv, (u32 *)&ptd, usb_hcd->regs +
1087 atl_regs, sizeof(ptd)); 1089 atl_regs, sizeof(ptd));
1088 1090
1091 priv->atl_queued++;
1092 if (priv->atl_queued == 2)
1093 isp1760_writel(INTERRUPT_ENABLE_SOT_MASK,
1094 usb_hcd->regs + HC_INTERRUPT_ENABLE);
1095
1089 buffstatus = isp1760_readl(usb_hcd->regs + 1096 buffstatus = isp1760_readl(usb_hcd->regs +
1090 HC_BUFFER_STATUS_REG); 1097 HC_BUFFER_STATUS_REG);
1091 buffstatus |= ATL_BUFFER; 1098 buffstatus |= ATL_BUFFER;
@@ -1191,6 +1198,9 @@ static void do_atl_int(struct usb_hcd *usb_hcd)
1191 skip_map = isp1760_readl(usb_hcd->regs + 1198 skip_map = isp1760_readl(usb_hcd->regs +
1192 HC_ATL_PTD_SKIPMAP_REG); 1199 HC_ATL_PTD_SKIPMAP_REG);
1193 } 1200 }
1201 if (priv->atl_queued <= 1)
1202 isp1760_writel(INTERRUPT_ENABLE_MASK,
1203 usb_hcd->regs + HC_INTERRUPT_ENABLE);
1194} 1204}
1195 1205
1196static void do_intl_int(struct usb_hcd *usb_hcd) 1206static void do_intl_int(struct usb_hcd *usb_hcd)
@@ -1770,7 +1780,7 @@ static irqreturn_t isp1760_irq(struct usb_hcd *usb_hcd)
1770 goto leave; 1780 goto leave;
1771 1781
1772 isp1760_writel(imask, usb_hcd->regs + HC_INTERRUPT_REG); 1782 isp1760_writel(imask, usb_hcd->regs + HC_INTERRUPT_REG);
1773 if (imask & HC_ATL_INT) 1783 if (imask & (HC_ATL_INT | HC_SOT_INT))
1774 do_atl_int(usb_hcd); 1784 do_atl_int(usb_hcd);
1775 1785
1776 if (imask & HC_INTL_INT) 1786 if (imask & HC_INTL_INT)