summaryrefslogtreecommitdiffstats
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
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
-rw-r--r--sys/dev/usb/controller/ehci.c19
-rw-r--r--sys/dev/usb/controller/uhci.c13
2 files changed, 32 insertions, 0 deletions
diff --git a/sys/dev/usb/controller/ehci.c b/sys/dev/usb/controller/ehci.c
index 51b012b..f0616f9 100644
--- a/sys/dev/usb/controller/ehci.c
+++ b/sys/dev/usb/controller/ehci.c
@@ -2454,6 +2454,7 @@ ehci_device_isoc_fs_enter(struct usb_xfer *xfer)
uint16_t tlen;
uint8_t sa;
uint8_t sb;
+ uint8_t first = 1;
#ifdef USB_DEBUG
uint8_t once = 1;
@@ -2618,6 +2619,16 @@ ehci_device_isoc_fs_enter(struct usb_xfer *xfer)
EHCI_SITD_IOC |
EHCI_SITD_ACTIVE |
EHCI_SITD_SET_LEN(*plen));
+ } else if (first != 0) {
+ /*
+ * Workaround for lost or too early
+ * completion interrupt:
+ */
+ first = 0;
+ td->sitd_status = htohc32(sc,
+ EHCI_SITD_IOC |
+ EHCI_SITD_ACTIVE |
+ EHCI_SITD_SET_LEN(*plen));
} else {
td->sitd_status = htohc32(sc,
EHCI_SITD_ACTIVE |
@@ -2759,6 +2770,7 @@ ehci_device_isoc_hs_enter(struct usb_xfer *xfer)
uint8_t td_no;
uint8_t page_no;
uint8_t shift = usbd_xfer_get_fps_shift(xfer);
+ uint8_t first = 1;
#ifdef USB_DEBUG
uint8_t once = 1;
@@ -2920,6 +2932,13 @@ ehci_device_isoc_hs_enter(struct usb_xfer *xfer)
/* set IOC bit if we are complete */
if (nframes == 0) {
td->itd_status[td_no - 1] |= htohc32(sc, EHCI_ITD_IOC);
+ } else if (first != 0) {
+ /*
+ * Workaround for lost or too early
+ * completion interrupt:
+ */
+ first = 0;
+ td->itd_status[0] |= htohc32(sc, EHCI_ITD_IOC);
}
usb_pc_cpu_flush(td->page_cache);
#ifdef USB_DEBUG
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