diff options
author | Hans de Goede <hdegoede@redhat.com> | 2012-11-17 12:47:17 +0100 |
---|---|---|
committer | Gerd Hoffmann <kraxel@redhat.com> | 2012-12-04 14:41:54 +0100 |
commit | 8082624099bce56a3139e6b9f72016c00fd10227 (patch) | |
tree | 09b3245dee39323e0b5d8d288fe63d89535f86b1 /hw | |
parent | 386ab487ebc25d780ddfc4a9aea0b21c4a9aaa94 (diff) |
ehci: Lower timer freq when the periodic schedule is idle
Lower the timer freq if no iso schedule packets complete for 64 frames in
a row.
We can safely do this, without adding latency, because:
1) If there is isoc traffic this will never trigger
2) For async handled interrupt packets (only usb-host), the completion handler
will immediately schedule the frame_timer from a bh
3) All devices using NAK to signal no data for interrupt endpoints now use
wakeup, which will immediately schedule the frame_timer from a bh
The advantage of this is that when we only have interrupt packets in the
periodic schedule, async_stepdown can do its work and significantly lower
the frequency at which the frame_timer runs.
Signed-off-by: Hans de Goede <hdegoede@redhat.com>
Signed-off-by: Gerd Hoffmann <kraxel@redhat.com>
Diffstat (limited to 'hw')
-rw-r--r-- | hw/usb/hcd-ehci.c | 39 | ||||
-rw-r--r-- | hw/usb/hcd-ehci.h | 1 |
2 files changed, 34 insertions, 6 deletions
diff --git a/hw/usb/hcd-ehci.c b/hw/usb/hcd-ehci.c index 7df8e21ecb..7536837fb2 100644 --- a/hw/usb/hcd-ehci.c +++ b/hw/usb/hcd-ehci.c @@ -114,6 +114,7 @@ #define BUFF_SIZE 5*4096 // Max bytes to transfer per transaction #define MAX_QH 100 // Max allowable queue heads in a chain #define MIN_FR_PER_TICK 3 // Min frames to process when catching up +#define PERIODIC_ACTIVE 64 /* Internal periodic / asynchronous schedule state machine states */ @@ -738,6 +739,19 @@ static int ehci_register_companion(USBBus *bus, USBPort *ports[], return 0; } +static void ehci_wakeup_endpoint(USBBus *bus, USBEndpoint *ep) +{ + EHCIState *s = container_of(bus, EHCIState, bus); + uint32_t portsc = s->portsc[ep->dev->port->index]; + + if (portsc & PORTSC_POWNER) { + return; + } + + s->periodic_sched_active = PERIODIC_ACTIVE; + qemu_bh_schedule(s->async_bh); +} + static USBDevice *ehci_find_device(EHCIState *ehci, uint8_t addr) { USBDevice *dev; @@ -1188,9 +1202,10 @@ static void ehci_async_complete_packet(USBPort *port, USBPacket *packet) trace_usb_ehci_packet_action(p->queue, p, "wakeup"); p->async = EHCI_ASYNC_FINISHED; - if (p->queue->async) { - qemu_bh_schedule(p->queue->ehci->async_bh); + if (!p->queue->async) { + s->periodic_sched_active = PERIODIC_ACTIVE; } + qemu_bh_schedule(s->async_bh); } static void ehci_execute_complete(EHCIQueue *q) @@ -1344,6 +1359,8 @@ static int ehci_process_itd(EHCIState *ehci, uint32_t i, len, pid, dir, devaddr, endp; uint32_t pg, off, ptr1, ptr2, max, mult; + ehci->periodic_sched_active = PERIODIC_ACTIVE; + dir =(itd->bufptr[1] & ITD_BUFPTR_DIRECTION); devaddr = get_field(itd->bufptr[0], ITD_BUFPTR_DEVADDR); endp = get_field(itd->bufptr[0], ITD_BUFPTR_EP); @@ -2033,6 +2050,9 @@ static void ehci_advance_state(EHCIState *ehci, int async) case EST_WRITEBACK: assert(q != NULL); again = ehci_state_writeback(q); + if (!async) { + ehci->periodic_sched_active = PERIODIC_ACTIVE; + } break; default: @@ -2198,7 +2218,6 @@ static void ehci_frame_timer(void *opaque) if (ehci_periodic_enabled(ehci) || ehci->pstate != EST_INACTIVE) { need_timer++; - ehci->async_stepdown = 0; if (frames > ehci->maxframes) { skipped_frames = frames - ehci->maxframes; @@ -2222,18 +2241,25 @@ static void ehci_frame_timer(void *opaque) break; } } + if (ehci->periodic_sched_active) { + ehci->periodic_sched_active--; + } ehci_update_frindex(ehci, 1); ehci_advance_periodic_state(ehci); ehci->last_run_ns += FRAME_TIMER_NS; } } else { - if (ehci->async_stepdown < ehci->maxframes / 2) { - ehci->async_stepdown++; - } + ehci->periodic_sched_active = 0; ehci_update_frindex(ehci, frames); ehci->last_run_ns += FRAME_TIMER_NS * frames; } + if (ehci->periodic_sched_active) { + ehci->async_stepdown = 0; + } else if (ehci->async_stepdown < ehci->maxframes / 2) { + ehci->async_stepdown++; + } + /* Async is not inside loop since it executes everything it can once * called */ @@ -2301,6 +2327,7 @@ static USBPortOps ehci_port_ops = { static USBBusOps ehci_bus_ops = { .register_companion = ehci_register_companion, + .wakeup_endpoint = ehci_wakeup_endpoint, }; static int usb_ehci_post_load(void *opaque, int version_id) diff --git a/hw/usb/hcd-ehci.h b/hw/usb/hcd-ehci.h index d8078f4555..772870b727 100644 --- a/hw/usb/hcd-ehci.h +++ b/hw/usb/hcd-ehci.h @@ -311,6 +311,7 @@ struct EHCIState { uint64_t last_run_ns; uint32_t async_stepdown; + uint32_t periodic_sched_active; bool int_req_by_async; }; |