#include "hw.h"
#include "pc.h"
-#include "console.h"
+#include "ui/console.h"
#include "qemu-timer.h"
#include "hpet_emul.h"
#include "sysbus.h"
#include "mc146818rtc.h"
+#include "i8254.h"
//#define HPET_DEBUG
#ifdef HPET_DEBUG
#define DPRINTF(...)
#endif
+#define HPET_MSI_SUPPORT 0
+
struct HPETState;
typedef struct HPETTimer { /* timers */
uint8_t tn; /*timer number*/
/* Memory-mapped, software visible timer registers */
uint64_t config; /* configuration/cap */
uint64_t cmp; /* comparator */
- uint64_t fsb; /* FSB route, not supported now */
+ uint64_t fsb; /* FSB route */
/* 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
typedef struct HPETState {
SysBusDevice busdev;
+ MemoryRegion iomem;
uint64_t hpet_offset;
qemu_irq irqs[HPET_NUM_IRQ_ROUTES];
+ uint32_t flags;
uint8_t rtc_irq_level;
+ qemu_irq pit_enabled;
uint8_t num_timers;
HPETTimer timer[HPET_MAX_TIMERS];
uint64_t config; /* configuration */
uint64_t isr; /* interrupt status reg */
uint64_t hpet_counter; /* main counter */
+ uint8_t hpet_id; /* instance id */
} HPETState;
static uint32_t hpet_in_legacy_mode(HPETState *s)
return (timer->config & HPET_TN_INT_ROUTE_MASK) >> HPET_TN_INT_ROUTE_SHIFT;
}
+static uint32_t timer_fsb_route(HPETTimer *t)
+{
+ return t->config & HPET_TN_FSB_ENABLE;
+}
+
static uint32_t hpet_enabled(HPETState *s)
{
return s->config & HPET_CFG_ENABLE;
static uint64_t hpet_get_ticks(HPETState *s)
{
- return ns_to_ticks(qemu_get_clock(vm_clock) + s->hpet_offset);
+ return ns_to_ticks(qemu_get_clock_ns(vm_clock) + s->hpet_offset);
}
/*
cmp = (uint32_t)t->cmp;
diff = cmp - (uint32_t)current;
- diff = (int32_t)diff > 0 ? diff : (uint32_t)0;
+ diff = (int32_t)diff > 0 ? diff : (uint32_t)1;
return (uint64_t)diff;
} else {
uint64_t diff, cmp;
cmp = t->cmp;
diff = cmp - current;
- diff = (int64_t)diff > 0 ? diff : (uint64_t)0;
+ diff = (int64_t)diff > 0 ? diff : (uint64_t)1;
return diff;
}
}
mask = 1 << timer->tn;
if (!set || !timer_enabled(timer) || !hpet_enabled(timer->state)) {
s->isr &= ~mask;
- qemu_irq_lower(s->irqs[route]);
+ if (!timer_fsb_route(timer)) {
+ qemu_irq_lower(s->irqs[route]);
+ }
+ } else if (timer_fsb_route(timer)) {
+ stl_le_phys(timer->fsb >> 32, timer->fsb & 0xffffffff);
} else if (timer->config & HPET_TN_TYPE_LEVEL) {
s->isr |= mask;
qemu_irq_raise(s->irqs[route]);
HPETState *s = opaque;
/* Recalculate the offset between the main counter and guest time */
- s->hpet_offset = ticks_to_ns(s->hpet_counter) - qemu_get_clock(vm_clock);
+ s->hpet_offset = ticks_to_ns(s->hpet_counter) - qemu_get_clock_ns(vm_clock);
/* Push number of timers into capability returned via HPET_ID */
s->capability &= ~HPET_ID_NUM_TIM_MASK;
s->capability |= (s->num_timers - 1) << HPET_ID_NUM_TIM_SHIFT;
+ hpet_cfg.hpet[s->hpet_id].event_timer_block_id = (uint32_t)s->capability;
+
+ /* Derive HPET_MSI_SUPPORT from the capability of the first timer. */
+ s->flags &= ~(1 << HPET_MSI_SUPPORT);
+ if (s->timer[0].config & HPET_TN_FSB_CAP) {
+ s->flags |= 1 << HPET_MSI_SUPPORT;
+ }
return 0;
}
+static bool hpet_rtc_irq_level_needed(void *opaque)
+{
+ HPETState *s = opaque;
+
+ return s->rtc_irq_level != 0;
+}
+
+static const VMStateDescription vmstate_hpet_rtc_irq_level = {
+ .name = "hpet/rtc_irq_level",
+ .version_id = 1,
+ .minimum_version_id = 1,
+ .minimum_version_id_old = 1,
+ .fields = (VMStateField[]) {
+ VMSTATE_UINT8(rtc_irq_level, HPETState),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
static const VMStateDescription vmstate_hpet_timer = {
.name = "hpet_timer",
.version_id = 1,
VMSTATE_STRUCT_VARRAY_UINT8(timer, HPETState, num_timers, 0,
vmstate_hpet_timer, HPETTimer),
VMSTATE_END_OF_LIST()
+ },
+ .subsections = (VMStateSubsection[]) {
+ {
+ .vmsd = &vmstate_hpet_rtc_irq_level,
+ .needed = hpet_rtc_irq_level_needed,
+ }, {
+ /* empty */
+ }
}
};
}
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_get_clock_ns(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) +
+ qemu_mod_timer(t->qemu_timer, qemu_get_clock_ns(vm_clock) +
(int64_t)ticks_to_ns(diff));
t->wrap_flag = 0;
}
}
}
qemu_mod_timer(t->qemu_timer,
- qemu_get_clock(vm_clock) + (int64_t)ticks_to_ns(diff));
+ qemu_get_clock_ns(vm_clock) + (int64_t)ticks_to_ns(diff));
}
static void hpet_del_timer(HPETTimer *t)
}
#ifdef HPET_DEBUG
-static uint32_t hpet_ram_readb(void *opaque, target_phys_addr_t addr)
+static uint32_t hpet_ram_readb(void *opaque, hwaddr addr)
{
printf("qemu: hpet_read b at %" PRIx64 "\n", addr);
return 0;
}
-static uint32_t hpet_ram_readw(void *opaque, target_phys_addr_t addr)
+static uint32_t hpet_ram_readw(void *opaque, hwaddr addr)
{
printf("qemu: hpet_read w at %" PRIx64 "\n", addr);
return 0;
}
#endif
-static uint32_t hpet_ram_readl(void *opaque, target_phys_addr_t addr)
+static uint64_t hpet_ram_read(void *opaque, hwaddr addr,
+ unsigned size)
{
HPETState *s = opaque;
uint64_t cur_tick, index;
case HPET_TN_CMP + 4:
return timer->cmp >> 32;
case HPET_TN_ROUTE:
+ return timer->fsb;
+ case HPET_TN_ROUTE + 4:
return timer->fsb >> 32;
default:
DPRINTF("qemu: invalid hpet_ram_readl\n");
case HPET_CFG:
return s->config;
case HPET_CFG + 4:
- DPRINTF("qemu: invalid HPET_CFG + 4 hpet_ram_readl \n");
+ DPRINTF("qemu: invalid HPET_CFG + 4 hpet_ram_readl\n");
return 0;
case HPET_COUNTER:
if (hpet_enabled(s)) {
return 0;
}
-#ifdef HPET_DEBUG
-static void hpet_ram_writeb(void *opaque, target_phys_addr_t addr,
- uint32_t value)
-{
- printf("qemu: invalid hpet_write b at %" PRIx64 " = %#x\n",
- addr, value);
-}
-
-static void hpet_ram_writew(void *opaque, target_phys_addr_t addr,
- uint32_t value)
-{
- printf("qemu: invalid hpet_write w at %" PRIx64 " = %#x\n",
- addr, value);
-}
-#endif
-
-static void hpet_ram_writel(void *opaque, target_phys_addr_t addr,
- uint32_t value)
+static void hpet_ram_write(void *opaque, hwaddr addr,
+ uint64_t value, unsigned size)
{
int i;
HPETState *s = opaque;
DPRINTF("qemu: Enter hpet_ram_writel at %" PRIx64 " = %#x\n", addr, value);
index = addr;
- old_val = hpet_ram_readl(opaque, addr);
+ old_val = hpet_ram_read(opaque, addr, 4);
new_val = value;
/*address range of all TN regs*/
uint8_t timer_id = (addr - 0x100) / 0x20;
HPETTimer *timer = &s->timer[timer_id];
- DPRINTF("qemu: hpet_ram_writel timer_id = %#x \n", timer_id);
+ DPRINTF("qemu: hpet_ram_writel timer_id = %#x\n", timer_id);
if (timer_id > s->num_timers) {
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");
+ if (activating_bit(old_val, new_val, HPET_TN_FSB_ENABLE)) {
+ update_irq(timer, 0);
+ }
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) {
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");
+ DPRINTF("qemu: hpet_ram_writel HPET_TN_CMP\n");
if (timer->config & HPET_TN_32BIT) {
new_val = (uint32_t)new_val;
}
hpet_set_timer(timer);
}
break;
+ case HPET_TN_ROUTE:
+ timer->fsb = (timer->fsb & 0xffffffff00000000ULL) | new_val;
+ break;
case HPET_TN_ROUTE + 4:
- DPRINTF("qemu: hpet_ram_writel HPET_TN_ROUTE + 4\n");
+ timer->fsb = (new_val << 32) | (timer->fsb & 0xffffffff);
break;
default:
DPRINTF("qemu: invalid hpet_ram_writel\n");
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);
+ ticks_to_ns(s->hpet_counter) - qemu_get_clock_ns(vm_clock);
for (i = 0; i < s->num_timers; i++) {
if ((&s->timer[i])->cmp != ~0ULL) {
hpet_set_timer(&s->timer[i]);
hpet_del_timer(&s->timer[i]);
}
}
- /* i8254 and RTC are disabled when HPET is in legacy mode */
+ /* i8254 and RTC output pins are disabled
+ * when HPET is in legacy mode */
if (activating_bit(old_val, new_val, HPET_CFG_LEGACY)) {
- hpet_pit_disable();
+ qemu_set_irq(s->pit_enabled, 0);
+ qemu_irq_lower(s->irqs[0]);
qemu_irq_lower(s->irqs[RTC_ISA_IRQ]);
} else if (deactivating_bit(old_val, new_val, HPET_CFG_LEGACY)) {
- hpet_pit_enable();
+ qemu_irq_lower(s->irqs[0]);
+ qemu_set_irq(s->pit_enabled, 1);
qemu_set_irq(s->irqs[RTC_ISA_IRQ], s->rtc_irq_level);
}
break;
case HPET_CFG + 4:
- DPRINTF("qemu: invalid HPET_CFG+4 write \n");
+ DPRINTF("qemu: invalid HPET_CFG+4 write\n");
break;
case HPET_STATUS:
val = new_val & s->isr;
}
}
-static CPUReadMemoryFunc * const hpet_ram_read[] = {
-#ifdef HPET_DEBUG
- hpet_ram_readb,
- hpet_ram_readw,
-#else
- NULL,
- NULL,
-#endif
- hpet_ram_readl,
-};
-
-static CPUWriteMemoryFunc * const hpet_ram_write[] = {
-#ifdef HPET_DEBUG
- hpet_ram_writeb,
- hpet_ram_writew,
-#else
- NULL,
- NULL,
-#endif
- hpet_ram_writel,
+static const MemoryRegionOps hpet_ram_ops = {
+ .read = hpet_ram_read,
+ .write = hpet_ram_write,
+ .valid = {
+ .min_access_size = 4,
+ .max_access_size = 4,
+ },
+ .endianness = DEVICE_NATIVE_ENDIAN,
};
static void hpet_reset(DeviceState *d)
{
HPETState *s = FROM_SYSBUS(HPETState, sysbus_from_qdev(d));
int i;
- static int count = 0;
for (i = 0; i < s->num_timers; i++) {
HPETTimer *timer = &s->timer[i];
hpet_del_timer(timer);
timer->cmp = ~0ULL;
- timer->config = HPET_TN_PERIODIC_CAP | HPET_TN_SIZE_CAP;
+ timer->config = HPET_TN_PERIODIC_CAP | HPET_TN_SIZE_CAP;
+ if (s->flags & (1 << HPET_MSI_SUPPORT)) {
+ timer->config |= HPET_TN_FSB_CAP;
+ }
/* advertise availability of ioapic inti2 */
timer->config |= 0x00000004ULL << 32;
timer->period = 0ULL;
timer->wrap_flag = 0;
}
+ qemu_set_irq(s->pit_enabled, 1);
s->hpet_counter = 0ULL;
s->hpet_offset = 0ULL;
- /* 64-bit main counter; LegacyReplacementRoute. */
- s->capability = 0x8086a001ULL;
- s->capability |= (s->num_timers - 1) << HPET_ID_NUM_TIM_SHIFT;
- s->capability |= ((HPET_CLK_PERIOD) << 32);
s->config = 0ULL;
- 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;
+ hpet_cfg.hpet[s->hpet_id].event_timer_block_id = (uint32_t)s->capability;
+ hpet_cfg.hpet[s->hpet_id].address = sysbus_from_qdev(d)->mmio[0].addr;
+
+ /* to document that the RTC lowers its output on reset as well */
+ s->rtc_irq_level = 0;
}
-static void hpet_handle_rtc_irq(void *opaque, int n, int level)
+static void hpet_handle_legacy_irq(void *opaque, int n, int level)
{
HPETState *s = FROM_SYSBUS(HPETState, opaque);
- s->rtc_irq_level = level;
- if (!hpet_in_legacy_mode(s)) {
- qemu_set_irq(s->irqs[RTC_ISA_IRQ], level);
+ if (n == HPET_LEGACY_PIT_INT) {
+ if (!hpet_in_legacy_mode(s)) {
+ qemu_set_irq(s->irqs[0], level);
+ }
+ } else {
+ s->rtc_irq_level = level;
+ if (!hpet_in_legacy_mode(s)) {
+ qemu_set_irq(s->irqs[RTC_ISA_IRQ], level);
+ }
}
}
static int hpet_init(SysBusDevice *dev)
{
HPETState *s = FROM_SYSBUS(HPETState, dev);
- int i, iomemtype;
+ int i;
HPETTimer *timer;
+ if (hpet_cfg.count == UINT8_MAX) {
+ /* first instance */
+ hpet_cfg.count = 0;
+ }
+
+ if (hpet_cfg.count == 8) {
+ fprintf(stderr, "Only 8 instances of HPET is allowed\n");
+ return -1;
+ }
+
+ s->hpet_id = hpet_cfg.count++;
+
for (i = 0; i < HPET_NUM_IRQ_ROUTES; i++) {
sysbus_init_irq(dev, &s->irqs[i]);
}
}
for (i = 0; i < HPET_MAX_TIMERS; i++) {
timer = &s->timer[i];
- timer->qemu_timer = qemu_new_timer(vm_clock, hpet_timer, timer);
+ timer->qemu_timer = qemu_new_timer_ns(vm_clock, hpet_timer, timer);
timer->tn = i;
timer->state = s;
}
- isa_reserve_irq(RTC_ISA_IRQ);
- qdev_init_gpio_in(&dev->qdev, hpet_handle_rtc_irq, 1);
+ /* 64-bit main counter; LegacyReplacementRoute. */
+ s->capability = 0x8086a001ULL;
+ s->capability |= (s->num_timers - 1) << HPET_ID_NUM_TIM_SHIFT;
+ s->capability |= ((HPET_CLK_PERIOD) << 32);
+
+ qdev_init_gpio_in(&dev->qdev, hpet_handle_legacy_irq, 2);
+ qdev_init_gpio_out(&dev->qdev, &s->pit_enabled, 1);
/* HPET Area */
- iomemtype = cpu_register_io_memory(hpet_ram_read,
- hpet_ram_write, s);
- sysbus_init_mmio(dev, 0x400, iomemtype);
+ memory_region_init_io(&s->iomem, &hpet_ram_ops, s, "hpet", 0x400);
+ sysbus_init_mmio(dev, &s->iomem);
return 0;
}
-static SysBusDeviceInfo hpet_device_info = {
- .qdev.name = "hpet",
- .qdev.size = sizeof(HPETState),
- .qdev.no_user = 1,
- .qdev.vmsd = &vmstate_hpet,
- .qdev.reset = hpet_reset,
- .init = hpet_init,
- .qdev.props = (Property[]) {
- DEFINE_PROP_UINT8("timers", HPETState, num_timers, HPET_MIN_TIMERS),
- DEFINE_PROP_END_OF_LIST(),
- },
+static Property hpet_device_properties[] = {
+ DEFINE_PROP_UINT8("timers", HPETState, num_timers, HPET_MIN_TIMERS),
+ DEFINE_PROP_BIT("msi", HPETState, flags, HPET_MSI_SUPPORT, false),
+ DEFINE_PROP_END_OF_LIST(),
+};
+
+static void hpet_device_class_init(ObjectClass *klass, void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(klass);
+ SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass);
+
+ k->init = hpet_init;
+ dc->no_user = 1;
+ dc->reset = hpet_reset;
+ dc->vmsd = &vmstate_hpet;
+ dc->props = hpet_device_properties;
+}
+
+static TypeInfo hpet_device_info = {
+ .name = "hpet",
+ .parent = TYPE_SYS_BUS_DEVICE,
+ .instance_size = sizeof(HPETState),
+ .class_init = hpet_device_class_init,
};
-static void hpet_register_device(void)
+static void hpet_register_types(void)
{
- sysbus_register_withprop(&hpet_device_info);
+ type_register_static(&hpet_device_info);
}
-device_init(hpet_register_device)
+type_init(hpet_register_types)