aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorFabian Godehardt <fg@emlix.com>2011-02-07 12:53:05 +0200
committerGreg Kroah-Hartman <gregkh@suse.de>2011-02-17 10:47:55 -0800
commit16f08a08d8148945022eb7d5ebb89e6ee8029f91 (patch)
treefac512e2900645fec08f9a28bd875a45b214573d
parentc17f459c6ea5b569825445279fa21adb3cf3067f (diff)
USB: s3c2410_udc: Add handling for S3C244X dual-packet mode
This is a patch that seems to make the USB hangs on the S3C244X go away. At least a good amount of ping torture didn't make them come back so far. The issue is that, if there are several back-to-back packets, sometimes no interrupt is generated for one of them. This seems to be caused by the mysterious dual packet mode, which the USB hardware enters automatically if the endpoint size is half that of the FIFO. (On the 244X, this is the normal situation for bulk data endpoints.) There is also a timing factor in this. It seems that what happens is that the USB hardware automatically sends an acknowledgement if there is only one packet in the FIFO (the FIFO has space for two). If another packet arrives before the host has retrieved and acknowledged the previous one, no interrupt is generated for that second one. However, there may be an indication. There is one undocumented bit (none of the 244x manuals document it), OUT_CRS1_REG[1], that seems to be set suspiciously often when this condition occurs. There is also CLR_DATA_TOGGLE, OUT_CRS1_REG[7], which may have a function related to this. (The Samsung manual is rather terse on that, as usual.) This needs to be examined further. For now, the patch seems to do the trick. Signed-off-by: Vasily Khoruzhick <anarsoul@gmail.com> Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
-rw-r--r--drivers/usb/gadget/s3c2410_udc.c16
1 files changed, 15 insertions, 1 deletions
diff --git a/drivers/usb/gadget/s3c2410_udc.c b/drivers/usb/gadget/s3c2410_udc.c
index c2448950a8d..2b025200a69 100644
--- a/drivers/usb/gadget/s3c2410_udc.c
+++ b/drivers/usb/gadget/s3c2410_udc.c
@@ -902,7 +902,7 @@ static irqreturn_t s3c2410_udc_irq(int dummy, void *_dev)
int pwr_reg;
int ep0csr;
int i;
- u32 idx;
+ u32 idx, idx2;
unsigned long flags;
spin_lock_irqsave(&dev->lock, flags);
@@ -1017,6 +1017,20 @@ static irqreturn_t s3c2410_udc_irq(int dummy, void *_dev)
}
}
+ /* what else causes this interrupt? a receive! who is it? */
+ if (!usb_status && !usbd_status && !pwr_reg && !ep0csr) {
+ for (i = 1; i < S3C2410_ENDPOINTS; i++) {
+ idx2 = udc_read(S3C2410_UDC_INDEX_REG);
+ udc_write(i, S3C2410_UDC_INDEX_REG);
+
+ if (udc_read(S3C2410_UDC_OUT_CSR1_REG) & 0x1)
+ s3c2410_udc_handle_ep(&dev->ep[i]);
+
+ /* restore index */
+ udc_write(idx2, S3C2410_UDC_INDEX_REG);
+ }
+ }
+
dprintk(DEBUG_VERBOSE, "irq: %d s3c2410_udc_done.\n", IRQ_USBD);
/* Restore old index */