diff options
Diffstat (limited to 'hw/hpet.c')
-rw-r--r-- | hw/hpet.c | 413 |
1 files changed, 225 insertions, 188 deletions
@@ -37,21 +37,47 @@ #define DPRINTF(...) #endif +struct HPETState; +typedef struct HPETTimer { /* timers */ + uint8_t tn; /*timer number*/ + QEMUTimer *qemu_timer; + struct HPETState *state; + /* Memory-mapped, software visible timer registers */ + uint64_t config; /* configuration/cap */ + uint64_t cmp; /* comparator */ + uint64_t fsb; /* FSB route, not supported now */ + /* Hidden register state */ + uint64_t period; /* Last value written to comparator */ + uint8_t wrap_flag; /* timer pop will indicate wrap for one-shot 32-bit + * mode. Next pop will be actual timer expiration. + */ +} HPETTimer; + +typedef struct HPETState { + uint64_t hpet_offset; + qemu_irq *irqs; + HPETTimer timer[HPET_NUM_TIMERS]; + + /* Memory-mapped, software visible registers */ + uint64_t capability; /* capabilities */ + uint64_t config; /* configuration */ + uint64_t isr; /* interrupt status reg */ + uint64_t hpet_counter; /* main counter */ +} HPETState; + static HPETState *hpet_statep; uint32_t hpet_in_legacy_mode(void) { - if (hpet_statep) - return hpet_statep->config & HPET_CFG_LEGACY; - else + if (!hpet_statep) { return 0; + } + return hpet_statep->config & HPET_CFG_LEGACY; } static uint32_t timer_int_route(struct HPETTimer *timer) { - uint32_t route; - route = (timer->config & HPET_TN_INT_ROUTE_MASK) >> HPET_TN_INT_ROUTE_SHIFT; - return route; + return (timer->config & HPET_TN_INT_ROUTE_MASK) >> HPET_TN_INT_ROUTE_SHIFT; } static uint32_t hpet_enabled(void) @@ -108,9 +134,7 @@ static int deactivating_bit(uint64_t old, uint64_t new, uint64_t mask) static uint64_t hpet_get_ticks(void) { - uint64_t ticks; - ticks = ns_to_ticks(qemu_get_clock(vm_clock) + hpet_statep->hpet_offset); - return ticks; + return ns_to_ticks(qemu_get_clock(vm_clock) + hpet_statep->hpet_offset); } /* @@ -121,12 +145,14 @@ static inline uint64_t hpet_calculate_diff(HPETTimer *t, uint64_t current) if (t->config & HPET_TN_32BIT) { uint32_t diff, cmp; + cmp = (uint32_t)t->cmp; diff = cmp - (uint32_t)current; diff = (int32_t)diff > 0 ? diff : (uint32_t)0; return (uint64_t)diff; } else { uint64_t diff, cmp; + cmp = t->cmp; diff = cmp - current; diff = (int64_t)diff > 0 ? diff : (uint64_t)0; @@ -136,7 +162,6 @@ static inline uint64_t hpet_calculate_diff(HPETTimer *t, uint64_t current) static void update_irq(struct HPETTimer *timer) { - qemu_irq irq; int route; if (timer->tn <= 1 && hpet_in_legacy_mode()) { @@ -144,22 +169,20 @@ static void update_irq(struct HPETTimer *timer) * timer0 be routed to IRQ0 in NON-APIC or IRQ2 in the I/O APIC, * timer1 be routed to IRQ8 in NON-APIC or IRQ8 in the I/O APIC. */ - if (timer->tn == 0) { - irq=timer->state->irqs[0]; - } else - irq=timer->state->irqs[8]; + route = (timer->tn == 0) ? 0 : 8; } else { - route=timer_int_route(timer); - irq=timer->state->irqs[route]; + route = timer_int_route(timer); } - if (timer_enabled(timer) && hpet_enabled()) { - qemu_irq_pulse(irq); + if (!timer_enabled(timer) || !hpet_enabled()) { + return; } + qemu_irq_pulse(timer->state->irqs[route]); } static void hpet_pre_save(void *opaque) { HPETState *s = opaque; + /* save current counter value */ s->hpet_counter = hpet_get_ticks(); } @@ -212,7 +235,7 @@ static const VMStateDescription vmstate_hpet = { */ static void hpet_timer(void *opaque) { - HPETTimer *t = (HPETTimer*)opaque; + HPETTimer *t = opaque; uint64_t diff; uint64_t period = t->period; @@ -220,20 +243,22 @@ static void hpet_timer(void *opaque) if (timer_is_periodic(t) && period != 0) { if (t->config & HPET_TN_32BIT) { - while (hpet_time_after(cur_tick, t->cmp)) + while (hpet_time_after(cur_tick, t->cmp)) { t->cmp = (uint32_t)(t->cmp + t->period); - } else - while (hpet_time_after64(cur_tick, t->cmp)) + } + } else { + while (hpet_time_after64(cur_tick, t->cmp)) { t->cmp += period; - + } + } diff = hpet_calculate_diff(t, cur_tick); - qemu_mod_timer(t->qemu_timer, qemu_get_clock(vm_clock) - + (int64_t)ticks_to_ns(diff)); + qemu_mod_timer(t->qemu_timer, + qemu_get_clock(vm_clock) + (int64_t)ticks_to_ns(diff)); } else if (t->config & HPET_TN_32BIT && !timer_is_periodic(t)) { if (t->wrap_flag) { diff = hpet_calculate_diff(t, cur_tick); - qemu_mod_timer(t->qemu_timer, qemu_get_clock(vm_clock) - + (int64_t)ticks_to_ns(diff)); + qemu_mod_timer(t->qemu_timer, qemu_get_clock(vm_clock) + + (int64_t)ticks_to_ns(diff)); t->wrap_flag = 0; } } @@ -260,8 +285,8 @@ static void hpet_set_timer(HPETTimer *t) t->wrap_flag = 1; } } - qemu_mod_timer(t->qemu_timer, qemu_get_clock(vm_clock) - + (int64_t)ticks_to_ns(diff)); + qemu_mod_timer(t->qemu_timer, + qemu_get_clock(vm_clock) + (int64_t)ticks_to_ns(diff)); } static void hpet_del_timer(HPETTimer *t) @@ -285,7 +310,7 @@ static uint32_t hpet_ram_readw(void *opaque, target_phys_addr_t addr) static uint32_t hpet_ram_readl(void *opaque, target_phys_addr_t addr) { - HPETState *s = (HPETState *)opaque; + HPETState *s = opaque; uint64_t cur_tick, index; DPRINTF("qemu: Enter hpet_ram_readl at %" PRIx64 "\n", addr); @@ -293,57 +318,60 @@ static uint32_t hpet_ram_readl(void *opaque, target_phys_addr_t addr) /*address range of all TN regs*/ if (index >= 0x100 && index <= 0x3ff) { uint8_t timer_id = (addr - 0x100) / 0x20; + HPETTimer *timer = &s->timer[timer_id]; + if (timer_id > HPET_NUM_TIMERS - 1) { DPRINTF("qemu: timer id out of range\n"); return 0; } - HPETTimer *timer = &s->timer[timer_id]; switch ((addr - 0x100) % 0x20) { - case HPET_TN_CFG: - return timer->config; - case HPET_TN_CFG + 4: // Interrupt capabilities - return timer->config >> 32; - case HPET_TN_CMP: // comparator register - return timer->cmp; - case HPET_TN_CMP + 4: - return timer->cmp >> 32; - case HPET_TN_ROUTE: - return timer->fsb >> 32; - default: - DPRINTF("qemu: invalid hpet_ram_readl\n"); - break; + case HPET_TN_CFG: + return timer->config; + case HPET_TN_CFG + 4: // Interrupt capabilities + return timer->config >> 32; + case HPET_TN_CMP: // comparator register + return timer->cmp; + case HPET_TN_CMP + 4: + return timer->cmp >> 32; + case HPET_TN_ROUTE: + return timer->fsb >> 32; + default: + DPRINTF("qemu: invalid hpet_ram_readl\n"); + break; } } else { switch (index) { - case HPET_ID: - return s->capability; - case HPET_PERIOD: - return s->capability >> 32; - case HPET_CFG: - return s->config; - case HPET_CFG + 4: - DPRINTF("qemu: invalid HPET_CFG + 4 hpet_ram_readl \n"); - return 0; - case HPET_COUNTER: - if (hpet_enabled()) - cur_tick = hpet_get_ticks(); - else - cur_tick = s->hpet_counter; - DPRINTF("qemu: reading counter = %" PRIx64 "\n", cur_tick); - return cur_tick; - case HPET_COUNTER + 4: - if (hpet_enabled()) - cur_tick = hpet_get_ticks(); - else - cur_tick = s->hpet_counter; - DPRINTF("qemu: reading counter + 4 = %" PRIx64 "\n", cur_tick); - return cur_tick >> 32; - case HPET_STATUS: - return s->isr; - default: - DPRINTF("qemu: invalid hpet_ram_readl\n"); - break; + case HPET_ID: + return s->capability; + case HPET_PERIOD: + return s->capability >> 32; + case HPET_CFG: + return s->config; + case HPET_CFG + 4: + DPRINTF("qemu: invalid HPET_CFG + 4 hpet_ram_readl \n"); + return 0; + case HPET_COUNTER: + if (hpet_enabled()) { + cur_tick = hpet_get_ticks(); + } else { + cur_tick = s->hpet_counter; + } + DPRINTF("qemu: reading counter = %" PRIx64 "\n", cur_tick); + return cur_tick; + case HPET_COUNTER + 4: + if (hpet_enabled()) { + cur_tick = hpet_get_ticks(); + } else { + cur_tick = s->hpet_counter; + } + DPRINTF("qemu: reading counter + 4 = %" PRIx64 "\n", cur_tick); + return cur_tick >> 32; + case HPET_STATUS: + return s->isr; + default: + DPRINTF("qemu: invalid hpet_ram_readl\n"); + break; } } return 0; @@ -369,7 +397,7 @@ static void hpet_ram_writel(void *opaque, target_phys_addr_t addr, uint32_t value) { int i; - HPETState *s = (HPETState *)opaque; + HPETState *s = opaque; uint64_t old_val, new_val, val, index; DPRINTF("qemu: Enter hpet_ram_writel at %" PRIx64 " = %#x\n", addr, value); @@ -380,133 +408,137 @@ static void hpet_ram_writel(void *opaque, target_phys_addr_t addr, /*address range of all TN regs*/ if (index >= 0x100 && index <= 0x3ff) { uint8_t timer_id = (addr - 0x100) / 0x20; - DPRINTF("qemu: hpet_ram_writel timer_id = %#x \n", timer_id); HPETTimer *timer = &s->timer[timer_id]; + DPRINTF("qemu: hpet_ram_writel timer_id = %#x \n", timer_id); if (timer_id > HPET_NUM_TIMERS - 1) { DPRINTF("qemu: timer id out of range\n"); return; } switch ((addr - 0x100) % 0x20) { - case HPET_TN_CFG: - DPRINTF("qemu: hpet_ram_writel HPET_TN_CFG\n"); - val = hpet_fixup_reg(new_val, old_val, HPET_TN_CFG_WRITE_MASK); - timer->config = (timer->config & 0xffffffff00000000ULL) | val; - if (new_val & HPET_TN_32BIT) { - timer->cmp = (uint32_t)timer->cmp; - timer->period = (uint32_t)timer->period; - } - if (new_val & HPET_TIMER_TYPE_LEVEL) { - printf("qemu: level-triggered hpet not supported\n"); - exit (-1); - } - - break; - case HPET_TN_CFG + 4: // Interrupt capabilities - DPRINTF("qemu: invalid HPET_TN_CFG+4 write\n"); - break; - case HPET_TN_CMP: // comparator register - DPRINTF("qemu: hpet_ram_writel HPET_TN_CMP \n"); - if (timer->config & HPET_TN_32BIT) - new_val = (uint32_t)new_val; - if (!timer_is_periodic(timer) || - (timer->config & HPET_TN_SETVAL)) - timer->cmp = (timer->cmp & 0xffffffff00000000ULL) - | new_val; - if (timer_is_periodic(timer)) { - /* - * FIXME: Clamp period to reasonable min value? - * Clamp period to reasonable max value - */ - new_val &= (timer->config & HPET_TN_32BIT ? ~0u : ~0ull) >> 1; - timer->period = (timer->period & 0xffffffff00000000ULL) - | new_val; + case HPET_TN_CFG: + DPRINTF("qemu: hpet_ram_writel HPET_TN_CFG\n"); + val = hpet_fixup_reg(new_val, old_val, HPET_TN_CFG_WRITE_MASK); + timer->config = (timer->config & 0xffffffff00000000ULL) | val; + if (new_val & HPET_TN_32BIT) { + timer->cmp = (uint32_t)timer->cmp; + timer->period = (uint32_t)timer->period; + } + if (new_val & HPET_TN_TYPE_LEVEL) { + printf("qemu: level-triggered hpet not supported\n"); + exit (-1); + } + break; + case HPET_TN_CFG + 4: // Interrupt capabilities + DPRINTF("qemu: invalid HPET_TN_CFG+4 write\n"); + break; + case HPET_TN_CMP: // comparator register + DPRINTF("qemu: hpet_ram_writel HPET_TN_CMP \n"); + if (timer->config & HPET_TN_32BIT) { + new_val = (uint32_t)new_val; + } + if (!timer_is_periodic(timer) + || (timer->config & HPET_TN_SETVAL)) { + timer->cmp = (timer->cmp & 0xffffffff00000000ULL) | new_val; + } + if (timer_is_periodic(timer)) { + /* + * FIXME: Clamp period to reasonable min value? + * Clamp period to reasonable max value + */ + new_val &= (timer->config & HPET_TN_32BIT ? ~0u : ~0ull) >> 1; + timer->period = + (timer->period & 0xffffffff00000000ULL) | new_val; + } + timer->config &= ~HPET_TN_SETVAL; + if (hpet_enabled()) { + hpet_set_timer(timer); + } + break; + case HPET_TN_CMP + 4: // comparator register high order + DPRINTF("qemu: hpet_ram_writel HPET_TN_CMP + 4\n"); + if (!timer_is_periodic(timer) + || (timer->config & HPET_TN_SETVAL)) { + timer->cmp = (timer->cmp & 0xffffffffULL) | new_val << 32; + } else { + /* + * FIXME: Clamp period to reasonable min value? + * Clamp period to reasonable max value + */ + new_val &= (timer->config & HPET_TN_32BIT ? ~0u : ~0ull) >> 1; + timer->period = + (timer->period & 0xffffffffULL) | new_val << 32; } timer->config &= ~HPET_TN_SETVAL; - if (hpet_enabled()) + if (hpet_enabled()) { hpet_set_timer(timer); - break; - case HPET_TN_CMP + 4: // comparator register high order - DPRINTF("qemu: hpet_ram_writel HPET_TN_CMP + 4\n"); - if (!timer_is_periodic(timer) || - (timer->config & HPET_TN_SETVAL)) - timer->cmp = (timer->cmp & 0xffffffffULL) - | new_val << 32; - else { - /* - * FIXME: Clamp period to reasonable min value? - * Clamp period to reasonable max value - */ - new_val &= (timer->config - & HPET_TN_32BIT ? ~0u : ~0ull) >> 1; - timer->period = (timer->period & 0xffffffffULL) - | new_val << 32; } - timer->config &= ~HPET_TN_SETVAL; - if (hpet_enabled()) - hpet_set_timer(timer); - break; - case HPET_TN_ROUTE + 4: - DPRINTF("qemu: hpet_ram_writel HPET_TN_ROUTE + 4\n"); - break; - default: - DPRINTF("qemu: invalid hpet_ram_writel\n"); break; + case HPET_TN_ROUTE + 4: + DPRINTF("qemu: hpet_ram_writel HPET_TN_ROUTE + 4\n"); + break; + default: + DPRINTF("qemu: invalid hpet_ram_writel\n"); + break; } return; } else { switch (index) { - case HPET_ID: - return; - case HPET_CFG: - val = hpet_fixup_reg(new_val, old_val, HPET_CFG_WRITE_MASK); - s->config = (s->config & 0xffffffff00000000ULL) | val; - if (activating_bit(old_val, new_val, HPET_CFG_ENABLE)) { - /* Enable main counter and interrupt generation. */ - s->hpet_offset = ticks_to_ns(s->hpet_counter) - - qemu_get_clock(vm_clock); - for (i = 0; i < HPET_NUM_TIMERS; i++) - if ((&s->timer[i])->cmp != ~0ULL) - hpet_set_timer(&s->timer[i]); - } - else if (deactivating_bit(old_val, new_val, HPET_CFG_ENABLE)) { - /* Halt main counter and disable interrupt generation. */ - s->hpet_counter = hpet_get_ticks(); - for (i = 0; i < HPET_NUM_TIMERS; i++) - hpet_del_timer(&s->timer[i]); + case HPET_ID: + return; + case HPET_CFG: + val = hpet_fixup_reg(new_val, old_val, HPET_CFG_WRITE_MASK); + s->config = (s->config & 0xffffffff00000000ULL) | val; + if (activating_bit(old_val, new_val, HPET_CFG_ENABLE)) { + /* Enable main counter and interrupt generation. */ + s->hpet_offset = + ticks_to_ns(s->hpet_counter) - qemu_get_clock(vm_clock); + for (i = 0; i < HPET_NUM_TIMERS; i++) { + if ((&s->timer[i])->cmp != ~0ULL) { + hpet_set_timer(&s->timer[i]); + } } - /* i8254 and RTC are disabled when HPET is in legacy mode */ - if (activating_bit(old_val, new_val, HPET_CFG_LEGACY)) { - hpet_pit_disable(); - } else if (deactivating_bit(old_val, new_val, HPET_CFG_LEGACY)) { - hpet_pit_enable(); + } else if (deactivating_bit(old_val, new_val, HPET_CFG_ENABLE)) { + /* Halt main counter and disable interrupt generation. */ + s->hpet_counter = hpet_get_ticks(); + for (i = 0; i < HPET_NUM_TIMERS; i++) { + hpet_del_timer(&s->timer[i]); } - break; - case HPET_CFG + 4: - DPRINTF("qemu: invalid HPET_CFG+4 write \n"); - break; - case HPET_STATUS: - /* FIXME: need to handle level-triggered interrupts */ - break; - case HPET_COUNTER: - if (hpet_enabled()) - printf("qemu: Writing counter while HPET enabled!\n"); - s->hpet_counter = (s->hpet_counter & 0xffffffff00000000ULL) - | value; - DPRINTF("qemu: HPET counter written. ctr = %#x -> %" PRIx64 "\n", - value, s->hpet_counter); - break; - case HPET_COUNTER + 4: - if (hpet_enabled()) - printf("qemu: Writing counter while HPET enabled!\n"); - s->hpet_counter = (s->hpet_counter & 0xffffffffULL) - | (((uint64_t)value) << 32); - DPRINTF("qemu: HPET counter + 4 written. ctr = %#x -> %" PRIx64 "\n", - value, s->hpet_counter); - break; - default: - DPRINTF("qemu: invalid hpet_ram_writel\n"); - break; + } + /* i8254 and RTC are disabled when HPET is in legacy mode */ + if (activating_bit(old_val, new_val, HPET_CFG_LEGACY)) { + hpet_pit_disable(); + } else if (deactivating_bit(old_val, new_val, HPET_CFG_LEGACY)) { + hpet_pit_enable(); + } + break; + case HPET_CFG + 4: + DPRINTF("qemu: invalid HPET_CFG+4 write \n"); + break; + case HPET_STATUS: + /* FIXME: need to handle level-triggered interrupts */ + break; + case HPET_COUNTER: + if (hpet_enabled()) { + printf("qemu: Writing counter while HPET enabled!\n"); + } + s->hpet_counter = + (s->hpet_counter & 0xffffffff00000000ULL) | value; + DPRINTF("qemu: HPET counter written. ctr = %#x -> %" PRIx64 "\n", + value, s->hpet_counter); + break; + case HPET_COUNTER + 4: + if (hpet_enabled()) { + printf("qemu: Writing counter while HPET enabled!\n"); + } + s->hpet_counter = + (s->hpet_counter & 0xffffffffULL) | (((uint64_t)value) << 32); + DPRINTF("qemu: HPET counter + 4 written. ctr = %#x -> %" PRIx64 "\n", + value, s->hpet_counter); + break; + default: + DPRINTF("qemu: invalid hpet_ram_writel\n"); + break; } } } @@ -533,13 +565,15 @@ static CPUWriteMemoryFunc * const hpet_ram_write[] = { hpet_ram_writel, }; -static void hpet_reset(void *opaque) { +static void hpet_reset(void *opaque) +{ HPETState *s = opaque; int i; static int count = 0; - for (i=0; i<HPET_NUM_TIMERS; i++) { + for (i = 0; i < HPET_NUM_TIMERS; i++) { HPETTimer *timer = &s->timer[i]; + hpet_del_timer(timer); timer->tn = i; timer->cmp = ~0ULL; @@ -557,19 +591,22 @@ static void hpet_reset(void *opaque) { s->capability = 0x8086a201ULL; s->capability |= ((HPET_CLK_PERIOD) << 32); s->config = 0ULL; - if (count > 0) + if (count > 0) { /* we don't enable pit when hpet_reset is first called (by hpet_init) * because hpet is taking over for pit here. On subsequent invocations, * hpet_reset is called due to system reset. At this point control must * be returned to pit until SW reenables hpet. */ hpet_pit_enable(); + } count = 1; } -void hpet_init(qemu_irq *irq) { +void hpet_init(qemu_irq *irq) +{ int i, iomemtype; + HPETTimer *timer; HPETState *s; DPRINTF ("hpet_init\n"); @@ -577,8 +614,8 @@ void hpet_init(qemu_irq *irq) { s = qemu_mallocz(sizeof(HPETState)); hpet_statep = s; s->irqs = irq; - for (i=0; i<HPET_NUM_TIMERS; i++) { - HPETTimer *timer = &s->timer[i]; + for (i = 0; i < HPET_NUM_TIMERS; i++) { + timer = &s->timer[i]; timer->qemu_timer = qemu_new_timer(vm_clock, hpet_timer, timer); } vmstate_register(-1, &vmstate_hpet, s); |