mirror of
https://github.com/torvalds/linux.git
synced 2024-11-23 12:42:02 +00:00
USB: PS3: Fix EHCI ISO transfer bug
This adds a workaround for an issue reported with ISO transfers on some EHCI controllers, most recently with VIA KT800 and PS3 EHCI silicon. The issue is that the silicon doesn't necessarily seem to be done using ISO DMA descriptors (itd, sitd) when it marks them inactive. (One theory is that the ill-defined mechanism where hardware caches periodic transfer descriptors isn't invalidating their state...) With such silicon, quick re-use of those descriptors makes trouble. Waiting until the next frame seems to be a sufficient workaround. This patch ensures that the relevant descriptors aren't available for immediate re-use. It does so by not recycling them until after issuing the completion callback which would reuse them by enqueueing an URB and thus (re)allocating ISO DMA descriptors. Signed-off-by: David Brownell <dbrownell@users.sourceforge.net> Acked-by: Geoff Levand <geoffrey.levand@am.sony.com> Cc: Masashi Kimoto <Masashi_Kimoto@hq.scei.sony.co.jp> Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
This commit is contained in:
parent
dc0d5c1e5c
commit
30bf54e62a
@ -1565,6 +1565,16 @@ itd_link_urb (
|
||||
|
||||
#define ISO_ERRS (EHCI_ISOC_BUF_ERR | EHCI_ISOC_BABBLE | EHCI_ISOC_XACTERR)
|
||||
|
||||
/* Process and recycle a completed ITD. Return true iff its urb completed,
|
||||
* and hence its completion callback probably added things to the hardware
|
||||
* schedule.
|
||||
*
|
||||
* Note that we carefully avoid recycling this descriptor until after any
|
||||
* completion callback runs, so that it won't be reused quickly. That is,
|
||||
* assuming (a) no more than two urbs per frame on this endpoint, and also
|
||||
* (b) only this endpoint's completions submit URBs. It seems some silicon
|
||||
* corrupts things if you reuse completed descriptors very quickly...
|
||||
*/
|
||||
static unsigned
|
||||
itd_complete (
|
||||
struct ehci_hcd *ehci,
|
||||
@ -1577,6 +1587,7 @@ itd_complete (
|
||||
int urb_index = -1;
|
||||
struct ehci_iso_stream *stream = itd->stream;
|
||||
struct usb_device *dev;
|
||||
unsigned retval = false;
|
||||
|
||||
/* for each uframe with a packet */
|
||||
for (uframe = 0; uframe < 8; uframe++) {
|
||||
@ -1610,15 +1621,9 @@ itd_complete (
|
||||
}
|
||||
}
|
||||
|
||||
usb_put_urb (urb);
|
||||
itd->urb = NULL;
|
||||
itd->stream = NULL;
|
||||
list_move (&itd->itd_list, &stream->free_list);
|
||||
iso_stream_put (ehci, stream);
|
||||
|
||||
/* handle completion now? */
|
||||
if (likely ((urb_index + 1) != urb->number_of_packets))
|
||||
return 0;
|
||||
goto done;
|
||||
|
||||
/* ASSERT: it's really the last itd for this urb
|
||||
list_for_each_entry (itd, &stream->td_list, itd_list)
|
||||
@ -1628,6 +1633,7 @@ itd_complete (
|
||||
/* give urb back to the driver ... can be out-of-order */
|
||||
dev = urb->dev;
|
||||
ehci_urb_done(ehci, urb, 0);
|
||||
retval = true;
|
||||
urb = NULL;
|
||||
|
||||
/* defer stopping schedule; completion can submit */
|
||||
@ -1645,8 +1651,15 @@ itd_complete (
|
||||
(stream->bEndpointAddress & USB_DIR_IN) ? "in" : "out");
|
||||
}
|
||||
iso_stream_put (ehci, stream);
|
||||
/* OK to recycle this ITD now that its completion callback ran. */
|
||||
done:
|
||||
usb_put_urb(urb);
|
||||
itd->urb = NULL;
|
||||
itd->stream = NULL;
|
||||
list_move(&itd->itd_list, &stream->free_list);
|
||||
iso_stream_put(ehci, stream);
|
||||
|
||||
return 1;
|
||||
return retval;
|
||||
}
|
||||
|
||||
/*-------------------------------------------------------------------------*/
|
||||
@ -1950,6 +1963,16 @@ sitd_link_urb (
|
||||
#define SITD_ERRS (SITD_STS_ERR | SITD_STS_DBE | SITD_STS_BABBLE \
|
||||
| SITD_STS_XACT | SITD_STS_MMF)
|
||||
|
||||
/* Process and recycle a completed SITD. Return true iff its urb completed,
|
||||
* and hence its completion callback probably added things to the hardware
|
||||
* schedule.
|
||||
*
|
||||
* Note that we carefully avoid recycling this descriptor until after any
|
||||
* completion callback runs, so that it won't be reused quickly. That is,
|
||||
* assuming (a) no more than two urbs per frame on this endpoint, and also
|
||||
* (b) only this endpoint's completions submit URBs. It seems some silicon
|
||||
* corrupts things if you reuse completed descriptors very quickly...
|
||||
*/
|
||||
static unsigned
|
||||
sitd_complete (
|
||||
struct ehci_hcd *ehci,
|
||||
@ -1961,6 +1984,7 @@ sitd_complete (
|
||||
int urb_index = -1;
|
||||
struct ehci_iso_stream *stream = sitd->stream;
|
||||
struct usb_device *dev;
|
||||
unsigned retval = false;
|
||||
|
||||
urb_index = sitd->index;
|
||||
desc = &urb->iso_frame_desc [urb_index];
|
||||
@ -1981,17 +2005,11 @@ sitd_complete (
|
||||
desc->status = 0;
|
||||
desc->actual_length = desc->length - SITD_LENGTH (t);
|
||||
}
|
||||
|
||||
usb_put_urb (urb);
|
||||
sitd->urb = NULL;
|
||||
sitd->stream = NULL;
|
||||
list_move (&sitd->sitd_list, &stream->free_list);
|
||||
stream->depth -= stream->interval << 3;
|
||||
iso_stream_put (ehci, stream);
|
||||
|
||||
/* handle completion now? */
|
||||
if ((urb_index + 1) != urb->number_of_packets)
|
||||
return 0;
|
||||
goto done;
|
||||
|
||||
/* ASSERT: it's really the last sitd for this urb
|
||||
list_for_each_entry (sitd, &stream->td_list, sitd_list)
|
||||
@ -2001,6 +2019,7 @@ sitd_complete (
|
||||
/* give urb back to the driver */
|
||||
dev = urb->dev;
|
||||
ehci_urb_done(ehci, urb, 0);
|
||||
retval = true;
|
||||
urb = NULL;
|
||||
|
||||
/* defer stopping schedule; completion can submit */
|
||||
@ -2018,8 +2037,15 @@ sitd_complete (
|
||||
(stream->bEndpointAddress & USB_DIR_IN) ? "in" : "out");
|
||||
}
|
||||
iso_stream_put (ehci, stream);
|
||||
/* OK to recycle this SITD now that its completion callback ran. */
|
||||
done:
|
||||
usb_put_urb(urb);
|
||||
sitd->urb = NULL;
|
||||
sitd->stream = NULL;
|
||||
list_move(&sitd->sitd_list, &stream->free_list);
|
||||
iso_stream_put(ehci, stream);
|
||||
|
||||
return 1;
|
||||
return retval;
|
||||
}
|
||||
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user