aboutsummaryrefslogtreecommitdiff
path: root/hw
diff options
context:
space:
mode:
authorPeter Maydell <peter.maydell@linaro.org>2016-10-24 19:37:33 +0100
committerPeter Maydell <peter.maydell@linaro.org>2016-10-24 19:37:34 +0100
commitfe4c04071f702e008da7db06d0a220b27e1ab3ac (patch)
tree97c3699d2cd15ec48436d53218178b903389f6e3 /hw
parent45b567d645c22fb79f4698a13396718084f7cf72 (diff)
parentcc083d8a25e0a886c3cd4bea0bf57ac4e896fa3f (diff)
downloadqemu-fe4c04071f702e008da7db06d0a220b27e1ab3ac.zip
qemu-fe4c04071f702e008da7db06d0a220b27e1ab3ac.tar.gz
qemu-fe4c04071f702e008da7db06d0a220b27e1ab3ac.tar.bz2
Merge remote-tracking branch 'remotes/pmaydell/tags/pull-target-arm-20161024' into staging
target-arm queue: * support variable (runtime-determined) page sizes, for a nearly-20% speedup of TCG for ARMv7 and v8 CPUs with 4K pages * ptimer: add tests, support more flexible behaviour around what happens on the "zero" tick, use ptimer for a9gtimer * virt: ACPI: Add IORT Structure definition * i2c: Fix SMBus read transactions to avoid double events * timer: stm32f2xx_timer: add check for prescaler value * QOMify musicpal, pxa2xx_gpio, strongarm, pl110 * target-arm: Implement new HLT trap for semihosting * i2c: Add asserts for second smbus i2c_start_transfer() # gpg: Signature made Mon 24 Oct 2016 18:24:17 BST # gpg: using RSA key 0x3C2525ED14360CDE # gpg: Good signature from "Peter Maydell <peter.maydell@linaro.org>" # gpg: aka "Peter Maydell <pmaydell@gmail.com>" # gpg: aka "Peter Maydell <pmaydell@chiark.greenend.org.uk>" # Primary key fingerprint: E1A5 C593 CD41 9DE2 8E83 15CF 3C25 25ED 1436 0CDE * remotes/pmaydell/tags/pull-target-arm-20161024: (32 commits) i2c: Add asserts for second smbus i2c_start_transfer() target-arm: Implement new HLT trap for semihosting hw/display: QOM'ify pl110.c hw/arm: QOM'ify strongarm.c hw/arm: QOM'ify pxa2xx_gpio.c hw/arm: QOM'ify musicpal.c timer: stm32f2xx_timer: add check for prescaler value i2c: Fix SMBus read transactions to avoid double events timer: a9gtimer: remove loop to auto-increment comparator ARM: Virt: ACPI: Build an IORT table with RC and ITS nodes ACPI: Add IORT Structure definition tests: Add tests for the ARM MPTimer arm_mptimer: Convert to use ptimer tests: ptimer: Replace 10000 with 1 tests: ptimer: Change the copyright comment tests: ptimer: Add tests for "no counter round down" policy hw/ptimer: Add "no counter round down" policy tests: ptimer: Add tests for "no immediate reload" policy hw/ptimer: Add "no immediate reload" policy tests: ptimer: Add tests for "no immediate trigger" policy ... Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
Diffstat (limited to 'hw')
-rw-r--r--hw/arm/musicpal.c88
-rw-r--r--hw/arm/pxa2xx_gpio.c25
-rw-r--r--hw/arm/strongarm.c15
-rw-r--r--hw/arm/virt-acpi-build.c71
-rw-r--r--hw/arm/virt.c4
-rw-r--r--hw/core/ptimer.c130
-rw-r--r--hw/display/pl110.c8
-rw-r--r--hw/i2c/core.c39
-rw-r--r--hw/i2c/smbus.c12
-rw-r--r--hw/timer/a9gtimer.c14
-rw-r--r--hw/timer/arm_mptimer.c149
-rw-r--r--hw/timer/stm32f2xx_timer.c2
12 files changed, 373 insertions, 184 deletions
diff --git a/hw/arm/musicpal.c b/hw/arm/musicpal.c
index 7527037..cbbca4e 100644
--- a/hw/arm/musicpal.c
+++ b/hw/arm/musicpal.c
@@ -384,18 +384,24 @@ static NetClientInfo net_mv88w8618_info = {
.cleanup = eth_cleanup,
};
-static int mv88w8618_eth_init(SysBusDevice *sbd)
+static void mv88w8618_eth_init(Object *obj)
{
+ SysBusDevice *sbd = SYS_BUS_DEVICE(obj);
DeviceState *dev = DEVICE(sbd);
mv88w8618_eth_state *s = MV88W8618_ETH(dev);
sysbus_init_irq(sbd, &s->irq);
- s->nic = qemu_new_nic(&net_mv88w8618_info, &s->conf,
- object_get_typename(OBJECT(dev)), dev->id, s);
- memory_region_init_io(&s->iomem, OBJECT(s), &mv88w8618_eth_ops, s,
+ memory_region_init_io(&s->iomem, obj, &mv88w8618_eth_ops, s,
"mv88w8618-eth", MP_ETH_SIZE);
sysbus_init_mmio(sbd, &s->iomem);
- return 0;
+}
+
+static void mv88w8618_eth_realize(DeviceState *dev, Error **errp)
+{
+ mv88w8618_eth_state *s = MV88W8618_ETH(dev);
+
+ s->nic = qemu_new_nic(&net_mv88w8618_info, &s->conf,
+ object_get_typename(OBJECT(dev)), dev->id, s);
}
static const VMStateDescription mv88w8618_eth_vmsd = {
@@ -423,17 +429,17 @@ static Property mv88w8618_eth_properties[] = {
static void mv88w8618_eth_class_init(ObjectClass *klass, void *data)
{
DeviceClass *dc = DEVICE_CLASS(klass);
- SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass);
- k->init = mv88w8618_eth_init;
dc->vmsd = &mv88w8618_eth_vmsd;
dc->props = mv88w8618_eth_properties;
+ dc->realize = mv88w8618_eth_realize;
}
static const TypeInfo mv88w8618_eth_info = {
.name = TYPE_MV88W8618_ETH,
.parent = TYPE_SYS_BUS_DEVICE,
.instance_size = sizeof(mv88w8618_eth_state),
+ .instance_init = mv88w8618_eth_init,
.class_init = mv88w8618_eth_class_init,
};
@@ -615,23 +621,26 @@ static const GraphicHwOps musicpal_gfx_ops = {
.gfx_update = lcd_refresh,
};
-static int musicpal_lcd_init(SysBusDevice *sbd)
+static void musicpal_lcd_realize(DeviceState *dev, Error **errp)
+{
+ musicpal_lcd_state *s = MUSICPAL_LCD(dev);
+ s->con = graphic_console_init(dev, 0, &musicpal_gfx_ops, s);
+ qemu_console_resize(s->con, 128 * 3, 64 * 3);
+}
+
+static void musicpal_lcd_init(Object *obj)
{
+ SysBusDevice *sbd = SYS_BUS_DEVICE(obj);
DeviceState *dev = DEVICE(sbd);
musicpal_lcd_state *s = MUSICPAL_LCD(dev);
s->brightness = 7;
- memory_region_init_io(&s->iomem, OBJECT(s), &musicpal_lcd_ops, s,
+ memory_region_init_io(&s->iomem, obj, &musicpal_lcd_ops, s,
"musicpal-lcd", MP_LCD_SIZE);
sysbus_init_mmio(sbd, &s->iomem);
- s->con = graphic_console_init(dev, 0, &musicpal_gfx_ops, s);
- qemu_console_resize(s->con, 128*3, 64*3);
-
qdev_init_gpio_in(dev, musicpal_lcd_gpio_brightness_in, 3);
-
- return 0;
}
static const VMStateDescription musicpal_lcd_vmsd = {
@@ -652,16 +661,16 @@ static const VMStateDescription musicpal_lcd_vmsd = {
static void musicpal_lcd_class_init(ObjectClass *klass, void *data)
{
DeviceClass *dc = DEVICE_CLASS(klass);
- SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass);
- k->init = musicpal_lcd_init;
dc->vmsd = &musicpal_lcd_vmsd;
+ dc->realize = musicpal_lcd_realize;
}
static const TypeInfo musicpal_lcd_info = {
.name = TYPE_MUSICPAL_LCD,
.parent = TYPE_SYS_BUS_DEVICE,
.instance_size = sizeof(musicpal_lcd_state),
+ .instance_init = musicpal_lcd_init,
.class_init = musicpal_lcd_class_init,
};
@@ -748,16 +757,16 @@ static const MemoryRegionOps mv88w8618_pic_ops = {
.endianness = DEVICE_NATIVE_ENDIAN,
};
-static int mv88w8618_pic_init(SysBusDevice *dev)
+static void mv88w8618_pic_init(Object *obj)
{
+ SysBusDevice *dev = SYS_BUS_DEVICE(obj);
mv88w8618_pic_state *s = MV88W8618_PIC(dev);
qdev_init_gpio_in(DEVICE(dev), mv88w8618_pic_set_irq, 32);
sysbus_init_irq(dev, &s->parent_irq);
- memory_region_init_io(&s->iomem, OBJECT(s), &mv88w8618_pic_ops, s,
+ memory_region_init_io(&s->iomem, obj, &mv88w8618_pic_ops, s,
"musicpal-pic", MP_PIC_SIZE);
sysbus_init_mmio(dev, &s->iomem);
- return 0;
}
static const VMStateDescription mv88w8618_pic_vmsd = {
@@ -774,9 +783,7 @@ static const VMStateDescription mv88w8618_pic_vmsd = {
static void mv88w8618_pic_class_init(ObjectClass *klass, void *data)
{
DeviceClass *dc = DEVICE_CLASS(klass);
- SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass);
- k->init = mv88w8618_pic_init;
dc->reset = mv88w8618_pic_reset;
dc->vmsd = &mv88w8618_pic_vmsd;
}
@@ -785,6 +792,7 @@ static const TypeInfo mv88w8618_pic_info = {
.name = TYPE_MV88W8618_PIC,
.parent = TYPE_SYS_BUS_DEVICE,
.instance_size = sizeof(mv88w8618_pic_state),
+ .instance_init = mv88w8618_pic_init,
.class_init = mv88w8618_pic_class_init,
};
@@ -913,8 +921,9 @@ static const MemoryRegionOps mv88w8618_pit_ops = {
.endianness = DEVICE_NATIVE_ENDIAN,
};
-static int mv88w8618_pit_init(SysBusDevice *dev)
+static void mv88w8618_pit_init(Object *obj)
{
+ SysBusDevice *dev = SYS_BUS_DEVICE(obj);
mv88w8618_pit_state *s = MV88W8618_PIT(dev);
int i;
@@ -924,10 +933,9 @@ static int mv88w8618_pit_init(SysBusDevice *dev)
mv88w8618_timer_init(dev, &s->timer[i], 1000000);
}
- memory_region_init_io(&s->iomem, OBJECT(s), &mv88w8618_pit_ops, s,
+ memory_region_init_io(&s->iomem, obj, &mv88w8618_pit_ops, s,
"musicpal-pit", MP_PIT_SIZE);
sysbus_init_mmio(dev, &s->iomem);
- return 0;
}
static const VMStateDescription mv88w8618_timer_vmsd = {
@@ -955,9 +963,7 @@ static const VMStateDescription mv88w8618_pit_vmsd = {
static void mv88w8618_pit_class_init(ObjectClass *klass, void *data)
{
DeviceClass *dc = DEVICE_CLASS(klass);
- SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass);
- k->init = mv88w8618_pit_init;
dc->reset = mv88w8618_pit_reset;
dc->vmsd = &mv88w8618_pit_vmsd;
}
@@ -966,6 +972,7 @@ static const TypeInfo mv88w8618_pit_info = {
.name = TYPE_MV88W8618_PIT,
.parent = TYPE_SYS_BUS_DEVICE,
.instance_size = sizeof(mv88w8618_pit_state),
+ .instance_init = mv88w8618_pit_init,
.class_init = mv88w8618_pit_class_init,
};
@@ -1018,15 +1025,15 @@ static const MemoryRegionOps mv88w8618_flashcfg_ops = {
.endianness = DEVICE_NATIVE_ENDIAN,
};
-static int mv88w8618_flashcfg_init(SysBusDevice *dev)
+static void mv88w8618_flashcfg_init(Object *obj)
{
+ SysBusDevice *dev = SYS_BUS_DEVICE(obj);
mv88w8618_flashcfg_state *s = MV88W8618_FLASHCFG(dev);
s->cfgr0 = 0xfffe4285; /* Default as set by U-Boot for 8 MB flash */
- memory_region_init_io(&s->iomem, OBJECT(s), &mv88w8618_flashcfg_ops, s,
+ memory_region_init_io(&s->iomem, obj, &mv88w8618_flashcfg_ops, s,
"musicpal-flashcfg", MP_FLASHCFG_SIZE);
sysbus_init_mmio(dev, &s->iomem);
- return 0;
}
static const VMStateDescription mv88w8618_flashcfg_vmsd = {
@@ -1042,9 +1049,7 @@ static const VMStateDescription mv88w8618_flashcfg_vmsd = {
static void mv88w8618_flashcfg_class_init(ObjectClass *klass, void *data)
{
DeviceClass *dc = DEVICE_CLASS(klass);
- SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass);
- k->init = mv88w8618_flashcfg_init;
dc->vmsd = &mv88w8618_flashcfg_vmsd;
}
@@ -1052,6 +1057,7 @@ static const TypeInfo mv88w8618_flashcfg_info = {
.name = TYPE_MV88W8618_FLASHCFG,
.parent = TYPE_SYS_BUS_DEVICE,
.instance_size = sizeof(mv88w8618_flashcfg_state),
+ .instance_init = mv88w8618_flashcfg_init,
.class_init = mv88w8618_flashcfg_class_init,
};
@@ -1350,22 +1356,21 @@ static void musicpal_gpio_reset(DeviceState *d)
s->isr = 0;
}
-static int musicpal_gpio_init(SysBusDevice *sbd)
+static void musicpal_gpio_init(Object *obj)
{
+ SysBusDevice *sbd = SYS_BUS_DEVICE(obj);
DeviceState *dev = DEVICE(sbd);
musicpal_gpio_state *s = MUSICPAL_GPIO(dev);
sysbus_init_irq(sbd, &s->irq);
- memory_region_init_io(&s->iomem, OBJECT(s), &musicpal_gpio_ops, s,
+ memory_region_init_io(&s->iomem, obj, &musicpal_gpio_ops, s,
"musicpal-gpio", MP_GPIO_SIZE);
sysbus_init_mmio(sbd, &s->iomem);
qdev_init_gpio_out(dev, s->out, ARRAY_SIZE(s->out));
qdev_init_gpio_in(dev, musicpal_gpio_pin_event, 32);
-
- return 0;
}
static const VMStateDescription musicpal_gpio_vmsd = {
@@ -1386,9 +1391,7 @@ static const VMStateDescription musicpal_gpio_vmsd = {
static void musicpal_gpio_class_init(ObjectClass *klass, void *data)
{
DeviceClass *dc = DEVICE_CLASS(klass);
- SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass);
- k->init = musicpal_gpio_init;
dc->reset = musicpal_gpio_reset;
dc->vmsd = &musicpal_gpio_vmsd;
}
@@ -1397,6 +1400,7 @@ static const TypeInfo musicpal_gpio_info = {
.name = TYPE_MUSICPAL_GPIO,
.parent = TYPE_SYS_BUS_DEVICE,
.instance_size = sizeof(musicpal_gpio_state),
+ .instance_init = musicpal_gpio_init,
.class_init = musicpal_gpio_class_init,
};
@@ -1516,12 +1520,13 @@ static void musicpal_key_event(void *opaque, int keycode)
s->kbd_extended = 0;
}
-static int musicpal_key_init(SysBusDevice *sbd)
+static void musicpal_key_init(Object *obj)
{
+ SysBusDevice *sbd = SYS_BUS_DEVICE(obj);
DeviceState *dev = DEVICE(sbd);
musicpal_key_state *s = MUSICPAL_KEY(dev);
- memory_region_init(&s->iomem, OBJECT(s), "dummy", 0);
+ memory_region_init(&s->iomem, obj, "dummy", 0);
sysbus_init_mmio(sbd, &s->iomem);
s->kbd_extended = 0;
@@ -1530,8 +1535,6 @@ static int musicpal_key_init(SysBusDevice *sbd)
qdev_init_gpio_out(dev, s->out, ARRAY_SIZE(s->out));
qemu_add_kbd_event_handler(musicpal_key_event, s);
-
- return 0;
}
static const VMStateDescription musicpal_key_vmsd = {
@@ -1548,9 +1551,7 @@ static const VMStateDescription musicpal_key_vmsd = {
static void musicpal_key_class_init(ObjectClass *klass, void *data)
{
DeviceClass *dc = DEVICE_CLASS(klass);
- SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass);
- k->init = musicpal_key_init;
dc->vmsd = &musicpal_key_vmsd;
}
@@ -1558,6 +1559,7 @@ static const TypeInfo musicpal_key_info = {
.name = TYPE_MUSICPAL_KEY,
.parent = TYPE_SYS_BUS_DEVICE,
.instance_size = sizeof(musicpal_key_state),
+ .instance_init = musicpal_key_init,
.class_init = musicpal_key_class_init,
};
diff --git a/hw/arm/pxa2xx_gpio.c b/hw/arm/pxa2xx_gpio.c
index 576a8eb..521dbad 100644
--- a/hw/arm/pxa2xx_gpio.c
+++ b/hw/arm/pxa2xx_gpio.c
@@ -280,23 +280,28 @@ DeviceState *pxa2xx_gpio_init(hwaddr base,
return dev;
}
-static int pxa2xx_gpio_initfn(SysBusDevice *sbd)
+static void pxa2xx_gpio_initfn(Object *obj)
{
+ SysBusDevice *sbd = SYS_BUS_DEVICE(obj);
DeviceState *dev = DEVICE(sbd);
PXA2xxGPIOInfo *s = PXA2XX_GPIO(dev);
- s->cpu = ARM_CPU(qemu_get_cpu(s->ncpu));
-
- qdev_init_gpio_in(dev, pxa2xx_gpio_set, s->lines);
- qdev_init_gpio_out(dev, s->handler, s->lines);
-
- memory_region_init_io(&s->iomem, OBJECT(s), &pxa_gpio_ops, s, "pxa2xx-gpio", 0x1000);
+ memory_region_init_io(&s->iomem, obj, &pxa_gpio_ops,
+ s, "pxa2xx-gpio", 0x1000);
sysbus_init_mmio(sbd, &s->iomem);
sysbus_init_irq(sbd, &s->irq0);
sysbus_init_irq(sbd, &s->irq1);
sysbus_init_irq(sbd, &s->irqX);
+}
- return 0;
+static void pxa2xx_gpio_realize(DeviceState *dev, Error **errp)
+{
+ PXA2xxGPIOInfo *s = PXA2XX_GPIO(dev);
+
+ s->cpu = ARM_CPU(qemu_get_cpu(s->ncpu));
+
+ qdev_init_gpio_in(dev, pxa2xx_gpio_set, s->lines);
+ qdev_init_gpio_out(dev, s->handler, s->lines);
}
/*
@@ -336,18 +341,18 @@ static Property pxa2xx_gpio_properties[] = {
static void pxa2xx_gpio_class_init(ObjectClass *klass, void *data)
{
DeviceClass *dc = DEVICE_CLASS(klass);
- SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass);
- k->init = pxa2xx_gpio_initfn;
dc->desc = "PXA2xx GPIO controller";
dc->props = pxa2xx_gpio_properties;
dc->vmsd = &vmstate_pxa2xx_gpio_regs;
+ dc->realize = pxa2xx_gpio_realize;
}
static const TypeInfo pxa2xx_gpio_info = {
.name = TYPE_PXA2XX_GPIO,
.parent = TYPE_SYS_BUS_DEVICE,
.instance_size = sizeof(PXA2xxGPIOInfo),
+ .instance_init = pxa2xx_gpio_initfn,
.class_init = pxa2xx_gpio_class_init,
};
diff --git a/hw/arm/strongarm.c b/hw/arm/strongarm.c
index 85db1e2..3311cc3 100644
--- a/hw/arm/strongarm.c
+++ b/hw/arm/strongarm.c
@@ -1236,6 +1236,11 @@ static void strongarm_uart_init(Object *obj)
s->rx_timeout_timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, strongarm_uart_rx_to, s);
s->tx_timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, strongarm_uart_tx, s);
+}
+
+static void strongarm_uart_realize(DeviceState *dev, Error **errp)
+{
+ StrongARMUARTState *s = STRONGARM_UART(dev);
qemu_chr_fe_set_handlers(&s->chr,
strongarm_uart_can_receive,
@@ -1316,6 +1321,7 @@ static void strongarm_uart_class_init(ObjectClass *klass, void *data)
dc->reset = strongarm_uart_reset;
dc->vmsd = &vmstate_strongarm_uart_regs;
dc->props = strongarm_uart_properties;
+ dc->realize = strongarm_uart_realize;
}
static const TypeInfo strongarm_uart_info = {
@@ -1516,19 +1522,19 @@ static int strongarm_ssp_post_load(void *opaque, int version_id)
return 0;
}
-static int strongarm_ssp_init(SysBusDevice *sbd)
+static void strongarm_ssp_init(Object *obj)
{
+ SysBusDevice *sbd = SYS_BUS_DEVICE(obj);
DeviceState *dev = DEVICE(sbd);
StrongARMSSPState *s = STRONGARM_SSP(dev);
sysbus_init_irq(sbd, &s->irq);
- memory_region_init_io(&s->iomem, OBJECT(s), &strongarm_ssp_ops, s,
+ memory_region_init_io(&s->iomem, obj, &strongarm_ssp_ops, s,
"ssp", 0x1000);
sysbus_init_mmio(sbd, &s->iomem);
s->bus = ssi_create_bus(dev, "ssi");
- return 0;
}
static void strongarm_ssp_reset(DeviceState *dev)
@@ -1558,9 +1564,7 @@ static const VMStateDescription vmstate_strongarm_ssp_regs = {
static void strongarm_ssp_class_init(ObjectClass *klass, void *data)
{
DeviceClass *dc = DEVICE_CLASS(klass);
- SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass);
- k->init = strongarm_ssp_init;
dc->desc = "StrongARM SSP controller";
dc->reset = strongarm_ssp_reset;
dc->vmsd = &vmstate_strongarm_ssp_regs;
@@ -1570,6 +1574,7 @@ static const TypeInfo strongarm_ssp_info = {
.name = TYPE_STRONGARM_SSP,
.parent = TYPE_SYS_BUS_DEVICE,
.instance_size = sizeof(StrongARMSSPState),
+ .instance_init = strongarm_ssp_init,
.class_init = strongarm_ssp_class_init,
};
diff --git a/hw/arm/virt-acpi-build.c b/hw/arm/virt-acpi-build.c
index fa0655a..5fc10df 100644
--- a/hw/arm/virt-acpi-build.c
+++ b/hw/arm/virt-acpi-build.c
@@ -384,6 +384,61 @@ build_rsdp(GArray *rsdp_table, BIOSLinker *linker, unsigned rsdt_tbl_offset)
}
static void
+build_iort(GArray *table_data, BIOSLinker *linker, VirtGuestInfo *guest_info)
+{
+ int iort_start = table_data->len;
+ AcpiIortIdMapping *idmap;
+ AcpiIortItsGroup *its;
+ AcpiIortTable *iort;
+ size_t node_size, iort_length;
+ AcpiIortRC *rc;
+
+ iort = acpi_data_push(table_data, sizeof(*iort));
+
+ iort_length = sizeof(*iort);
+ iort->node_count = cpu_to_le32(2); /* RC and ITS nodes */
+ iort->node_offset = cpu_to_le32(sizeof(*iort));
+
+ /* ITS group node */
+ node_size = sizeof(*its) + sizeof(uint32_t);
+ iort_length += node_size;
+ its = acpi_data_push(table_data, node_size);
+
+ its->type = ACPI_IORT_NODE_ITS_GROUP;
+ its->length = cpu_to_le16(node_size);
+ its->its_count = cpu_to_le32(1);
+ its->identifiers[0] = 0; /* MADT translation_id */
+
+ /* Root Complex Node */
+ node_size = sizeof(*rc) + sizeof(*idmap);
+ iort_length += node_size;
+ rc = acpi_data_push(table_data, node_size);
+
+ rc->type = ACPI_IORT_NODE_PCI_ROOT_COMPLEX;
+ rc->length = cpu_to_le16(node_size);
+ rc->mapping_count = cpu_to_le32(1);
+ rc->mapping_offset = cpu_to_le32(sizeof(*rc));
+
+ /* fully coherent device */
+ rc->memory_properties.cache_coherency = cpu_to_le32(1);
+ rc->memory_properties.memory_flags = 0x3; /* CCA = CPM = DCAS = 1 */
+ rc->pci_segment_number = 0; /* MCFG pci_segment */
+
+ /* Identity RID mapping covering the whole input RID range */
+ idmap = &rc->id_mapping_array[0];
+ idmap->input_base = 0;
+ idmap->id_count = cpu_to_le32(0xFFFF);
+ idmap->output_base = 0;
+ /* output IORT node is the ITS group node (the first node) */
+ idmap->output_reference = cpu_to_le32(iort->node_offset);
+
+ iort->length = cpu_to_le32(iort_length);
+
+ build_header(linker, table_data, (void *)(table_data->data + iort_start),
+ "IORT", table_data->len - iort_start, 0, NULL, NULL);
+}
+
+static void
build_spcr(GArray *table_data, BIOSLinker *linker, VirtGuestInfo *guest_info)
{
AcpiSerialPortConsoleRedirection *spcr;
@@ -667,17 +722,6 @@ void virt_acpi_build(VirtGuestInfo *guest_info, AcpiBuildTables *tables)
ACPI_BUILD_TABLE_FILE, tables_blob,
64, false /* high memory */);
- /*
- * The ACPI v5.1 tables for Hardware-reduced ACPI platform are:
- * RSDP
- * RSDT
- * FADT
- * GTDT
- * MADT
- * MCFG
- * DSDT
- */
-
/* DSDT is pointed to by FADT */
dsdt = tables_blob->len;
build_dsdt(tables_blob, tables->linker, guest_info);
@@ -703,6 +747,11 @@ void virt_acpi_build(VirtGuestInfo *guest_info, AcpiBuildTables *tables)
build_srat(tables_blob, tables->linker, guest_info);
}
+ if (its_class_name() && !guest_info->no_its) {
+ acpi_add_table(table_offsets, tables_blob);
+ build_iort(tables_blob, tables->linker, guest_info);
+ }
+
/* RSDT is pointed to by RSDP */
rsdt = tables_blob->len;
build_rsdt(tables_blob, tables->linker, table_offsets, NULL, NULL);
diff --git a/hw/arm/virt.c b/hw/arm/virt.c
index 895446f..47ec7ff 100644
--- a/hw/arm/virt.c
+++ b/hw/arm/virt.c
@@ -1499,6 +1499,8 @@ static void virt_machine_class_init(ObjectClass *oc, void *data)
mc->block_default_type = IF_VIRTIO;
mc->no_cdrom = 1;
mc->pci_allow_0_address = true;
+ /* We know we will never create a pre-ARMv7 CPU which needs 1K pages */
+ mc->minimum_page_bits = 12;
}
static const TypeInfo virt_machine_info = {
@@ -1570,6 +1572,8 @@ static void virt_machine_2_7_options(MachineClass *mc)
SET_MACHINE_COMPAT(mc, VIRT_COMPAT_2_7);
/* ITS was introduced with 2.8 */
vmc->no_its = true;
+ /* Stick with 1K pages for migration compatibility */
+ mc->minimum_page_bits = 0;
}
DEFINE_VIRT_MACHINE(2, 7)
diff --git a/hw/core/ptimer.c b/hw/core/ptimer.c
index c45c835..3af82af 100644
--- a/hw/core/ptimer.c
+++ b/hw/core/ptimer.c
@@ -13,6 +13,9 @@
#include "sysemu/replay.h"
#include "sysemu/qtest.h"
+#define DELTA_ADJUST 1
+#define DELTA_NO_ADJUST -1
+
struct ptimer_state
{
uint8_t enabled; /* 0 = disabled, 1 = periodic, 2 = oneshot. */
@@ -35,16 +38,21 @@ static void ptimer_trigger(ptimer_state *s)
}
}
-static void ptimer_reload(ptimer_state *s)
+static void ptimer_reload(ptimer_state *s, int delta_adjust)
{
uint32_t period_frac = s->period_frac;
uint64_t period = s->period;
+ uint64_t delta = s->delta;
- if (s->delta == 0) {
+ if (delta == 0 && !(s->policy_mask & PTIMER_POLICY_NO_IMMEDIATE_TRIGGER)) {
ptimer_trigger(s);
- s->delta = s->limit;
}
- if (s->delta == 0 || s->period == 0) {
+
+ if (delta == 0 && !(s->policy_mask & PTIMER_POLICY_NO_IMMEDIATE_RELOAD)) {
+ delta = s->delta = s->limit;
+ }
+
+ if (s->period == 0) {
if (!qtest_enabled()) {
fprintf(stderr, "Timer with period zero, disabling\n");
}
@@ -53,6 +61,39 @@ static void ptimer_reload(ptimer_state *s)
return;
}
+ if (s->policy_mask & PTIMER_POLICY_WRAP_AFTER_ONE_PERIOD) {
+ if (delta_adjust != DELTA_NO_ADJUST) {
+ delta += delta_adjust;
+ }
+ }
+
+ if (delta == 0 && (s->policy_mask & PTIMER_POLICY_CONTINUOUS_TRIGGER)) {
+ if (s->enabled == 1 && s->limit == 0) {
+ delta = 1;
+ }
+ }
+
+ if (delta == 0 && (s->policy_mask & PTIMER_POLICY_NO_IMMEDIATE_TRIGGER)) {
+ if (delta_adjust != DELTA_NO_ADJUST) {
+ delta = 1;
+ }
+ }
+
+ if (delta == 0 && (s->policy_mask & PTIMER_POLICY_NO_IMMEDIATE_RELOAD)) {
+ if (s->enabled == 1 && s->limit != 0) {
+ delta = 1;
+ }
+ }
+
+ if (delta == 0) {
+ if (!qtest_enabled()) {
+ fprintf(stderr, "Timer with delta zero, disabling\n");
+ }
+ timer_del(s->timer);
+ s->enabled = 0;
+ return;
+ }
+
/*
* Artificially limit timeout rate to something
* achievable under QEMU. Otherwise, QEMU spends all
@@ -62,15 +103,15 @@ static void ptimer_reload(ptimer_state *s)
* on the current generation of host machines.
*/
- if (s->enabled == 1 && (s->delta * period < 10000) && !use_icount) {
- period = 10000 / s->delta;
+ if (s->enabled == 1 && (delta * period < 10000) && !use_icount) {
+ period = 10000 / delta;
period_frac = 0;
}
s->last_event = s->next_event;
- s->next_event = s->last_event + s->delta * period;
+ s->next_event = s->last_event + delta * period;
if (period_frac) {
- s->next_event += ((int64_t)period_frac * s->delta) >> 32;
+ s->next_event += ((int64_t)period_frac * delta) >> 32;
}
timer_mod(s->timer, s->next_event);
}
@@ -78,12 +119,35 @@ static void ptimer_reload(ptimer_state *s)
static void ptimer_tick(void *opaque)
{
ptimer_state *s = (ptimer_state *)opaque;
- ptimer_trigger(s);
- s->delta = 0;
+ bool trigger = true;
+
if (s->enabled == 2) {
+ s->delta = 0;
s->enabled = 0;
} else {
- ptimer_reload(s);
+ int delta_adjust = DELTA_ADJUST;
+
+ if (s->delta == 0 || s->limit == 0) {
+ /* If a "continuous trigger" policy is not used and limit == 0,
+ we should error out. delta == 0 means that this tick is
+ caused by a "no immediate reload" policy, so it shouldn't
+ be adjusted. */
+ delta_adjust = DELTA_NO_ADJUST;
+ }
+
+ if (!(s->policy_mask & PTIMER_POLICY_NO_IMMEDIATE_TRIGGER)) {
+ /* Avoid re-trigger on deferred reload if "no immediate trigger"
+ policy isn't used. */
+ trigger = (delta_adjust == DELTA_ADJUST);
+ }
+
+ s->delta = s->limit;
+
+ ptimer_reload(s, delta_adjust);
+ }
+
+ if (trigger) {
+ ptimer_trigger(s);
}
}
@@ -91,9 +155,10 @@ uint64_t ptimer_get_count(ptimer_state *s)
{
uint64_t counter;
- if (s->enabled) {
+ if (s->enabled && s->delta != 0) {
int64_t now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
int64_t next = s->next_event;
+ int64_t last = s->last_event;
bool expired = (now - next >= 0);
bool oneshot = (s->enabled == 2);
@@ -118,7 +183,7 @@ uint64_t ptimer_get_count(ptimer_state *s)
/* We need to divide time by period, where time is stored in
rem (64-bit integer) and period is stored in period/period_frac
(64.32 fixed point).
-
+
Doing full precision division is hard, so scale values and
do a 64-bit division. The result should be rounded down,
so that the rounding error never causes the timer to go
@@ -145,6 +210,35 @@ uint64_t ptimer_get_count(ptimer_state *s)
div += 1;
}
counter = rem / div;
+
+ if (s->policy_mask & PTIMER_POLICY_WRAP_AFTER_ONE_PERIOD) {
+ /* Before wrapping around, timer should stay with counter = 0
+ for a one period. */
+ if (!oneshot && s->delta == s->limit) {
+ if (now == last) {
+ /* Counter == delta here, check whether it was
+ adjusted and if it was, then right now it is
+ that "one period". */
+ if (counter == s->limit + DELTA_ADJUST) {
+ return 0;
+ }
+ } else if (counter == s->limit) {
+ /* Since the counter is rounded down and now != last,
+ the counter == limit means that delta was adjusted
+ by +1 and right now it is that adjusted period. */
+ return 0;
+ }
+ }
+ }
+ }
+
+ if (s->policy_mask & PTIMER_POLICY_NO_COUNTER_ROUND_DOWN) {
+ /* If now == last then delta == limit, i.e. the counter already
+ represents the correct value. It would be rounded down a 1ns
+ later. */
+ if (now != last) {
+ counter += 1;
+ }
}
} else {
counter = s->delta;
@@ -157,7 +251,7 @@ void ptimer_set_count(ptimer_state *s, uint64_t count)
s->delta = count;
if (s->enabled) {
s->next_event = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
- ptimer_reload(s);
+ ptimer_reload(s, 0);
}
}
@@ -174,7 +268,7 @@ void ptimer_run(ptimer_state *s, int oneshot)
s->enabled = oneshot ? 2 : 1;
if (was_disabled) {
s->next_event = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
- ptimer_reload(s);
+ ptimer_reload(s, 0);
}
}
@@ -198,7 +292,7 @@ void ptimer_set_period(ptimer_state *s, int64_t period)
s->period_frac = 0;
if (s->enabled) {
s->next_event = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
- ptimer_reload(s);
+ ptimer_reload(s, 0);
}
}
@@ -210,7 +304,7 @@ void ptimer_set_freq(ptimer_state *s, uint32_t freq)
s->period_frac = (1000000000ll << 32) / freq;
if (s->enabled) {
s->next_event = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
- ptimer_reload(s);
+ ptimer_reload(s, 0);
}
}
@@ -223,7 +317,7 @@ void ptimer_set_limit(ptimer_state *s, uint64_t limit, int reload)
s->delta = limit;
if (s->enabled && reload) {
s->next_event = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
- ptimer_reload(s);
+ ptimer_reload(s, 0);
}
}
diff --git a/hw/display/pl110.c b/hw/display/pl110.c
index c069c0b..8c7dcc6 100644
--- a/hw/display/pl110.c
+++ b/hw/display/pl110.c
@@ -466,17 +466,16 @@ static const GraphicHwOps pl110_gfx_ops = {
.gfx_update = pl110_update_display,
};
-static int pl110_initfn(SysBusDevice *sbd)
+static void pl110_realize(DeviceState *dev, Error **errp)
{
- DeviceState *dev = DEVICE(sbd);
PL110State *s = PL110(dev);
+ SysBusDevice *sbd = SYS_BUS_DEVICE(dev);
memory_region_init_io(&s->iomem, OBJECT(s), &pl110_ops, s, "pl110", 0x1000);
sysbus_init_mmio(sbd, &s->iomem);
sysbus_init_irq(sbd, &s->irq);
qdev_init_gpio_in(dev, pl110_mux_ctrl_set, 1);
s->con = graphic_console_init(dev, 0, &pl110_gfx_ops, s);
- return 0;
}
static void pl110_init(Object *obj)
@@ -503,11 +502,10 @@ static void pl111_init(Object *obj)
static void pl110_class_init(ObjectClass *klass, void *data)
{
DeviceClass *dc = DEVICE_CLASS(klass);
- SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass);
- k->init = pl110_initfn;
set_bit(DEVICE_CATEGORY_DISPLAY, dc->categories);
dc->vmsd = &vmstate_pl110;
+ dc->realize = pl110_realize;
}
static const TypeInfo pl110_info = {
diff --git a/hw/i2c/core.c b/hw/i2c/core.c
index 4afbe0b..abd4c4c 100644
--- a/hw/i2c/core.c
+++ b/hw/i2c/core.c
@@ -88,7 +88,12 @@ int i2c_bus_busy(I2CBus *bus)
return !QLIST_EMPTY(&bus->current_devs);
}
-/* Returns non-zero if the address is not valid. */
+/*
+ * Returns non-zero if the address is not valid. If this is called
+ * again without an intervening i2c_end_transfer(), like in the SMBus
+ * case where the operation is switched from write to read, this
+ * function will not rescan the bus and thus cannot fail.
+ */
/* TODO: Make this handle multiple masters. */
int i2c_start_transfer(I2CBus *bus, uint8_t address, int recv)
{
@@ -104,15 +109,25 @@ int i2c_start_transfer(I2CBus *bus, uint8_t address, int recv)
bus->broadcast = true;
}
- QTAILQ_FOREACH(kid, &bus->qbus.children, sibling) {
- DeviceState *qdev = kid->child;
- I2CSlave *candidate = I2C_SLAVE(qdev);
- if ((candidate->address == address) || (bus->broadcast)) {
- node = g_malloc(sizeof(struct I2CNode));
- node->elt = candidate;
- QLIST_INSERT_HEAD(&bus->current_devs, node, next);
- if (!bus->broadcast) {
- break;
+ /*
+ * If there are already devices in the list, that means we are in
+ * the middle of a transaction and we shouldn't rescan the bus.
+ *
+ * This happens with any SMBus transaction, even on a pure I2C
+ * device. The interface does a transaction start without
+ * terminating the previous transaction.
+ */
+ if (QLIST_EMPTY(&bus->current_devs)) {
+ QTAILQ_FOREACH(kid, &bus->qbus.children, sibling) {
+ DeviceState *qdev = kid->child;
+ I2CSlave *candidate = I2C_SLAVE(qdev);
+ if ((candidate->address == address) || (bus->broadcast)) {
+ node = g_malloc(sizeof(struct I2CNode));
+ node->elt = candidate;
+ QLIST_INSERT_HEAD(&bus->current_devs, node, next);
+ if (!bus->broadcast) {
+ break;
+ }
}
}
}
@@ -137,10 +152,6 @@ void i2c_end_transfer(I2CBus *bus)
I2CSlaveClass *sc;
I2CNode *node, *next;
- if (QLIST_EMPTY(&bus->current_devs)) {
- return;
- }
-
QLIST_FOREACH_SAFE(node, &bus->current_devs, next, next) {
sc = I2C_SLAVE_GET_CLASS(node->elt);
if (sc->event) {
diff --git a/hw/i2c/smbus.c b/hw/i2c/smbus.c
index 3979b3d..5b4dd3e 100644
--- a/hw/i2c/smbus.c
+++ b/hw/i2c/smbus.c
@@ -248,7 +248,9 @@ int smbus_read_byte(I2CBus *bus, uint8_t addr, uint8_t command)
return -1;
}
i2c_send(bus, command);
- i2c_start_transfer(bus, addr, 1);
+ if (i2c_start_transfer(bus, addr, 1)) {
+ assert(0);
+ }
data = i2c_recv(bus);
i2c_nack(bus);
i2c_end_transfer(bus);
@@ -273,7 +275,9 @@ int smbus_read_word(I2CBus *bus, uint8_t addr, uint8_t command)
return -1;
}
i2c_send(bus, command);
- i2c_start_transfer(bus, addr, 1);
+ if (i2c_start_transfer(bus, addr, 1)) {
+ assert(0);
+ }
data = i2c_recv(bus);
data |= i2c_recv(bus) << 8;
i2c_nack(bus);
@@ -302,7 +306,9 @@ int smbus_read_block(I2CBus *bus, uint8_t addr, uint8_t command, uint8_t *data)
return -1;
}
i2c_send(bus, command);
- i2c_start_transfer(bus, addr, 1);
+ if (i2c_start_transfer(bus, addr, 1)) {
+ assert(0);
+ }
len = i2c_recv(bus);
if (len > 32) {
len = 0;
diff --git a/hw/timer/a9gtimer.c b/hw/timer/a9gtimer.c
index 772f85f..ce1dc63 100644
--- a/hw/timer/a9gtimer.c
+++ b/hw/timer/a9gtimer.c
@@ -82,15 +82,15 @@ static void a9_gtimer_update(A9GTimerState *s, bool sync)
if ((s->control & R_CONTROL_TIMER_ENABLE) &&
(gtb->control & R_CONTROL_COMP_ENABLE)) {
/* R2p0+, where the compare function is >= */
- while (gtb->compare < update.new) {
+ if (gtb->compare < update.new) {
DB_PRINT("Compare event happened for CPU %d\n", i);
gtb->status = 1;
- if (gtb->control & R_CONTROL_AUTO_INCREMENT) {
- DB_PRINT("Auto incrementing timer compare by %" PRId32 "\n",
- gtb->inc);
- gtb->compare += gtb->inc;
- } else {
- break;
+ if (gtb->control & R_CONTROL_AUTO_INCREMENT && gtb->inc) {
+ uint64_t inc =
+ QEMU_ALIGN_UP(update.new - gtb->compare, gtb->inc);
+ DB_PRINT("Auto incrementing timer compare by %"
+ PRId64 "\n", inc);
+ gtb->compare += inc;
}
}
cdiff = (int64_t)gtb->compare - (int64_t)update.new + 1;
diff --git a/hw/timer/arm_mptimer.c b/hw/timer/arm_mptimer.c
index d66bbf0..daf6c48 100644
--- a/hw/timer/arm_mptimer.c
+++ b/hw/timer/arm_mptimer.c
@@ -20,22 +20,33 @@
*/
#include "qemu/osdep.h"
+#include "hw/ptimer.h"
#include "hw/timer/arm_mptimer.h"
#include "qapi/error.h"
-#include "qemu/timer.h"
+#include "qemu/main-loop.h"
#include "qom/cpu.h"
+#define PTIMER_POLICY \
+ (PTIMER_POLICY_WRAP_AFTER_ONE_PERIOD | \
+ PTIMER_POLICY_CONTINUOUS_TRIGGER | \
+ PTIMER_POLICY_NO_IMMEDIATE_TRIGGER | \
+ PTIMER_POLICY_NO_IMMEDIATE_RELOAD | \
+ PTIMER_POLICY_NO_COUNTER_ROUND_DOWN)
+
/* This device implements the per-cpu private timer and watchdog block
* which is used in both the ARM11MPCore and Cortex-A9MP.
*/
static inline int get_current_cpu(ARMMPTimerState *s)
{
- if (current_cpu->cpu_index >= s->num_cpu) {
+ int cpu_id = current_cpu ? current_cpu->cpu_index : 0;
+
+ if (cpu_id >= s->num_cpu) {
hw_error("arm_mptimer: num-cpu %d but this cpu is %d!\n",
- s->num_cpu, current_cpu->cpu_index);
+ s->num_cpu, cpu_id);
}
- return current_cpu->cpu_index;
+
+ return cpu_id;
}
static inline void timerblock_update_irq(TimerBlock *tb)
@@ -44,33 +55,42 @@ static inline void timerblock_update_irq(TimerBlock *tb)
}
/* Return conversion factor from mpcore timer ticks to qemu timer ticks. */
-static inline uint32_t timerblock_scale(TimerBlock *tb)
+static inline uint32_t timerblock_scale(uint32_t control)
{
- return (((tb->control >> 8) & 0xff) + 1) * 10;
+ return (((control >> 8) & 0xff) + 1) * 10;
}
-static void timerblock_reload(TimerBlock *tb, int restart)
+static inline void timerblock_set_count(struct ptimer_state *timer,
+ uint32_t control, uint64_t *count)
{
- if (tb->count == 0) {
- return;
+ /* PTimer would trigger interrupt for periodic timer when counter set
+ * to 0, MPtimer under certain condition only.
+ */
+ if ((control & 3) == 3 && (control & 0xff00) == 0 && *count == 0) {
+ *count = ptimer_get_limit(timer);
}
- if (restart) {
- tb->tick = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
+ ptimer_set_count(timer, *count);
+}
+
+static inline void timerblock_run(struct ptimer_state *timer,
+ uint32_t control, uint32_t load)
+{
+ if ((control & 1) && ((control & 0xff00) || load != 0)) {
+ ptimer_run(timer, !(control & 2));
}
- tb->tick += (int64_t)tb->count * timerblock_scale(tb);
- timer_mod(tb->timer, tb->tick);
}
static void timerblock_tick(void *opaque)
{
TimerBlock *tb = (TimerBlock *)opaque;
- tb->status = 1;
- if (tb->control & 2) {
- tb->count = tb->load;
- timerblock_reload(tb, 0);
- } else {
- tb->count = 0;
+ /* Periodic timer with load = 0 and prescaler != 0 would re-trigger
+ * IRQ after one period, otherwise it either stops or wraps around.
+ */
+ if ((tb->control & 2) && (tb->control & 0xff00) == 0 &&
+ ptimer_get_limit(tb->timer) == 0) {
+ ptimer_stop(tb->timer);
}
+ tb->status = 1;
timerblock_update_irq(tb);
}
@@ -78,21 +98,11 @@ static uint64_t timerblock_read(void *opaque, hwaddr addr,
unsigned size)
{
TimerBlock *tb = (TimerBlock *)opaque;
- int64_t val;
switch (addr) {
case 0: /* Load */
- return tb->load;
+ return ptimer_get_limit(tb->timer);
case 4: /* Counter. */
- if (((tb->control & 1) == 0) || (tb->count == 0)) {
- return 0;
- }
- /* Slow and ugly, but hopefully won't happen too often. */
- val = tb->tick - qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
- val /= timerblock_scale(tb);
- if (val < 0) {
- val = 0;
- }
- return val;
+ return ptimer_get_count(tb->timer);
case 8: /* Control. */
return tb->control;
case 12: /* Interrupt status. */
@@ -106,37 +116,45 @@ static void timerblock_write(void *opaque, hwaddr addr,
uint64_t value, unsigned size)
{
TimerBlock *tb = (TimerBlock *)opaque;
- int64_t old;
+ uint32_t control = tb->control;
switch (addr) {
case 0: /* Load */
- tb->load = value;
- /* Fall through. */
- case 4: /* Counter. */
- if ((tb->control & 1) && tb->count) {
- /* Cancel the previous timer. */
- timer_del(tb->timer);
+ /* Setting load to 0 stops the timer without doing the tick if
+ * prescaler = 0.
+ */
+ if ((control & 1) && (control & 0xff00) == 0 && value == 0) {
+ ptimer_stop(tb->timer);
}
- tb->count = value;
- if (tb->control & 1) {
- timerblock_reload(tb, 1);
+ ptimer_set_limit(tb->timer, value, 1);
+ timerblock_run(tb->timer, control, value);
+ break;
+ case 4: /* Counter. */
+ /* Setting counter to 0 stops the one-shot timer, or periodic with
+ * load = 0, without doing the tick if prescaler = 0.
+ */
+ if ((control & 1) && (control & 0xff00) == 0 && value == 0 &&
+ (!(control & 2) || ptimer_get_limit(tb->timer) == 0)) {
+ ptimer_stop(tb->timer);
}
+ timerblock_set_count(tb->timer, control, &value);
+ timerblock_run(tb->timer, control, value);
break;
case 8: /* Control. */
- old = tb->control;
- tb->control = value;
+ if ((control & 3) != (value & 3)) {
+ ptimer_stop(tb->timer);
+ }
+ if ((control & 0xff00) != (value & 0xff00)) {
+ ptimer_set_period(tb->timer, timerblock_scale(value));
+ }
if (value & 1) {
- if ((old & 1) && (tb->count != 0)) {
- /* Do nothing if timer is ticking right now. */
- break;
+ uint64_t count = ptimer_get_count(tb->timer);
+ /* Re-load periodic timer counter if needed. */
+ if ((value & 2) && count == 0) {
+ timerblock_set_count(tb->timer, value, &count);
}
- if (tb->control & 2) {
- tb->count = tb->load;
- }
- timerblock_reload(tb, 1);
- } else if (old & 1) {
- /* Shutdown the timer. */
- timer_del(tb->timer);
+ timerblock_run(tb->timer, value, count);
}
+ tb->control = value;
break;
case 12: /* Interrupt status. */
tb->status &= ~value;
@@ -186,13 +204,12 @@ static const MemoryRegionOps timerblock_ops = {
static void timerblock_reset(TimerBlock *tb)
{
- tb->count = 0;
- tb->load = 0;
tb->control = 0;
tb->status = 0;
- tb->tick = 0;
if (tb->timer) {
- timer_del(tb->timer);
+ ptimer_stop(tb->timer);
+ ptimer_set_limit(tb->timer, 0, 1);
+ ptimer_set_period(tb->timer, timerblock_scale(0));
}
}
@@ -238,7 +255,8 @@ static void arm_mptimer_realize(DeviceState *dev, Error **errp)
*/
for (i = 0; i < s->num_cpu; i++) {
TimerBlock *tb = &s->timerblock[i];
- tb->timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, timerblock_tick, tb);
+ QEMUBH *bh = qemu_bh_new(timerblock_tick, tb);
+ tb->timer = ptimer_init(bh, PTIMER_POLICY);
sysbus_init_irq(sbd, &tb->irq);
memory_region_init_io(&tb->iomem, OBJECT(s), &timerblock_ops, tb,
"arm_mptimer_timerblock", 0x20);
@@ -248,26 +266,23 @@ static void arm_mptimer_realize(DeviceState *dev, Error **errp)
static const VMStateDescription vmstate_timerblock = {
.name = "arm_mptimer_timerblock",
- .version_id = 2,
- .minimum_version_id = 2,
+ .version_id = 3,
+ .minimum_version_id = 3,
.fields = (VMStateField[]) {
- VMSTATE_UINT32(count, TimerBlock),
- VMSTATE_UINT32(load, TimerBlock),
VMSTATE_UINT32(control, TimerBlock),
VMSTATE_UINT32(status, TimerBlock),
- VMSTATE_INT64(tick, TimerBlock),
- VMSTATE_TIMER_PTR(timer, TimerBlock),
+ VMSTATE_PTIMER(timer, TimerBlock),
VMSTATE_END_OF_LIST()
}
};
static const VMStateDescription vmstate_arm_mptimer = {
.name = "arm_mptimer",
- .version_id = 2,
- .minimum_version_id = 2,
+ .version_id = 3,
+ .minimum_version_id = 3,
.fields = (VMStateField[]) {
VMSTATE_STRUCT_VARRAY_UINT32(timerblock, ARMMPTimerState, num_cpu,
- 2, vmstate_timerblock, TimerBlock),
+ 3, vmstate_timerblock, TimerBlock),
VMSTATE_END_OF_LIST()
}
};
diff --git a/hw/timer/stm32f2xx_timer.c b/hw/timer/stm32f2xx_timer.c
index 8c4c1f9..e5f5e14 100644
--- a/hw/timer/stm32f2xx_timer.c
+++ b/hw/timer/stm32f2xx_timer.c
@@ -217,7 +217,7 @@ static void stm32f2xx_timer_write(void *opaque, hwaddr offset,
return;
case TIM_PSC:
timer_val = stm32f2xx_ns_to_ticks(s, now) - s->tick_offset;
- s->tim_psc = value;
+ s->tim_psc = value & 0xFFFF;
value = timer_val;
break;
case TIM_CNT: