aboutsummaryrefslogtreecommitdiff
path: root/hw/hpet.c
diff options
context:
space:
mode:
Diffstat (limited to 'hw/hpet.c')
-rw-r--r--hw/hpet.c413
1 files changed, 225 insertions, 188 deletions
diff --git a/hw/hpet.c b/hw/hpet.c
index 198090643e..2836fb0b26 100644
--- a/hw/hpet.c
+++ b/hw/hpet.c
@@ -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);