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
This commit is contained in:
Hans Petter Selasky 2013-05-27 06:32:07 +00:00
parent 0242992132
commit 6e7bc5b60d
2 changed files with 32 additions and 0 deletions
sys/dev/usb/controller

@ -2454,6 +2454,7 @@ ehci_device_isoc_fs_enter(struct usb_xfer *xfer)
uint16_t tlen; uint16_t tlen;
uint8_t sa; uint8_t sa;
uint8_t sb; uint8_t sb;
uint8_t first = 1;
#ifdef USB_DEBUG #ifdef USB_DEBUG
uint8_t once = 1; uint8_t once = 1;
@ -2618,6 +2619,16 @@ ehci_device_isoc_fs_enter(struct usb_xfer *xfer)
EHCI_SITD_IOC | EHCI_SITD_IOC |
EHCI_SITD_ACTIVE | EHCI_SITD_ACTIVE |
EHCI_SITD_SET_LEN(*plen)); 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 { } else {
td->sitd_status = htohc32(sc, td->sitd_status = htohc32(sc,
EHCI_SITD_ACTIVE | EHCI_SITD_ACTIVE |
@ -2759,6 +2770,7 @@ ehci_device_isoc_hs_enter(struct usb_xfer *xfer)
uint8_t td_no; uint8_t td_no;
uint8_t page_no; uint8_t page_no;
uint8_t shift = usbd_xfer_get_fps_shift(xfer); uint8_t shift = usbd_xfer_get_fps_shift(xfer);
uint8_t first = 1;
#ifdef USB_DEBUG #ifdef USB_DEBUG
uint8_t once = 1; uint8_t once = 1;
@ -2920,6 +2932,13 @@ ehci_device_isoc_hs_enter(struct usb_xfer *xfer)
/* set IOC bit if we are complete */ /* set IOC bit if we are complete */
if (nframes == 0) { if (nframes == 0) {
td->itd_status[td_no - 1] |= htohc32(sc, EHCI_ITD_IOC); 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); usb_pc_cpu_flush(td->page_cache);
#ifdef USB_DEBUG #ifdef USB_DEBUG

@ -2138,6 +2138,7 @@ uhci_device_isoc_enter(struct usb_xfer *xfer)
uint32_t nframes; uint32_t nframes;
uint32_t temp; uint32_t temp;
uint32_t *plen; uint32_t *plen;
uint8_t first = 1;
#ifdef USB_DEBUG #ifdef USB_DEBUG
uint8_t once = 1; uint8_t once = 1;
@ -2253,6 +2254,18 @@ uhci_device_isoc_enter(struct usb_xfer *xfer)
UHCI_TD_ACTIVE | UHCI_TD_ACTIVE |
UHCI_TD_IOS | UHCI_TD_IOS |
UHCI_TD_IOC)); 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 { } else {
td->td_status = htole32 td->td_status = htole32
(UHCI_TD_ZERO_ACTLEN (UHCI_TD_ZERO_ACTLEN