summaryrefslogtreecommitdiffstats
path: root/sys/dev/usb/controller/uhci.c
diff options
context:
space:
mode:
authorhselasky <hselasky@FreeBSD.org>2013-05-27 06:32:07 +0000
committerhselasky <hselasky@FreeBSD.org>2013-05-27 06:32:07 +0000
commite97a0dc9656bc3f5214866731094513064e59725 (patch)
tree3b01e56476f35235925f4890cf33cc5ba129592c /sys/dev/usb/controller/uhci.c
parent2ed2ec066062b8067971d284fd1a77af06937b49 (diff)
downloadFreeBSD-src-e97a0dc9656bc3f5214866731094513064e59725.zip
FreeBSD-src-e97a0dc9656bc3f5214866731094513064e59725.tar.gz
Workaround for for a problem seen with ATI Technologies EHCI
controller hardware most likely present on UHCI chipsets aswell. The bug manifests itself when issuing isochronous transfers and bulk transfers towards the same device simultaneously. From time to time it happens that either the completion IRQ was missing or that the completion IRQ was happening before the ITD/SITD was completely written back to memory. The workaround assumes that double buffered isochronous transfers are used, and that a second interrupt is generated at the beginning of the next isochronous transfer to complete the previous one. Possibly skipping the interrupt at the last isochronous frame is possible, but will then break single buffered isochronous transfers. For now we can live with some extra interrupts. MFC after: 1 week
Diffstat (limited to 'sys/dev/usb/controller/uhci.c')
-rw-r--r--sys/dev/usb/controller/uhci.c13
1 files changed, 13 insertions, 0 deletions
diff --git a/sys/dev/usb/controller/uhci.c b/sys/dev/usb/controller/uhci.c
index fe3cf93..49ce4e1 100644
--- a/sys/dev/usb/controller/uhci.c
+++ b/sys/dev/usb/controller/uhci.c
@@ -2138,6 +2138,7 @@ uhci_device_isoc_enter(struct usb_xfer *xfer)
uint32_t nframes;
uint32_t temp;
uint32_t *plen;
+ uint8_t first = 1;
#ifdef USB_DEBUG
uint8_t once = 1;
@@ -2253,6 +2254,18 @@ uhci_device_isoc_enter(struct usb_xfer *xfer)
UHCI_TD_ACTIVE |
UHCI_TD_IOS |
UHCI_TD_IOC));
+ } else if (first != 0) {
+ /*
+ * Workaround for lost or too early completion
+ * interrupt:
+ */
+ first = 0;
+ td->td_status = htole32
+ (UHCI_TD_ZERO_ACTLEN
+ (UHCI_TD_SET_ERRCNT(0) |
+ UHCI_TD_ACTIVE |
+ UHCI_TD_IOS |
+ UHCI_TD_IOC));
} else {
td->td_status = htole32
(UHCI_TD_ZERO_ACTLEN
OpenPOWER on IntegriCloud