aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/usb/host/isp1760-hcd.c
diff options
context:
space:
mode:
authorArvid Brodin <arvid.brodin@enea.com>2011-08-21 02:29:26 -0400
committerGreg Kroah-Hartman <gregkh@suse.de>2011-08-22 18:32:45 -0400
commit6d50c60e6d169b04b0cf0b267eb14ab0839f9540 (patch)
treed79f632ef413323a0bbec04cbe0f72863c89ca10 /drivers/usb/host/isp1760-hcd.c
parent0ba7905e030a409fd0bb92cb965e915c0cb01030 (diff)
usb/isp1760: Use polling instead of SOF interrupts to fix Errata 2
Errata 2 for the isp1760 explains that the chip sometimes does not issue interrupts when an ATL (bulk or control) transfer is completed. There are several issues with the current work-around (SOF interrupts) for this: 1) It seems the chip sometimes does not even set the done bit for a completed transfer, in which case SOF interrupts does not solve the problem since we still check the done map to find out which transfer descriptors to handle. 2) The above point seems to happen only when ATL and SOF interrupts are enabled at the same time. However, disabling ATL interrupts increases the latency between transfer completion and handling. This is very noticeable in the testusb suite, which take several minutes more to run with ATL interrupts disabled. This patch removes the code to switch on SOF interrupts, and instead use a kernel timer to periodically check for "old" descriptors that have their VALID and ACTIVE flags unset, indicating completion, thus avoiding the dependency on the chip's done map (and SOF interrupts) to find transfers affected by this HW bug. [bigeasy@linutronix: 80 lines limit] Signed-off-by: Arvid Brodin <arvid.brodin@enea.com> 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.c138
1 files changed, 94 insertions, 44 deletions
diff --git a/drivers/usb/host/isp1760-hcd.c b/drivers/usb/host/isp1760-hcd.c
index e399e235f656..14c9238a5017 100644
--- a/drivers/usb/host/isp1760-hcd.c
+++ b/drivers/usb/host/isp1760-hcd.c
@@ -21,6 +21,7 @@
21#include <linux/uaccess.h> 21#include <linux/uaccess.h>
22#include <linux/io.h> 22#include <linux/io.h>
23#include <linux/mm.h> 23#include <linux/mm.h>
24#include <linux/timer.h>
24#include <asm/unaligned.h> 25#include <asm/unaligned.h>
25#include <asm/cacheflush.h> 26#include <asm/cacheflush.h>
26 27
@@ -39,7 +40,6 @@ struct isp1760_hcd {
39 int int_done_map; 40 int int_done_map;
40 struct memory_chunk memory_pool[BLOCKS]; 41 struct memory_chunk memory_pool[BLOCKS];
41 struct list_head controlqhs, bulkqhs, interruptqhs; 42 struct list_head controlqhs, bulkqhs, interruptqhs;
42 int active_ptds;
43 43
44 /* periodic schedule support */ 44 /* periodic schedule support */
45#define DEFAULT_I_TDPS 1024 45#define DEFAULT_I_TDPS 1024
@@ -489,10 +489,6 @@ static int isp1760_hc_setup(struct usb_hcd *hcd)
489 16 : 32, (priv->devflags & ISP1760_FLAG_ANALOG_OC) ? 489 16 : 32, (priv->devflags & ISP1760_FLAG_ANALOG_OC) ?
490 "analog" : "digital"); 490 "analog" : "digital");
491 491
492 /* This is weird: at the first plug-in of a device there seems to be
493 one packet queued that never gets returned? */
494 priv->active_ptds = -1;
495
496 /* ATL reset */ 492 /* ATL reset */
497 reg_write32(hcd->regs, HC_HW_MODE_CTRL, hwmode | ALL_ATX_RESET); 493 reg_write32(hcd->regs, HC_HW_MODE_CTRL, hwmode | ALL_ATX_RESET);
498 mdelay(10); 494 mdelay(10);
@@ -741,8 +737,8 @@ static void start_bus_transfer(struct usb_hcd *hcd, u32 ptd_offset, int slot,
741 qh->slot = slot; 737 qh->slot = slot;
742 qtd->status = QTD_XFER_STARTED; /* Set this before writing ptd, since 738 qtd->status = QTD_XFER_STARTED; /* Set this before writing ptd, since
743 interrupt routine may preempt and expects this value. */ 739 interrupt routine may preempt and expects this value. */
740 slots[slot].timestamp = jiffies;
744 ptd_write(hcd->regs, ptd_offset, slot, ptd); 741 ptd_write(hcd->regs, ptd_offset, slot, ptd);
745 priv->active_ptds++;
746 742
747 /* Make sure done map has not triggered from some unlinked transfer */ 743 /* Make sure done map has not triggered from some unlinked transfer */
748 if (ptd_offset == ATL_PTD_OFFSET) { 744 if (ptd_offset == ATL_PTD_OFFSET) {
@@ -1091,11 +1087,9 @@ static int check_atl_transfer(struct usb_hcd *hcd, struct ptd *ptd,
1091 return PTD_STATE_QTD_DONE; 1087 return PTD_STATE_QTD_DONE;
1092} 1088}
1093 1089
1094static irqreturn_t isp1760_irq(struct usb_hcd *hcd) 1090static void handle_done_ptds(struct usb_hcd *hcd)
1095{ 1091{
1096 struct isp1760_hcd *priv = hcd_to_priv(hcd); 1092 struct isp1760_hcd *priv = hcd_to_priv(hcd);
1097 u32 imask;
1098 irqreturn_t irqret = IRQ_NONE;
1099 struct ptd ptd; 1093 struct ptd ptd;
1100 struct isp1760_qh *qh; 1094 struct isp1760_qh *qh;
1101 int slot; 1095 int slot;
@@ -1104,27 +1098,14 @@ static irqreturn_t isp1760_irq(struct usb_hcd *hcd)
1104 u32 ptd_offset; 1098 u32 ptd_offset;
1105 struct isp1760_qtd *qtd; 1099 struct isp1760_qtd *qtd;
1106 int modified; 1100 int modified;
1107 static int last_active_ptds; 1101 int skip_map;
1108 int int_skip_map, atl_skip_map;
1109
1110 spin_lock(&priv->lock);
1111
1112 if (!(hcd->state & HC_STATE_RUNNING))
1113 goto leave;
1114
1115 imask = reg_read32(hcd->regs, HC_INTERRUPT_REG);
1116 if (unlikely(!imask))
1117 goto leave;
1118 reg_write32(hcd->regs, HC_INTERRUPT_REG, imask); /* Clear */
1119 1102
1120 int_skip_map = reg_read32(hcd->regs, HC_INT_PTD_SKIPMAP_REG); 1103 skip_map = reg_read32(hcd->regs, HC_INT_PTD_SKIPMAP_REG);
1121 atl_skip_map = reg_read32(hcd->regs, HC_ATL_PTD_SKIPMAP_REG); 1104 priv->int_done_map &= ~skip_map;
1122 priv->int_done_map |= reg_read32(hcd->regs, HC_INT_PTD_DONEMAP_REG); 1105 skip_map = reg_read32(hcd->regs, HC_ATL_PTD_SKIPMAP_REG);
1123 priv->atl_done_map |= reg_read32(hcd->regs, HC_ATL_PTD_DONEMAP_REG); 1106 priv->atl_done_map &= ~skip_map;
1124 priv->int_done_map &= ~int_skip_map;
1125 priv->atl_done_map &= ~atl_skip_map;
1126 1107
1127 modified = priv->int_done_map | priv->atl_done_map; 1108 modified = priv->int_done_map || priv->atl_done_map;
1128 1109
1129 while (priv->int_done_map || priv->atl_done_map) { 1110 while (priv->int_done_map || priv->atl_done_map) {
1130 if (priv->int_done_map) { 1111 if (priv->int_done_map) {
@@ -1163,7 +1144,6 @@ static irqreturn_t isp1760_irq(struct usb_hcd *hcd)
1163 slots[slot].qtd = NULL; 1144 slots[slot].qtd = NULL;
1164 qh = slots[slot].qh; 1145 qh = slots[slot].qh;
1165 slots[slot].qh = NULL; 1146 slots[slot].qh = NULL;
1166 priv->active_ptds--;
1167 qh->slot = -1; 1147 qh->slot = -1;
1168 1148
1169 WARN_ON(qtd->status != QTD_XFER_STARTED); 1149 WARN_ON(qtd->status != QTD_XFER_STARTED);
@@ -1234,22 +1214,28 @@ static irqreturn_t isp1760_irq(struct usb_hcd *hcd)
1234 1214
1235 if (modified) 1215 if (modified)
1236 schedule_ptds(hcd); 1216 schedule_ptds(hcd);
1217}
1237 1218
1238 /* ISP1760 Errata 2 explains that interrupts may be missed (or not 1219static irqreturn_t isp1760_irq(struct usb_hcd *hcd)
1239 happen?) if two USB devices are running simultaneously. Perhaps 1220{
1240 this happens when a PTD is finished during interrupt handling; 1221 struct isp1760_hcd *priv = hcd_to_priv(hcd);
1241 enable SOF interrupts if PTDs are still scheduled when exiting this 1222 u32 imask;
1242 interrupt handler, just to be safe. */ 1223 irqreturn_t irqret = IRQ_NONE;
1243 1224
1244 if (priv->active_ptds != last_active_ptds) { 1225 spin_lock(&priv->lock);
1245 if (priv->active_ptds > 0) 1226
1246 reg_write32(hcd->regs, HC_INTERRUPT_ENABLE, 1227 if (!(hcd->state & HC_STATE_RUNNING))
1247 INTERRUPT_ENABLE_SOT_MASK); 1228 goto leave;
1248 else 1229
1249 reg_write32(hcd->regs, HC_INTERRUPT_ENABLE, 1230 imask = reg_read32(hcd->regs, HC_INTERRUPT_REG);
1250 INTERRUPT_ENABLE_MASK); 1231 if (unlikely(!imask))
1251 last_active_ptds = priv->active_ptds; 1232 goto leave;
1252 } 1233 reg_write32(hcd->regs, HC_INTERRUPT_REG, imask); /* Clear */
1234
1235 priv->int_done_map |= reg_read32(hcd->regs, HC_INT_PTD_DONEMAP_REG);
1236 priv->atl_done_map |= reg_read32(hcd->regs, HC_ATL_PTD_DONEMAP_REG);
1237
1238 handle_done_ptds(hcd);
1253 1239
1254 irqret = IRQ_HANDLED; 1240 irqret = IRQ_HANDLED;
1255leave: 1241leave:
@@ -1258,6 +1244,63 @@ leave:
1258 return irqret; 1244 return irqret;
1259} 1245}
1260 1246
1247/*
1248 * Workaround for problem described in chip errata 2:
1249 *
1250 * Sometimes interrupts are not generated when ATL (not INT?) completion occurs.
1251 * One solution suggested in the errata is to use SOF interrupts _instead_of_
1252 * ATL done interrupts (the "instead of" might be important since it seems
1253 * enabling ATL interrupts also causes the chip to sometimes - rarely - "forget"
1254 * to set the PTD's done bit in addition to not generating an interrupt!).
1255 *
1256 * So if we use SOF + ATL interrupts, we sometimes get stale PTDs since their
1257 * done bit is not being set. This is bad - it blocks the endpoint until reboot.
1258 *
1259 * If we use SOF interrupts only, we get latency between ptd completion and the
1260 * actual handling. This is very noticeable in testusb runs which takes several
1261 * minutes longer without ATL interrupts.
1262 *
1263 * A better solution is to run the code below every SLOT_CHECK_PERIOD ms. If it
1264 * finds active ATL slots which are older than SLOT_TIMEOUT ms, it checks the
1265 * slot's ACTIVE and VALID bits. If these are not set, the ptd is considered
1266 * completed and its done map bit is set.
1267 *
1268 * The values of SLOT_TIMEOUT and SLOT_CHECK_PERIOD have been arbitrarily chosen
1269 * not to cause too much lag when this HW bug occurs, while still hopefully
1270 * ensuring that the check does not falsely trigger.
1271 */
1272#define SLOT_TIMEOUT 180
1273#define SLOT_CHECK_PERIOD 200
1274static struct timer_list errata2_timer;
1275
1276void errata2_function(unsigned long data)
1277{
1278 struct usb_hcd *hcd = (struct usb_hcd *) data;
1279 struct isp1760_hcd *priv = hcd_to_priv(hcd);
1280 int slot;
1281 struct ptd ptd;
1282 unsigned long spinflags;
1283
1284 spin_lock_irqsave(&priv->lock, spinflags);
1285
1286 for (slot = 0; slot < 32; slot++)
1287 if ((priv->atl_slots[slot].qh || priv->atl_slots[slot].qtd) &&
1288 time_after(jiffies + SLOT_TIMEOUT * HZ / 1000,
1289 priv->atl_slots[slot].timestamp)) {
1290 ptd_read(hcd->regs, ATL_PTD_OFFSET, slot, &ptd);
1291 if (!FROM_DW0_VALID(ptd.dw0) &&
1292 !FROM_DW3_ACTIVE(ptd.dw3))
1293 priv->atl_done_map |= 1 << slot;
1294 }
1295
1296 handle_done_ptds(hcd);
1297
1298 spin_unlock_irqrestore(&priv->lock, spinflags);
1299
1300 errata2_timer.expires = jiffies + SLOT_CHECK_PERIOD * HZ / 1000;
1301 add_timer(&errata2_timer);
1302}
1303
1261static int isp1760_run(struct usb_hcd *hcd) 1304static int isp1760_run(struct usb_hcd *hcd)
1262{ 1305{
1263 int retval; 1306 int retval;
@@ -1303,6 +1346,12 @@ static int isp1760_run(struct usb_hcd *hcd)
1303 if (retval) 1346 if (retval)
1304 return retval; 1347 return retval;
1305 1348
1349 init_timer(&errata2_timer);
1350 errata2_timer.function = errata2_function;
1351 errata2_timer.data = (unsigned long) hcd;
1352 errata2_timer.expires = jiffies + SLOT_CHECK_PERIOD * HZ / 1000;
1353 add_timer(&errata2_timer);
1354
1306 chipid = reg_read32(hcd->regs, HC_CHIP_ID_REG); 1355 chipid = reg_read32(hcd->regs, HC_CHIP_ID_REG);
1307 dev_info(hcd->self.controller, "USB ISP %04x HW rev. %d started\n", 1356 dev_info(hcd->self.controller, "USB ISP %04x HW rev. %d started\n",
1308 chipid & 0xffff, chipid >> 16); 1357 chipid & 0xffff, chipid >> 16);
@@ -1561,7 +1610,6 @@ static void kill_transfer(struct usb_hcd *hcd, struct urb *urb,
1561 } 1610 }
1562 1611
1563 qh->slot = -1; 1612 qh->slot = -1;
1564 priv->active_ptds--;
1565} 1613}
1566 1614
1567static int isp1760_urb_dequeue(struct usb_hcd *hcd, struct urb *urb, 1615static int isp1760_urb_dequeue(struct usb_hcd *hcd, struct urb *urb,
@@ -2012,6 +2060,8 @@ static void isp1760_stop(struct usb_hcd *hcd)
2012 struct isp1760_hcd *priv = hcd_to_priv(hcd); 2060 struct isp1760_hcd *priv = hcd_to_priv(hcd);
2013 u32 temp; 2061 u32 temp;
2014 2062
2063 del_timer(&errata2_timer);
2064
2015 isp1760_hub_control(hcd, ClearPortFeature, USB_PORT_FEAT_POWER, 1, 2065 isp1760_hub_control(hcd, ClearPortFeature, USB_PORT_FEAT_POWER, 1,
2016 NULL, 0); 2066 NULL, 0);
2017 mdelay(20); 2067 mdelay(20);