diff options
Diffstat (limited to 'drivers/usb/host/ehci-sched.c')
-rw-r--r-- | drivers/usb/host/ehci-sched.c | 30 |
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 | ||
1488 | fail: | 1498 | fail: |