aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/usb/host/ehci-sched.c
diff options
context:
space:
mode:
authorSarah Sharp <sarah.a.sharp@linux.intel.com>2009-10-27 13:55:05 -0400
committerGreg Kroah-Hartman <gregkh@suse.de>2009-12-11 14:55:18 -0500
commitdccd574cccad950d9ed9bc192eae4089c6044d9d (patch)
treecd4a5c599c5a50c84dee8074e25e939dae71a276 /drivers/usb/host/ehci-sched.c
parentd7e055f1975cac560427c924d2bff4b5d41fe442 (diff)
USB: ehci: Respect IST when scheduling new split iTDs.
The EHCI specification says that an EHCI host controller may cache part of the isochronous schedule. The EHCI controller must advertise how much it caches in the schedule through the HCCPARAMS register isochronous scheduling threshold (IST) bits. In theory, adding new iTDs within the IST should be harmless. The HW will follow the old cached linked list and miss the new iTD. SW will notice HW missed the iTD and return 0 for the transfer length. However, Intel ICH9 chipsets (and some later chipsets) have issues when SW attempts to schedule a split transaction within the IST. All transfers will cease being sent out that port, and the drivers will see isochronous packets complete with a length of zero. Start of frames may or may not also disappear, causing the device to go into auto-suspend. This "bus stall" will continue until a control or bulk transfer is queued to a device under that roothub. Most drivers will never cause this behavior, because they use multiple URBs with multiple packets to keep the bus busy. If you limit the number of URBs to one, you may be able to hit this bug. Make sure the EHCI driver does not schedule full-speed transfers within the IST under an Intel chipset. Make sure that when we fall behind the current microframe plus IST, we schedule the new transfer at the next periodic interval after the IST. Don't change the scheduling for new transfers, since the schedule slop will always be greater than the IST. Allow high speed isochronous transfers to be scheduled within the IST, since this doesn't trigger the Intel chipset bug. Make sure that if the host caches the full frame, the EHCI driver's internal isochronous threshold (ehci->i_thresh) is set to 8 microframes + 2 microframes wiggle room. This is similar to what is done in the case where the host caches less than the full frame. Signed-off-by: Sarah Sharp <sarah.a.sharp@linux.intel.com> Cc: Alan Stern <stern@rowland.harvard.edu> Cc: David Brownell <dbrownell@users.sourceforge.net> Cc: Clemens Ladisch <clemens@ladisch.de> Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
Diffstat (limited to 'drivers/usb/host/ehci-sched.c')
-rw-r--r--drivers/usb/host/ehci-sched.c30
1 files changed, 20 insertions, 10 deletions
diff --git a/drivers/usb/host/ehci-sched.c b/drivers/usb/host/ehci-sched.c
index 84079ebbe656..1e391e624c8a 100644
--- a/drivers/usb/host/ehci-sched.c
+++ b/drivers/usb/host/ehci-sched.c
@@ -1394,10 +1394,11 @@ iso_stream_schedule (
1394 struct ehci_iso_stream *stream 1394 struct ehci_iso_stream *stream
1395) 1395)
1396{ 1396{
1397 u32 now, start, max, period; 1397 u32 now, next, start, period;
1398 int status; 1398 int status;
1399 unsigned mod = ehci->periodic_size << 3; 1399 unsigned mod = ehci->periodic_size << 3;
1400 struct ehci_iso_sched *sched = urb->hcpriv; 1400 struct ehci_iso_sched *sched = urb->hcpriv;
1401 struct pci_dev *pdev;
1401 1402
1402 if (sched->span > (mod - SCHEDULE_SLOP)) { 1403 if (sched->span > (mod - SCHEDULE_SLOP)) {
1403 ehci_dbg (ehci, "iso request %p too long\n", urb); 1404 ehci_dbg (ehci, "iso request %p too long\n", urb);
@@ -1418,26 +1419,35 @@ iso_stream_schedule (
1418 1419
1419 now = ehci_readl(ehci, &ehci->regs->frame_index) % mod; 1420 now = ehci_readl(ehci, &ehci->regs->frame_index) % mod;
1420 1421
1421 /* when's the last uframe this urb could start? */
1422 max = now + mod;
1423
1424 /* Typical case: reuse current schedule, stream is still active. 1422 /* Typical case: reuse current schedule, stream is still active.
1425 * Hopefully there are no gaps from the host falling behind 1423 * Hopefully there are no gaps from the host falling behind
1426 * (irq delays etc), but if there are we'll take the next 1424 * (irq delays etc), but if there are we'll take the next
1427 * slot in the schedule, implicitly assuming URB_ISO_ASAP. 1425 * slot in the schedule, implicitly assuming URB_ISO_ASAP.
1428 */ 1426 */
1429 if (likely (!list_empty (&stream->td_list))) { 1427 if (likely (!list_empty (&stream->td_list))) {
1428 pdev = to_pci_dev(ehci_to_hcd(ehci)->self.controller);
1430 start = stream->next_uframe; 1429 start = stream->next_uframe;
1431 if (start < now) 1430
1432 start += mod; 1431 /* For high speed devices, allow scheduling within the
1432 * isochronous scheduling threshold. For full speed devices,
1433 * don't. (Work around for Intel ICH9 bug.)
1434 */
1435 if (!stream->highspeed &&
1436 pdev->vendor == PCI_VENDOR_ID_INTEL)
1437 next = now + ehci->i_thresh;
1438 else
1439 next = now;
1433 1440
1434 /* Fell behind (by up to twice the slop amount)? */ 1441 /* Fell behind (by up to twice the slop amount)? */
1435 if (start >= max - 2 * SCHEDULE_SLOP) 1442 if (((start - next) & (mod - 1)) >=
1443 mod - 2 * SCHEDULE_SLOP)
1436 start += period * DIV_ROUND_UP( 1444 start += period * DIV_ROUND_UP(
1437 max - start, period) - mod; 1445 (next - start) & (mod - 1),
1446 period);
1438 1447
1439 /* Tried to schedule too far into the future? */ 1448 /* Tried to schedule too far into the future? */
1440 if (unlikely((start + sched->span) >= max)) { 1449 if (unlikely(((start - now) & (mod - 1)) + sched->span
1450 >= mod - 2 * SCHEDULE_SLOP)) {
1441 status = -EFBIG; 1451 status = -EFBIG;
1442 goto fail; 1452 goto fail;
1443 } 1453 }
@@ -1482,7 +1492,7 @@ iso_stream_schedule (
1482 /* no room in the schedule */ 1492 /* no room in the schedule */
1483 ehci_dbg (ehci, "iso %ssched full %p (now %d max %d)\n", 1493 ehci_dbg (ehci, "iso %ssched full %p (now %d max %d)\n",
1484 list_empty (&stream->td_list) ? "" : "re", 1494 list_empty (&stream->td_list) ? "" : "re",
1485 urb, now, max); 1495 urb, now, now + mod);
1486 status = -ENOSPC; 1496 status = -ENOSPC;
1487 1497
1488fail: 1498fail: