[PATCH] isp1760-hcd: rework usb errata poller to isr

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



The errata2 workaround is described in the Datasheet as follows:

  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.

Therefore, we should do so, and ignore the check
for the ACTIVE_BIT in DW3. Because it is possible
that this bit is still set when the issue accures.

Signed-off-by: Michael Grzeschik <m.grzeschik@xxxxxxxxxxxxxx>
---
 drivers/usb/host/isp1760-hcd.c |  110 +++++++++++++++++-----------------------
 drivers/usb/host/isp1760-hcd.h |    1 +
 2 files changed, 48 insertions(+), 63 deletions(-)

diff --git a/drivers/usb/host/isp1760-hcd.c b/drivers/usb/host/isp1760-hcd.c
index 90c57dc..4beedba 100644
--- a/drivers/usb/host/isp1760-hcd.c
+++ b/drivers/usb/host/isp1760-hcd.c
@@ -48,6 +48,8 @@ struct isp1760_hcd {
 	int			int_done_map;
 	struct memory_chunk memory_pool[BLOCKS];
 	struct list_head	qh_list[QH_END];
+	int			active_ptds;
+	int			last_active_ptds;
 
 	/* periodic schedule support */
 #define	DEFAULT_I_TDPS		1024
@@ -512,6 +514,11 @@ static int isp1760_hc_setup(struct usb_hcd *hcd)
 			   16 : 32, (priv->devflags & ISP1760_FLAG_ANALOG_OC) ?
 			   "analog" : "digital");
 
+	/* This is weird: at the first plug-in of a device there seems to be
+	   one packet queued that never gets returned? */
+	priv->active_ptds = -1;
+	priv->last_active_ptds = 0;
+
 	/* ATL reset */
 	reg_write32(hcd->regs, HC_HW_MODE_CTRL, hwmode | ALL_ATX_RESET);
 	mdelay(10);
@@ -775,6 +782,7 @@ static void start_bus_transfer(struct usb_hcd *hcd, u32 ptd_offset, int slot,
 	slots[slot].qtd = qtd;
 	slots[slot].qh = qh;
 	ptd_write(hcd->regs, ptd_offset, slot, ptd);
+	priv->active_ptds++;
 
 	if (ptd_offset == ATL_PTD_OFFSET) {
 		skip_map = reg_read32(hcd->regs, HC_ATL_PTD_SKIPMAP_REG);
@@ -1147,6 +1155,7 @@ static void handle_done_ptds(struct usb_hcd *hcd)
 		slots[slot].qtd = NULL;
 		qh = slots[slot].qh;
 		slots[slot].qh = NULL;
+		priv->active_ptds--;
 		qh->slot = -1;
 
 		WARN_ON(qtd->status != QTD_XFER_STARTED);
@@ -1228,10 +1237,13 @@ static void handle_done_ptds(struct usb_hcd *hcd)
 		schedule_ptds(hcd);
 }
 
+#define SLOT_TIMEOUT 300
 static irqreturn_t isp1760_irq(struct usb_hcd *hcd)
 {
 	struct isp1760_hcd *priv = hcd_to_priv(hcd);
 	u32 imask;
+	int slot;
+	struct ptd ptd;
 	irqreturn_t irqret = IRQ_NONE;
 
 	spin_lock(&priv->lock);
@@ -1247,71 +1259,50 @@ static irqreturn_t isp1760_irq(struct usb_hcd *hcd)
 	priv->int_done_map |= reg_read32(hcd->regs, HC_INT_PTD_DONEMAP_REG);
 	priv->atl_done_map |= reg_read32(hcd->regs, HC_ATL_PTD_DONEMAP_REG);
 
-	handle_done_ptds(hcd);
-
-	irqret = IRQ_HANDLED;
-leave:
-	spin_unlock(&priv->lock);
-
-	return irqret;
-}
-
-/*
- * Workaround for problem described in chip errata 2:
- *
- * Sometimes interrupts are not generated when ATL (not INT?) completion occurs.
- * One solution suggested in the errata is to use SOF interrupts _instead_of_
- * ATL done interrupts (the "instead of" might be important since it seems
- * enabling ATL interrupts also causes the chip to sometimes - rarely - "forget"
- * to set the PTD's done bit in addition to not generating an interrupt!).
- *
- * So if we use SOF + ATL interrupts, we sometimes get stale PTDs since their
- * done bit is not being set. This is bad - it blocks the endpoint until reboot.
- *
- * If we use SOF interrupts only, we get latency between ptd completion and the
- * actual handling. This is very noticeable in testusb runs which takes several
- * minutes longer without ATL interrupts.
- *
- * A better solution is to run the code below every SLOT_CHECK_PERIOD ms. If it
- * finds active ATL slots which are older than SLOT_TIMEOUT ms, it checks the
- * slot's ACTIVE and VALID bits. If these are not set, the ptd is considered
- * completed and its done map bit is set.
- *
- * The values of SLOT_TIMEOUT and SLOT_CHECK_PERIOD have been arbitrarily chosen
- * not to cause too much lag when this HW bug occurs, while still hopefully
- * ensuring that the check does not falsely trigger.
- */
-#define SLOT_TIMEOUT 300
-#define SLOT_CHECK_PERIOD 200
-static struct timer_list errata2_timer;
-
-void errata2_function(unsigned long data)
-{
-	struct usb_hcd *hcd = (struct usb_hcd *) data;
-	struct isp1760_hcd *priv = hcd_to_priv(hcd);
-	int slot;
-	struct ptd ptd;
-	unsigned long spinflags;
-
-	spin_lock_irqsave(&priv->lock, spinflags);
+	/* Errata2: A time-out can be implemented and if a certain Done bit is
+	   never set, verification of the PTD completion is done by reading PTD
+	   contents (valid bit). This is a proven workaround implemented in
+	   software. */
 
 	for (slot = 0; slot < 32; slot++)
 		if (priv->atl_slots[slot].qh && time_after(jiffies,
 					priv->atl_slots[slot].timestamp +
-					SLOT_TIMEOUT * HZ / 1000)) {
+					msecs_to_jiffies(SLOT_TIMEOUT)) &&
+					!(priv->atl_done_map & (1 << slot))) {
 			ptd_read(hcd->regs, ATL_PTD_OFFSET, slot, &ptd);
-			if (!FROM_DW0_VALID(ptd.dw0) &&
-					!FROM_DW3_ACTIVE(ptd.dw3))
+
+			priv->atl_slots[slot].timestamp = jiffies;
+			if (!FROM_DW0_VALID(ptd.dw0)) {
 				priv->atl_done_map |= 1 << slot;
+				dev_info(hcd->self.controller, "errata2 issue!\n");
+				continue;
+			}
 		}
 
-	if (priv->atl_done_map)
-		handle_done_ptds(hcd);
 
-	spin_unlock_irqrestore(&priv->lock, spinflags);
+	handle_done_ptds(hcd);
 
-	errata2_timer.expires = jiffies + SLOT_CHECK_PERIOD * HZ / 1000;
-	add_timer(&errata2_timer);
+	/* ISP1760 Errata 2 explains that interrupts may be missed (or not
+	   happen?) if two USB devices are running simultaneously. Perhaps
+	   this happens when a PTD is finished during interrupt handling;
+	   enable SOF interrupts if PTDs are still scheduled when exiting this
+	   interrupt handler, just to be safe. */
+
+	if (priv->active_ptds != priv->last_active_ptds) {
+		if (priv->active_ptds > 0)
+			reg_write32(hcd->regs, HC_INTERRUPT_ENABLE,
+						INTERRUPT_ENABLE_SOT_MASK);
+		else
+			reg_write32(hcd->regs, HC_INTERRUPT_ENABLE,
+						INTERRUPT_ENABLE_MASK);
+		priv->last_active_ptds = priv->active_ptds;
+	}
+
+	irqret = IRQ_HANDLED;
+leave:
+	spin_unlock(&priv->lock);
+
+	return irqret;
 }
 
 static int isp1760_run(struct usb_hcd *hcd)
@@ -1359,12 +1350,6 @@ static int isp1760_run(struct usb_hcd *hcd)
 	if (retval)
 		return retval;
 
-	init_timer(&errata2_timer);
-	errata2_timer.function = errata2_function;
-	errata2_timer.data = (unsigned long) hcd;
-	errata2_timer.expires = jiffies + SLOT_CHECK_PERIOD * HZ / 1000;
-	add_timer(&errata2_timer);
-
 	chipid = reg_read32(hcd->regs, HC_CHIP_ID_REG);
 	dev_info(hcd->self.controller, "USB ISP %04x HW rev. %d started\n",
 					chipid & 0xffff, chipid >> 16);
@@ -1627,6 +1612,7 @@ static void kill_transfer(struct usb_hcd *hcd, struct urb *urb,
 	}
 
 	qh->slot = -1;
+	priv->active_ptds--;
 }
 
 /*
@@ -2113,8 +2099,6 @@ static void isp1760_stop(struct usb_hcd *hcd)
 	struct isp1760_hcd *priv = hcd_to_priv(hcd);
 	u32 temp;
 
-	del_timer(&errata2_timer);
-
 	isp1760_hub_control(hcd, ClearPortFeature, USB_PORT_FEAT_POWER,	1,
 			NULL, 0);
 	mdelay(20);
diff --git a/drivers/usb/host/isp1760-hcd.h b/drivers/usb/host/isp1760-hcd.h
index 33dc79c..b20ae45 100644
--- a/drivers/usb/host/isp1760-hcd.h
+++ b/drivers/usb/host/isp1760-hcd.h
@@ -74,6 +74,7 @@ void deinit_kmem_cache(void);
 #define HC_EOT_INT		(1 << 3)
 #define HC_SOT_INT		(1 << 1)
 #define INTERRUPT_ENABLE_MASK	(HC_INTL_INT | HC_ATL_INT)
+#define INTERRUPT_ENABLE_SOT_MASK      (HC_SOT_INT)
 
 #define HC_ISO_IRQ_MASK_OR_REG	0x318
 #define HC_INT_IRQ_MASK_OR_REG	0x31C
-- 
1.7.10

--
To unsubscribe from this list: send the line "unsubscribe linux-usb" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html


[Index of Archives]     [Linux Media]     [Linux Input]     [Linux Audio Users]     [Yosemite News]     [Linux Kernel]     [Linux SCSI]     [Old Linux USB Devel Archive]

  Powered by Linux