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

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

 



On 2012-04-13 12:59, Michael Grzeschik wrote:
> 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.

I believe you trust the datasheet too much! :) I've tried this before (in fact, most of
your code seems to be from an earlier patch of mine). It doesn't work, as explained in the
comment to errata2_function() (and as tested with the usbtest/testusb suite).

Do you have a specific bug that this patch solves for you? Then please describe the bug in
detail so that we can try to find a solution.


> 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


-- 
Arvid Brodin
Enea Services Stockholm AB - since February 16 a part of Xdin in the Alten Group. Soon we
will be working under the common brand name Xdin. Read more at www.xdin.com.--
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