diff mbox

[v2] usb: fsl_udc: errata - postpone freeing current dTD

Message ID 20120521065722.GA4363@mars (mailing list archive)
State Not Applicable
Delegated to: Kumar Gala
Headers show

Commit Message

Christoph Fritz May 21, 2012, 6:57 a.m. UTC
USB controller may access a wrong address for the dTD (endpoint transfer
descriptor) and then hang. This happens a lot when doing tests with
g_ether module and iperf, a tool for measuring maximum TCP and UDP
bandwidth.

This hardware bug is explained in detail by errata number 2858 for i.MX23:
http://cache.freescale.com/files/dsp/doc/errata/IMX23CE.pdf

All (?) SOCs with an IP from chipidea suffer from this problem.
mv_udc_core fixes this bug by commit daec765.  There still may be
unfixed drivers.

Signed-off-by: Christoph Fritz <chf.fritz@googlemail.com>
Signed-off-by: Christian Hemp <c.hemp@phytec.de>
---
 drivers/usb/gadget/fsl_udc_core.c |   15 ++++++++++++++-
 1 files changed, 14 insertions(+), 1 deletions(-)

Comments

Chen Peter-B29397 May 21, 2012, 7:25 a.m. UTC | #1
> 
> USB controller may access a wrong address for the dTD (endpoint transfer
> descriptor) and then hang. This happens a lot when doing tests with
> g_ether module and iperf, a tool for measuring maximum TCP and UDP
> bandwidth.
> 
> This hardware bug is explained in detail by errata number 2858 for i.MX23:
> http://cache.freescale.com/files/dsp/doc/errata/IMX23CE.pdf
> 

> All (?) SOCs with an IP from chipidea suffer from this problem.
> mv_udc_core fixes this bug by commit daec765.  There still may be
> unfixed drivers.
> 
> Signed-off-by: Christoph Fritz <chf.fritz@googlemail.com>
> Signed-off-by: Christian Hemp <c.hemp@phytec.de>
> ---
>  drivers/usb/gadget/fsl_udc_core.c |   15 ++++++++++++++-
>  1 files changed, 14 insertions(+), 1 deletions(-)
> 
> diff --git a/drivers/usb/gadget/fsl_udc_core.c
> b/drivers/usb/gadget/fsl_udc_core.c
> index 55abfb6..72f2139 100644
> --- a/drivers/usb/gadget/fsl_udc_core.c
> +++ b/drivers/usb/gadget/fsl_udc_core.c
> @@ -65,6 +65,8 @@ static struct usb_sys_interface *usb_sys_regs;
>  /* it is initialized in probe()  */
>  static struct fsl_udc *udc_controller = NULL;
> 
> +static struct ep_td_struct *last_free_td;
> +
>  static const struct usb_endpoint_descriptor
>  fsl_ep0_desc = {
>  	.bLength =		USB_DT_ENDPOINT_SIZE,
> @@ -180,8 +182,13 @@ static void done(struct fsl_ep *ep, struct fsl_req
> *req, int status)
>  		curr_td = next_td;
>  		if (j != req->dtd_count - 1) {
>  			next_td = curr_td->next_td_virt;
> +			dma_pool_free(udc->td_pool, curr_td, curr_td->td_dma);
> +		} else {
> +			if (last_free_td != NULL)
> +				dma_pool_free(udc->td_pool, last_free_td,
> +						last_free_td->td_dma);
> +			last_free_td = curr_td;
>  		}
> -		dma_pool_free(udc->td_pool, curr_td, curr_td->td_dma);
>  	}
> 
>  	if (req->mapped) {
> @@ -2579,6 +2586,8 @@ static int __init fsl_udc_probe(struct
> platform_device *pdev)
>  		goto err_unregister;
>  	}
> 
> +	last_free_td = NULL;
> +
>  	ret = usb_add_gadget_udc(&pdev->dev, &udc_controller->gadget);
>  	if (ret)
>  		goto err_del_udc;
> @@ -2633,6 +2642,10 @@ static int __exit fsl_udc_remove(struct
> platform_device *pdev)
>  	kfree(udc_controller->status_req);
>  	kfree(udc_controller->eps);
> 
> +	if (last_free_td != NULL)
> +		dma_pool_free(udc_controller->td_pool, last_free_td,
> +				last_free_td->td_dma);
> +
>  	dma_pool_destroy(udc_controller->td_pool);
>  	free_irq(udc_controller->irq, udc_controller);
>  	iounmap(dr_regs);

Reviewed-by: Peter Chen <peter.chen@freescale.com>
> --
> 1.7.2.5
> 
>
Felipe Balbi May 21, 2012, 7:04 p.m. UTC | #2
Hi,

On Mon, May 21, 2012 at 08:57:22AM +0200, Christoph Fritz wrote:
> USB controller may access a wrong address for the dTD (endpoint transfer
> descriptor) and then hang. This happens a lot when doing tests with
> g_ether module and iperf, a tool for measuring maximum TCP and UDP
> bandwidth.
> 
> This hardware bug is explained in detail by errata number 2858 for i.MX23:
> http://cache.freescale.com/files/dsp/doc/errata/IMX23CE.pdf
> 
> All (?) SOCs with an IP from chipidea suffer from this problem.
> mv_udc_core fixes this bug by commit daec765.  There still may be
> unfixed drivers.
> 
> Signed-off-by: Christoph Fritz <chf.fritz@googlemail.com>
> Signed-off-by: Christian Hemp <c.hemp@phytec.de>
> ---
>  drivers/usb/gadget/fsl_udc_core.c |   15 ++++++++++++++-
>  1 files changed, 14 insertions(+), 1 deletions(-)
> 
> diff --git a/drivers/usb/gadget/fsl_udc_core.c b/drivers/usb/gadget/fsl_udc_core.c
> index 55abfb6..72f2139 100644
> --- a/drivers/usb/gadget/fsl_udc_core.c
> +++ b/drivers/usb/gadget/fsl_udc_core.c
> @@ -65,6 +65,8 @@ static struct usb_sys_interface *usb_sys_regs;
>  /* it is initialized in probe()  */
>  static struct fsl_udc *udc_controller = NULL;
>  
> +static struct ep_td_struct *last_free_td;

I don't want to see global variables anymore. In fact, please convert
this to the new udc_start()/udc_stop() calls and use the generic
map/unmap routines.

That'll help you get rid of a bunch of useless code on the driver. After
that you should remove all <asm/*> header includes and drop the ARCH
dependency.

You can also drop the big-/little-endian helpers as you can make use of
generic writel()/readl() routines.

Please make sure these series comes in with enough time to reach v3.6
merge window in about 3 months.

You can put this fix together on that series after you drop the global.
Christoph Fritz Oct. 19, 2012, 10:22 a.m. UTC | #3
This series updates USB gadget driver fsl-usb2-udc.

Christoph Fritz (7):
  usb: gadget: fsl_udc: simplify driver init
  usb: gadget: fsl_udc: protect fsl_pullup() with spin_lock
  usb: gadget: fsl_udc: convert to new ulc style
  usb: gadget: fsl_udc: drop ARCH dependency
  usb: gadget: fsl_udc: postpone freeing current dTD
  usb: gadget: fsl_udc: purge global pointer usb_sys_regs
  usb: gadget: fsl_udc: purge global pointer dr_regs

 drivers/usb/gadget/fsl_udc_core.c |  755 ++++++++++++++++---------------------
 drivers/usb/gadget/fsl_usb2_udc.h |    4 +
 2 files changed, 322 insertions(+), 437 deletions(-)
Sascha Hauer Oct. 19, 2012, 3:36 p.m. UTC | #4
Hi Christoph,

What system are you working on? If it's i.MX you should use/work on the
chipidea driver instead.

Sascha

On Fri, Oct 19, 2012 at 12:22:36PM +0200, Christoph Fritz wrote:
> This series updates USB gadget driver fsl-usb2-udc.
> 
> Christoph Fritz (7):
>   usb: gadget: fsl_udc: simplify driver init
>   usb: gadget: fsl_udc: protect fsl_pullup() with spin_lock
>   usb: gadget: fsl_udc: convert to new ulc style
>   usb: gadget: fsl_udc: drop ARCH dependency
>   usb: gadget: fsl_udc: postpone freeing current dTD
>   usb: gadget: fsl_udc: purge global pointer usb_sys_regs
>   usb: gadget: fsl_udc: purge global pointer dr_regs
> 
>  drivers/usb/gadget/fsl_udc_core.c |  755 ++++++++++++++++---------------------
>  drivers/usb/gadget/fsl_usb2_udc.h |    4 +
>  2 files changed, 322 insertions(+), 437 deletions(-)
> 
> -- 
> 1.7.2.5
> 
>
diff mbox

Patch

diff --git a/drivers/usb/gadget/fsl_udc_core.c b/drivers/usb/gadget/fsl_udc_core.c
index 55abfb6..72f2139 100644
--- a/drivers/usb/gadget/fsl_udc_core.c
+++ b/drivers/usb/gadget/fsl_udc_core.c
@@ -65,6 +65,8 @@  static struct usb_sys_interface *usb_sys_regs;
 /* it is initialized in probe()  */
 static struct fsl_udc *udc_controller = NULL;
 
+static struct ep_td_struct *last_free_td;
+
 static const struct usb_endpoint_descriptor
 fsl_ep0_desc = {
 	.bLength =		USB_DT_ENDPOINT_SIZE,
@@ -180,8 +182,13 @@  static void done(struct fsl_ep *ep, struct fsl_req *req, int status)
 		curr_td = next_td;
 		if (j != req->dtd_count - 1) {
 			next_td = curr_td->next_td_virt;
+			dma_pool_free(udc->td_pool, curr_td, curr_td->td_dma);
+		} else {
+			if (last_free_td != NULL)
+				dma_pool_free(udc->td_pool, last_free_td,
+						last_free_td->td_dma);
+			last_free_td = curr_td;
 		}
-		dma_pool_free(udc->td_pool, curr_td, curr_td->td_dma);
 	}
 
 	if (req->mapped) {
@@ -2579,6 +2586,8 @@  static int __init fsl_udc_probe(struct platform_device *pdev)
 		goto err_unregister;
 	}
 
+	last_free_td = NULL;
+
 	ret = usb_add_gadget_udc(&pdev->dev, &udc_controller->gadget);
 	if (ret)
 		goto err_del_udc;
@@ -2633,6 +2642,10 @@  static int __exit fsl_udc_remove(struct platform_device *pdev)
 	kfree(udc_controller->status_req);
 	kfree(udc_controller->eps);
 
+	if (last_free_td != NULL)
+		dma_pool_free(udc_controller->td_pool, last_free_td,
+				last_free_td->td_dma);
+
 	dma_pool_destroy(udc_controller->td_pool);
 	free_irq(udc_controller->irq, udc_controller);
 	iounmap(dr_regs);