summaryrefslogtreecommitdiff
path: root/hw
diff options
context:
space:
mode:
Diffstat (limited to 'hw')
-rw-r--r--hw/9pfs/virtio-9p-device.c4
-rw-r--r--hw/9pfs/virtio-9p.c8
-rw-r--r--hw/a15mpcore.c103
-rw-r--r--hw/a9mpcore.c4
-rw-r--r--hw/ac97.c4
-rw-r--r--hw/acpi.c172
-rw-r--r--hw/acpi.h93
-rw-r--r--hw/acpi_piix4.c74
-rw-r--r--hw/ads7846.c4
-rw-r--r--hw/alpha_dp264.c3
-rw-r--r--hw/alpha_typhoon.c5
-rw-r--r--hw/apb_pci.c4
-rw-r--r--hw/apic.c130
-rw-r--r--hw/apic.h2
-rw-r--r--hw/apic_common.c80
-rw-r--r--hw/apic_internal.h27
-rw-r--r--hw/applesmc.c4
-rw-r--r--hw/arm-misc.h2
-rw-r--r--hw/arm11mpcore.c4
-rw-r--r--hw/arm_boot.c25
-rw-r--r--hw/arm_l2x0.c4
-rw-r--r--hw/arm_mptimer.c4
-rw-r--r--hw/arm_sysctl.c20
-rw-r--r--hw/arm_timer.c4
-rw-r--r--hw/armv7m.c4
-rw-r--r--hw/armv7m_nvic.c4
-rw-r--r--hw/baum.c7
-rw-r--r--hw/baum.h2
-rw-r--r--hw/bitbang_i2c.c4
-rw-r--r--hw/blizzard.c8
-rw-r--r--hw/boards.h1
-rw-r--r--hw/bonito.c5
-rw-r--r--hw/ccid-card-emulated.c4
-rw-r--r--hw/ccid-card-passthru.c4
-rw-r--r--hw/cirrus_vga.c12
-rw-r--r--hw/cs4231.c4
-rw-r--r--hw/cs4231a.c5
-rw-r--r--hw/debugcon.c4
-rw-r--r--hw/dec_pci.c4
-rw-r--r--hw/ds1225y.c4
-rw-r--r--hw/ds1338.c4
-rw-r--r--hw/e1000.c4
-rw-r--r--hw/eccmemctl.c4
-rw-r--r--hw/eepro100.c4
-rw-r--r--hw/empty_slot.c4
-rw-r--r--hw/es1370.c5
-rw-r--r--hw/escc.c4
-rw-r--r--hw/esp.c7
-rw-r--r--hw/etraxfs_eth.c4
-rw-r--r--hw/etraxfs_pic.c4
-rw-r--r--hw/etraxfs_ser.c4
-rw-r--r--hw/etraxfs_timer.c4
-rw-r--r--hw/exynos4210.c270
-rw-r--r--hw/exynos4210.h131
-rw-r--r--hw/exynos4210_combiner.c469
-rw-r--r--hw/exynos4210_fimd.c1928
-rw-r--r--hw/exynos4210_gic.c458
-rw-r--r--hw/exynos4210_mct.c1488
-rw-r--r--hw/exynos4210_pmu.c499
-rw-r--r--hw/exynos4210_pwm.c422
-rw-r--r--hw/exynos4210_uart.c676
-rw-r--r--hw/exynos4_boards.c177
-rw-r--r--hw/fdc.c146
-rw-r--r--hw/fmopl.c4
-rw-r--r--hw/fw_cfg.c4
-rw-r--r--hw/g364fb.c6
-rw-r--r--hw/grackle_pci.c4
-rw-r--r--hw/grlib_apbuart.c4
-rw-r--r--hw/grlib_gptimer.c4
-rw-r--r--hw/grlib_irqmp.c4
-rw-r--r--hw/gt64xxx.c4
-rw-r--r--hw/gus.c5
-rw-r--r--hw/hda-audio.c5
-rw-r--r--hw/highbank.c5
-rw-r--r--hw/hpet.c72
-rw-r--r--hw/hpet_emul.h3
-rw-r--r--hw/i2c.c4
-rw-r--r--hw/i82374.c4
-rw-r--r--hw/i82378.c10
-rw-r--r--hw/i8254.c97
-rw-r--r--hw/i8254.h57
-rw-r--r--hw/i8259.c6
-rw-r--r--hw/i8259_common.c6
-rw-r--r--hw/ide/ahci.c93
-rw-r--r--hw/ide/atapi.c7
-rw-r--r--hw/ide/cmd646.c5
-rw-r--r--hw/ide/core.c40
-rw-r--r--hw/ide/ich.c5
-rw-r--r--hw/ide/isa.c4
-rw-r--r--hw/ide/pci.c2
-rw-r--r--hw/ide/piix.c9
-rw-r--r--hw/ide/qdev.c5
-rw-r--r--hw/ide/via.c5
-rw-r--r--hw/integratorcp.c5
-rw-r--r--hw/intel-hda.c5
-rw-r--r--hw/ioapic.c4
-rw-r--r--hw/ioapic_common.c5
-rw-r--r--hw/ioh3420.c4
-rw-r--r--hw/isa-bus.c4
-rw-r--r--hw/ivshmem.c4
-rw-r--r--hw/jazz_led.c184
-rw-r--r--hw/kvm/apic.c36
-rw-r--r--hw/kvm/clock.c4
-rw-r--r--hw/kvm/i8259.c6
-rw-r--r--hw/kvm/ioapic.c4
-rw-r--r--hw/kvmvapic.c805
-rw-r--r--hw/lan9118.c128
-rw-r--r--hw/lance.c5
-rw-r--r--hw/lm32_juart.c4
-rw-r--r--hw/lm32_pic.c4
-rw-r--r--hw/lm32_sys.c4
-rw-r--r--hw/lm32_timer.c4
-rw-r--r--hw/lm32_uart.c4
-rw-r--r--hw/lm832x.c4
-rw-r--r--hw/lsi53c895a.c6
-rw-r--r--hw/m48t59.c4
-rw-r--r--hw/macio.c4
-rw-r--r--hw/marvell_88w8618_audio.c4
-rw-r--r--hw/max111x.c4
-rw-r--r--hw/max7310.c4
-rw-r--r--hw/mc146818rtc.c23
-rw-r--r--hw/milkymist-ac97.c4
-rw-r--r--hw/milkymist-hpdmc.c4
-rw-r--r--hw/milkymist-memcard.c4
-rw-r--r--hw/milkymist-minimac2.c4
-rw-r--r--hw/milkymist-pfpu.c4
-rw-r--r--hw/milkymist-softusb.c4
-rw-r--r--hw/milkymist-sysctl.c4
-rw-r--r--hw/milkymist-tmu2.c4
-rw-r--r--hw/milkymist-uart.c4
-rw-r--r--hw/milkymist-vgafb.c4
-rw-r--r--hw/mips.h3
-rw-r--r--hw/mips_fulong2e.c3
-rw-r--r--hw/mips_jazz.c8
-rw-r--r--hw/mips_malta.c9
-rw-r--r--hw/mips_r4k.c3
-rw-r--r--hw/mipsnet.c4
-rw-r--r--hw/mpc8544_guts.c5
-rw-r--r--hw/msmouse.c5
-rw-r--r--hw/msmouse.h2
-rw-r--r--hw/mst_fpga.c5
-rw-r--r--hw/musicpal.c4
-rw-r--r--hw/nand.c4
-rw-r--r--hw/ne2000-isa.c4
-rw-r--r--hw/ne2000.c13
-rw-r--r--hw/nseries.c2
-rw-r--r--hw/omap_gpio.c4
-rw-r--r--hw/omap_intc.c4
-rw-r--r--hw/omap_lcdc.c7
-rw-r--r--hw/onenand.c4
-rw-r--r--hw/opencores_eth.c4
-rw-r--r--hw/parallel.c4
-rw-r--r--hw/pc.c98
-rw-r--r--hw/pc.h35
-rw-r--r--hw/pc_piix.c104
-rw-r--r--hw/pc_sysfw.c254
-rw-r--r--hw/pci.c19
-rw-r--r--hw/pci.h62
-rw-r--r--hw/pckbd.c5
-rw-r--r--hw/pcnet-pci.c13
-rw-r--r--hw/pcspk.c84
-rw-r--r--hw/pcspk.h45
-rw-r--r--hw/pflash_cfi01.c44
-rw-r--r--hw/pflash_cfi02.c83
-rw-r--r--hw/piix4.c5
-rw-r--r--hw/piix_pci.c5
-rw-r--r--hw/pl011.c4
-rw-r--r--hw/pl022.c5
-rw-r--r--hw/pl031.c6
-rw-r--r--hw/pl041.c4
-rw-r--r--hw/pl050.c4
-rw-r--r--hw/pl061.c4
-rw-r--r--hw/pl080.c4
-rw-r--r--hw/pl110.c4
-rw-r--r--hw/pl181.c4
-rw-r--r--hw/pl190.c4
-rw-r--r--hw/ppc4xx_pci.c5
-rw-r--r--hw/ppce500_pci.c5
-rw-r--r--hw/ppce500_spin.c5
-rw-r--r--hw/prep_pci.c4
-rw-r--r--hw/primecell.h6
-rw-r--r--hw/ps2.c6
-rw-r--r--hw/pxa2xx.c4
-rw-r--r--hw/pxa2xx_dma.c5
-rw-r--r--hw/pxa2xx_gpio.c5
-rw-r--r--hw/pxa2xx_lcd.c20
-rw-r--r--hw/pxa2xx_pic.c5
-rw-r--r--hw/pxa2xx_timer.c7
-rw-r--r--hw/qdev-monitor.c18
-rw-r--r--hw/qdev-properties.c227
-rw-r--r--hw/qdev.c7
-rw-r--r--hw/qxl-render.c168
-rw-r--r--hw/qxl.c318
-rw-r--r--hw/qxl.h35
-rw-r--r--hw/realview.c16
-rw-r--r--hw/realview_gic.c4
-rw-r--r--hw/rtl8139.c4
-rw-r--r--hw/s390-virtio-bus.c44
-rw-r--r--hw/s390-virtio-bus.h2
-rw-r--r--hw/sb16.c5
-rw-r--r--hw/sbi.c4
-rw-r--r--hw/scsi-bus.c163
-rw-r--r--hw/scsi-disk.c156
-rw-r--r--hw/scsi-generic.c30
-rw-r--r--hw/scsi.h22
-rw-r--r--hw/serial.c10
-rw-r--r--hw/sga.c4
-rw-r--r--hw/sh_pci.c4
-rw-r--r--hw/slavio_intctl.c4
-rw-r--r--hw/slavio_misc.c4
-rw-r--r--hw/slavio_timer.c4
-rw-r--r--hw/smbus.c4
-rw-r--r--hw/smbus_eeprom.c4
-rw-r--r--hw/smc91c111.c4
-rw-r--r--hw/spapr_hcall.c5
-rw-r--r--hw/spapr_llan.c5
-rw-r--r--hw/spapr_pci.c4
-rw-r--r--hw/spapr_rtas.c5
-rw-r--r--hw/spapr_vio.c4
-rw-r--r--hw/spapr_vscsi.c7
-rw-r--r--hw/spapr_vty.c5
-rw-r--r--hw/sparc32_dma.c4
-rw-r--r--hw/spitz.c4
-rw-r--r--hw/ssd0303.c4
-rw-r--r--hw/ssd0323.c4
-rw-r--r--hw/ssi-sd.c4
-rw-r--r--hw/ssi.c4
-rw-r--r--hw/stellaris.c4
-rw-r--r--hw/stellaris_enet.c4
-rw-r--r--hw/strongarm.c5
-rw-r--r--hw/sun4c_intctl.c4
-rw-r--r--hw/sun4m.c37
-rw-r--r--hw/sun4m_iommu.c4
-rw-r--r--hw/sun4u.c29
-rw-r--r--hw/sysbus.c4
-rw-r--r--hw/tcx.c12
-rw-r--r--hw/tmp105.c4
-rw-r--r--hw/tosa.c4
-rw-r--r--hw/tusb6010.c4
-rw-r--r--hw/twl92230.c25
-rw-r--r--hw/unin_pci.c4
-rw-r--r--hw/usb-audio.c9
-rw-r--r--hw/usb-bt.c14
-rw-r--r--hw/usb-bus.c36
-rw-r--r--hw/usb-ccid.c14
-rw-r--r--hw/usb-desc.c20
-rw-r--r--hw/usb-ehci.c161
-rw-r--r--hw/usb-hid.c15
-rw-r--r--hw/usb-hub.c77
-rw-r--r--hw/usb-msd.c14
-rw-r--r--hw/usb-musb.c18
-rw-r--r--hw/usb-net.c16
-rw-r--r--hw/usb-ohci.c86
-rw-r--r--hw/usb-serial.c17
-rw-r--r--hw/usb-uhci.c404
-rw-r--r--hw/usb-wacom.c8
-rw-r--r--hw/usb-xhci.c255
-rw-r--r--hw/usb.c223
-rw-r--r--hw/usb.h63
-rw-r--r--hw/versatile_pci.c4
-rw-r--r--hw/versatilepb.c5
-rw-r--r--hw/vexpress.c410
-rw-r--r--hw/vga-isa.c5
-rw-r--r--hw/vga-pci.c5
-rw-r--r--hw/vga.c46
-rw-r--r--hw/virtio-blk.c6
-rw-r--r--hw/virtio-console.c12
-rw-r--r--hw/virtio-pci.c60
-rw-r--r--hw/virtio-pci.h2
-rw-r--r--hw/virtio-scsi.c617
-rw-r--r--hw/virtio-scsi.h36
-rw-r--r--hw/virtio-serial-bus.c4
-rw-r--r--hw/virtio.c3
-rw-r--r--hw/virtio.h3
-rw-r--r--hw/vmmouse.c5
-rw-r--r--hw/vmport.c5
-rw-r--r--hw/vmware_vga.c9
-rw-r--r--hw/vt82c686.c71
-rw-r--r--hw/wdt_i6300esb.c4
-rw-r--r--hw/wdt_ib700.c4
-rw-r--r--hw/wm8750.c4
-rw-r--r--hw/xen_platform.c4
-rw-r--r--hw/xgmac.c4
-rw-r--r--hw/xilinx_axidma.c4
-rw-r--r--hw/xilinx_axienet.c5
-rw-r--r--hw/xilinx_ethlite.c4
-rw-r--r--hw/xilinx_intc.c4
-rw-r--r--hw/xilinx_timer.c4
-rw-r--r--hw/xilinx_uartlite.c4
-rw-r--r--hw/xio3130_downstream.c4
-rw-r--r--hw/xio3130_upstream.c4
-rw-r--r--hw/zaurus.c5
292 files changed, 12352 insertions, 2384 deletions
diff --git a/hw/9pfs/virtio-9p-device.c b/hw/9pfs/virtio-9p-device.c
index 791a5572bb..b8220abae7 100644
--- a/hw/9pfs/virtio-9p-device.c
+++ b/hw/9pfs/virtio-9p-device.c
@@ -184,10 +184,10 @@ static TypeInfo virtio_9p_info = {
.class_init = virtio_9p_class_init,
};
-static void virtio_9p_register_devices(void)
+static void virtio_9p_register_types(void)
{
type_register_static(&virtio_9p_info);
virtio_9p_set_fd_limit();
}
-device_init(virtio_9p_register_devices)
+type_init(virtio_9p_register_types)
diff --git a/hw/9pfs/virtio-9p.c b/hw/9pfs/virtio-9p.c
index a72ffc3390..c633fb9b7e 100644
--- a/hw/9pfs/virtio-9p.c
+++ b/hw/9pfs/virtio-9p.c
@@ -1349,7 +1349,9 @@ static void v9fs_open(void *opaque)
if (s->proto_version == V9FS_PROTO_2000L) {
err = pdu_unmarshal(pdu, offset, "dd", &fid, &mode);
} else {
- err = pdu_unmarshal(pdu, offset, "db", &fid, &mode);
+ uint8_t modebyte;
+ err = pdu_unmarshal(pdu, offset, "db", &fid, &modebyte);
+ mode = modebyte;
}
if (err < 0) {
goto out_nofid;
@@ -3260,9 +3262,9 @@ void handle_9p_output(VirtIODevice *vdev, VirtQueue *vq)
ptr = pdu->elem.out_sg[0].iov_base;
- memcpy(&pdu->size, ptr, 4);
+ pdu->size = le32_to_cpu(*(uint32_t *)ptr);
pdu->id = ptr[4];
- memcpy(&pdu->tag, ptr + 5, 2);
+ pdu->tag = le16_to_cpu(*(uint16_t *)(ptr + 5));
qemu_co_queue_init(&pdu->complete);
submit_pdu(s, pdu);
}
diff --git a/hw/a15mpcore.c b/hw/a15mpcore.c
new file mode 100644
index 0000000000..71142e51f5
--- /dev/null
+++ b/hw/a15mpcore.c
@@ -0,0 +1,103 @@
+/*
+ * Cortex-A15MPCore internal peripheral emulation.
+ *
+ * Copyright (c) 2012 Linaro Limited.
+ * Written by Peter Maydell.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "sysbus.h"
+
+/* Configuration for arm_gic.c:
+ * max number of CPUs, how to ID current CPU
+ */
+#define NCPU 4
+
+static inline int gic_get_current_cpu(void)
+{
+ return cpu_single_env->cpu_index;
+}
+
+#include "arm_gic.c"
+
+/* A15MP private memory region. */
+
+typedef struct A15MPPrivState {
+ gic_state gic;
+ uint32_t num_cpu;
+ uint32_t num_irq;
+ MemoryRegion container;
+} A15MPPrivState;
+
+static int a15mp_priv_init(SysBusDevice *dev)
+{
+ A15MPPrivState *s = FROM_SYSBUSGIC(A15MPPrivState, dev);
+
+ if (s->num_cpu > NCPU) {
+ hw_error("a15mp_priv_init: num-cpu may not be more than %d\n", NCPU);
+ }
+
+ gic_init(&s->gic, s->num_cpu, s->num_irq);
+
+ /* Memory map (addresses are offsets from PERIPHBASE):
+ * 0x0000-0x0fff -- reserved
+ * 0x1000-0x1fff -- GIC Distributor
+ * 0x2000-0x2fff -- GIC CPU interface
+ * 0x4000-0x4fff -- GIC virtual interface control (not modelled)
+ * 0x5000-0x5fff -- GIC virtual interface control (not modelled)
+ * 0x6000-0x7fff -- GIC virtual CPU interface (not modelled)
+ */
+ memory_region_init(&s->container, "a15mp-priv-container", 0x8000);
+ memory_region_add_subregion(&s->container, 0x1000, &s->gic.iomem);
+ memory_region_add_subregion(&s->container, 0x2000, &s->gic.cpuiomem[0]);
+
+ sysbus_init_mmio(dev, &s->container);
+ return 0;
+}
+
+static Property a15mp_priv_properties[] = {
+ DEFINE_PROP_UINT32("num-cpu", A15MPPrivState, num_cpu, 1),
+ /* The Cortex-A15MP may have anything from 0 to 224 external interrupt
+ * IRQ lines (with another 32 internal). We default to 64+32, which
+ * is the number provided by the Cortex-A15MP test chip in the
+ * Versatile Express A15 development board.
+ * Other boards may differ and should set this property appropriately.
+ */
+ DEFINE_PROP_UINT32("num-irq", A15MPPrivState, num_irq, 96),
+ DEFINE_PROP_END_OF_LIST(),
+};
+
+static void a15mp_priv_class_init(ObjectClass *klass, void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(klass);
+ SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass);
+ k->init = a15mp_priv_init;
+ dc->props = a15mp_priv_properties;
+ /* We currently have no savable state outside the common GIC state */
+}
+
+static TypeInfo a15mp_priv_info = {
+ .name = "a15mpcore_priv",
+ .parent = TYPE_SYS_BUS_DEVICE,
+ .instance_size = sizeof(A15MPPrivState),
+ .class_init = a15mp_priv_class_init,
+};
+
+static void a15mp_register_types(void)
+{
+ type_register_static(&a15mp_priv_info);
+}
+
+type_init(a15mp_register_types)
diff --git a/hw/a9mpcore.c b/hw/a9mpcore.c
index 19de12b4b4..03b128ce5b 100644
--- a/hw/a9mpcore.c
+++ b/hw/a9mpcore.c
@@ -238,9 +238,9 @@ static TypeInfo a9mp_priv_info = {
.class_init = a9mp_priv_class_init,
};
-static void a9mp_register_devices(void)
+static void a9mp_register_types(void)
{
type_register_static(&a9mp_priv_info);
}
-device_init(a9mp_register_devices)
+type_init(a9mp_register_types)
diff --git a/hw/ac97.c b/hw/ac97.c
index 7bf9171487..c0fd0197a0 100644
--- a/hw/ac97.c
+++ b/hw/ac97.c
@@ -1372,9 +1372,9 @@ static TypeInfo ac97_info = {
.class_init = ac97_class_init,
};
-static void ac97_register (void)
+static void ac97_register_types (void)
{
type_register_static (&ac97_info);
}
-device_init (ac97_register);
+type_init (ac97_register_types)
diff --git a/hw/acpi.c b/hw/acpi.c
index 79b179be1c..5d521e5133 100644
--- a/hw/acpi.c
+++ b/hw/acpi.c
@@ -248,64 +248,97 @@ int acpi_table_add(const char *t)
}
+static void acpi_notify_wakeup(Notifier *notifier, void *data)
+{
+ ACPIREGS *ar = container_of(notifier, ACPIREGS, wakeup);
+ WakeupReason *reason = data;
+
+ switch (*reason) {
+ case QEMU_WAKEUP_REASON_RTC:
+ ar->pm1.evt.sts |=
+ (ACPI_BITMASK_WAKE_STATUS | ACPI_BITMASK_RT_CLOCK_STATUS);
+ break;
+ case QEMU_WAKEUP_REASON_PMTIMER:
+ ar->pm1.evt.sts |=
+ (ACPI_BITMASK_WAKE_STATUS | ACPI_BITMASK_TIMER_STATUS);
+ break;
+ case QEMU_WAKEUP_REASON_OTHER:
+ default:
+ /* ACPI_BITMASK_WAKE_STATUS should be set on resume.
+ Pretend that resume was caused by power button */
+ ar->pm1.evt.sts |=
+ (ACPI_BITMASK_WAKE_STATUS | ACPI_BITMASK_POWER_BUTTON_STATUS);
+ break;
+ }
+}
+
/* ACPI PM1a EVT */
-uint16_t acpi_pm1_evt_get_sts(ACPIPM1EVT *pm1, int64_t overflow_time)
+uint16_t acpi_pm1_evt_get_sts(ACPIREGS *ar)
{
int64_t d = acpi_pm_tmr_get_clock();
- if (d >= overflow_time) {
- pm1->sts |= ACPI_BITMASK_TIMER_STATUS;
+ if (d >= ar->tmr.overflow_time) {
+ ar->pm1.evt.sts |= ACPI_BITMASK_TIMER_STATUS;
}
- return pm1->sts;
+ return ar->pm1.evt.sts;
}
-void acpi_pm1_evt_write_sts(ACPIPM1EVT *pm1, ACPIPMTimer *tmr, uint16_t val)
+void acpi_pm1_evt_write_sts(ACPIREGS *ar, uint16_t val)
{
- uint16_t pm1_sts = acpi_pm1_evt_get_sts(pm1, tmr->overflow_time);
+ uint16_t pm1_sts = acpi_pm1_evt_get_sts(ar);
if (pm1_sts & val & ACPI_BITMASK_TIMER_STATUS) {
/* if TMRSTS is reset, then compute the new overflow time */
- acpi_pm_tmr_calc_overflow_time(tmr);
+ acpi_pm_tmr_calc_overflow_time(ar);
}
- pm1->sts &= ~val;
+ ar->pm1.evt.sts &= ~val;
}
-void acpi_pm1_evt_power_down(ACPIPM1EVT *pm1, ACPIPMTimer *tmr)
+void acpi_pm1_evt_write_en(ACPIREGS *ar, uint16_t val)
{
- if (!pm1) {
- qemu_system_shutdown_request();
- } else if (pm1->en & ACPI_BITMASK_POWER_BUTTON_ENABLE) {
- pm1->sts |= ACPI_BITMASK_POWER_BUTTON_STATUS;
- tmr->update_sci(tmr);
+ ar->pm1.evt.en = val;
+ qemu_system_wakeup_enable(QEMU_WAKEUP_REASON_RTC,
+ val & ACPI_BITMASK_RT_CLOCK_ENABLE);
+ qemu_system_wakeup_enable(QEMU_WAKEUP_REASON_PMTIMER,
+ val & ACPI_BITMASK_TIMER_ENABLE);
+}
+
+void acpi_pm1_evt_power_down(ACPIREGS *ar)
+{
+ if (ar->pm1.evt.en & ACPI_BITMASK_POWER_BUTTON_ENABLE) {
+ ar->pm1.evt.sts |= ACPI_BITMASK_POWER_BUTTON_STATUS;
+ ar->tmr.update_sci(ar);
}
}
-void acpi_pm1_evt_reset(ACPIPM1EVT *pm1)
+void acpi_pm1_evt_reset(ACPIREGS *ar)
{
- pm1->sts = 0;
- pm1->en = 0;
+ ar->pm1.evt.sts = 0;
+ ar->pm1.evt.en = 0;
+ qemu_system_wakeup_enable(QEMU_WAKEUP_REASON_RTC, 0);
+ qemu_system_wakeup_enable(QEMU_WAKEUP_REASON_PMTIMER, 0);
}
/* ACPI PM_TMR */
-void acpi_pm_tmr_update(ACPIPMTimer *tmr, bool enable)
+void acpi_pm_tmr_update(ACPIREGS *ar, bool enable)
{
int64_t expire_time;
/* schedule a timer interruption if needed */
if (enable) {
- expire_time = muldiv64(tmr->overflow_time, get_ticks_per_sec(),
+ expire_time = muldiv64(ar->tmr.overflow_time, get_ticks_per_sec(),
PM_TIMER_FREQUENCY);
- qemu_mod_timer(tmr->timer, expire_time);
+ qemu_mod_timer(ar->tmr.timer, expire_time);
} else {
- qemu_del_timer(tmr->timer);
+ qemu_del_timer(ar->tmr.timer);
}
}
-void acpi_pm_tmr_calc_overflow_time(ACPIPMTimer *tmr)
+void acpi_pm_tmr_calc_overflow_time(ACPIREGS *ar)
{
int64_t d = acpi_pm_tmr_get_clock();
- tmr->overflow_time = (d + 0x800000LL) & ~0x7fffffLL;
+ ar->tmr.overflow_time = (d + 0x800000LL) & ~0x7fffffLL;
}
-uint32_t acpi_pm_tmr_get(ACPIPMTimer *tmr)
+uint32_t acpi_pm_tmr_get(ACPIREGS *ar)
{
uint32_t d = acpi_pm_tmr_get_clock();
return d & 0xffffff;
@@ -313,31 +346,33 @@ uint32_t acpi_pm_tmr_get(ACPIPMTimer *tmr)
static void acpi_pm_tmr_timer(void *opaque)
{
- ACPIPMTimer *tmr = opaque;
- tmr->update_sci(tmr);
+ ACPIREGS *ar = opaque;
+ qemu_system_wakeup_request(QEMU_WAKEUP_REASON_PMTIMER);
+ ar->tmr.update_sci(ar);
}
-void acpi_pm_tmr_init(ACPIPMTimer *tmr, acpi_update_sci_fn update_sci)
+void acpi_pm_tmr_init(ACPIREGS *ar, acpi_update_sci_fn update_sci)
{
- tmr->update_sci = update_sci;
- tmr->timer = qemu_new_timer_ns(vm_clock, acpi_pm_tmr_timer, tmr);
+ ar->tmr.update_sci = update_sci;
+ ar->tmr.timer = qemu_new_timer_ns(vm_clock, acpi_pm_tmr_timer, ar);
}
-void acpi_pm_tmr_reset(ACPIPMTimer *tmr)
+void acpi_pm_tmr_reset(ACPIREGS *ar)
{
- tmr->overflow_time = 0;
- qemu_del_timer(tmr->timer);
+ ar->tmr.overflow_time = 0;
+ qemu_del_timer(ar->tmr.timer);
}
/* ACPI PM1aCNT */
-void acpi_pm1_cnt_init(ACPIPM1CNT *pm1_cnt, qemu_irq cmos_s3)
+void acpi_pm1_cnt_init(ACPIREGS *ar)
{
- pm1_cnt->cmos_s3 = cmos_s3;
+ ar->wakeup.notify = acpi_notify_wakeup;
+ qemu_register_wakeup_notifier(&ar->wakeup);
}
-void acpi_pm1_cnt_write(ACPIPM1EVT *pm1a, ACPIPM1CNT *pm1_cnt, uint16_t val)
+void acpi_pm1_cnt_write(ACPIREGS *ar, uint16_t val)
{
- pm1_cnt->cnt = val & ~(ACPI_BITMASK_SLEEP_ENABLE);
+ ar->pm1.cnt.cnt = val & ~(ACPI_BITMASK_SLEEP_ENABLE);
if (val & ACPI_BITMASK_SLEEP_ENABLE) {
/* change suspend type */
@@ -347,64 +382,57 @@ void acpi_pm1_cnt_write(ACPIPM1EVT *pm1a, ACPIPM1CNT *pm1_cnt, uint16_t val)
qemu_system_shutdown_request();
break;
case 1:
- /* ACPI_BITMASK_WAKE_STATUS should be set on resume.
- Pretend that resume was caused by power button */
- pm1a->sts |=
- (ACPI_BITMASK_WAKE_STATUS | ACPI_BITMASK_POWER_BUTTON_STATUS);
- qemu_system_reset_request();
- qemu_irq_raise(pm1_cnt->cmos_s3);
+ qemu_system_suspend_request();
+ break;
default:
break;
}
}
}
-void acpi_pm1_cnt_update(ACPIPM1CNT *pm1_cnt,
+void acpi_pm1_cnt_update(ACPIREGS *ar,
bool sci_enable, bool sci_disable)
{
/* ACPI specs 3.0, 4.7.2.5 */
if (sci_enable) {
- pm1_cnt->cnt |= ACPI_BITMASK_SCI_ENABLE;
+ ar->pm1.cnt.cnt |= ACPI_BITMASK_SCI_ENABLE;
} else if (sci_disable) {
- pm1_cnt->cnt &= ~ACPI_BITMASK_SCI_ENABLE;
+ ar->pm1.cnt.cnt &= ~ACPI_BITMASK_SCI_ENABLE;
}
}
-void acpi_pm1_cnt_reset(ACPIPM1CNT *pm1_cnt)
+void acpi_pm1_cnt_reset(ACPIREGS *ar)
{
- pm1_cnt->cnt = 0;
- if (pm1_cnt->cmos_s3) {
- qemu_irq_lower(pm1_cnt->cmos_s3);
- }
+ ar->pm1.cnt.cnt = 0;
}
/* ACPI GPE */
-void acpi_gpe_init(ACPIGPE *gpe, uint8_t len)
+void acpi_gpe_init(ACPIREGS *ar, uint8_t len)
{
- gpe->len = len;
- gpe->sts = g_malloc0(len / 2);
- gpe->en = g_malloc0(len / 2);
+ ar->gpe.len = len;
+ ar->gpe.sts = g_malloc0(len / 2);
+ ar->gpe.en = g_malloc0(len / 2);
}
-void acpi_gpe_blk(ACPIGPE *gpe, uint32_t blk)
+void acpi_gpe_blk(ACPIREGS *ar, uint32_t blk)
{
- gpe->blk = blk;
+ ar->gpe.blk = blk;
}
-void acpi_gpe_reset(ACPIGPE *gpe)
+void acpi_gpe_reset(ACPIREGS *ar)
{
- memset(gpe->sts, 0, gpe->len / 2);
- memset(gpe->en, 0, gpe->len / 2);
+ memset(ar->gpe.sts, 0, ar->gpe.len / 2);
+ memset(ar->gpe.en, 0, ar->gpe.len / 2);
}
-static uint8_t *acpi_gpe_ioport_get_ptr(ACPIGPE *gpe, uint32_t addr)
+static uint8_t *acpi_gpe_ioport_get_ptr(ACPIREGS *ar, uint32_t addr)
{
uint8_t *cur = NULL;
- if (addr < gpe->len / 2) {
- cur = gpe->sts + addr;
- } else if (addr < gpe->len) {
- cur = gpe->en + addr - gpe->len / 2;
+ if (addr < ar->gpe.len / 2) {
+ cur = ar->gpe.sts + addr;
+ } else if (addr < ar->gpe.len) {
+ cur = ar->gpe.en + addr - ar->gpe.len / 2;
} else {
abort();
}
@@ -412,16 +440,16 @@ static uint8_t *acpi_gpe_ioport_get_ptr(ACPIGPE *gpe, uint32_t addr)
return cur;
}
-void acpi_gpe_ioport_writeb(ACPIGPE *gpe, uint32_t addr, uint32_t val)
+void acpi_gpe_ioport_writeb(ACPIREGS *ar, uint32_t addr, uint32_t val)
{
uint8_t *cur;
- addr -= gpe->blk;
- cur = acpi_gpe_ioport_get_ptr(gpe, addr);
- if (addr < gpe->len / 2) {
+ addr -= ar->gpe.blk;
+ cur = acpi_gpe_ioport_get_ptr(ar, addr);
+ if (addr < ar->gpe.len / 2) {
/* GPE_STS */
*cur = (*cur) & ~val;
- } else if (addr < gpe->len) {
+ } else if (addr < ar->gpe.len) {
/* GPE_EN */
*cur = val;
} else {
@@ -429,13 +457,13 @@ void acpi_gpe_ioport_writeb(ACPIGPE *gpe, uint32_t addr, uint32_t val)
}
}
-uint32_t acpi_gpe_ioport_readb(ACPIGPE *gpe, uint32_t addr)
+uint32_t acpi_gpe_ioport_readb(ACPIREGS *ar, uint32_t addr)
{
uint8_t *cur;
uint32_t val;
- addr -= gpe->blk;
- cur = acpi_gpe_ioport_get_ptr(gpe, addr);
+ addr -= ar->gpe.blk;
+ cur = acpi_gpe_ioport_get_ptr(ar, addr);
val = 0;
if (cur != NULL) {
val = *cur;
diff --git a/hw/acpi.h b/hw/acpi.h
index c141e65f4f..fe8cdb48e0 100644
--- a/hw/acpi.h
+++ b/hw/acpi.h
@@ -73,11 +73,14 @@
/* PM2_CNT */
#define ACPI_BITMASK_ARB_DISABLE 0x0001
-/* PM_TMR */
-struct ACPIPMTimer;
+/* structs */
typedef struct ACPIPMTimer ACPIPMTimer;
+typedef struct ACPIPM1EVT ACPIPM1EVT;
+typedef struct ACPIPM1CNT ACPIPM1CNT;
+typedef struct ACPIGPE ACPIGPE;
+typedef struct ACPIREGS ACPIREGS;
-typedef void (*acpi_update_sci_fn)(ACPIPMTimer *tmr);
+typedef void (*acpi_update_sci_fn)(ACPIREGS *ar);
struct ACPIPMTimer {
QEMUTimer *timer;
@@ -86,47 +89,15 @@ struct ACPIPMTimer {
acpi_update_sci_fn update_sci;
};
-void acpi_pm_tmr_update(ACPIPMTimer *tmr, bool enable);
-void acpi_pm_tmr_calc_overflow_time(ACPIPMTimer *tmr);
-uint32_t acpi_pm_tmr_get(ACPIPMTimer *tmr);
-void acpi_pm_tmr_init(ACPIPMTimer *tmr, acpi_update_sci_fn update_sci);
-void acpi_pm_tmr_reset(ACPIPMTimer *tmr);
-
-#include "qemu-timer.h"
-static inline int64_t acpi_pm_tmr_get_clock(void)
-{
- return muldiv64(qemu_get_clock_ns(vm_clock), PM_TIMER_FREQUENCY,
- get_ticks_per_sec());
-}
-
-/* PM1a_EVT: piix and ich9 don't implement PM1b. */
-struct ACPIPM1EVT
-{
+struct ACPIPM1EVT {
uint16_t sts;
uint16_t en;
};
-typedef struct ACPIPM1EVT ACPIPM1EVT;
-uint16_t acpi_pm1_evt_get_sts(ACPIPM1EVT *pm1, int64_t overflow_time);
-void acpi_pm1_evt_write_sts(ACPIPM1EVT *pm1, ACPIPMTimer *tmr, uint16_t val);
-void acpi_pm1_evt_power_down(ACPIPM1EVT *pm1, ACPIPMTimer *tmr);
-void acpi_pm1_evt_reset(ACPIPM1EVT *pm1);
-
-/* PM1a_CNT: piix and ich9 don't implement PM1b CNT. */
struct ACPIPM1CNT {
uint16_t cnt;
-
- qemu_irq cmos_s3;
};
-typedef struct ACPIPM1CNT ACPIPM1CNT;
-
-void acpi_pm1_cnt_init(ACPIPM1CNT *pm1_cnt, qemu_irq cmos_s3);
-void acpi_pm1_cnt_write(ACPIPM1EVT *pm1a, ACPIPM1CNT *pm1_cnt, uint16_t val);
-void acpi_pm1_cnt_update(ACPIPM1CNT *pm1_cnt,
- bool sci_enable, bool sci_disable);
-void acpi_pm1_cnt_reset(ACPIPM1CNT *pm1_cnt);
-/* GPE0 */
struct ACPIGPE {
uint32_t blk;
uint8_t len;
@@ -134,13 +105,51 @@ struct ACPIGPE {
uint8_t *sts;
uint8_t *en;
};
-typedef struct ACPIGPE ACPIGPE;
-void acpi_gpe_init(ACPIGPE *gpe, uint8_t len);
-void acpi_gpe_blk(ACPIGPE *gpe, uint32_t blk);
-void acpi_gpe_reset(ACPIGPE *gpe);
+struct ACPIREGS {
+ ACPIPMTimer tmr;
+ ACPIGPE gpe;
+ struct {
+ ACPIPM1EVT evt;
+ ACPIPM1CNT cnt;
+ } pm1;
+ Notifier wakeup;
+};
+
+/* PM_TMR */
+void acpi_pm_tmr_update(ACPIREGS *ar, bool enable);
+void acpi_pm_tmr_calc_overflow_time(ACPIREGS *ar);
+uint32_t acpi_pm_tmr_get(ACPIREGS *ar);
+void acpi_pm_tmr_init(ACPIREGS *ar, acpi_update_sci_fn update_sci);
+void acpi_pm_tmr_reset(ACPIREGS *ar);
+
+#include "qemu-timer.h"
+static inline int64_t acpi_pm_tmr_get_clock(void)
+{
+ return muldiv64(qemu_get_clock_ns(vm_clock), PM_TIMER_FREQUENCY,
+ get_ticks_per_sec());
+}
+
+/* PM1a_EVT: piix and ich9 don't implement PM1b. */
+uint16_t acpi_pm1_evt_get_sts(ACPIREGS *ar);
+void acpi_pm1_evt_write_sts(ACPIREGS *ar, uint16_t val);
+void acpi_pm1_evt_write_en(ACPIREGS *ar, uint16_t val);
+void acpi_pm1_evt_power_down(ACPIREGS *ar);
+void acpi_pm1_evt_reset(ACPIREGS *ar);
+
+/* PM1a_CNT: piix and ich9 don't implement PM1b CNT. */
+void acpi_pm1_cnt_init(ACPIREGS *ar);
+void acpi_pm1_cnt_write(ACPIREGS *ar, uint16_t val);
+void acpi_pm1_cnt_update(ACPIREGS *ar,
+ bool sci_enable, bool sci_disable);
+void acpi_pm1_cnt_reset(ACPIREGS *ar);
+
+/* GPE0 */
+void acpi_gpe_init(ACPIREGS *ar, uint8_t len);
+void acpi_gpe_blk(ACPIREGS *ar, uint32_t blk);
+void acpi_gpe_reset(ACPIREGS *ar);
-void acpi_gpe_ioport_writeb(ACPIGPE *gpe, uint32_t addr, uint32_t val);
-uint32_t acpi_gpe_ioport_readb(ACPIGPE *gpe, uint32_t addr);
+void acpi_gpe_ioport_writeb(ACPIREGS *ar, uint32_t addr, uint32_t val);
+uint32_t acpi_gpe_ioport_readb(ACPIREGS *ar, uint32_t addr);
#endif /* !QEMU_HW_ACPI_H */
diff --git a/hw/acpi_piix4.c b/hw/acpi_piix4.c
index 21484aec42..797ed245fc 100644
--- a/hw/acpi_piix4.c
+++ b/hw/acpi_piix4.c
@@ -54,13 +54,10 @@ struct pci_status {
typedef struct PIIX4PMState {
PCIDevice dev;
IORange ioport;
- ACPIPM1EVT pm1a;
- ACPIPM1CNT pm1_cnt;
+ ACPIREGS ar;
APMState apm;
- ACPIPMTimer tmr;
-
PMSMBus smb;
uint32_t smb_io_base;
@@ -70,7 +67,6 @@ typedef struct PIIX4PMState {
Notifier machine_ready;
/* for pci hotplug */
- ACPIGPE gpe;
struct pci_status pci0_status;
uint32_t pci0_hotplug_enable;
} PIIX4PMState;
@@ -84,23 +80,24 @@ static void pm_update_sci(PIIX4PMState *s)
{
int sci_level, pmsts;
- pmsts = acpi_pm1_evt_get_sts(&s->pm1a, s->tmr.overflow_time);
- sci_level = (((pmsts & s->pm1a.en) &
+ pmsts = acpi_pm1_evt_get_sts(&s->ar);
+ sci_level = (((pmsts & s->ar.pm1.evt.en) &
(ACPI_BITMASK_RT_CLOCK_ENABLE |
ACPI_BITMASK_POWER_BUTTON_ENABLE |
ACPI_BITMASK_GLOBAL_LOCK_ENABLE |
ACPI_BITMASK_TIMER_ENABLE)) != 0) ||
- (((s->gpe.sts[0] & s->gpe.en[0]) & PIIX4_PCI_HOTPLUG_STATUS) != 0);
+ (((s->ar.gpe.sts[0] & s->ar.gpe.en[0])
+ & PIIX4_PCI_HOTPLUG_STATUS) != 0);
qemu_set_irq(s->irq, sci_level);
/* schedule a timer interruption if needed */
- acpi_pm_tmr_update(&s->tmr, (s->pm1a.en & ACPI_BITMASK_TIMER_ENABLE) &&
+ acpi_pm_tmr_update(&s->ar, (s->ar.pm1.evt.en & ACPI_BITMASK_TIMER_ENABLE) &&
!(pmsts & ACPI_BITMASK_TIMER_STATUS));
}
-static void pm_tmr_timer(ACPIPMTimer *tmr)
+static void pm_tmr_timer(ACPIREGS *ar)
{
- PIIX4PMState *s = container_of(tmr, PIIX4PMState, tmr);
+ PIIX4PMState *s = container_of(ar, PIIX4PMState, ar);
pm_update_sci(s);
}
@@ -116,15 +113,15 @@ static void pm_ioport_write(IORange *ioport, uint64_t addr, unsigned width,
switch(addr) {
case 0x00:
- acpi_pm1_evt_write_sts(&s->pm1a, &s->tmr, val);
+ acpi_pm1_evt_write_sts(&s->ar, val);
pm_update_sci(s);
break;
case 0x02:
- s->pm1a.en = val;
+ acpi_pm1_evt_write_en(&s->ar, val);
pm_update_sci(s);
break;
case 0x04:
- acpi_pm1_cnt_write(&s->pm1a, &s->pm1_cnt, val);
+ acpi_pm1_cnt_write(&s->ar, val);
break;
default:
break;
@@ -141,16 +138,16 @@ static void pm_ioport_read(IORange *ioport, uint64_t addr, unsigned width,
switch(addr) {
case 0x00:
- val = acpi_pm1_evt_get_sts(&s->pm1a, s->tmr.overflow_time);
+ val = acpi_pm1_evt_get_sts(&s->ar);
break;
case 0x02:
- val = s->pm1a.en;
+ val = s->ar.pm1.evt.en;
break;
case 0x04:
- val = s->pm1_cnt.cnt;
+ val = s->ar.pm1.cnt.cnt;
break;
case 0x08:
- val = acpi_pm_tmr_get(&s->tmr);
+ val = acpi_pm_tmr_get(&s->ar);
break;
default:
val = 0;
@@ -170,7 +167,7 @@ static void apm_ctrl_changed(uint32_t val, void *arg)
PIIX4PMState *s = arg;
/* ACPI specs 3.0, 4.7.2.5 */
- acpi_pm1_cnt_update(&s->pm1_cnt, val == ACPI_ENABLE, val == ACPI_DISABLE);
+ acpi_pm1_cnt_update(&s->ar, val == ACPI_ENABLE, val == ACPI_DISABLE);
if (s->dev.config[0x5b] & (1 << 1)) {
if (s->smi_irq) {
@@ -258,13 +255,13 @@ static const VMStateDescription vmstate_acpi = {
.post_load = vmstate_acpi_post_load,
.fields = (VMStateField []) {
VMSTATE_PCI_DEVICE(dev, PIIX4PMState),
- VMSTATE_UINT16(pm1a.sts, PIIX4PMState),
- VMSTATE_UINT16(pm1a.en, PIIX4PMState),
- VMSTATE_UINT16(pm1_cnt.cnt, PIIX4PMState),
+ VMSTATE_UINT16(ar.pm1.evt.sts, PIIX4PMState),
+ VMSTATE_UINT16(ar.pm1.evt.en, PIIX4PMState),
+ VMSTATE_UINT16(ar.pm1.cnt.cnt, PIIX4PMState),
VMSTATE_STRUCT(apm, PIIX4PMState, 0, vmstate_apm, APMState),
- VMSTATE_TIMER(tmr.timer, PIIX4PMState),
- VMSTATE_INT64(tmr.overflow_time, PIIX4PMState),
- VMSTATE_STRUCT(gpe, PIIX4PMState, 2, vmstate_gpe, ACPIGPE),
+ VMSTATE_TIMER(ar.tmr.timer, PIIX4PMState),
+ VMSTATE_INT64(ar.tmr.overflow_time, PIIX4PMState),
+ VMSTATE_STRUCT(ar.gpe, PIIX4PMState, 2, vmstate_gpe, ACPIGPE),
VMSTATE_STRUCT(pci0_status, PIIX4PMState, 2, vmstate_pci_status,
struct pci_status),
VMSTATE_END_OF_LIST()
@@ -310,10 +307,9 @@ static void piix4_reset(void *opaque)
static void piix4_powerdown(void *opaque, int irq, int power_failing)
{
PIIX4PMState *s = opaque;
- ACPIPM1EVT *pm1a = s? &s->pm1a: NULL;
- ACPIPMTimer *tmr = s? &s->tmr: NULL;
- acpi_pm1_evt_power_down(pm1a, tmr);
+ assert(s != NULL);
+ acpi_pm1_evt_power_down(&s->ar);
}
static void piix4_pm_machine_ready(Notifier *n, void *opaque)
@@ -361,8 +357,8 @@ static int piix4_pm_initfn(PCIDevice *dev)
register_ioport_write(s->smb_io_base, 64, 1, smb_ioport_writeb, &s->smb);
register_ioport_read(s->smb_io_base, 64, 1, smb_ioport_readb, &s->smb);
- acpi_pm_tmr_init(&s->tmr, pm_tmr_timer);
- acpi_gpe_init(&s->gpe, GPE_LEN);
+ acpi_pm_tmr_init(&s->ar, pm_tmr_timer);
+ acpi_gpe_init(&s->ar, GPE_LEN);
qemu_system_powerdown = *qemu_allocate_irqs(piix4_powerdown, s, 1);
@@ -376,7 +372,7 @@ static int piix4_pm_initfn(PCIDevice *dev)
}
i2c_bus *piix4_pm_init(PCIBus *bus, int devfn, uint32_t smb_io_base,
- qemu_irq sci_irq, qemu_irq cmos_s3, qemu_irq smi_irq,
+ qemu_irq sci_irq, qemu_irq smi_irq,
int kvm_enabled)
{
PCIDevice *dev;
@@ -387,7 +383,7 @@ i2c_bus *piix4_pm_init(PCIBus *bus, int devfn, uint32_t smb_io_base,
s = DO_UPCAST(PIIX4PMState, dev, dev);
s->irq = sci_irq;
- acpi_pm1_cnt_init(&s->pm1_cnt, cmos_s3);
+ acpi_pm1_cnt_init(&s->ar);
s->smi_irq = smi_irq;
s->kvm_enabled = kvm_enabled;
@@ -426,17 +422,17 @@ static TypeInfo piix4_pm_info = {
.class_init = piix4_pm_class_init,
};
-static void piix4_pm_register(void)
+static void piix4_pm_register_types(void)
{
type_register_static(&piix4_pm_info);
}
-device_init(piix4_pm_register);
+type_init(piix4_pm_register_types)
static uint32_t gpe_readb(void *opaque, uint32_t addr)
{
PIIX4PMState *s = opaque;
- uint32_t val = acpi_gpe_ioport_readb(&s->gpe, addr);
+ uint32_t val = acpi_gpe_ioport_readb(&s->ar, addr);
PIIX4_DPRINTF("gpe read %x == %x\n", addr, val);
return val;
@@ -446,7 +442,7 @@ static void gpe_writeb(void *opaque, uint32_t addr, uint32_t val)
{
PIIX4PMState *s = opaque;
- acpi_gpe_ioport_writeb(&s->gpe, addr, val);
+ acpi_gpe_ioport_writeb(&s->ar, addr, val);
pm_update_sci(s);
PIIX4_DPRINTF("gpe write %x <== %d\n", addr, val);
@@ -531,7 +527,7 @@ static void piix4_acpi_system_hot_add_init(PCIBus *bus, PIIX4PMState *s)
register_ioport_write(GPE_BASE, GPE_LEN, 1, gpe_writeb, s);
register_ioport_read(GPE_BASE, GPE_LEN, 1, gpe_readb, s);
- acpi_gpe_blk(&s->gpe, GPE_BASE);
+ acpi_gpe_blk(&s->ar, GPE_BASE);
register_ioport_write(PCI_BASE, 8, 4, pcihotplug_write, pci0_status);
register_ioport_read(PCI_BASE, 8, 4, pcihotplug_read, pci0_status);
@@ -547,13 +543,13 @@ static void piix4_acpi_system_hot_add_init(PCIBus *bus, PIIX4PMState *s)
static void enable_device(PIIX4PMState *s, int slot)
{
- s->gpe.sts[0] |= PIIX4_PCI_HOTPLUG_STATUS;
+ s->ar.gpe.sts[0] |= PIIX4_PCI_HOTPLUG_STATUS;
s->pci0_status.up |= (1 << slot);
}
static void disable_device(PIIX4PMState *s, int slot)
{
- s->gpe.sts[0] |= PIIX4_PCI_HOTPLUG_STATUS;
+ s->ar.gpe.sts[0] |= PIIX4_PCI_HOTPLUG_STATUS;
s->pci0_status.down |= (1 << slot);
}
diff --git a/hw/ads7846.c b/hw/ads7846.c
index 3cfdecbe5c..41c7f101c4 100644
--- a/hw/ads7846.c
+++ b/hw/ads7846.c
@@ -168,9 +168,9 @@ static TypeInfo ads7846_info = {
.class_init = ads7846_class_init,
};
-static void ads7846_register_devices(void)
+static void ads7846_register_types(void)
{
type_register_static(&ads7846_info);
}
-device_init(ads7846_register_devices)
+type_init(ads7846_register_types)
diff --git a/hw/alpha_dp264.c b/hw/alpha_dp264.c
index 876335a773..ea0fd95f84 100644
--- a/hw/alpha_dp264.c
+++ b/hw/alpha_dp264.c
@@ -14,6 +14,7 @@
#include "sysemu.h"
#include "mc146818rtc.h"
#include "ide.h"
+#include "i8254.h"
#define MAX_IDE_BUS 2
@@ -72,7 +73,7 @@ static void clipper_init(ram_addr_t ram_size,
clipper_pci_map_irq);
rtc_init(isa_bus, 1980, rtc_irq);
- pit_init(isa_bus, 0x40, 0);
+ pit_init(isa_bus, 0x40, 0, NULL);
isa_create_simple(isa_bus, "i8042");
/* VGA setup. Don't bother loading the bios. */
diff --git a/hw/alpha_typhoon.c b/hw/alpha_typhoon.c
index 736c28a3a9..b539416589 100644
--- a/hw/alpha_typhoon.c
+++ b/hw/alpha_typhoon.c
@@ -824,8 +824,9 @@ static TypeInfo typhoon_pcihost_info = {
.class_init = typhoon_pcihost_class_init,
};
-static void typhoon_register(void)
+static void typhoon_register_types(void)
{
type_register_static(&typhoon_pcihost_info);
}
-device_init(typhoon_register);
+
+type_init(typhoon_register_types)
diff --git a/hw/apb_pci.c b/hw/apb_pci.c
index c7aaa72d07..1d25da8da9 100644
--- a/hw/apb_pci.c
+++ b/hw/apb_pci.c
@@ -493,11 +493,11 @@ static TypeInfo pbm_pci_bridge_info = {
.class_init = pbm_pci_bridge_class_init,
};
-static void pbm_register_devices(void)
+static void pbm_register_types(void)
{
type_register_static(&pbm_host_info);
type_register_static(&pbm_pci_host_info);
type_register_static(&pbm_pci_bridge_info);
}
-device_init(pbm_register_devices)
+type_init(pbm_register_types)
diff --git a/hw/apic.c b/hw/apic.c
index 086c544bf3..4eeaf8801c 100644
--- a/hw/apic.c
+++ b/hw/apic.c
@@ -35,6 +35,10 @@
#define MSI_ADDR_DEST_ID_SHIFT 12
#define MSI_ADDR_DEST_ID_MASK 0x00ffff0
+#define SYNC_FROM_VAPIC 0x1
+#define SYNC_TO_VAPIC 0x2
+#define SYNC_ISR_IRR_TO_VAPIC 0x4
+
static APICCommonState *local_apics[MAX_APICS + 1];
static void apic_set_irq(APICCommonState *s, int vector_num, int trigger_mode);
@@ -78,6 +82,70 @@ static inline int get_bit(uint32_t *tab, int index)
return !!(tab[i] & mask);
}
+/* return -1 if no bit is set */
+static int get_highest_priority_int(uint32_t *tab)
+{
+ int i;
+ for (i = 7; i >= 0; i--) {
+ if (tab[i] != 0) {
+ return i * 32 + fls_bit(tab[i]);
+ }
+ }
+ return -1;
+}
+
+static void apic_sync_vapic(APICCommonState *s, int sync_type)
+{
+ VAPICState vapic_state;
+ size_t length;
+ off_t start;
+ int vector;
+
+ if (!s->vapic_paddr) {
+ return;
+ }
+ if (sync_type & SYNC_FROM_VAPIC) {
+ cpu_physical_memory_rw(s->vapic_paddr, (void *)&vapic_state,
+ sizeof(vapic_state), 0);
+ s->tpr = vapic_state.tpr;
+ }
+ if (sync_type & (SYNC_TO_VAPIC | SYNC_ISR_IRR_TO_VAPIC)) {
+ start = offsetof(VAPICState, isr);
+ length = offsetof(VAPICState, enabled) - offsetof(VAPICState, isr);
+
+ if (sync_type & SYNC_TO_VAPIC) {
+ assert(qemu_cpu_is_self(s->cpu_env));
+
+ vapic_state.tpr = s->tpr;
+ vapic_state.enabled = 1;
+ start = 0;
+ length = sizeof(VAPICState);
+ }
+
+ vector = get_highest_priority_int(s->isr);
+ if (vector < 0) {
+ vector = 0;
+ }
+ vapic_state.isr = vector & 0xf0;
+
+ vapic_state.zero = 0;
+
+ vector = get_highest_priority_int(s->irr);
+ if (vector < 0) {
+ vector = 0;
+ }
+ vapic_state.irr = vector & 0xff;
+
+ cpu_physical_memory_write_rom(s->vapic_paddr + start,
+ ((void *)&vapic_state) + start, length);
+ }
+}
+
+static void apic_vapic_base_update(APICCommonState *s)
+{
+ apic_sync_vapic(s, SYNC_TO_VAPIC);
+}
+
static void apic_local_deliver(APICCommonState *s, int vector)
{
uint32_t lvt = s->lvt[vector];
@@ -239,20 +307,17 @@ static void apic_set_base(APICCommonState *s, uint64_t val)
static void apic_set_tpr(APICCommonState *s, uint8_t val)
{
- s->tpr = (val & 0x0f) << 4;
- apic_update_irq(s);
+ /* Updates from cr8 are ignored while the VAPIC is active */
+ if (!s->vapic_paddr) {
+ s->tpr = val << 4;
+ apic_update_irq(s);
+ }
}
-/* return -1 if no bit is set */
-static int get_highest_priority_int(uint32_t *tab)
+static uint8_t apic_get_tpr(APICCommonState *s)
{
- int i;
- for(i = 7; i >= 0; i--) {
- if (tab[i] != 0) {
- return i * 32 + fls_bit(tab[i]);
- }
- }
- return -1;
+ apic_sync_vapic(s, SYNC_FROM_VAPIC);
+ return s->tpr >> 4;
}
static int apic_get_ppr(APICCommonState *s)
@@ -312,6 +377,14 @@ static void apic_update_irq(APICCommonState *s)
}
}
+void apic_poll_irq(DeviceState *d)
+{
+ APICCommonState *s = APIC_COMMON(d);
+
+ apic_sync_vapic(s, SYNC_FROM_VAPIC);
+ apic_update_irq(s);
+}
+
static void apic_set_irq(APICCommonState *s, int vector_num, int trigger_mode)
{
apic_report_irq_delivered(!get_bit(s->irr, vector_num));
@@ -321,6 +394,16 @@ static void apic_set_irq(APICCommonState *s, int vector_num, int trigger_mode)
set_bit(s->tmr, vector_num);
else
reset_bit(s->tmr, vector_num);
+ if (s->vapic_paddr) {
+ apic_sync_vapic(s, SYNC_ISR_IRR_TO_VAPIC);
+ /*
+ * The vcpu thread needs to see the new IRR before we pull its current
+ * TPR value. That way, if we miss a lowering of the TRP, the guest
+ * has the chance to notice the new IRR and poll for IRQs on its own.
+ */
+ smp_wmb();
+ apic_sync_vapic(s, SYNC_FROM_VAPIC);
+ }
apic_update_irq(s);
}
@@ -334,6 +417,7 @@ static void apic_eoi(APICCommonState *s)
if (!(s->spurious_vec & APIC_SV_DIRECTED_IO) && get_bit(s->tmr, isrv)) {
ioapic_eoi_broadcast(isrv);
}
+ apic_sync_vapic(s, SYNC_FROM_VAPIC | SYNC_TO_VAPIC);
apic_update_irq(s);
}
@@ -471,15 +555,19 @@ int apic_get_interrupt(DeviceState *d)
if (!(s->spurious_vec & APIC_SV_ENABLE))
return -1;
+ apic_sync_vapic(s, SYNC_FROM_VAPIC);
intno = apic_irq_pending(s);
if (intno == 0) {
+ apic_sync_vapic(s, SYNC_TO_VAPIC);
return -1;
} else if (intno < 0) {
+ apic_sync_vapic(s, SYNC_TO_VAPIC);
return s->spurious_vec & 0xff;
}
reset_bit(s->irr, intno);
set_bit(s->isr, intno);
+ apic_sync_vapic(s, SYNC_TO_VAPIC);
apic_update_irq(s);
return intno;
}
@@ -576,6 +664,10 @@ static uint32_t apic_mem_readl(void *opaque, target_phys_addr_t addr)
val = 0x11 | ((APIC_LVT_NB - 1) << 16); /* version 0x11 */
break;
case 0x08:
+ apic_sync_vapic(s, SYNC_FROM_VAPIC);
+ if (apic_report_tpr_access) {
+ cpu_report_tpr_access(s->cpu_env, TPR_ACCESS_READ);
+ }
val = s->tpr;
break;
case 0x09:
@@ -675,7 +767,11 @@ static void apic_mem_writel(void *opaque, target_phys_addr_t addr, uint32_t val)
case 0x03:
break;
case 0x08:
+ if (apic_report_tpr_access) {
+ cpu_report_tpr_access(s->cpu_env, TPR_ACCESS_WRITE);
+ }
s->tpr = val;
+ apic_sync_vapic(s, SYNC_TO_VAPIC);
apic_update_irq(s);
break;
case 0x09:
@@ -737,6 +833,11 @@ static void apic_mem_writel(void *opaque, target_phys_addr_t addr, uint32_t val)
}
}
+static void apic_pre_save(APICCommonState *s)
+{
+ apic_sync_vapic(s, SYNC_FROM_VAPIC);
+}
+
static void apic_post_load(APICCommonState *s)
{
if (s->timer_expiry != -1) {
@@ -770,7 +871,10 @@ static void apic_class_init(ObjectClass *klass, void *data)
k->init = apic_init;
k->set_base = apic_set_base;
k->set_tpr = apic_set_tpr;
+ k->get_tpr = apic_get_tpr;
+ k->vapic_base_update = apic_vapic_base_update;
k->external_nmi = apic_external_nmi;
+ k->pre_save = apic_pre_save;
k->post_load = apic_post_load;
}
@@ -781,9 +885,9 @@ static TypeInfo apic_info = {
.class_init = apic_class_init,
};
-static void apic_register_devices(void)
+static void apic_register_types(void)
{
type_register_static(&apic_info);
}
-device_init(apic_register_devices)
+type_init(apic_register_types)
diff --git a/hw/apic.h b/hw/apic.h
index a62d83ba9f..d6d6d440ee 100644
--- a/hw/apic.h
+++ b/hw/apic.h
@@ -18,6 +18,8 @@ void cpu_set_apic_tpr(DeviceState *s, uint8_t val);
uint8_t cpu_get_apic_tpr(DeviceState *s);
void apic_init_reset(DeviceState *s);
void apic_sipi(DeviceState *s);
+void apic_handle_tpr_access_report(DeviceState *d, target_ulong ip,
+ TPRAccess access);
/* pc.c */
int cpu_is_bsp(CPUState *env);
diff --git a/hw/apic_common.c b/hw/apic_common.c
index 26991b4516..60b82596e7 100644
--- a/hw/apic_common.c
+++ b/hw/apic_common.c
@@ -20,8 +20,10 @@
#include "apic.h"
#include "apic_internal.h"
#include "trace.h"
+#include "kvm.h"
static int apic_irq_delivered;
+bool apic_report_tpr_access;
void cpu_set_apic_base(DeviceState *d, uint64_t val)
{
@@ -63,9 +65,45 @@ void cpu_set_apic_tpr(DeviceState *d, uint8_t val)
uint8_t cpu_get_apic_tpr(DeviceState *d)
{
+ APICCommonState *s;
+ APICCommonClass *info;
+
+ if (!d) {
+ return 0;
+ }
+
+ s = APIC_COMMON(d);
+ info = APIC_COMMON_GET_CLASS(s);
+
+ return info->get_tpr(s);
+}
+
+void apic_enable_tpr_access_reporting(DeviceState *d, bool enable)
+{
+ APICCommonState *s = DO_UPCAST(APICCommonState, busdev.qdev, d);
+ APICCommonClass *info = APIC_COMMON_GET_CLASS(s);
+
+ apic_report_tpr_access = enable;
+ if (info->enable_tpr_reporting) {
+ info->enable_tpr_reporting(s, enable);
+ }
+}
+
+void apic_enable_vapic(DeviceState *d, target_phys_addr_t paddr)
+{
+ APICCommonState *s = DO_UPCAST(APICCommonState, busdev.qdev, d);
+ APICCommonClass *info = APIC_COMMON_GET_CLASS(s);
+
+ s->vapic_paddr = paddr;
+ info->vapic_base_update(s);
+}
+
+void apic_handle_tpr_access_report(DeviceState *d, target_ulong ip,
+ TPRAccess access)
+{
APICCommonState *s = DO_UPCAST(APICCommonState, busdev.qdev, d);
- return s ? s->tpr >> 4 : 0;
+ vapic_report_tpr_access(s->vapic, s->cpu_env, ip, access);
}
void apic_report_irq_delivered(int delivered)
@@ -166,12 +204,16 @@ void apic_init_reset(DeviceState *d)
static void apic_reset_common(DeviceState *d)
{
APICCommonState *s = DO_UPCAST(APICCommonState, busdev.qdev, d);
+ APICCommonClass *info = APIC_COMMON_GET_CLASS(s);
bool bsp;
bsp = cpu_is_bsp(s->cpu_env);
s->apicbase = 0xfee00000 |
(bsp ? MSR_IA32_APICBASE_BSP : 0) | MSR_IA32_APICBASE_ENABLE;
+ s->vapic_paddr = 0;
+ info->vapic_base_update(s);
+
apic_init_reset(d);
if (bsp) {
@@ -188,6 +230,7 @@ static void apic_reset_common(DeviceState *d)
static int apic_load_old(QEMUFile *f, void *opaque, int version_id)
{
APICCommonState *s = opaque;
+ APICCommonClass *info = APIC_COMMON_GET_CLASS(s);
int i;
if (version_id > 2) {
@@ -220,7 +263,11 @@ static int apic_load_old(QEMUFile *f, void *opaque, int version_id)
s->next_time = qemu_get_be64(f);
if (version_id >= 2) {
- qemu_get_timer(f, s->timer);
+ s->timer_expiry = qemu_get_be64(f);
+ }
+
+ if (info->post_load) {
+ info->post_load(s);
}
return 0;
}
@@ -229,6 +276,7 @@ static int apic_init_common(SysBusDevice *dev)
{
APICCommonState *s = APIC_COMMON(dev);
APICCommonClass *info;
+ static DeviceState *vapic;
static int apic_no;
if (apic_no >= MAX_APICS) {
@@ -239,10 +287,29 @@ static int apic_init_common(SysBusDevice *dev)
info = APIC_COMMON_GET_CLASS(s);
info->init(s);
- sysbus_init_mmio(&s->busdev, &s->io_memory);
+ sysbus_init_mmio(dev, &s->io_memory);
+
+ if (!vapic && s->vapic_control & VAPIC_ENABLE_MASK) {
+ vapic = sysbus_create_simple("kvmvapic", -1, NULL);
+ }
+ s->vapic = vapic;
+ if (apic_report_tpr_access && info->enable_tpr_reporting) {
+ info->enable_tpr_reporting(s, true);
+ }
+
return 0;
}
+static void apic_dispatch_pre_save(void *opaque)
+{
+ APICCommonState *s = APIC_COMMON(opaque);
+ APICCommonClass *info = APIC_COMMON_GET_CLASS(s);
+
+ if (info->pre_save) {
+ info->pre_save(s);
+ }
+}
+
static int apic_dispatch_post_load(void *opaque, int version_id)
{
APICCommonState *s = APIC_COMMON(opaque);
@@ -260,6 +327,7 @@ static const VMStateDescription vmstate_apic_common = {
.minimum_version_id = 3,
.minimum_version_id_old = 1,
.load_state_old = apic_load_old,
+ .pre_save = apic_dispatch_pre_save,
.post_load = apic_dispatch_post_load,
.fields = (VMStateField[]) {
VMSTATE_UINT32(apicbase, APICCommonState),
@@ -289,6 +357,8 @@ static const VMStateDescription vmstate_apic_common = {
static Property apic_properties_common[] = {
DEFINE_PROP_UINT8("id", APICCommonState, id, -1),
DEFINE_PROP_PTR("cpu_env", APICCommonState, cpu_env),
+ DEFINE_PROP_BIT("vapic", APICCommonState, vapic_control, VAPIC_ENABLE_BIT,
+ true),
DEFINE_PROP_END_OF_LIST(),
};
@@ -313,9 +383,9 @@ static TypeInfo apic_common_type = {
.abstract = true,
};
-static void register_devices(void)
+static void register_types(void)
{
type_register_static(&apic_common_type);
}
-device_init(register_devices);
+type_init(register_types)
diff --git a/hw/apic_internal.h b/hw/apic_internal.h
index 0cab010717..60a6a8bdae 100644
--- a/hw/apic_internal.h
+++ b/hw/apic_internal.h
@@ -61,6 +61,9 @@
#define APIC_SV_DIRECTED_IO (1<<12)
#define APIC_SV_ENABLE (1<<8)
+#define VAPIC_ENABLE_BIT 0
+#define VAPIC_ENABLE_MASK (1 << VAPIC_ENABLE_BIT)
+
#define MAX_APICS 255
#define MSI_SPACE_SIZE 0x100000
@@ -82,7 +85,11 @@ typedef struct APICCommonClass
void (*init)(APICCommonState *s);
void (*set_base)(APICCommonState *s, uint64_t val);
void (*set_tpr)(APICCommonState *s, uint8_t val);
+ uint8_t (*get_tpr)(APICCommonState *s);
+ void (*enable_tpr_reporting)(APICCommonState *s, bool enable);
+ void (*vapic_base_update)(APICCommonState *s);
void (*external_nmi)(APICCommonState *s);
+ void (*pre_save)(APICCommonState *s);
void (*post_load)(APICCommonState *s);
} APICCommonClass;
@@ -114,9 +121,29 @@ struct APICCommonState {
int64_t timer_expiry;
int sipi_vector;
int wait_for_sipi;
+
+ uint32_t vapic_control;
+ DeviceState *vapic;
+ target_phys_addr_t vapic_paddr; /* note: persistence via kvmvapic */
};
+typedef struct VAPICState {
+ uint8_t tpr;
+ uint8_t isr;
+ uint8_t zero;
+ uint8_t irr;
+ uint8_t enabled;
+} QEMU_PACKED VAPICState;
+
+extern bool apic_report_tpr_access;
+
void apic_report_irq_delivered(int delivered);
bool apic_next_timer(APICCommonState *s, int64_t current_time);
+void apic_enable_tpr_access_reporting(DeviceState *d, bool enable);
+void apic_enable_vapic(DeviceState *d, target_phys_addr_t paddr);
+void apic_poll_irq(DeviceState *d);
+
+void vapic_report_tpr_access(DeviceState *dev, void *cpu, target_ulong ip,
+ TPRAccess access);
#endif /* !QEMU_APIC_INTERNAL_H */
diff --git a/hw/applesmc.c b/hw/applesmc.c
index b06487f70d..8bedaad310 100644
--- a/hw/applesmc.c
+++ b/hw/applesmc.c
@@ -243,9 +243,9 @@ static TypeInfo applesmc_isa_info = {
.class_init = qdev_applesmc_class_init,
};
-static void applesmc_register_devices(void)
+static void applesmc_register_types(void)
{
type_register_static(&applesmc_isa_info);
}
-device_init(applesmc_register_devices)
+type_init(applesmc_register_types)
diff --git a/hw/arm-misc.h b/hw/arm-misc.h
index 5e5204bbf5..306013aeeb 100644
--- a/hw/arm-misc.h
+++ b/hw/arm-misc.h
@@ -37,7 +37,7 @@ struct arm_boot_info {
*/
target_phys_addr_t smp_loader_start;
target_phys_addr_t smp_bootreg_addr;
- target_phys_addr_t smp_priv_base;
+ target_phys_addr_t gic_cpu_if_addr;
int nb_cpus;
int board_id;
int (*atag_board)(const struct arm_boot_info *info, void *p);
diff --git a/hw/arm11mpcore.c b/hw/arm11mpcore.c
index 3c0839c4b4..102348bb70 100644
--- a/hw/arm11mpcore.c
+++ b/hw/arm11mpcore.c
@@ -252,10 +252,10 @@ static TypeInfo mpcore_priv_info = {
.class_init = mpcore_priv_class_init,
};
-static void arm11mpcore_register_devices(void)
+static void arm11mpcore_register_types(void)
{
type_register_static(&mpcore_rirq_info);
type_register_static(&mpcore_priv_info);
}
-device_init(arm11mpcore_register_devices)
+type_init(arm11mpcore_register_types)
diff --git a/hw/arm_boot.c b/hw/arm_boot.c
index 5f163fda02..2ef25ca9dd 100644
--- a/hw/arm_boot.c
+++ b/hw/arm_boot.c
@@ -43,16 +43,16 @@ static uint32_t bootloader[] = {
* location for the kernel secondary CPU entry point.
*/
static uint32_t smpboot[] = {
- 0xe59f201c, /* ldr r2, privbase */
+ 0xe59f201c, /* ldr r2, gic_cpu_if */
0xe59f001c, /* ldr r0, startaddr */
0xe3a01001, /* mov r1, #1 */
- 0xe5821100, /* str r1, [r2, #256] */
+ 0xe5821000, /* str r1, [r2] */
0xe320f003, /* wfi */
0xe5901000, /* ldr r1, [r0] */
0xe1110001, /* tst r1, r1 */
0x0afffffb, /* beq <wfi> */
0xe12fff11, /* bx r1 */
- 0, /* privbase: Private memory region base address. */
+ 0, /* gic_cpu_if: base address of GIC CPU interface */
0 /* bootreg: Boot register address is held here */
};
@@ -61,7 +61,7 @@ static void default_write_secondary(CPUState *env,
{
int n;
smpboot[ARRAY_SIZE(smpboot) - 1] = info->smp_bootreg_addr;
- smpboot[ARRAY_SIZE(smpboot) - 2] = info->smp_priv_base;
+ smpboot[ARRAY_SIZE(smpboot) - 2] = info->gic_cpu_if_addr;
for (n = 0; n < ARRAY_SIZE(smpboot); n++) {
smpboot[n] = tswap32(smpboot[n]);
}
@@ -81,9 +81,10 @@ static void default_reset_secondary(CPUState *env,
p += 4; \
} while (0)
-static void set_kernel_args(const struct arm_boot_info *info,
- int initrd_size, target_phys_addr_t base)
+static void set_kernel_args(const struct arm_boot_info *info)
{
+ int initrd_size = info->initrd_size;
+ target_phys_addr_t base = info->loader_start;
target_phys_addr_t p;
p = base + KERNEL_ARGS_ADDR;
@@ -134,12 +135,12 @@ static void set_kernel_args(const struct arm_boot_info *info,
WRITE_WORD(p, 0);
}
-static void set_kernel_args_old(const struct arm_boot_info *info,
- int initrd_size, target_phys_addr_t base)
+static void set_kernel_args_old(const struct arm_boot_info *info)
{
target_phys_addr_t p;
const char *s;
-
+ int initrd_size = info->initrd_size;
+ target_phys_addr_t base = info->loader_start;
/* see linux/include/asm-arm/setup.h */
p = base + KERNEL_ARGS_ADDR;
@@ -222,11 +223,9 @@ static void do_cpu_reset(void *opaque)
if (env == first_cpu) {
env->regs[15] = info->loader_start;
if (old_param) {
- set_kernel_args_old(info, info->initrd_size,
- info->loader_start);
+ set_kernel_args_old(info);
} else {
- set_kernel_args(info, info->initrd_size,
- info->loader_start);
+ set_kernel_args(info);
}
} else {
info->secondary_cpu_reset_hook(env, info);
diff --git a/hw/arm_l2x0.c b/hw/arm_l2x0.c
index ba26abc6f4..09f290c85f 100644
--- a/hw/arm_l2x0.c
+++ b/hw/arm_l2x0.c
@@ -184,9 +184,9 @@ static TypeInfo l2x0_info = {
.class_init = l2x0_class_init,
};
-static void l2x0_register_device(void)
+static void l2x0_register_types(void)
{
type_register_static(&l2x0_info);
}
-device_init(l2x0_register_device)
+type_init(l2x0_register_types)
diff --git a/hw/arm_mptimer.c b/hw/arm_mptimer.c
index 5a02365b6f..361e887dec 100644
--- a/hw/arm_mptimer.c
+++ b/hw/arm_mptimer.c
@@ -335,9 +335,9 @@ static TypeInfo arm_mptimer_info = {
.class_init = arm_mptimer_class_init,
};
-static void arm_mptimer_register_devices(void)
+static void arm_mptimer_register_types(void)
{
type_register_static(&arm_mptimer_info);
}
-device_init(arm_mptimer_register_devices)
+type_init(arm_mptimer_register_types)
diff --git a/hw/arm_sysctl.c b/hw/arm_sysctl.c
index 9d257994c0..5f1237b8c2 100644
--- a/hw/arm_sysctl.c
+++ b/hw/arm_sysctl.c
@@ -378,7 +378,7 @@ static void arm_sysctl_gpio_set(void *opaque, int line, int level)
}
}
-static int arm_sysctl_init1(SysBusDevice *dev)
+static int arm_sysctl_init(SysBusDevice *dev)
{
arm_sysctl_state *s = FROM_SYSBUS(arm_sysctl_state, dev);
@@ -389,18 +389,6 @@ static int arm_sysctl_init1(SysBusDevice *dev)
return 0;
}
-/* Legacy helper function. */
-void arm_sysctl_init(uint32_t base, uint32_t sys_id, uint32_t proc_id)
-{
- DeviceState *dev;
-
- dev = qdev_create(NULL, "realview_sysctl");
- qdev_prop_set_uint32(dev, "sys_id", sys_id);
- qdev_init_nofail(dev);
- qdev_prop_set_uint32(dev, "proc_id", proc_id);
- sysbus_mmio_map(sysbus_from_qdev(dev), 0, base);
-}
-
static Property arm_sysctl_properties[] = {
DEFINE_PROP_UINT32("sys_id", arm_sysctl_state, sys_id, 0),
DEFINE_PROP_UINT32("proc_id", arm_sysctl_state, proc_id, 0),
@@ -412,7 +400,7 @@ static void arm_sysctl_class_init(ObjectClass *klass, void *data)
DeviceClass *dc = DEVICE_CLASS(klass);
SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass);
- k->init = arm_sysctl_init1;
+ k->init = arm_sysctl_init;
dc->reset = arm_sysctl_reset;
dc->vmsd = &vmstate_arm_sysctl;
dc->props = arm_sysctl_properties;
@@ -425,9 +413,9 @@ static TypeInfo arm_sysctl_info = {
.class_init = arm_sysctl_class_init,
};
-static void arm_sysctl_register_devices(void)
+static void arm_sysctl_register_types(void)
{
type_register_static(&arm_sysctl_info);
}
-device_init(arm_sysctl_register_devices)
+type_init(arm_sysctl_register_types)
diff --git a/hw/arm_timer.c b/hw/arm_timer.c
index 1019d41b3e..e3ecce29f0 100644
--- a/hw/arm_timer.c
+++ b/hw/arm_timer.c
@@ -383,10 +383,10 @@ static TypeInfo sp804_info = {
.class_init = sp804_class_init,
};
-static void arm_timer_register_devices(void)
+static void arm_timer_register_types(void)
{
type_register_static(&icp_pit_info);
type_register_static(&sp804_info);
}
-device_init(arm_timer_register_devices)
+type_init(arm_timer_register_types)
diff --git a/hw/armv7m.c b/hw/armv7m.c
index de3d7e0828..6b805798e6 100644
--- a/hw/armv7m.c
+++ b/hw/armv7m.c
@@ -266,9 +266,9 @@ static TypeInfo bitband_info = {
.class_init = bitband_class_init,
};
-static void armv7m_register_devices(void)
+static void armv7m_register_types(void)
{
type_register_static(&bitband_info);
}
-device_init(armv7m_register_devices)
+type_init(armv7m_register_types)
diff --git a/hw/armv7m_nvic.c b/hw/armv7m_nvic.c
index 1ed0abc3e7..3210129c3f 100644
--- a/hw/armv7m_nvic.c
+++ b/hw/armv7m_nvic.c
@@ -417,9 +417,9 @@ static TypeInfo armv7m_nvic_info = {
.class_init = armv7m_nvic_class_init,
};
-static void armv7m_nvic_register_devices(void)
+static void armv7m_nvic_register_types(void)
{
type_register_static(&armv7m_nvic_info);
}
-device_init(armv7m_nvic_register_devices)
+type_init(armv7m_nvic_register_types)
diff --git a/hw/baum.c b/hw/baum.c
index 86d780a617..3e94f84e51 100644
--- a/hw/baum.c
+++ b/hw/baum.c
@@ -562,7 +562,7 @@ static void baum_close(struct CharDriverState *chr)
g_free(baum);
}
-int chr_baum_init(QemuOpts *opts, CharDriverState **_chr)
+CharDriverState *chr_baum_init(QemuOpts *opts)
{
BaumDriverState *baum;
CharDriverState *chr;
@@ -614,8 +614,7 @@ int chr_baum_init(QemuOpts *opts, CharDriverState **_chr)
qemu_chr_generic_open(chr);
- *_chr = chr;
- return 0;
+ return chr;
fail:
qemu_free_timer(baum->cellCount_timer);
@@ -624,5 +623,5 @@ fail_handle:
g_free(handle);
g_free(chr);
g_free(baum);
- return -EIO;
+ return NULL;
}
diff --git a/hw/baum.h b/hw/baum.h
index 3f28cc339a..8af710fa21 100644
--- a/hw/baum.h
+++ b/hw/baum.h
@@ -23,4 +23,4 @@
*/
/* char device */
-int chr_baum_init(QemuOpts *opts, CharDriverState **_chr);
+CharDriverState *chr_baum_init(QemuOpts *opts);
diff --git a/hw/bitbang_i2c.c b/hw/bitbang_i2c.c
index c9c11823af..44ed7f4d61 100644
--- a/hw/bitbang_i2c.c
+++ b/hw/bitbang_i2c.c
@@ -237,9 +237,9 @@ static TypeInfo gpio_i2c_info = {
.class_init = gpio_i2c_class_init,
};
-static void bitbang_i2c_register(void)
+static void bitbang_i2c_register_types(void)
{
type_register_static(&gpio_i2c_info);
}
-device_init(bitbang_i2c_register)
+type_init(bitbang_i2c_register_types)
diff --git a/hw/blizzard.c b/hw/blizzard.c
index b2c1b22844..c7d844d105 100644
--- a/hw/blizzard.c
+++ b/hw/blizzard.c
@@ -932,10 +932,14 @@ static void blizzard_update_display(void *opaque)
s->my[1] = 0;
}
-static void blizzard_screen_dump(void *opaque, const char *filename) {
+static void blizzard_screen_dump(void *opaque, const char *filename,
+ bool cswitch)
+{
BlizzardState *s = (BlizzardState *) opaque;
- blizzard_update_display(opaque);
+ if (cswitch) {
+ blizzard_update_display(opaque);
+ }
if (s && ds_get_data(s->state))
ppm_save(filename, s->state->surface);
}
diff --git a/hw/boards.h b/hw/boards.h
index f6d3784cf1..667177d76d 100644
--- a/hw/boards.h
+++ b/hw/boards.h
@@ -32,6 +32,7 @@ typedef struct QEMUMachine {
} QEMUMachine;
int qemu_register_machine(QEMUMachine *m);
+QEMUMachine *find_default_machine(void);
extern QEMUMachine *current_machine;
diff --git a/hw/bonito.c b/hw/bonito.c
index 7350a4f9ec..77786f8883 100644
--- a/hw/bonito.c
+++ b/hw/bonito.c
@@ -804,9 +804,10 @@ static TypeInfo bonito_pcihost_info = {
.class_init = bonito_pcihost_class_init,
};
-static void bonito_register(void)
+static void bonito_register_types(void)
{
type_register_static(&bonito_pcihost_info);
type_register_static(&bonito_info);
}
-device_init(bonito_register);
+
+type_init(bonito_register_types)
diff --git a/hw/ccid-card-emulated.c b/hw/ccid-card-emulated.c
index 9510ed4d94..f4a6da4283 100644
--- a/hw/ccid-card-emulated.c
+++ b/hw/ccid-card-emulated.c
@@ -594,9 +594,9 @@ static TypeInfo emulated_card_info = {
.class_init = emulated_class_initfn,
};
-static void ccid_card_emulated_register_devices(void)
+static void ccid_card_emulated_register_types(void)
{
type_register_static(&emulated_card_info);
}
-device_init(ccid_card_emulated_register_devices)
+type_init(ccid_card_emulated_register_types)
diff --git a/hw/ccid-card-passthru.c b/hw/ccid-card-passthru.c
index a7006ca035..bd6c77777d 100644
--- a/hw/ccid-card-passthru.c
+++ b/hw/ccid-card-passthru.c
@@ -343,9 +343,9 @@ static TypeInfo passthru_card_info = {
.class_init = passthru_class_initfn,
};
-static void ccid_card_passthru_register_devices(void)
+static void ccid_card_passthru_register_types(void)
{
type_register_static(&passthru_card_info);
}
-device_init(ccid_card_passthru_register_devices)
+type_init(ccid_card_passthru_register_types)
diff --git a/hw/cirrus_vga.c b/hw/cirrus_vga.c
index a8e8ab7660..4edcb94774 100644
--- a/hw/cirrus_vga.c
+++ b/hw/cirrus_vga.c
@@ -2923,12 +2923,6 @@ static TypeInfo isa_cirrus_vga_info = {
.class_init = isa_cirrus_vga_class_init,
};
-static void isa_cirrus_vga_register(void)
-{
- type_register_static(&isa_cirrus_vga_info);
-}
-device_init(isa_cirrus_vga_register)
-
/***************************************
*
* PCI bus support
@@ -2996,8 +2990,10 @@ static TypeInfo cirrus_vga_info = {
.class_init = cirrus_vga_class_init,
};
-static void cirrus_vga_register(void)
+static void cirrus_vga_register_types(void)
{
+ type_register_static(&isa_cirrus_vga_info);
type_register_static(&cirrus_vga_info);
}
-device_init(cirrus_vga_register);
+
+type_init(cirrus_vga_register_types)
diff --git a/hw/cs4231.c b/hw/cs4231.c
index c0badbff5c..cfec1d9cd1 100644
--- a/hw/cs4231.c
+++ b/hw/cs4231.c
@@ -173,9 +173,9 @@ static TypeInfo cs4231_info = {
.class_init = cs4231_class_init,
};
-static void cs4231_register_devices(void)
+static void cs4231_register_types(void)
{
type_register_static(&cs4231_info);
}
-device_init(cs4231_register_devices)
+type_init(cs4231_register_types)
diff --git a/hw/cs4231a.c b/hw/cs4231a.c
index ad04ad667c..e07b9d6237 100644
--- a/hw/cs4231a.c
+++ b/hw/cs4231a.c
@@ -689,8 +689,9 @@ static TypeInfo cs4231a_info = {
.class_init = cs4231a_class_initfn,
};
-static void cs4231a_register (void)
+static void cs4231a_register_types (void)
{
type_register_static (&cs4231a_info);
}
-device_init (cs4231a_register)
+
+type_init (cs4231a_register_types)
diff --git a/hw/debugcon.c b/hw/debugcon.c
index 3903b2605d..14ab326be3 100644
--- a/hw/debugcon.c
+++ b/hw/debugcon.c
@@ -109,9 +109,9 @@ static TypeInfo debugcon_isa_info = {
.class_init = debugcon_isa_class_initfn,
};
-static void debugcon_register_devices(void)
+static void debugcon_register_types(void)
{
type_register_static(&debugcon_isa_info);
}
-device_init(debugcon_register_devices)
+type_init(debugcon_register_types)
diff --git a/hw/dec_pci.c b/hw/dec_pci.c
index a40fbcf3e5..37337bf4b6 100644
--- a/hw/dec_pci.c
+++ b/hw/dec_pci.c
@@ -140,11 +140,11 @@ static TypeInfo pci_dec_21154_device_info = {
.class_init = pci_dec_21154_device_class_init,
};
-static void dec_register_devices(void)
+static void dec_register_types(void)
{
type_register_static(&pci_dec_21154_device_info);
type_register_static(&dec_21154_pci_host_info);
type_register_static(&dec_21154_pci_bridge_info);
}
-device_init(dec_register_devices)
+type_init(dec_register_types)
diff --git a/hw/ds1225y.c b/hw/ds1225y.c
index 539bcebae0..2cd355bd0a 100644
--- a/hw/ds1225y.c
+++ b/hw/ds1225y.c
@@ -157,9 +157,9 @@ static TypeInfo nvram_sysbus_info = {
.class_init = nvram_sysbus_class_init,
};
-static void nvram_register(void)
+static void nvram_register_types(void)
{
type_register_static(&nvram_sysbus_info);
}
-device_init(nvram_register)
+type_init(nvram_register_types)
diff --git a/hw/ds1338.c b/hw/ds1338.c
index b137e13379..6397f0aa6f 100644
--- a/hw/ds1338.c
+++ b/hw/ds1338.c
@@ -135,9 +135,9 @@ static TypeInfo ds1338_info = {
.class_init = ds1338_class_init,
};
-static void ds1338_register_devices(void)
+static void ds1338_register_types(void)
{
type_register_static(&ds1338_info);
}
-device_init(ds1338_register_devices)
+type_init(ds1338_register_types)
diff --git a/hw/e1000.c b/hw/e1000.c
index 751f79d5ff..7babc0b06e 100644
--- a/hw/e1000.c
+++ b/hw/e1000.c
@@ -1227,9 +1227,9 @@ static TypeInfo e1000_info = {
.class_init = e1000_class_init,
};
-static void e1000_register_devices(void)
+static void e1000_register_types(void)
{
type_register_static(&e1000_info);
}
-device_init(e1000_register_devices)
+type_init(e1000_register_types)
diff --git a/hw/eccmemctl.c b/hw/eccmemctl.c
index 1cf2090c1f..fe1cd90007 100644
--- a/hw/eccmemctl.c
+++ b/hw/eccmemctl.c
@@ -332,9 +332,9 @@ static TypeInfo ecc_info = {
};
-static void ecc_register_devices(void)
+static void ecc_register_types(void)
{
type_register_static(&ecc_info);
}
-device_init(ecc_register_devices)
+type_init(ecc_register_types)
diff --git a/hw/eepro100.c b/hw/eepro100.c
index 843610c933..e3ba71974e 100644
--- a/hw/eepro100.c
+++ b/hw/eepro100.c
@@ -2089,7 +2089,7 @@ static void eepro100_class_init(ObjectClass *klass, void *data)
k->subsystem_id = info->subsystem_id;
}
-static void eepro100_register_devices(void)
+static void eepro100_register_types(void)
{
size_t i;
for (i = 0; i < ARRAY_SIZE(e100_devices); i++) {
@@ -2105,4 +2105,4 @@ static void eepro100_register_devices(void)
}
}
-device_init(eepro100_register_devices)
+type_init(eepro100_register_types)
diff --git a/hw/empty_slot.c b/hw/empty_slot.c
index 1bc181555f..099c85e583 100644
--- a/hw/empty_slot.c
+++ b/hw/empty_slot.c
@@ -90,9 +90,9 @@ static TypeInfo empty_slot_info = {
.class_init = empty_slot_class_init,
};
-static void empty_slot_register_devices(void)
+static void empty_slot_register_types(void)
{
type_register_static(&empty_slot_info);
}
-device_init(empty_slot_register_devices);
+type_init(empty_slot_register_types)
diff --git a/hw/es1370.c b/hw/es1370.c
index e377c48e49..f19cef31a6 100644
--- a/hw/es1370.c
+++ b/hw/es1370.c
@@ -1054,9 +1054,10 @@ static TypeInfo es1370_info = {
.class_init = es1370_class_init,
};
-static void es1370_register (void)
+static void es1370_register_types (void)
{
type_register_static (&es1370_info);
}
-device_init (es1370_register);
+
+type_init (es1370_register_types)
diff --git a/hw/escc.c b/hw/escc.c
index 9fe282f7b8..4d8a8e8886 100644
--- a/hw/escc.c
+++ b/hw/escc.c
@@ -931,9 +931,9 @@ static TypeInfo escc_info = {
.class_init = escc_class_init,
};
-static void escc_register_devices(void)
+static void escc_register_types(void)
{
type_register_static(&escc_info);
}
-device_init(escc_register_devices)
+type_init(escc_register_types)
diff --git a/hw/esp.c b/hw/esp.c
index 2f44386233..8d73e56886 100644
--- a/hw/esp.c
+++ b/hw/esp.c
@@ -390,7 +390,8 @@ static void esp_do_dma(ESPState *s)
esp_dma_done(s);
}
-static void esp_command_complete(SCSIRequest *req, uint32_t status)
+static void esp_command_complete(SCSIRequest *req, uint32_t status,
+ size_t resid)
{
ESPState *s = DO_UPCAST(ESPState, busdev.qdev, req->bus->qbus.parent);
@@ -775,9 +776,9 @@ static TypeInfo esp_info = {
.class_init = esp_class_init,
};
-static void esp_register_devices(void)
+static void esp_register_types(void)
{
type_register_static(&esp_info);
}
-device_init(esp_register_devices)
+type_init(esp_register_types)
diff --git a/hw/etraxfs_eth.c b/hw/etraxfs_eth.c
index aefd5778ab..16a0637a4a 100644
--- a/hw/etraxfs_eth.c
+++ b/hw/etraxfs_eth.c
@@ -637,9 +637,9 @@ static TypeInfo etraxfs_eth_info = {
.class_init = etraxfs_eth_class_init,
};
-static void etraxfs_eth_register(void)
+static void etraxfs_eth_register_types(void)
{
type_register_static(&etraxfs_eth_info);
}
-device_init(etraxfs_eth_register)
+type_init(etraxfs_eth_register_types)
diff --git a/hw/etraxfs_pic.c b/hw/etraxfs_pic.c
index 33541fcc61..dc27f88ac9 100644
--- a/hw/etraxfs_pic.c
+++ b/hw/etraxfs_pic.c
@@ -172,9 +172,9 @@ static TypeInfo etraxfs_pic_info = {
.class_init = etraxfs_pic_class_init,
};
-static void etraxfs_pic_register(void)
+static void etraxfs_pic_register_types(void)
{
type_register_static(&etraxfs_pic_info);
}
-device_init(etraxfs_pic_register)
+type_init(etraxfs_pic_register_types)
diff --git a/hw/etraxfs_ser.c b/hw/etraxfs_ser.c
index 567cb8cc2d..cecd819584 100644
--- a/hw/etraxfs_ser.c
+++ b/hw/etraxfs_ser.c
@@ -240,9 +240,9 @@ static TypeInfo etraxfs_ser_info = {
.class_init = etraxfs_ser_class_init,
};
-static void etraxfs_serial_register(void)
+static void etraxfs_serial_register_types(void)
{
type_register_static(&etraxfs_ser_info);
}
-device_init(etraxfs_serial_register)
+type_init(etraxfs_serial_register_types)
diff --git a/hw/etraxfs_timer.c b/hw/etraxfs_timer.c
index b71c5ee9d5..9076a49884 100644
--- a/hw/etraxfs_timer.c
+++ b/hw/etraxfs_timer.c
@@ -343,9 +343,9 @@ static TypeInfo etraxfs_timer_info = {
.class_init = etraxfs_timer_class_init,
};
-static void etraxfs_timer_register(void)
+static void etraxfs_timer_register_types(void)
{
type_register_static(&etraxfs_timer_info);
}
-device_init(etraxfs_timer_register)
+type_init(etraxfs_timer_register_types)
diff --git a/hw/exynos4210.c b/hw/exynos4210.c
new file mode 100644
index 0000000000..f904370505
--- /dev/null
+++ b/hw/exynos4210.c
@@ -0,0 +1,270 @@
+/*
+ * Samsung exynos4210 SoC emulation
+ *
+ * Copyright (c) 2011 Samsung Electronics Co., Ltd. All rights reserved.
+ * Maksim Kozlov <m.kozlov@samsung.com>
+ * Evgeny Voevodin <e.voevodin@samsung.com>
+ * Igor Mitsyanko <i.mitsyanko@samsung.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "boards.h"
+#include "sysemu.h"
+#include "sysbus.h"
+#include "arm-misc.h"
+#include "exynos4210.h"
+
+#define EXYNOS4210_CHIPID_ADDR 0x10000000
+
+/* PWM */
+#define EXYNOS4210_PWM_BASE_ADDR 0x139D0000
+
+/* MCT */
+#define EXYNOS4210_MCT_BASE_ADDR 0x10050000
+
+/* UART's definitions */
+#define EXYNOS4210_UART0_BASE_ADDR 0x13800000
+#define EXYNOS4210_UART1_BASE_ADDR 0x13810000
+#define EXYNOS4210_UART2_BASE_ADDR 0x13820000
+#define EXYNOS4210_UART3_BASE_ADDR 0x13830000
+#define EXYNOS4210_UART0_FIFO_SIZE 256
+#define EXYNOS4210_UART1_FIFO_SIZE 64
+#define EXYNOS4210_UART2_FIFO_SIZE 16
+#define EXYNOS4210_UART3_FIFO_SIZE 16
+/* Interrupt Group of External Interrupt Combiner for UART */
+#define EXYNOS4210_UART_INT_GRP 26
+
+/* External GIC */
+#define EXYNOS4210_EXT_GIC_CPU_BASE_ADDR 0x10480000
+#define EXYNOS4210_EXT_GIC_DIST_BASE_ADDR 0x10490000
+
+/* Combiner */
+#define EXYNOS4210_EXT_COMBINER_BASE_ADDR 0x10440000
+#define EXYNOS4210_INT_COMBINER_BASE_ADDR 0x10448000
+
+/* PMU SFR base address */
+#define EXYNOS4210_PMU_BASE_ADDR 0x10020000
+
+/* Display controllers (FIMD) */
+#define EXYNOS4210_FIMD0_BASE_ADDR 0x11C00000
+
+static uint8_t chipid_and_omr[] = { 0x11, 0x02, 0x21, 0x43,
+ 0x09, 0x00, 0x00, 0x00 };
+
+Exynos4210State *exynos4210_init(MemoryRegion *system_mem,
+ unsigned long ram_size)
+{
+ qemu_irq cpu_irq[4];
+ int n;
+ Exynos4210State *s = g_new(Exynos4210State, 1);
+ qemu_irq *irqp;
+ qemu_irq gate_irq[EXYNOS4210_IRQ_GATE_NINPUTS];
+ unsigned long mem_size;
+ DeviceState *dev;
+ SysBusDevice *busdev;
+
+ for (n = 0; n < EXYNOS4210_NCPUS; n++) {
+ s->env[n] = cpu_init("cortex-a9");
+ if (!s->env[n]) {
+ fprintf(stderr, "Unable to find CPU %d definition\n", n);
+ exit(1);
+ }
+ /* Create PIC controller for each processor instance */
+ irqp = arm_pic_init_cpu(s->env[n]);
+
+ /*
+ * Get GICs gpio_in cpu_irq to connect a combiner to them later.
+ * Use only IRQ for a while.
+ */
+ cpu_irq[n] = irqp[ARM_PIC_CPU_IRQ];
+ }
+
+ /*** IRQs ***/
+
+ s->irq_table = exynos4210_init_irq(&s->irqs);
+
+ /* IRQ Gate */
+ dev = qdev_create(NULL, "exynos4210.irq_gate");
+ qdev_init_nofail(dev);
+ /* Get IRQ Gate input in gate_irq */
+ for (n = 0; n < EXYNOS4210_IRQ_GATE_NINPUTS; n++) {
+ gate_irq[n] = qdev_get_gpio_in(dev, n);
+ }
+ busdev = sysbus_from_qdev(dev);
+ /* Connect IRQ Gate output to cpu_irq */
+ for (n = 0; n < EXYNOS4210_NCPUS; n++) {
+ sysbus_connect_irq(busdev, n, cpu_irq[n]);
+ }
+
+ /* Private memory region and Internal GIC */
+ dev = qdev_create(NULL, "a9mpcore_priv");
+ qdev_prop_set_uint32(dev, "num-cpu", EXYNOS4210_NCPUS);
+ qdev_init_nofail(dev);
+ busdev = sysbus_from_qdev(dev);
+ sysbus_mmio_map(busdev, 0, EXYNOS4210_SMP_PRIVATE_BASE_ADDR);
+ for (n = 0; n < EXYNOS4210_NCPUS; n++) {
+ sysbus_connect_irq(busdev, n, gate_irq[n * 2]);
+ }
+ for (n = 0; n < EXYNOS4210_INT_GIC_NIRQ; n++) {
+ s->irqs.int_gic_irq[n] = qdev_get_gpio_in(dev, n);
+ }
+
+ /* Cache controller */
+ sysbus_create_simple("l2x0", EXYNOS4210_L2X0_BASE_ADDR, NULL);
+
+ /* External GIC */
+ dev = qdev_create(NULL, "exynos4210.gic");
+ qdev_prop_set_uint32(dev, "num-cpu", EXYNOS4210_NCPUS);
+ qdev_init_nofail(dev);
+ busdev = sysbus_from_qdev(dev);
+ /* Map CPU interface */
+ sysbus_mmio_map(busdev, 0, EXYNOS4210_EXT_GIC_CPU_BASE_ADDR);
+ /* Map Distributer interface */
+ sysbus_mmio_map(busdev, 1, EXYNOS4210_EXT_GIC_DIST_BASE_ADDR);
+ for (n = 0; n < EXYNOS4210_NCPUS; n++) {
+ sysbus_connect_irq(busdev, n, gate_irq[n * 2 + 1]);
+ }
+ for (n = 0; n < EXYNOS4210_EXT_GIC_NIRQ; n++) {
+ s->irqs.ext_gic_irq[n] = qdev_get_gpio_in(dev, n);
+ }
+
+ /* Internal Interrupt Combiner */
+ dev = qdev_create(NULL, "exynos4210.combiner");
+ qdev_init_nofail(dev);
+ busdev = sysbus_from_qdev(dev);
+ for (n = 0; n < EXYNOS4210_MAX_INT_COMBINER_OUT_IRQ; n++) {
+ sysbus_connect_irq(busdev, n, s->irqs.int_gic_irq[n]);
+ }
+ exynos4210_combiner_get_gpioin(&s->irqs, dev, 0);
+ sysbus_mmio_map(busdev, 0, EXYNOS4210_INT_COMBINER_BASE_ADDR);
+
+ /* External Interrupt Combiner */
+ dev = qdev_create(NULL, "exynos4210.combiner");
+ qdev_prop_set_uint32(dev, "external", 1);
+ qdev_init_nofail(dev);
+ busdev = sysbus_from_qdev(dev);
+ for (n = 0; n < EXYNOS4210_MAX_INT_COMBINER_OUT_IRQ; n++) {
+ sysbus_connect_irq(busdev, n, s->irqs.ext_gic_irq[n]);
+ }
+ exynos4210_combiner_get_gpioin(&s->irqs, dev, 1);
+ sysbus_mmio_map(busdev, 0, EXYNOS4210_EXT_COMBINER_BASE_ADDR);
+
+ /* Initialize board IRQs. */
+ exynos4210_init_board_irqs(&s->irqs);
+
+ /*** Memory ***/
+
+ /* Chip-ID and OMR */
+ memory_region_init_ram_ptr(&s->chipid_mem, "exynos4210.chipid",
+ sizeof(chipid_and_omr), chipid_and_omr);
+ memory_region_set_readonly(&s->chipid_mem, true);
+ memory_region_add_subregion(system_mem, EXYNOS4210_CHIPID_ADDR,
+ &s->chipid_mem);
+
+ /* Internal ROM */
+ memory_region_init_ram(&s->irom_mem, "exynos4210.irom",
+ EXYNOS4210_IROM_SIZE);
+ memory_region_set_readonly(&s->irom_mem, true);
+ memory_region_add_subregion(system_mem, EXYNOS4210_IROM_BASE_ADDR,
+ &s->irom_mem);
+ /* mirror of iROM */
+ memory_region_init_alias(&s->irom_alias_mem, "exynos4210.irom_alias",
+ &s->irom_mem,
+ EXYNOS4210_IROM_BASE_ADDR,
+ EXYNOS4210_IROM_SIZE);
+ memory_region_set_readonly(&s->irom_alias_mem, true);
+ memory_region_add_subregion(system_mem, EXYNOS4210_IROM_MIRROR_BASE_ADDR,
+ &s->irom_alias_mem);
+
+ /* Internal RAM */
+ memory_region_init_ram(&s->iram_mem, "exynos4210.iram",
+ EXYNOS4210_IRAM_SIZE);
+ vmstate_register_ram_global(&s->iram_mem);
+ memory_region_add_subregion(system_mem, EXYNOS4210_IRAM_BASE_ADDR,
+ &s->iram_mem);
+
+ /* DRAM */
+ mem_size = ram_size;
+ if (mem_size > EXYNOS4210_DRAM_MAX_SIZE) {
+ memory_region_init_ram(&s->dram1_mem, "exynos4210.dram1",
+ mem_size - EXYNOS4210_DRAM_MAX_SIZE);
+ vmstate_register_ram_global(&s->dram1_mem);
+ memory_region_add_subregion(system_mem, EXYNOS4210_DRAM1_BASE_ADDR,
+ &s->dram1_mem);
+ mem_size = EXYNOS4210_DRAM_MAX_SIZE;
+ }
+ memory_region_init_ram(&s->dram0_mem, "exynos4210.dram0", mem_size);
+ vmstate_register_ram_global(&s->dram0_mem);
+ memory_region_add_subregion(system_mem, EXYNOS4210_DRAM0_BASE_ADDR,
+ &s->dram0_mem);
+
+ /* PMU.
+ * The only reason of existence at the moment is that secondary CPU boot
+ * loader uses PMU INFORM5 register as a holding pen.
+ */
+ sysbus_create_simple("exynos4210.pmu", EXYNOS4210_PMU_BASE_ADDR, NULL);
+
+ /* PWM */
+ sysbus_create_varargs("exynos4210.pwm", EXYNOS4210_PWM_BASE_ADDR,
+ s->irq_table[exynos4210_get_irq(22, 0)],
+ s->irq_table[exynos4210_get_irq(22, 1)],
+ s->irq_table[exynos4210_get_irq(22, 2)],
+ s->irq_table[exynos4210_get_irq(22, 3)],
+ s->irq_table[exynos4210_get_irq(22, 4)],
+ NULL);
+
+ /* Multi Core Timer */
+ dev = qdev_create(NULL, "exynos4210.mct");
+ qdev_init_nofail(dev);
+ busdev = sysbus_from_qdev(dev);
+ for (n = 0; n < 4; n++) {
+ /* Connect global timer interrupts to Combiner gpio_in */
+ sysbus_connect_irq(busdev, n,
+ s->irq_table[exynos4210_get_irq(1, 4 + n)]);
+ }
+ /* Connect local timer interrupts to Combiner gpio_in */
+ sysbus_connect_irq(busdev, 4,
+ s->irq_table[exynos4210_get_irq(51, 0)]);
+ sysbus_connect_irq(busdev, 5,
+ s->irq_table[exynos4210_get_irq(35, 3)]);
+ sysbus_mmio_map(busdev, 0, EXYNOS4210_MCT_BASE_ADDR);
+
+ /*** UARTs ***/
+ exynos4210_uart_create(EXYNOS4210_UART0_BASE_ADDR,
+ EXYNOS4210_UART0_FIFO_SIZE, 0, NULL,
+ s->irq_table[exynos4210_get_irq(EXYNOS4210_UART_INT_GRP, 0)]);
+
+ exynos4210_uart_create(EXYNOS4210_UART1_BASE_ADDR,
+ EXYNOS4210_UART1_FIFO_SIZE, 1, NULL,
+ s->irq_table[exynos4210_get_irq(EXYNOS4210_UART_INT_GRP, 1)]);
+
+ exynos4210_uart_create(EXYNOS4210_UART2_BASE_ADDR,
+ EXYNOS4210_UART2_FIFO_SIZE, 2, NULL,
+ s->irq_table[exynos4210_get_irq(EXYNOS4210_UART_INT_GRP, 2)]);
+
+ exynos4210_uart_create(EXYNOS4210_UART3_BASE_ADDR,
+ EXYNOS4210_UART3_FIFO_SIZE, 3, NULL,
+ s->irq_table[exynos4210_get_irq(EXYNOS4210_UART_INT_GRP, 3)]);
+
+ /*** Display controller (FIMD) ***/
+ sysbus_create_varargs("exynos4210.fimd", EXYNOS4210_FIMD0_BASE_ADDR,
+ s->irq_table[exynos4210_get_irq(11, 0)],
+ s->irq_table[exynos4210_get_irq(11, 1)],
+ s->irq_table[exynos4210_get_irq(11, 2)],
+ NULL);
+
+ return s;
+}
diff --git a/hw/exynos4210.h b/hw/exynos4210.h
new file mode 100644
index 0000000000..e7522f851a
--- /dev/null
+++ b/hw/exynos4210.h
@@ -0,0 +1,131 @@
+/*
+ * Samsung exynos4210 SoC emulation
+ *
+ * Copyright (c) 2011 Samsung Electronics Co., Ltd. All rights reserved.
+ * Maksim Kozlov <m.kozlov@samsung.com>
+ * Evgeny Voevodin <e.voevodin@samsung.com>
+ * Igor Mitsyanko <i.mitsyanko@samsung.com>
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+
+#ifndef EXYNOS4210_H_
+#define EXYNOS4210_H_
+
+#include "qemu-common.h"
+#include "memory.h"
+
+#define EXYNOS4210_NCPUS 2
+
+#define EXYNOS4210_DRAM0_BASE_ADDR 0x40000000
+#define EXYNOS4210_DRAM1_BASE_ADDR 0xa0000000
+#define EXYNOS4210_DRAM_MAX_SIZE 0x60000000 /* 1.5 GB */
+
+#define EXYNOS4210_IROM_BASE_ADDR 0x00000000
+#define EXYNOS4210_IROM_SIZE 0x00010000 /* 64 KB */
+#define EXYNOS4210_IROM_MIRROR_BASE_ADDR 0x02000000
+#define EXYNOS4210_IROM_MIRROR_SIZE 0x00010000 /* 64 KB */
+
+#define EXYNOS4210_IRAM_BASE_ADDR 0x02020000
+#define EXYNOS4210_IRAM_SIZE 0x00020000 /* 128 KB */
+
+/* Secondary CPU startup code is in IROM memory */
+#define EXYNOS4210_SMP_BOOT_ADDR EXYNOS4210_IROM_BASE_ADDR
+#define EXYNOS4210_SMP_BOOT_SIZE 0x1000
+#define EXYNOS4210_BASE_BOOT_ADDR EXYNOS4210_DRAM0_BASE_ADDR
+/* Secondary CPU polling address to get loader start from */
+#define EXYNOS4210_SECOND_CPU_BOOTREG 0x10020814
+
+#define EXYNOS4210_SMP_PRIVATE_BASE_ADDR 0x10500000
+#define EXYNOS4210_L2X0_BASE_ADDR 0x10502000
+
+/*
+ * exynos4210 IRQ subsystem stub definitions.
+ */
+#define EXYNOS4210_IRQ_GATE_NINPUTS 8
+
+#define EXYNOS4210_MAX_INT_COMBINER_OUT_IRQ 64
+#define EXYNOS4210_MAX_EXT_COMBINER_OUT_IRQ 16
+#define EXYNOS4210_MAX_INT_COMBINER_IN_IRQ \
+ (EXYNOS4210_MAX_INT_COMBINER_OUT_IRQ * 8)
+#define EXYNOS4210_MAX_EXT_COMBINER_IN_IRQ \
+ (EXYNOS4210_MAX_EXT_COMBINER_OUT_IRQ * 8)
+
+#define EXYNOS4210_COMBINER_GET_IRQ_NUM(grp, bit) ((grp)*8 + (bit))
+#define EXYNOS4210_COMBINER_GET_GRP_NUM(irq) ((irq) / 8)
+#define EXYNOS4210_COMBINER_GET_BIT_NUM(irq) \
+ ((irq) - 8 * EXYNOS4210_COMBINER_GET_GRP_NUM(irq))
+
+/* IRQs number for external and internal GIC */
+#define EXYNOS4210_EXT_GIC_NIRQ (160-32)
+#define EXYNOS4210_INT_GIC_NIRQ 64
+
+typedef struct Exynos4210Irq {
+ qemu_irq int_combiner_irq[EXYNOS4210_MAX_INT_COMBINER_IN_IRQ];
+ qemu_irq ext_combiner_irq[EXYNOS4210_MAX_EXT_COMBINER_IN_IRQ];
+ qemu_irq int_gic_irq[EXYNOS4210_INT_GIC_NIRQ];
+ qemu_irq ext_gic_irq[EXYNOS4210_EXT_GIC_NIRQ];
+ qemu_irq board_irqs[EXYNOS4210_MAX_INT_COMBINER_IN_IRQ];
+} Exynos4210Irq;
+
+typedef struct Exynos4210State {
+ CPUState * env[EXYNOS4210_NCPUS];
+ Exynos4210Irq irqs;
+ qemu_irq *irq_table;
+
+ MemoryRegion chipid_mem;
+ MemoryRegion iram_mem;
+ MemoryRegion irom_mem;
+ MemoryRegion irom_alias_mem;
+ MemoryRegion dram0_mem;
+ MemoryRegion dram1_mem;
+ MemoryRegion boot_secondary;
+ MemoryRegion bootreg_mem;
+} Exynos4210State;
+
+Exynos4210State *exynos4210_init(MemoryRegion *system_mem,
+ unsigned long ram_size);
+
+/* Initialize exynos4210 IRQ subsystem stub */
+qemu_irq *exynos4210_init_irq(Exynos4210Irq *env);
+
+/* Initialize board IRQs.
+ * These IRQs contain splitted Int/External Combiner and External Gic IRQs */
+void exynos4210_init_board_irqs(Exynos4210Irq *s);
+
+/* Get IRQ number from exynos4210 IRQ subsystem stub.
+ * To identify IRQ source use internal combiner group and bit number
+ * grp - group number
+ * bit - bit number inside group */
+uint32_t exynos4210_get_irq(uint32_t grp, uint32_t bit);
+
+/*
+ * Get Combiner input GPIO into irqs structure
+ */
+void exynos4210_combiner_get_gpioin(Exynos4210Irq *irqs, DeviceState *dev,
+ int ext);
+
+/*
+ * exynos4210 UART
+ */
+DeviceState *exynos4210_uart_create(target_phys_addr_t addr,
+ int fifo_size,
+ int channel,
+ CharDriverState *chr,
+ qemu_irq irq);
+
+#endif /* EXYNOS4210_H_ */
diff --git a/hw/exynos4210_combiner.c b/hw/exynos4210_combiner.c
new file mode 100644
index 0000000000..6110c19d5d
--- /dev/null
+++ b/hw/exynos4210_combiner.c
@@ -0,0 +1,469 @@
+/*
+ * Samsung exynos4210 Interrupt Combiner
+ *
+ * Copyright (c) 2000 - 2011 Samsung Electronics Co., Ltd.
+ * All rights reserved.
+ *
+ * Evgeny Voevodin <e.voevodin@samsung.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ * See the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+/*
+ * Exynos4210 Combiner represents an OR gate for SOC's IRQ lines. It combines
+ * IRQ sources into groups and provides signal output to GIC from each group. It
+ * is driven by common mask and enable/disable logic. Take a note that not all
+ * IRQs are passed to GIC through Combiner.
+ */
+
+#include "sysbus.h"
+
+#include "exynos4210.h"
+
+//#define DEBUG_COMBINER
+
+#ifdef DEBUG_COMBINER
+#define DPRINTF(fmt, ...) \
+ do { fprintf(stdout, "COMBINER: [%s:%d] " fmt, __func__ , __LINE__, \
+ ## __VA_ARGS__); } while (0)
+#else
+#define DPRINTF(fmt, ...) do {} while (0)
+#endif
+
+#define IIC_NGRP 64 /* Internal Interrupt Combiner
+ Groups number */
+#define IIC_NIRQ (IIC_NGRP * 8)/* Internal Interrupt Combiner
+ Interrupts number */
+#define IIC_REGION_SIZE 0x108 /* Size of memory mapped region */
+#define IIC_REGSET_SIZE 0x41
+
+/*
+ * State for each output signal of internal combiner
+ */
+typedef struct CombinerGroupState {
+ uint8_t src_mask; /* 1 - source enabled, 0 - disabled */
+ uint8_t src_pending; /* Pending source interrupts before masking */
+} CombinerGroupState;
+
+typedef struct Exynos4210CombinerState {
+ SysBusDevice busdev;
+ MemoryRegion iomem;
+
+ struct CombinerGroupState group[IIC_NGRP];
+ uint32_t reg_set[IIC_REGSET_SIZE];
+ uint32_t icipsr[2];
+ uint32_t external; /* 1 means that this combiner is external */
+
+ qemu_irq output_irq[IIC_NGRP];
+} Exynos4210CombinerState;
+
+static const VMStateDescription vmstate_exynos4210_combiner_group_state = {
+ .name = "exynos4210.combiner.groupstate",
+ .version_id = 1,
+ .minimum_version_id = 1,
+ .minimum_version_id_old = 1,
+ .fields = (VMStateField[]) {
+ VMSTATE_UINT8(src_mask, CombinerGroupState),
+ VMSTATE_UINT8(src_pending, CombinerGroupState),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
+static const VMStateDescription vmstate_exynos4210_combiner = {
+ .name = "exynos4210.combiner",
+ .version_id = 1,
+ .minimum_version_id = 1,
+ .minimum_version_id_old = 1,
+ .fields = (VMStateField[]) {
+ VMSTATE_STRUCT_ARRAY(group, Exynos4210CombinerState, IIC_NGRP, 0,
+ vmstate_exynos4210_combiner_group_state, CombinerGroupState),
+ VMSTATE_UINT32_ARRAY(reg_set, Exynos4210CombinerState,
+ IIC_REGSET_SIZE),
+ VMSTATE_UINT32_ARRAY(icipsr, Exynos4210CombinerState, 2),
+ VMSTATE_UINT32(external, Exynos4210CombinerState),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
+/*
+ * Get Combiner input GPIO into irqs structure
+ */
+void exynos4210_combiner_get_gpioin(Exynos4210Irq *irqs, DeviceState *dev,
+ int ext)
+{
+ int n;
+ int bit;
+ int max;
+ qemu_irq *irq;
+
+ max = ext ? EXYNOS4210_MAX_EXT_COMBINER_IN_IRQ :
+ EXYNOS4210_MAX_INT_COMBINER_IN_IRQ;
+ irq = ext ? irqs->ext_combiner_irq : irqs->int_combiner_irq;
+
+ /*
+ * Some IRQs of Int/External Combiner are going to two Combiners groups,
+ * so let split them.
+ */
+ for (n = 0; n < max; n++) {
+
+ bit = EXYNOS4210_COMBINER_GET_BIT_NUM(n);
+
+ switch (n) {
+ /* MDNIE_LCD1 INTG1 */
+ case EXYNOS4210_COMBINER_GET_IRQ_NUM(1, 0) ...
+ EXYNOS4210_COMBINER_GET_IRQ_NUM(1, 3):
+ irq[n] = qemu_irq_split(qdev_get_gpio_in(dev, n),
+ irq[EXYNOS4210_COMBINER_GET_IRQ_NUM(0, bit + 4)]);
+ continue;
+
+ /* TMU INTG3 */
+ case EXYNOS4210_COMBINER_GET_IRQ_NUM(3, 4):
+ irq[n] = qemu_irq_split(qdev_get_gpio_in(dev, n),
+ irq[EXYNOS4210_COMBINER_GET_IRQ_NUM(2, bit)]);
+ continue;
+
+ /* LCD1 INTG12 */
+ case EXYNOS4210_COMBINER_GET_IRQ_NUM(12, 0) ...
+ EXYNOS4210_COMBINER_GET_IRQ_NUM(12, 3):
+ irq[n] = qemu_irq_split(qdev_get_gpio_in(dev, n),
+ irq[EXYNOS4210_COMBINER_GET_IRQ_NUM(11, bit + 4)]);
+ continue;
+
+ /* Multi-Core Timer INTG12 */
+ case EXYNOS4210_COMBINER_GET_IRQ_NUM(12, 4) ...
+ EXYNOS4210_COMBINER_GET_IRQ_NUM(12, 8):
+ irq[n] = qemu_irq_split(qdev_get_gpio_in(dev, n),
+ irq[EXYNOS4210_COMBINER_GET_IRQ_NUM(1, bit + 4)]);
+ continue;
+
+ /* Multi-Core Timer INTG35 */
+ case EXYNOS4210_COMBINER_GET_IRQ_NUM(35, 4) ...
+ EXYNOS4210_COMBINER_GET_IRQ_NUM(35, 8):
+ irq[n] = qemu_irq_split(qdev_get_gpio_in(dev, n),
+ irq[EXYNOS4210_COMBINER_GET_IRQ_NUM(1, bit + 4)]);
+ continue;
+
+ /* Multi-Core Timer INTG51 */
+ case EXYNOS4210_COMBINER_GET_IRQ_NUM(51, 4) ...
+ EXYNOS4210_COMBINER_GET_IRQ_NUM(51, 8):
+ irq[n] = qemu_irq_split(qdev_get_gpio_in(dev, n),
+ irq[EXYNOS4210_COMBINER_GET_IRQ_NUM(1, bit + 4)]);
+ continue;
+
+ /* Multi-Core Timer INTG53 */
+ case EXYNOS4210_COMBINER_GET_IRQ_NUM(53, 4) ...
+ EXYNOS4210_COMBINER_GET_IRQ_NUM(53, 8):
+ irq[n] = qemu_irq_split(qdev_get_gpio_in(dev, n),
+ irq[EXYNOS4210_COMBINER_GET_IRQ_NUM(1, bit + 4)]);
+ continue;
+ }
+
+ irq[n] = qdev_get_gpio_in(dev, n);
+ }
+}
+
+static uint64_t
+exynos4210_combiner_read(void *opaque, target_phys_addr_t offset, unsigned size)
+{
+ struct Exynos4210CombinerState *s =
+ (struct Exynos4210CombinerState *)opaque;
+ uint32_t req_quad_base_n; /* Base of registers quad. Multiply it by 4 and
+ get a start of corresponding group quad */
+ uint32_t grp_quad_base_n; /* Base of group quad */
+ uint32_t reg_n; /* Register number inside the quad */
+ uint32_t val;
+
+ if (s->external && (offset > 0x3c && offset != 0x100)) {
+ hw_error("exynos4210.combiner: unallowed read access at offset 0x"
+ TARGET_FMT_plx "\n", offset);
+ }
+
+ req_quad_base_n = offset >> 4;
+ grp_quad_base_n = req_quad_base_n << 2;
+ reg_n = (offset - (req_quad_base_n << 4)) >> 2;
+
+ if (req_quad_base_n >= IIC_NGRP) {
+ /* Read of ICIPSR register */
+ return s->icipsr[reg_n];
+ }
+
+ val = 0;
+
+ switch (reg_n) {
+ /* IISTR */
+ case 2:
+ val |= s->group[grp_quad_base_n].src_pending;
+ val |= s->group[grp_quad_base_n + 1].src_pending << 8;
+ val |= s->group[grp_quad_base_n + 2].src_pending << 16;
+ val |= s->group[grp_quad_base_n + 3].src_pending << 24;
+ break;
+ /* IIMSR */
+ case 3:
+ val |= s->group[grp_quad_base_n].src_mask &
+ s->group[grp_quad_base_n].src_pending;
+ val |= (s->group[grp_quad_base_n + 1].src_mask &
+ s->group[grp_quad_base_n + 1].src_pending) << 8;
+ val |= (s->group[grp_quad_base_n + 2].src_mask &
+ s->group[grp_quad_base_n + 2].src_pending) << 16;
+ val |= (s->group[grp_quad_base_n + 3].src_mask &
+ s->group[grp_quad_base_n + 3].src_pending) << 24;
+ break;
+ default:
+ if (offset >> 2 >= IIC_REGSET_SIZE) {
+ hw_error("exynos4210.combiner: overflow of reg_set by 0x"
+ TARGET_FMT_plx "offset\n", offset);
+ }
+ val = s->reg_set[offset >> 2];
+ return 0;
+ }
+ return val;
+}
+
+static void exynos4210_combiner_update(void *opaque, uint8_t group_n)
+{
+ struct Exynos4210CombinerState *s =
+ (struct Exynos4210CombinerState *)opaque;
+
+ /* Send interrupt if needed */
+ if (s->group[group_n].src_mask & s->group[group_n].src_pending) {
+#ifdef DEBUG_COMBINER
+ if (group_n != 26) {
+ /* skip uart */
+ DPRINTF("%s raise IRQ[%d]\n", s->external ? "EXT" : "INT", group_n);
+ }
+#endif
+
+ /* Set Combiner interrupt pending status after masking */
+ if (group_n >= 32) {
+ s->icipsr[1] |= 1 << (group_n - 32);
+ } else {
+ s->icipsr[0] |= 1 << group_n;
+ }
+
+ qemu_irq_raise(s->output_irq[group_n]);
+ } else {
+#ifdef DEBUG_COMBINER
+ if (group_n != 26) {
+ /* skip uart */
+ DPRINTF("%s lower IRQ[%d]\n", s->external ? "EXT" : "INT", group_n);
+ }
+#endif
+
+ /* Set Combiner interrupt pending status after masking */
+ if (group_n >= 32) {
+ s->icipsr[1] &= ~(1 << (group_n - 32));
+ } else {
+ s->icipsr[0] &= ~(1 << group_n);
+ }
+
+ qemu_irq_lower(s->output_irq[group_n]);
+ }
+}
+
+static void exynos4210_combiner_write(void *opaque, target_phys_addr_t offset,
+ uint64_t val, unsigned size)
+{
+ struct Exynos4210CombinerState *s =
+ (struct Exynos4210CombinerState *)opaque;
+ uint32_t req_quad_base_n; /* Base of registers quad. Multiply it by 4 and
+ get a start of corresponding group quad */
+ uint32_t grp_quad_base_n; /* Base of group quad */
+ uint32_t reg_n; /* Register number inside the quad */
+
+ if (s->external && (offset > 0x3c && offset != 0x100)) {
+ hw_error("exynos4210.combiner: unallowed write access at offset 0x"
+ TARGET_FMT_plx "\n", offset);
+ }
+
+ req_quad_base_n = offset >> 4;
+ grp_quad_base_n = req_quad_base_n << 2;
+ reg_n = (offset - (req_quad_base_n << 4)) >> 2;
+
+ if (req_quad_base_n >= IIC_NGRP) {
+ hw_error("exynos4210.combiner: unallowed write access at offset 0x"
+ TARGET_FMT_plx "\n", offset);
+ return;
+ }
+
+ if (reg_n > 1) {
+ hw_error("exynos4210.combiner: unallowed write access at offset 0x"
+ TARGET_FMT_plx "\n", offset);
+ return;
+ }
+
+ if (offset >> 2 >= IIC_REGSET_SIZE) {
+ hw_error("exynos4210.combiner: overflow of reg_set by 0x"
+ TARGET_FMT_plx "offset\n", offset);
+ }
+ s->reg_set[offset >> 2] = val;
+
+ switch (reg_n) {
+ /* IIESR */
+ case 0:
+ /* FIXME: what if irq is pending, allowed by mask, and we allow it
+ * again. Interrupt will rise again! */
+
+ DPRINTF("%s enable IRQ for groups %d, %d, %d, %d\n",
+ s->external ? "EXT" : "INT",
+ grp_quad_base_n,
+ grp_quad_base_n + 1,
+ grp_quad_base_n + 2,
+ grp_quad_base_n + 3);
+
+ /* Enable interrupt sources */
+ s->group[grp_quad_base_n].src_mask |= val & 0xFF;
+ s->group[grp_quad_base_n + 1].src_mask |= (val & 0xFF00) >> 8;
+ s->group[grp_quad_base_n + 2].src_mask |= (val & 0xFF0000) >> 16;
+ s->group[grp_quad_base_n + 3].src_mask |= (val & 0xFF000000) >> 24;
+
+ exynos4210_combiner_update(s, grp_quad_base_n);
+ exynos4210_combiner_update(s, grp_quad_base_n + 1);
+ exynos4210_combiner_update(s, grp_quad_base_n + 2);
+ exynos4210_combiner_update(s, grp_quad_base_n + 3);
+ break;
+ /* IIECR */
+ case 1:
+ DPRINTF("%s disable IRQ for groups %d, %d, %d, %d\n",
+ s->external ? "EXT" : "INT",
+ grp_quad_base_n,
+ grp_quad_base_n + 1,
+ grp_quad_base_n + 2,
+ grp_quad_base_n + 3);
+
+ /* Disable interrupt sources */
+ s->group[grp_quad_base_n].src_mask &= ~(val & 0xFF);
+ s->group[grp_quad_base_n + 1].src_mask &= ~((val & 0xFF00) >> 8);
+ s->group[grp_quad_base_n + 2].src_mask &= ~((val & 0xFF0000) >> 16);
+ s->group[grp_quad_base_n + 3].src_mask &= ~((val & 0xFF000000) >> 24);
+
+ exynos4210_combiner_update(s, grp_quad_base_n);
+ exynos4210_combiner_update(s, grp_quad_base_n + 1);
+ exynos4210_combiner_update(s, grp_quad_base_n + 2);
+ exynos4210_combiner_update(s, grp_quad_base_n + 3);
+ break;
+ default:
+ hw_error("exynos4210.combiner: unallowed write access at offset 0x"
+ TARGET_FMT_plx "\n", offset);
+ break;
+ }
+
+ return;
+}
+
+/* Get combiner group and bit from irq number */
+static uint8_t get_combiner_group_and_bit(int irq, uint8_t *bit)
+{
+ *bit = irq - ((irq >> 3) << 3);
+ return irq >> 3;
+}
+
+/* Process a change in an external IRQ input. */
+static void exynos4210_combiner_handler(void *opaque, int irq, int level)
+{
+ struct Exynos4210CombinerState *s =
+ (struct Exynos4210CombinerState *)opaque;
+ uint8_t bit_n, group_n;
+
+ group_n = get_combiner_group_and_bit(irq, &bit_n);
+
+ if (s->external && group_n >= EXYNOS4210_MAX_EXT_COMBINER_OUT_IRQ) {
+ DPRINTF("%s unallowed IRQ group 0x%x\n", s->external ? "EXT" : "INT"
+ , group_n);
+ return;
+ }
+
+ if (level) {
+ s->group[group_n].src_pending |= 1 << bit_n;
+ } else {
+ s->group[group_n].src_pending &= ~(1 << bit_n);
+ }
+
+ exynos4210_combiner_update(s, group_n);
+
+ return;
+}
+
+static void exynos4210_combiner_reset(DeviceState *d)
+{
+ struct Exynos4210CombinerState *s = (struct Exynos4210CombinerState *)d;
+
+ memset(&s->group, 0, sizeof(s->group));
+ memset(&s->reg_set, 0, sizeof(s->reg_set));
+
+ s->reg_set[0xC0 >> 2] = 0x01010101;
+ s->reg_set[0xC4 >> 2] = 0x01010101;
+ s->reg_set[0xD0 >> 2] = 0x01010101;
+ s->reg_set[0xD4 >> 2] = 0x01010101;
+}
+
+static const MemoryRegionOps exynos4210_combiner_ops = {
+ .read = exynos4210_combiner_read,
+ .write = exynos4210_combiner_write,
+ .endianness = DEVICE_NATIVE_ENDIAN,
+};
+
+/*
+ * Internal Combiner initialization.
+ */
+static int exynos4210_combiner_init(SysBusDevice *dev)
+{
+ unsigned int i;
+ struct Exynos4210CombinerState *s =
+ FROM_SYSBUS(struct Exynos4210CombinerState, dev);
+
+ /* Allocate general purpose input signals and connect a handler to each of
+ * them */
+ qdev_init_gpio_in(&s->busdev.qdev, exynos4210_combiner_handler, IIC_NIRQ);
+
+ /* Connect SysBusDev irqs to device specific irqs */
+ for (i = 0; i < IIC_NIRQ; i++) {
+ sysbus_init_irq(dev, &s->output_irq[i]);
+ }
+
+ memory_region_init_io(&s->iomem, &exynos4210_combiner_ops, s,
+ "exynos4210-combiner", IIC_REGION_SIZE);
+ sysbus_init_mmio(dev, &s->iomem);
+
+ return 0;
+}
+
+static Property exynos4210_combiner_properties[] = {
+ DEFINE_PROP_UINT32("external", Exynos4210CombinerState, external, 0),
+ DEFINE_PROP_END_OF_LIST(),
+};
+
+static void exynos4210_combiner_class_init(ObjectClass *klass, void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(klass);
+ SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass);
+
+ k->init = exynos4210_combiner_init;
+ dc->reset = exynos4210_combiner_reset;
+ dc->props = exynos4210_combiner_properties;
+ dc->vmsd = &vmstate_exynos4210_combiner;
+}
+
+static TypeInfo exynos4210_combiner_info = {
+ .name = "exynos4210.combiner",
+ .parent = TYPE_SYS_BUS_DEVICE,
+ .instance_size = sizeof(Exynos4210CombinerState),
+ .class_init = exynos4210_combiner_class_init,
+};
+
+static void exynos4210_combiner_register_types(void)
+{
+ type_register_static(&exynos4210_combiner_info);
+}
+
+type_init(exynos4210_combiner_register_types)
diff --git a/hw/exynos4210_fimd.c b/hw/exynos4210_fimd.c
new file mode 100644
index 0000000000..3313f00a71
--- /dev/null
+++ b/hw/exynos4210_fimd.c
@@ -0,0 +1,1928 @@
+/*
+ * Samsung exynos4210 Display Controller (FIMD)
+ *
+ * Copyright (c) 2000 - 2011 Samsung Electronics Co., Ltd.
+ * All rights reserved.
+ * Based on LCD controller for Samsung S5PC1xx-based board emulation
+ * by Kirill Batuzov <batuzovk@ispras.ru>
+ *
+ * Contributed by Mitsyanko Igor <i.mitsyanko@samsung.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ * See the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "qemu-common.h"
+#include "cpu-all.h"
+#include "sysbus.h"
+#include "console.h"
+#include "pixel_ops.h"
+#include "bswap.h"
+
+/* Debug messages configuration */
+#define EXYNOS4210_FIMD_DEBUG 0
+#define EXYNOS4210_FIMD_MODE_TRACE 0
+
+#if EXYNOS4210_FIMD_DEBUG == 0
+ #define DPRINT_L1(fmt, args...) do { } while (0)
+ #define DPRINT_L2(fmt, args...) do { } while (0)
+ #define DPRINT_ERROR(fmt, args...) do { } while (0)
+#elif EXYNOS4210_FIMD_DEBUG == 1
+ #define DPRINT_L1(fmt, args...) \
+ do {fprintf(stderr, "QEMU FIMD: "fmt, ## args); } while (0)
+ #define DPRINT_L2(fmt, args...) do { } while (0)
+ #define DPRINT_ERROR(fmt, args...) \
+ do {fprintf(stderr, "QEMU FIMD ERROR: "fmt, ## args); } while (0)
+#else
+ #define DPRINT_L1(fmt, args...) \
+ do {fprintf(stderr, "QEMU FIMD: "fmt, ## args); } while (0)
+ #define DPRINT_L2(fmt, args...) \
+ do {fprintf(stderr, "QEMU FIMD: "fmt, ## args); } while (0)
+ #define DPRINT_ERROR(fmt, args...) \
+ do {fprintf(stderr, "QEMU FIMD ERROR: "fmt, ## args); } while (0)
+#endif
+
+#if EXYNOS4210_FIMD_MODE_TRACE == 0
+ #define DPRINT_TRACE(fmt, args...) do { } while (0)
+#else
+ #define DPRINT_TRACE(fmt, args...) \
+ do {fprintf(stderr, "QEMU FIMD: "fmt, ## args); } while (0)
+#endif
+
+#define NUM_OF_WINDOWS 5
+#define FIMD_REGS_SIZE 0x4114
+
+/* Video main control registers */
+#define FIMD_VIDCON0 0x0000
+#define FIMD_VIDCON1 0x0004
+#define FIMD_VIDCON2 0x0008
+#define FIMD_VIDCON3 0x000C
+#define FIMD_VIDCON0_ENVID_F (1 << 0)
+#define FIMD_VIDCON0_ENVID (1 << 1)
+#define FIMD_VIDCON0_ENVID_MASK ((1 << 0) | (1 << 1))
+#define FIMD_VIDCON1_ROMASK 0x07FFE000
+
+/* Video time control registers */
+#define FIMD_VIDTCON_START 0x10
+#define FIMD_VIDTCON_END 0x1C
+#define FIMD_VIDTCON2_SIZE_MASK 0x07FF
+#define FIMD_VIDTCON2_HOR_SHIFT 0
+#define FIMD_VIDTCON2_VER_SHIFT 11
+
+/* Window control registers */
+#define FIMD_WINCON_START 0x0020
+#define FIMD_WINCON_END 0x0030
+#define FIMD_WINCON_ROMASK 0x82200000
+#define FIMD_WINCON_ENWIN (1 << 0)
+#define FIMD_WINCON_BLD_PIX (1 << 6)
+#define FIMD_WINCON_ALPHA_MUL (1 << 7)
+#define FIMD_WINCON_ALPHA_SEL (1 << 1)
+#define FIMD_WINCON_SWAP 0x078000
+#define FIMD_WINCON_SWAP_SHIFT 15
+#define FIMD_WINCON_SWAP_WORD 0x1
+#define FIMD_WINCON_SWAP_HWORD 0x2
+#define FIMD_WINCON_SWAP_BYTE 0x4
+#define FIMD_WINCON_SWAP_BITS 0x8
+#define FIMD_WINCON_BUFSTAT_L (1 << 21)
+#define FIMD_WINCON_BUFSTAT_H (1 << 31)
+#define FIMD_WINCON_BUFSTATUS ((1 << 21) | (1 << 31))
+#define FIMD_WINCON_BUF0_STAT ((0 << 21) | (0 << 31))
+#define FIMD_WINCON_BUF1_STAT ((1 << 21) | (0 << 31))
+#define FIMD_WINCON_BUF2_STAT ((0 << 21) | (1 << 31))
+#define FIMD_WINCON_BUFSELECT ((1 << 20) | (1 << 30))
+#define FIMD_WINCON_BUF0_SEL ((0 << 20) | (0 << 30))
+#define FIMD_WINCON_BUF1_SEL ((1 << 20) | (0 << 30))
+#define FIMD_WINCON_BUF2_SEL ((0 << 20) | (1 << 30))
+#define FIMD_WINCON_BUFMODE (1 << 14)
+#define IS_PALETTIZED_MODE(w) (w->wincon & 0xC)
+#define PAL_MODE_WITH_ALPHA(x) ((x) == 7)
+#define WIN_BPP_MODE(w) ((w->wincon >> 2) & 0xF)
+#define WIN_BPP_MODE_WITH_ALPHA(w) \
+ (WIN_BPP_MODE(w) == 0xD || WIN_BPP_MODE(w) == 0xE)
+
+/* Shadow control register */
+#define FIMD_SHADOWCON 0x0034
+#define FIMD_WINDOW_PROTECTED(s, w) ((s) & (1 << (10 + (w))))
+/* Channel mapping control register */
+#define FIMD_WINCHMAP 0x003C
+
+/* Window position control registers */
+#define FIMD_VIDOSD_START 0x0040
+#define FIMD_VIDOSD_END 0x0088
+#define FIMD_VIDOSD_COORD_MASK 0x07FF
+#define FIMD_VIDOSD_HOR_SHIFT 11
+#define FIMD_VIDOSD_VER_SHIFT 0
+#define FIMD_VIDOSD_ALPHA_AEN0 0xFFF000
+#define FIMD_VIDOSD_AEN0_SHIFT 12
+#define FIMD_VIDOSD_ALPHA_AEN1 0x000FFF
+
+/* Frame buffer address registers */
+#define FIMD_VIDWADD0_START 0x00A0
+#define FIMD_VIDWADD0_END 0x00C4
+#define FIMD_VIDWADD0_END 0x00C4
+#define FIMD_VIDWADD1_START 0x00D0
+#define FIMD_VIDWADD1_END 0x00F4
+#define FIMD_VIDWADD2_START 0x0100
+#define FIMD_VIDWADD2_END 0x0110
+#define FIMD_VIDWADD2_PAGEWIDTH 0x1FFF
+#define FIMD_VIDWADD2_OFFSIZE 0x1FFF
+#define FIMD_VIDWADD2_OFFSIZE_SHIFT 13
+#define FIMD_VIDW0ADD0_B2 0x20A0
+#define FIMD_VIDW4ADD0_B2 0x20C0
+
+/* Video interrupt control registers */
+#define FIMD_VIDINTCON0 0x130
+#define FIMD_VIDINTCON1 0x134
+
+/* Window color key registers */
+#define FIMD_WKEYCON_START 0x140
+#define FIMD_WKEYCON_END 0x15C
+#define FIMD_WKEYCON0_COMPKEY 0x00FFFFFF
+#define FIMD_WKEYCON0_CTL_SHIFT 24
+#define FIMD_WKEYCON0_DIRCON (1 << 24)
+#define FIMD_WKEYCON0_KEYEN (1 << 25)
+#define FIMD_WKEYCON0_KEYBLEN (1 << 26)
+/* Window color key alpha control register */
+#define FIMD_WKEYALPHA_START 0x160
+#define FIMD_WKEYALPHA_END 0x16C
+
+/* Dithering control register */
+#define FIMD_DITHMODE 0x170
+
+/* Window alpha control registers */
+#define FIMD_VIDALPHA_ALPHA_LOWER 0x000F0F0F
+#define FIMD_VIDALPHA_ALPHA_UPPER 0x00F0F0F0
+#define FIMD_VIDWALPHA_START 0x21C
+#define FIMD_VIDWALPHA_END 0x240
+
+/* Window color map registers */
+#define FIMD_WINMAP_START 0x180
+#define FIMD_WINMAP_END 0x190
+#define FIMD_WINMAP_EN (1 << 24)
+#define FIMD_WINMAP_COLOR_MASK 0x00FFFFFF
+
+/* Window palette control registers */
+#define FIMD_WPALCON_HIGH 0x019C
+#define FIMD_WPALCON_LOW 0x01A0
+#define FIMD_WPALCON_UPDATEEN (1 << 9)
+#define FIMD_WPAL_W0PAL_L 0x07
+#define FIMD_WPAL_W0PAL_L_SHT 0
+#define FIMD_WPAL_W1PAL_L 0x07
+#define FIMD_WPAL_W1PAL_L_SHT 3
+#define FIMD_WPAL_W2PAL_L 0x01
+#define FIMD_WPAL_W2PAL_L_SHT 6
+#define FIMD_WPAL_W2PAL_H 0x06
+#define FIMD_WPAL_W2PAL_H_SHT 8
+#define FIMD_WPAL_W3PAL_L 0x01
+#define FIMD_WPAL_W3PAL_L_SHT 7
+#define FIMD_WPAL_W3PAL_H 0x06
+#define FIMD_WPAL_W3PAL_H_SHT 12
+#define FIMD_WPAL_W4PAL_L 0x01
+#define FIMD_WPAL_W4PAL_L_SHT 8
+#define FIMD_WPAL_W4PAL_H 0x06
+#define FIMD_WPAL_W4PAL_H_SHT 16
+
+/* Trigger control registers */
+#define FIMD_TRIGCON 0x01A4
+#define FIMD_TRIGCON_ROMASK 0x00000004
+
+/* LCD I80 Interface Control */
+#define FIMD_I80IFCON_START 0x01B0
+#define FIMD_I80IFCON_END 0x01BC
+/* Color gain control register */
+#define FIMD_COLORGAINCON 0x01C0
+/* LCD i80 Interface Command Control */
+#define FIMD_LDI_CMDCON0 0x01D0
+#define FIMD_LDI_CMDCON1 0x01D4
+/* I80 System Interface Manual Command Control */
+#define FIMD_SIFCCON0 0x01E0
+#define FIMD_SIFCCON2 0x01E8
+
+/* Hue Control Registers */
+#define FIMD_HUECOEFCR_START 0x01EC
+#define FIMD_HUECOEFCR_END 0x01F4
+#define FIMD_HUECOEFCB_START 0x01FC
+#define FIMD_HUECOEFCB_END 0x0208
+#define FIMD_HUEOFFSET 0x020C
+
+/* Video interrupt control registers */
+#define FIMD_VIDINT_INTFIFOPEND (1 << 0)
+#define FIMD_VIDINT_INTFRMPEND (1 << 1)
+#define FIMD_VIDINT_INTI80PEND (1 << 2)
+#define FIMD_VIDINT_INTEN (1 << 0)
+#define FIMD_VIDINT_INTFIFOEN (1 << 1)
+#define FIMD_VIDINT_INTFRMEN (1 << 12)
+#define FIMD_VIDINT_I80IFDONE (1 << 17)
+
+/* Window blend equation control registers */
+#define FIMD_BLENDEQ_START 0x0244
+#define FIMD_BLENDEQ_END 0x0250
+#define FIMD_BLENDCON 0x0260
+#define FIMD_ALPHA_8BIT (1 << 0)
+#define FIMD_BLENDEQ_COEF_MASK 0xF
+
+/* Window RTQOS Control Registers */
+#define FIMD_WRTQOSCON_START 0x0264
+#define FIMD_WRTQOSCON_END 0x0274
+
+/* LCD I80 Interface Command */
+#define FIMD_I80IFCMD_START 0x0280
+#define FIMD_I80IFCMD_END 0x02AC
+
+/* Shadow windows control registers */
+#define FIMD_SHD_ADD0_START 0x40A0
+#define FIMD_SHD_ADD0_END 0x40C0
+#define FIMD_SHD_ADD1_START 0x40D0
+#define FIMD_SHD_ADD1_END 0x40F0
+#define FIMD_SHD_ADD2_START 0x4100
+#define FIMD_SHD_ADD2_END 0x4110
+
+/* Palette memory */
+#define FIMD_PAL_MEM_START 0x2400
+#define FIMD_PAL_MEM_END 0x37FC
+/* Palette memory aliases for windows 0 and 1 */
+#define FIMD_PALMEM_AL_START 0x0400
+#define FIMD_PALMEM_AL_END 0x0BFC
+
+typedef struct {
+ uint8_t r, g, b;
+ /* D[31..24]dummy, D[23..16]rAlpha, D[15..8]gAlpha, D[7..0]bAlpha */
+ uint32_t a;
+} rgba;
+#define RGBA_SIZE 7
+
+typedef void pixel_to_rgb_func(uint32_t pixel, rgba *p);
+typedef struct Exynos4210fimdWindow Exynos4210fimdWindow;
+
+struct Exynos4210fimdWindow {
+ uint32_t wincon; /* Window control register */
+ uint32_t buf_start[3]; /* Start address for video frame buffer */
+ uint32_t buf_end[3]; /* End address for video frame buffer */
+ uint32_t keycon[2]; /* Window color key registers */
+ uint32_t keyalpha; /* Color key alpha control register */
+ uint32_t winmap; /* Window color map register */
+ uint32_t blendeq; /* Window blending equation control register */
+ uint32_t rtqoscon; /* Window RTQOS Control Registers */
+ uint32_t palette[256]; /* Palette RAM */
+ uint32_t shadow_buf_start; /* Start address of shadow frame buffer */
+ uint32_t shadow_buf_end; /* End address of shadow frame buffer */
+ uint32_t shadow_buf_size; /* Virtual shadow screen width */
+
+ pixel_to_rgb_func *pixel_to_rgb;
+ void (*draw_line)(Exynos4210fimdWindow *w, uint8_t *src, uint8_t *dst,
+ bool blend);
+ uint32_t (*get_alpha)(Exynos4210fimdWindow *w, uint32_t pix_a);
+ uint16_t lefttop_x, lefttop_y; /* VIDOSD0 register */
+ uint16_t rightbot_x, rightbot_y; /* VIDOSD1 register */
+ uint32_t osdsize; /* VIDOSD2&3 register */
+ uint32_t alpha_val[2]; /* VIDOSD2&3, VIDWALPHA registers */
+ uint16_t virtpage_width; /* VIDWADD2 register */
+ uint16_t virtpage_offsize; /* VIDWADD2 register */
+ MemoryRegionSection mem_section; /* RAM fragment containing framebuffer */
+ uint8_t *host_fb_addr; /* Host pointer to window's framebuffer */
+ target_phys_addr_t fb_len; /* Framebuffer length */
+};
+
+typedef struct {
+ SysBusDevice busdev;
+ MemoryRegion iomem;
+ DisplayState *console;
+ qemu_irq irq[3];
+
+ uint32_t vidcon[4]; /* Video main control registers 0-3 */
+ uint32_t vidtcon[4]; /* Video time control registers 0-3 */
+ uint32_t shadowcon; /* Window shadow control register */
+ uint32_t winchmap; /* Channel mapping control register */
+ uint32_t vidintcon[2]; /* Video interrupt control registers */
+ uint32_t dithmode; /* Dithering control register */
+ uint32_t wpalcon[2]; /* Window palette control registers */
+ uint32_t trigcon; /* Trigger control register */
+ uint32_t i80ifcon[4]; /* I80 interface control registers */
+ uint32_t colorgaincon; /* Color gain control register */
+ uint32_t ldi_cmdcon[2]; /* LCD I80 interface command control */
+ uint32_t sifccon[3]; /* I80 System Interface Manual Command Control */
+ uint32_t huecoef_cr[4]; /* Hue control registers */
+ uint32_t huecoef_cb[4]; /* Hue control registers */
+ uint32_t hueoffset; /* Hue offset control register */
+ uint32_t blendcon; /* Blending control register */
+ uint32_t i80ifcmd[12]; /* LCD I80 Interface Command */
+
+ Exynos4210fimdWindow window[5]; /* Window-specific registers */
+ uint8_t *ifb; /* Internal frame buffer */
+ bool invalidate; /* Image needs to be redrawn */
+ bool enabled; /* Display controller is enabled */
+} Exynos4210fimdState;
+
+/* Perform byte/halfword/word swap of data according to WINCON */
+static inline void fimd_swap_data(unsigned int swap_ctl, uint64_t *data)
+{
+ int i;
+ uint64_t res;
+ uint64_t x = *data;
+
+ if (swap_ctl & FIMD_WINCON_SWAP_BITS) {
+ res = 0;
+ for (i = 0; i < 64; i++) {
+ if (x & (1ULL << (64 - i))) {
+ res |= (1ULL << i);
+ }
+ }
+ x = res;
+ }
+
+ if (swap_ctl & FIMD_WINCON_SWAP_BYTE) {
+ x = bswap64(x);
+ }
+
+ if (swap_ctl & FIMD_WINCON_SWAP_HWORD) {
+ x = ((x & 0x000000000000FFFFULL) << 48) |
+ ((x & 0x00000000FFFF0000ULL) << 16) |
+ ((x & 0x0000FFFF00000000ULL) >> 16) |
+ ((x & 0xFFFF000000000000ULL) >> 48);
+ }
+
+ if (swap_ctl & FIMD_WINCON_SWAP_WORD) {
+ x = ((x & 0x00000000FFFFFFFFULL) << 32) |
+ ((x & 0xFFFFFFFF00000000ULL) >> 32);
+ }
+
+ *data = x;
+}
+
+/* Conversion routines of Pixel data from frame buffer area to internal RGBA
+ * pixel representation.
+ * Every color component internally represented as 8-bit value. If original
+ * data has less than 8 bit for component, data is extended to 8 bit. For
+ * example, if blue component has only two possible values 0 and 1 it will be
+ * extended to 0 and 0xFF */
+
+/* One bit for alpha representation */
+#define DEF_PIXEL_TO_RGB_A1(N, R, G, B) \
+static void N(uint32_t pixel, rgba *p) \
+{ \
+ p->b = ((pixel & ((1 << (B)) - 1)) << (8 - (B))) | \
+ ((pixel >> (2 * (B) - 8)) & ((1 << (8 - (B))) - 1)); \
+ pixel >>= (B); \
+ p->g = (pixel & ((1 << (G)) - 1)) << (8 - (G)) | \
+ ((pixel >> (2 * (G) - 8)) & ((1 << (8 - (G))) - 1)); \
+ pixel >>= (G); \
+ p->r = (pixel & ((1 << (R)) - 1)) << (8 - (R)) | \
+ ((pixel >> (2 * (R) - 8)) & ((1 << (8 - (R))) - 1)); \
+ pixel >>= (R); \
+ p->a = (pixel & 0x1); \
+}
+
+DEF_PIXEL_TO_RGB_A1(pixel_a444_to_rgb, 4, 4, 4)
+DEF_PIXEL_TO_RGB_A1(pixel_a555_to_rgb, 5, 5, 5)
+DEF_PIXEL_TO_RGB_A1(pixel_a666_to_rgb, 6, 6, 6)
+DEF_PIXEL_TO_RGB_A1(pixel_a665_to_rgb, 6, 6, 5)
+DEF_PIXEL_TO_RGB_A1(pixel_a888_to_rgb, 8, 8, 8)
+DEF_PIXEL_TO_RGB_A1(pixel_a887_to_rgb, 8, 8, 7)
+
+/* Alpha component is always zero */
+#define DEF_PIXEL_TO_RGB_A0(N, R, G, B) \
+static void N(uint32_t pixel, rgba *p) \
+{ \
+ p->b = ((pixel & ((1 << (B)) - 1)) << (8 - (B))) | \
+ ((pixel >> (2 * (B) - 8)) & ((1 << (8 - (B))) - 1)); \
+ pixel >>= (B); \
+ p->g = (pixel & ((1 << (G)) - 1)) << (8 - (G)) | \
+ ((pixel >> (2 * (G) - 8)) & ((1 << (8 - (G))) - 1)); \
+ pixel >>= (G); \
+ p->r = (pixel & ((1 << (R)) - 1)) << (8 - (R)) | \
+ ((pixel >> (2 * (R) - 8)) & ((1 << (8 - (R))) - 1)); \
+ p->a = 0x0; \
+}
+
+DEF_PIXEL_TO_RGB_A0(pixel_565_to_rgb, 5, 6, 5)
+DEF_PIXEL_TO_RGB_A0(pixel_555_to_rgb, 5, 5, 5)
+DEF_PIXEL_TO_RGB_A0(pixel_666_to_rgb, 6, 6, 6)
+DEF_PIXEL_TO_RGB_A0(pixel_888_to_rgb, 8, 8, 8)
+
+/* Alpha component has some meaningful value */
+#define DEF_PIXEL_TO_RGB_A(N, R, G, B, A) \
+static void N(uint32_t pixel, rgba *p) \
+{ \
+ p->b = ((pixel & ((1 << (B)) - 1)) << (8 - (B))) | \
+ ((pixel >> (2 * (B) - 8)) & ((1 << (8 - (B))) - 1)); \
+ pixel >>= (B); \
+ p->g = (pixel & ((1 << (G)) - 1)) << (8 - (G)) | \
+ ((pixel >> (2 * (G) - 8)) & ((1 << (8 - (G))) - 1)); \
+ pixel >>= (G); \
+ p->r = (pixel & ((1 << (R)) - 1)) << (8 - (R)) | \
+ ((pixel >> (2 * (R) - 8)) & ((1 << (8 - (R))) - 1)); \
+ pixel >>= (R); \
+ p->a = (pixel & ((1 << (A)) - 1)) << (8 - (A)) | \
+ ((pixel >> (2 * (A) - 8)) & ((1 << (8 - (A))) - 1)); \
+ p->a = p->a | (p->a << 8) | (p->a << 16); \
+}
+
+DEF_PIXEL_TO_RGB_A(pixel_4444_to_rgb, 4, 4, 4, 4)
+DEF_PIXEL_TO_RGB_A(pixel_8888_to_rgb, 8, 8, 8, 8)
+
+/* Lookup table to extent 2-bit color component to 8 bit */
+static const uint8_t pixel_lutable_2b[4] = {
+ 0x0, 0x55, 0xAA, 0xFF
+};
+/* Lookup table to extent 3-bit color component to 8 bit */
+static const uint8_t pixel_lutable_3b[8] = {
+ 0x0, 0x24, 0x49, 0x6D, 0x92, 0xB6, 0xDB, 0xFF
+};
+/* Special case for a232 bpp mode */
+static void pixel_a232_to_rgb(uint32_t pixel, rgba *p)
+{
+ p->b = pixel_lutable_2b[(pixel & 0x3)];
+ pixel >>= 2;
+ p->g = pixel_lutable_3b[(pixel & 0x7)];
+ pixel >>= 3;
+ p->r = pixel_lutable_2b[(pixel & 0x3)];
+ pixel >>= 2;
+ p->a = (pixel & 0x1);
+}
+
+/* Special case for (5+1, 5+1, 5+1) mode. Data bit 15 is common LSB
+ * for all three color components */
+static void pixel_1555_to_rgb(uint32_t pixel, rgba *p)
+{
+ uint8_t comm = (pixel >> 15) & 1;
+ p->b = ((((pixel & 0x1F) << 1) | comm) << 2) | ((pixel >> 3) & 0x3);
+ pixel >>= 5;
+ p->g = ((((pixel & 0x1F) << 1) | comm) << 2) | ((pixel >> 3) & 0x3);
+ pixel >>= 5;
+ p->r = ((((pixel & 0x1F) << 1) | comm) << 2) | ((pixel >> 3) & 0x3);
+ p->a = 0x0;
+}
+
+/* Put/get pixel to/from internal LCD Controller framebuffer */
+
+static int put_pixel_ifb(const rgba p, uint8_t *d)
+{
+ *(uint8_t *)d++ = p.r;
+ *(uint8_t *)d++ = p.g;
+ *(uint8_t *)d++ = p.b;
+ *(uint32_t *)d = p.a;
+ return RGBA_SIZE;
+}
+
+static int get_pixel_ifb(const uint8_t *s, rgba *p)
+{
+ p->r = *(uint8_t *)s++;
+ p->g = *(uint8_t *)s++;
+ p->b = *(uint8_t *)s++;
+ p->a = (*(uint32_t *)s) & 0x00FFFFFF;
+ return RGBA_SIZE;
+}
+
+static pixel_to_rgb_func *palette_data_format[8] = {
+ [0] = pixel_565_to_rgb,
+ [1] = pixel_a555_to_rgb,
+ [2] = pixel_666_to_rgb,
+ [3] = pixel_a665_to_rgb,
+ [4] = pixel_a666_to_rgb,
+ [5] = pixel_888_to_rgb,
+ [6] = pixel_a888_to_rgb,
+ [7] = pixel_8888_to_rgb
+};
+
+/* Returns Index in palette data formats table for given window number WINDOW */
+static uint32_t
+exynos4210_fimd_palette_format(Exynos4210fimdState *s, int window)
+{
+ uint32_t ret;
+
+ switch (window) {
+ case 0:
+ ret = (s->wpalcon[1] >> FIMD_WPAL_W0PAL_L_SHT) & FIMD_WPAL_W0PAL_L;
+ if (ret != 7) {
+ ret = 6 - ret;
+ }
+ break;
+ case 1:
+ ret = (s->wpalcon[1] >> FIMD_WPAL_W1PAL_L_SHT) & FIMD_WPAL_W1PAL_L;
+ if (ret != 7) {
+ ret = 6 - ret;
+ }
+ break;
+ case 2:
+ ret = ((s->wpalcon[0] >> FIMD_WPAL_W2PAL_H_SHT) & FIMD_WPAL_W2PAL_H) |
+ ((s->wpalcon[1] >> FIMD_WPAL_W2PAL_L_SHT) & FIMD_WPAL_W2PAL_L);
+ break;
+ case 3:
+ ret = ((s->wpalcon[0] >> FIMD_WPAL_W3PAL_H_SHT) & FIMD_WPAL_W3PAL_H) |
+ ((s->wpalcon[1] >> FIMD_WPAL_W3PAL_L_SHT) & FIMD_WPAL_W3PAL_L);
+ break;
+ case 4:
+ ret = ((s->wpalcon[0] >> FIMD_WPAL_W4PAL_H_SHT) & FIMD_WPAL_W4PAL_H) |
+ ((s->wpalcon[1] >> FIMD_WPAL_W4PAL_L_SHT) & FIMD_WPAL_W4PAL_L);
+ break;
+ default:
+ hw_error("exynos4210.fimd: incorrect window number %d\n", window);
+ ret = 0;
+ break;
+ }
+ return ret;
+}
+
+#define FIMD_1_MINUS_COLOR(x) \
+ ((0xFF - ((x) & 0xFF)) | (0xFF00 - ((x) & 0xFF00)) | \
+ (0xFF0000 - ((x) & 0xFF0000)))
+#define EXTEND_LOWER_HALFBYTE(x) (((x) & 0xF0F0F) | (((x) << 4) & 0xF0F0F0))
+#define EXTEND_UPPER_HALFBYTE(x) (((x) & 0xF0F0F0) | (((x) >> 4) & 0xF0F0F))
+
+/* Multiply three lower bytes of two 32-bit words with each other.
+ * Each byte with values 0-255 is considered as a number with possible values
+ * in a range [0 - 1] */
+static inline uint32_t fimd_mult_each_byte(uint32_t a, uint32_t b)
+{
+ uint32_t tmp;
+ uint32_t ret;
+
+ ret = ((tmp = (((a & 0xFF) * (b & 0xFF)) / 0xFF)) > 0xFF) ? 0xFF : tmp;
+ ret |= ((tmp = ((((a >> 8) & 0xFF) * ((b >> 8) & 0xFF)) / 0xFF)) > 0xFF) ?
+ 0xFF00 : tmp << 8;
+ ret |= ((tmp = ((((a >> 16) & 0xFF) * ((b >> 16) & 0xFF)) / 0xFF)) > 0xFF) ?
+ 0xFF0000 : tmp << 16;
+ return ret;
+}
+
+/* For each corresponding bytes of two 32-bit words: (a*b + c*d)
+ * Byte values 0-255 are mapped to a range [0 .. 1] */
+static inline uint32_t
+fimd_mult_and_sum_each_byte(uint32_t a, uint32_t b, uint32_t c, uint32_t d)
+{
+ uint32_t tmp;
+ uint32_t ret;
+
+ ret = ((tmp = (((a & 0xFF) * (b & 0xFF) + (c & 0xFF) * (d & 0xFF)) / 0xFF))
+ > 0xFF) ? 0xFF : tmp;
+ ret |= ((tmp = ((((a >> 8) & 0xFF) * ((b >> 8) & 0xFF) + ((c >> 8) & 0xFF) *
+ ((d >> 8) & 0xFF)) / 0xFF)) > 0xFF) ? 0xFF00 : tmp << 8;
+ ret |= ((tmp = ((((a >> 16) & 0xFF) * ((b >> 16) & 0xFF) +
+ ((c >> 16) & 0xFF) * ((d >> 16) & 0xFF)) / 0xFF)) > 0xFF) ?
+ 0xFF0000 : tmp << 16;
+ return ret;
+}
+
+/* These routines cover all possible sources of window's transparent factor
+ * used in blending equation. Choice of routine is affected by WPALCON
+ * registers, BLENDCON register and window's WINCON register */
+
+static uint32_t fimd_get_alpha_pix(Exynos4210fimdWindow *w, uint32_t pix_a)
+{
+ return pix_a;
+}
+
+static uint32_t
+fimd_get_alpha_pix_extlow(Exynos4210fimdWindow *w, uint32_t pix_a)
+{
+ return EXTEND_LOWER_HALFBYTE(pix_a);
+}
+
+static uint32_t
+fimd_get_alpha_pix_exthigh(Exynos4210fimdWindow *w, uint32_t pix_a)
+{
+ return EXTEND_UPPER_HALFBYTE(pix_a);
+}
+
+static uint32_t fimd_get_alpha_mult(Exynos4210fimdWindow *w, uint32_t pix_a)
+{
+ return fimd_mult_each_byte(pix_a, w->alpha_val[0]);
+}
+
+static uint32_t fimd_get_alpha_mult_ext(Exynos4210fimdWindow *w, uint32_t pix_a)
+{
+ return fimd_mult_each_byte(EXTEND_LOWER_HALFBYTE(pix_a),
+ EXTEND_UPPER_HALFBYTE(w->alpha_val[0]));
+}
+
+static uint32_t fimd_get_alpha_aen(Exynos4210fimdWindow *w, uint32_t pix_a)
+{
+ return w->alpha_val[pix_a];
+}
+
+static uint32_t fimd_get_alpha_aen_ext(Exynos4210fimdWindow *w, uint32_t pix_a)
+{
+ return EXTEND_UPPER_HALFBYTE(w->alpha_val[pix_a]);
+}
+
+static uint32_t fimd_get_alpha_sel(Exynos4210fimdWindow *w, uint32_t pix_a)
+{
+ return w->alpha_val[(w->wincon & FIMD_WINCON_ALPHA_SEL) ? 1 : 0];
+}
+
+static uint32_t fimd_get_alpha_sel_ext(Exynos4210fimdWindow *w, uint32_t pix_a)
+{
+ return EXTEND_UPPER_HALFBYTE(w->alpha_val[(w->wincon &
+ FIMD_WINCON_ALPHA_SEL) ? 1 : 0]);
+}
+
+/* Updates currently active alpha value get function for specified window */
+static void fimd_update_get_alpha(Exynos4210fimdState *s, int win)
+{
+ Exynos4210fimdWindow *w = &s->window[win];
+ const bool alpha_is_8bit = s->blendcon & FIMD_ALPHA_8BIT;
+
+ if (w->wincon & FIMD_WINCON_BLD_PIX) {
+ if ((w->wincon & FIMD_WINCON_ALPHA_SEL) && WIN_BPP_MODE_WITH_ALPHA(w)) {
+ /* In this case, alpha component contains meaningful value */
+ if (w->wincon & FIMD_WINCON_ALPHA_MUL) {
+ w->get_alpha = alpha_is_8bit ?
+ fimd_get_alpha_mult : fimd_get_alpha_mult_ext;
+ } else {
+ w->get_alpha = alpha_is_8bit ?
+ fimd_get_alpha_pix : fimd_get_alpha_pix_extlow;
+ }
+ } else {
+ if (IS_PALETTIZED_MODE(w) &&
+ PAL_MODE_WITH_ALPHA(exynos4210_fimd_palette_format(s, win))) {
+ /* Alpha component has 8-bit numeric value */
+ w->get_alpha = alpha_is_8bit ?
+ fimd_get_alpha_pix : fimd_get_alpha_pix_exthigh;
+ } else {
+ /* Alpha has only two possible values (AEN) */
+ w->get_alpha = alpha_is_8bit ?
+ fimd_get_alpha_aen : fimd_get_alpha_aen_ext;
+ }
+ }
+ } else {
+ w->get_alpha = alpha_is_8bit ? fimd_get_alpha_sel :
+ fimd_get_alpha_sel_ext;
+ }
+}
+
+/* Blends current window's (w) pixel (foreground pixel *ret) with background
+ * window (w_blend) pixel p_bg according to formula:
+ * NEW_COLOR = a_coef x FG_PIXEL_COLOR + b_coef x BG_PIXEL_COLOR
+ * NEW_ALPHA = p_coef x FG_ALPHA + q_coef x BG_ALPHA
+ */
+static void
+exynos4210_fimd_blend_pixel(Exynos4210fimdWindow *w, rgba p_bg, rgba *ret)
+{
+ rgba p_fg = *ret;
+ uint32_t bg_color = ((p_bg.r & 0xFF) << 16) | ((p_bg.g & 0xFF) << 8) |
+ (p_bg.b & 0xFF);
+ uint32_t fg_color = ((p_fg.r & 0xFF) << 16) | ((p_fg.g & 0xFF) << 8) |
+ (p_fg.b & 0xFF);
+ uint32_t alpha_fg = p_fg.a;
+ int i;
+ /* It is possible that blending equation parameters a and b do not
+ * depend on window BLENEQ register. Account for this with first_coef */
+ enum { A_COEF = 0, B_COEF = 1, P_COEF = 2, Q_COEF = 3, COEF_NUM = 4};
+ uint32_t first_coef = A_COEF;
+ uint32_t blend_param[COEF_NUM];
+
+ if (w->keycon[0] & FIMD_WKEYCON0_KEYEN) {
+ uint32_t colorkey = (w->keycon[1] &
+ ~(w->keycon[0] & FIMD_WKEYCON0_COMPKEY)) & FIMD_WKEYCON0_COMPKEY;
+
+ if ((w->keycon[0] & FIMD_WKEYCON0_DIRCON) &&
+ (bg_color & ~(w->keycon[0] & FIMD_WKEYCON0_COMPKEY)) == colorkey) {
+ /* Foreground pixel is displayed */
+ if (w->keycon[0] & FIMD_WKEYCON0_KEYBLEN) {
+ alpha_fg = w->keyalpha;
+ blend_param[A_COEF] = alpha_fg;
+ blend_param[B_COEF] = FIMD_1_MINUS_COLOR(alpha_fg);
+ } else {
+ alpha_fg = 0;
+ blend_param[A_COEF] = 0xFFFFFF;
+ blend_param[B_COEF] = 0x0;
+ }
+ first_coef = P_COEF;
+ } else if ((w->keycon[0] & FIMD_WKEYCON0_DIRCON) == 0 &&
+ (fg_color & ~(w->keycon[0] & FIMD_WKEYCON0_COMPKEY)) == colorkey) {
+ /* Background pixel is displayed */
+ if (w->keycon[0] & FIMD_WKEYCON0_KEYBLEN) {
+ alpha_fg = w->keyalpha;
+ blend_param[A_COEF] = alpha_fg;
+ blend_param[B_COEF] = FIMD_1_MINUS_COLOR(alpha_fg);
+ } else {
+ alpha_fg = 0;
+ blend_param[A_COEF] = 0x0;
+ blend_param[B_COEF] = 0xFFFFFF;
+ }
+ first_coef = P_COEF;
+ }
+ }
+
+ for (i = first_coef; i < COEF_NUM; i++) {
+ switch ((w->blendeq >> i * 6) & FIMD_BLENDEQ_COEF_MASK) {
+ case 0:
+ blend_param[i] = 0;
+ break;
+ case 1:
+ blend_param[i] = 0xFFFFFF;
+ break;
+ case 2:
+ blend_param[i] = alpha_fg;
+ break;
+ case 3:
+ blend_param[i] = FIMD_1_MINUS_COLOR(alpha_fg);
+ break;
+ case 4:
+ blend_param[i] = p_bg.a;
+ break;
+ case 5:
+ blend_param[i] = FIMD_1_MINUS_COLOR(p_bg.a);
+ break;
+ case 6:
+ blend_param[i] = w->alpha_val[0];
+ break;
+ case 10:
+ blend_param[i] = fg_color;
+ break;
+ case 11:
+ blend_param[i] = FIMD_1_MINUS_COLOR(fg_color);
+ break;
+ case 12:
+ blend_param[i] = bg_color;
+ break;
+ case 13:
+ blend_param[i] = FIMD_1_MINUS_COLOR(bg_color);
+ break;
+ default:
+ hw_error("exynos4210.fimd: blend equation coef illegal value\n");
+ break;
+ }
+ }
+
+ fg_color = fimd_mult_and_sum_each_byte(bg_color, blend_param[B_COEF],
+ fg_color, blend_param[A_COEF]);
+ ret->b = fg_color & 0xFF;
+ fg_color >>= 8;
+ ret->g = fg_color & 0xFF;
+ fg_color >>= 8;
+ ret->r = fg_color & 0xFF;
+ ret->a = fimd_mult_and_sum_each_byte(alpha_fg, blend_param[P_COEF],
+ p_bg.a, blend_param[Q_COEF]);
+}
+
+/* These routines read data from video frame buffer in system RAM, convert
+ * this data to display controller internal representation, if necessary,
+ * perform pixel blending with data, currently presented in internal buffer.
+ * Result is stored in display controller internal frame buffer. */
+
+/* Draw line with index in palette table in RAM frame buffer data */
+#define DEF_DRAW_LINE_PALETTE(N) \
+static void glue(draw_line_palette_, N)(Exynos4210fimdWindow *w, uint8_t *src, \
+ uint8_t *dst, bool blend) \
+{ \
+ int width = w->rightbot_x - w->lefttop_x + 1; \
+ uint8_t *ifb = dst; \
+ uint8_t swap = (w->wincon & FIMD_WINCON_SWAP) >> FIMD_WINCON_SWAP_SHIFT; \
+ uint64_t data; \
+ rgba p, p_old; \
+ int i; \
+ do { \
+ data = ldq_raw((void *)src); \
+ src += 8; \
+ fimd_swap_data(swap, &data); \
+ for (i = (64 / (N) - 1); i >= 0; i--) { \
+ w->pixel_to_rgb(w->palette[(data >> ((N) * i)) & \
+ ((1ULL << (N)) - 1)], &p); \
+ p.a = w->get_alpha(w, p.a); \
+ if (blend) { \
+ ifb += get_pixel_ifb(ifb, &p_old); \
+ exynos4210_fimd_blend_pixel(w, p_old, &p); \
+ } \
+ dst += put_pixel_ifb(p, dst); \
+ } \
+ width -= (64 / (N)); \
+ } while (width > 0); \
+}
+
+/* Draw line with direct color value in RAM frame buffer data */
+#define DEF_DRAW_LINE_NOPALETTE(N) \
+static void glue(draw_line_, N)(Exynos4210fimdWindow *w, uint8_t *src, \
+ uint8_t *dst, bool blend) \
+{ \
+ int width = w->rightbot_x - w->lefttop_x + 1; \
+ uint8_t *ifb = dst; \
+ uint8_t swap = (w->wincon & FIMD_WINCON_SWAP) >> FIMD_WINCON_SWAP_SHIFT; \
+ uint64_t data; \
+ rgba p, p_old; \
+ int i; \
+ do { \
+ data = ldq_raw((void *)src); \
+ src += 8; \
+ fimd_swap_data(swap, &data); \
+ for (i = (64 / (N) - 1); i >= 0; i--) { \
+ w->pixel_to_rgb((data >> ((N) * i)) & ((1ULL << (N)) - 1), &p); \
+ p.a = w->get_alpha(w, p.a); \
+ if (blend) { \
+ ifb += get_pixel_ifb(ifb, &p_old); \
+ exynos4210_fimd_blend_pixel(w, p_old, &p); \
+ } \
+ dst += put_pixel_ifb(p, dst); \
+ } \
+ width -= (64 / (N)); \
+ } while (width > 0); \
+}
+
+DEF_DRAW_LINE_PALETTE(1)
+DEF_DRAW_LINE_PALETTE(2)
+DEF_DRAW_LINE_PALETTE(4)
+DEF_DRAW_LINE_PALETTE(8)
+DEF_DRAW_LINE_NOPALETTE(8) /* 8bpp mode has palette and non-palette versions */
+DEF_DRAW_LINE_NOPALETTE(16)
+DEF_DRAW_LINE_NOPALETTE(32)
+
+/* Special draw line routine for window color map case */
+static void draw_line_mapcolor(Exynos4210fimdWindow *w, uint8_t *src,
+ uint8_t *dst, bool blend)
+{
+ rgba p, p_old;
+ uint8_t *ifb = dst;
+ int width = w->rightbot_x - w->lefttop_x + 1;
+ uint32_t map_color = w->winmap & FIMD_WINMAP_COLOR_MASK;
+
+ do {
+ pixel_888_to_rgb(map_color, &p);
+ p.a = w->get_alpha(w, p.a);
+ if (blend) {
+ ifb += get_pixel_ifb(ifb, &p_old);
+ exynos4210_fimd_blend_pixel(w, p_old, &p);
+ }
+ dst += put_pixel_ifb(p, dst);
+ } while (--width);
+}
+
+/* Write RGB to QEMU's GraphicConsole framebuffer */
+
+static int put_to_qemufb_pixel8(const rgba p, uint8_t *d)
+{
+ uint32_t pixel = rgb_to_pixel8(p.r, p.g, p.b);
+ *(uint8_t *)d = pixel;
+ return 1;
+}
+
+static int put_to_qemufb_pixel15(const rgba p, uint8_t *d)
+{
+ uint32_t pixel = rgb_to_pixel15(p.r, p.g, p.b);
+ *(uint16_t *)d = pixel;
+ return 2;
+}
+
+static int put_to_qemufb_pixel16(const rgba p, uint8_t *d)
+{
+ uint32_t pixel = rgb_to_pixel16(p.r, p.g, p.b);
+ *(uint16_t *)d = pixel;
+ return 2;
+}
+
+static int put_to_qemufb_pixel24(const rgba p, uint8_t *d)
+{
+ uint32_t pixel = rgb_to_pixel24(p.r, p.g, p.b);
+ *(uint8_t *)d++ = (pixel >> 0) & 0xFF;
+ *(uint8_t *)d++ = (pixel >> 8) & 0xFF;
+ *(uint8_t *)d++ = (pixel >> 16) & 0xFF;
+ return 3;
+}
+
+static int put_to_qemufb_pixel32(const rgba p, uint8_t *d)
+{
+ uint32_t pixel = rgb_to_pixel24(p.r, p.g, p.b);
+ *(uint32_t *)d = pixel;
+ return 4;
+}
+
+/* Routine to copy pixel from internal buffer to QEMU buffer */
+static int (*put_pixel_toqemu)(const rgba p, uint8_t *pixel);
+static inline void fimd_update_putpix_qemu(int bpp)
+{
+ switch (bpp) {
+ case 8:
+ put_pixel_toqemu = put_to_qemufb_pixel8;
+ break;
+ case 15:
+ put_pixel_toqemu = put_to_qemufb_pixel15;
+ break;
+ case 16:
+ put_pixel_toqemu = put_to_qemufb_pixel16;
+ break;
+ case 24:
+ put_pixel_toqemu = put_to_qemufb_pixel24;
+ break;
+ case 32:
+ put_pixel_toqemu = put_to_qemufb_pixel32;
+ break;
+ default:
+ hw_error("exynos4210.fimd: unsupported BPP (%d)", bpp);
+ break;
+ }
+}
+
+/* Routine to copy a line from internal frame buffer to QEMU display */
+static void fimd_copy_line_toqemu(int width, uint8_t *src, uint8_t *dst)
+{
+ rgba p;
+
+ do {
+ src += get_pixel_ifb(src, &p);
+ dst += put_pixel_toqemu(p, dst);
+ } while (--width);
+}
+
+/* Parse BPPMODE_F = WINCON1[5:2] bits */
+static void exynos4210_fimd_update_win_bppmode(Exynos4210fimdState *s, int win)
+{
+ Exynos4210fimdWindow *w = &s->window[win];
+
+ if (w->winmap & FIMD_WINMAP_EN) {
+ w->draw_line = draw_line_mapcolor;
+ return;
+ }
+
+ switch (WIN_BPP_MODE(w)) {
+ case 0:
+ w->draw_line = draw_line_palette_1;
+ w->pixel_to_rgb =
+ palette_data_format[exynos4210_fimd_palette_format(s, win)];
+ break;
+ case 1:
+ w->draw_line = draw_line_palette_2;
+ w->pixel_to_rgb =
+ palette_data_format[exynos4210_fimd_palette_format(s, win)];
+ break;
+ case 2:
+ w->draw_line = draw_line_palette_4;
+ w->pixel_to_rgb =
+ palette_data_format[exynos4210_fimd_palette_format(s, win)];
+ break;
+ case 3:
+ w->draw_line = draw_line_palette_8;
+ w->pixel_to_rgb =
+ palette_data_format[exynos4210_fimd_palette_format(s, win)];
+ break;
+ case 4:
+ w->draw_line = draw_line_8;
+ w->pixel_to_rgb = pixel_a232_to_rgb;
+ break;
+ case 5:
+ w->draw_line = draw_line_16;
+ w->pixel_to_rgb = pixel_565_to_rgb;
+ break;
+ case 6:
+ w->draw_line = draw_line_16;
+ w->pixel_to_rgb = pixel_a555_to_rgb;
+ break;
+ case 7:
+ w->draw_line = draw_line_16;
+ w->pixel_to_rgb = pixel_1555_to_rgb;
+ break;
+ case 8:
+ w->draw_line = draw_line_32;
+ w->pixel_to_rgb = pixel_666_to_rgb;
+ break;
+ case 9:
+ w->draw_line = draw_line_32;
+ w->pixel_to_rgb = pixel_a665_to_rgb;
+ break;
+ case 10:
+ w->draw_line = draw_line_32;
+ w->pixel_to_rgb = pixel_a666_to_rgb;
+ break;
+ case 11:
+ w->draw_line = draw_line_32;
+ w->pixel_to_rgb = pixel_888_to_rgb;
+ break;
+ case 12:
+ w->draw_line = draw_line_32;
+ w->pixel_to_rgb = pixel_a887_to_rgb;
+ break;
+ case 13:
+ w->draw_line = draw_line_32;
+ if ((w->wincon & FIMD_WINCON_BLD_PIX) && (w->wincon &
+ FIMD_WINCON_ALPHA_SEL)) {
+ w->pixel_to_rgb = pixel_8888_to_rgb;
+ } else {
+ w->pixel_to_rgb = pixel_a888_to_rgb;
+ }
+ break;
+ case 14:
+ w->draw_line = draw_line_16;
+ if ((w->wincon & FIMD_WINCON_BLD_PIX) && (w->wincon &
+ FIMD_WINCON_ALPHA_SEL)) {
+ w->pixel_to_rgb = pixel_4444_to_rgb;
+ } else {
+ w->pixel_to_rgb = pixel_a444_to_rgb;
+ }
+ break;
+ case 15:
+ w->draw_line = draw_line_16;
+ w->pixel_to_rgb = pixel_555_to_rgb;
+ break;
+ }
+}
+
+#if EXYNOS4210_FIMD_MODE_TRACE > 0
+static const char *exynos4210_fimd_get_bppmode(int mode_code)
+{
+ switch (mode_code) {
+ case 0:
+ return "1 bpp";
+ case 1:
+ return "2 bpp";
+ case 2:
+ return "4 bpp";
+ case 3:
+ return "8 bpp (palettized)";
+ case 4:
+ return "8 bpp (non-palettized, A: 1-R:2-G:3-B:2)";
+ case 5:
+ return "16 bpp (non-palettized, R:5-G:6-B:5)";
+ case 6:
+ return "16 bpp (non-palettized, A:1-R:5-G:5-B:5)";
+ case 7:
+ return "16 bpp (non-palettized, I :1-R:5-G:5-B:5)";
+ case 8:
+ return "Unpacked 18 bpp (non-palettized, R:6-G:6-B:6)";
+ case 9:
+ return "Unpacked 18bpp (non-palettized,A:1-R:6-G:6-B:5)";
+ case 10:
+ return "Unpacked 19bpp (non-palettized,A:1-R:6-G:6-B:6)";
+ case 11:
+ return "Unpacked 24 bpp (non-palettized R:8-G:8-B:8)";
+ case 12:
+ return "Unpacked 24 bpp (non-palettized A:1-R:8-G:8-B:7)";
+ case 13:
+ return "Unpacked 25 bpp (non-palettized A:1-R:8-G:8-B:8)";
+ case 14:
+ return "Unpacked 13 bpp (non-palettized A:1-R:4-G:4-B:4)";
+ case 15:
+ return "Unpacked 15 bpp (non-palettized R:5-G:5-B:5)";
+ default:
+ return "Non-existing bpp mode";
+ }
+}
+
+static inline void exynos4210_fimd_trace_bppmode(Exynos4210fimdState *s,
+ int win_num, uint32_t val)
+{
+ Exynos4210fimdWindow *w = &s->window[win_num];
+
+ if (w->winmap & FIMD_WINMAP_EN) {
+ printf("QEMU FIMD: Window %d is mapped with MAPCOLOR=0x%x\n",
+ win_num, w->winmap & 0xFFFFFF);
+ return;
+ }
+
+ if ((val != 0xFFFFFFFF) && ((w->wincon >> 2) & 0xF) == ((val >> 2) & 0xF)) {
+ return;
+ }
+ printf("QEMU FIMD: Window %d BPP mode set to %s\n", win_num,
+ exynos4210_fimd_get_bppmode((val >> 2) & 0xF));
+}
+#else
+static inline void exynos4210_fimd_trace_bppmode(Exynos4210fimdState *s,
+ int win_num, uint32_t val)
+{
+
+}
+#endif
+
+static inline int fimd_get_buffer_id(Exynos4210fimdWindow *w)
+{
+ switch (w->wincon & FIMD_WINCON_BUFSTATUS) {
+ case FIMD_WINCON_BUF0_STAT:
+ return 0;
+ case FIMD_WINCON_BUF1_STAT:
+ return 1;
+ case FIMD_WINCON_BUF2_STAT:
+ return 2;
+ default:
+ DPRINT_ERROR("Non-existent buffer index\n");
+ return 0;
+ }
+}
+
+/* Updates specified window's MemorySection based on values of WINCON,
+ * VIDOSDA, VIDOSDB, VIDWADDx and SHADOWCON registers */
+static void fimd_update_memory_section(Exynos4210fimdState *s, unsigned win)
+{
+ Exynos4210fimdWindow *w = &s->window[win];
+ target_phys_addr_t fb_start_addr, fb_mapped_len;
+
+ if (!s->enabled || !(w->wincon & FIMD_WINCON_ENWIN) ||
+ FIMD_WINDOW_PROTECTED(s->shadowcon, win)) {
+ return;
+ }
+
+ if (w->host_fb_addr) {
+ cpu_physical_memory_unmap(w->host_fb_addr, w->fb_len, 0, 0);
+ w->host_fb_addr = NULL;
+ w->fb_len = 0;
+ }
+
+ fb_start_addr = w->buf_start[fimd_get_buffer_id(w)];
+ /* Total number of bytes of virtual screen used by current window */
+ w->fb_len = fb_mapped_len = (w->virtpage_width + w->virtpage_offsize) *
+ (w->rightbot_y - w->lefttop_y + 1);
+ w->mem_section = memory_region_find(sysbus_address_space(&s->busdev),
+ fb_start_addr, w->fb_len);
+ assert(w->mem_section.mr);
+ assert(w->mem_section.offset_within_address_space == fb_start_addr);
+ DPRINT_TRACE("Window %u framebuffer changed: address=0x%08x, len=0x%x\n",
+ win, fb_start_addr, w->fb_len);
+
+ if (w->mem_section.size != w->fb_len ||
+ !memory_region_is_ram(w->mem_section.mr)) {
+ DPRINT_ERROR("Failed to find window %u framebuffer region\n", win);
+ goto error_return;
+ }
+
+ w->host_fb_addr = cpu_physical_memory_map(fb_start_addr, &fb_mapped_len, 0);
+ if (!w->host_fb_addr) {
+ DPRINT_ERROR("Failed to map window %u framebuffer\n", win);
+ goto error_return;
+ }
+
+ if (fb_mapped_len != w->fb_len) {
+ DPRINT_ERROR("Window %u mapped framebuffer length is less then "
+ "expected\n", win);
+ cpu_physical_memory_unmap(w->host_fb_addr, fb_mapped_len, 0, 0);
+ goto error_return;
+ }
+ return;
+
+error_return:
+ w->mem_section.mr = NULL;
+ w->mem_section.size = 0;
+ w->host_fb_addr = NULL;
+ w->fb_len = 0;
+}
+
+static void exynos4210_fimd_enable(Exynos4210fimdState *s, bool enabled)
+{
+ if (enabled && !s->enabled) {
+ unsigned w;
+ s->enabled = true;
+ for (w = 0; w < NUM_OF_WINDOWS; w++) {
+ fimd_update_memory_section(s, w);
+ }
+ }
+ s->enabled = enabled;
+ DPRINT_TRACE("display controller %s\n", enabled ? "enabled" : "disabled");
+}
+
+static inline uint32_t unpack_upper_4(uint32_t x)
+{
+ return ((x & 0xF00) << 12) | ((x & 0xF0) << 8) | ((x & 0xF) << 4);
+}
+
+static inline uint32_t pack_upper_4(uint32_t x)
+{
+ return (((x & 0xF00000) >> 12) | ((x & 0xF000) >> 8) |
+ ((x & 0xF0) >> 4)) & 0xFFF;
+}
+
+static void exynos4210_fimd_update_irq(Exynos4210fimdState *s)
+{
+ if (!(s->vidintcon[0] & FIMD_VIDINT_INTEN)) {
+ qemu_irq_lower(s->irq[0]);
+ qemu_irq_lower(s->irq[1]);
+ qemu_irq_lower(s->irq[2]);
+ return;
+ }
+ if ((s->vidintcon[0] & FIMD_VIDINT_INTFIFOEN) &&
+ (s->vidintcon[1] & FIMD_VIDINT_INTFIFOPEND)) {
+ qemu_irq_raise(s->irq[0]);
+ } else {
+ qemu_irq_lower(s->irq[0]);
+ }
+ if ((s->vidintcon[0] & FIMD_VIDINT_INTFRMEN) &&
+ (s->vidintcon[1] & FIMD_VIDINT_INTFRMPEND)) {
+ qemu_irq_raise(s->irq[1]);
+ } else {
+ qemu_irq_lower(s->irq[1]);
+ }
+ if ((s->vidintcon[0] & FIMD_VIDINT_I80IFDONE) &&
+ (s->vidintcon[1] & FIMD_VIDINT_INTI80PEND)) {
+ qemu_irq_raise(s->irq[2]);
+ } else {
+ qemu_irq_lower(s->irq[2]);
+ }
+}
+
+static void exynos4210_fimd_invalidate(void *opaque)
+{
+ Exynos4210fimdState *s = (Exynos4210fimdState *)opaque;
+ s->invalidate = true;
+}
+
+static void exynos4210_update_resolution(Exynos4210fimdState *s)
+{
+ /* LCD resolution is stored in VIDEO TIME CONTROL REGISTER 2 */
+ uint32_t width = ((s->vidtcon[2] >> FIMD_VIDTCON2_HOR_SHIFT) &
+ FIMD_VIDTCON2_SIZE_MASK) + 1;
+ uint32_t height = ((s->vidtcon[2] >> FIMD_VIDTCON2_VER_SHIFT) &
+ FIMD_VIDTCON2_SIZE_MASK) + 1;
+
+ if (s->ifb == NULL || ds_get_width(s->console) != width ||
+ ds_get_height(s->console) != height) {
+ DPRINT_L1("Resolution changed from %ux%u to %ux%u\n",
+ ds_get_width(s->console), ds_get_height(s->console), width, height);
+ qemu_console_resize(s->console, width, height);
+ s->ifb = g_realloc(s->ifb, width * height * RGBA_SIZE + 1);
+ memset(s->ifb, 0, width * height * RGBA_SIZE + 1);
+ exynos4210_fimd_invalidate(s);
+ }
+}
+
+static void exynos4210_fimd_update(void *opaque)
+{
+ Exynos4210fimdState *s = (Exynos4210fimdState *)opaque;
+ Exynos4210fimdWindow *w;
+ int i, line;
+ target_phys_addr_t fb_line_addr, inc_size;
+ int scrn_height;
+ int first_line = -1, last_line = -1, scrn_width;
+ bool blend = false;
+ uint8_t *host_fb_addr;
+ bool is_dirty = false;
+ const int global_width = (s->vidtcon[2] & FIMD_VIDTCON2_SIZE_MASK) + 1;
+ const int global_height = ((s->vidtcon[2] >> FIMD_VIDTCON2_VER_SHIFT) &
+ FIMD_VIDTCON2_SIZE_MASK) + 1;
+
+ if (!s || !s->console || !ds_get_bits_per_pixel(s->console) ||
+ !s->enabled) {
+ return;
+ }
+ exynos4210_update_resolution(s);
+
+ for (i = 0; i < NUM_OF_WINDOWS; i++) {
+ w = &s->window[i];
+ if ((w->wincon & FIMD_WINCON_ENWIN) && w->host_fb_addr) {
+ scrn_height = w->rightbot_y - w->lefttop_y + 1;
+ scrn_width = w->virtpage_width;
+ /* Total width of virtual screen page in bytes */
+ inc_size = scrn_width + w->virtpage_offsize;
+ memory_region_sync_dirty_bitmap(w->mem_section.mr);
+ host_fb_addr = w->host_fb_addr;
+ fb_line_addr = w->mem_section.offset_within_region;
+
+ for (line = 0; line < scrn_height; line++) {
+ is_dirty = memory_region_get_dirty(w->mem_section.mr,
+ fb_line_addr, scrn_width, DIRTY_MEMORY_VGA);
+
+ if (s->invalidate || is_dirty) {
+ if (first_line == -1) {
+ first_line = line;
+ }
+ last_line = line;
+ w->draw_line(w, host_fb_addr, s->ifb +
+ w->lefttop_x * RGBA_SIZE + (w->lefttop_y + line) *
+ global_width * RGBA_SIZE, blend);
+ }
+ host_fb_addr += inc_size;
+ fb_line_addr += inc_size;
+ is_dirty = false;
+ }
+ memory_region_reset_dirty(w->mem_section.mr,
+ w->mem_section.offset_within_region,
+ w->fb_len, DIRTY_MEMORY_VGA);
+ blend = true;
+ }
+ }
+
+ /* Copy resulting image to QEMU_CONSOLE. */
+ if (first_line >= 0) {
+ uint8_t *d;
+ int bpp;
+
+ bpp = ds_get_bits_per_pixel(s->console);
+ fimd_update_putpix_qemu(bpp);
+ bpp = (bpp + 1) >> 3;
+ d = ds_get_data(s->console);
+ for (line = first_line; line <= last_line; line++) {
+ fimd_copy_line_toqemu(global_width, s->ifb + global_width * line *
+ RGBA_SIZE, d + global_width * line * bpp);
+ }
+ dpy_update(s->console, 0, 0, global_width, global_height);
+ }
+ s->invalidate = false;
+ s->vidintcon[1] |= FIMD_VIDINT_INTFRMPEND;
+ if ((s->vidcon[0] & FIMD_VIDCON0_ENVID_F) == 0) {
+ exynos4210_fimd_enable(s, false);
+ }
+ exynos4210_fimd_update_irq(s);
+}
+
+static void exynos4210_fimd_reset(DeviceState *d)
+{
+ Exynos4210fimdState *s = DO_UPCAST(Exynos4210fimdState, busdev.qdev, d);
+ unsigned w;
+
+ DPRINT_TRACE("Display controller reset\n");
+ /* Set all display controller registers to 0 */
+ memset(&s->vidcon, 0, (uint8_t *)&s->window - (uint8_t *)&s->vidcon);
+ for (w = 0; w < NUM_OF_WINDOWS; w++) {
+ memset(&s->window[w], 0, sizeof(Exynos4210fimdWindow));
+ s->window[w].blendeq = 0xC2;
+ exynos4210_fimd_update_win_bppmode(s, w);
+ exynos4210_fimd_trace_bppmode(s, w, 0xFFFFFFFF);
+ fimd_update_get_alpha(s, w);
+ }
+
+ if (s->ifb != NULL) {
+ g_free(s->ifb);
+ }
+ s->ifb = NULL;
+
+ exynos4210_fimd_invalidate(s);
+ exynos4210_fimd_enable(s, false);
+ /* Some registers have non-zero initial values */
+ s->winchmap = 0x7D517D51;
+ s->colorgaincon = 0x10040100;
+ s->huecoef_cr[0] = s->huecoef_cr[3] = 0x01000100;
+ s->huecoef_cb[0] = s->huecoef_cb[3] = 0x01000100;
+ s->hueoffset = 0x01800080;
+}
+
+static void exynos4210_fimd_write(void *opaque, target_phys_addr_t offset,
+ uint64_t val, unsigned size)
+{
+ Exynos4210fimdState *s = (Exynos4210fimdState *)opaque;
+ unsigned w, i;
+ uint32_t old_value;
+
+ DPRINT_L2("write offset 0x%08x, value=%llu(0x%08llx)\n", offset,
+ (long long unsigned int)val, (long long unsigned int)val);
+
+ switch (offset) {
+ case FIMD_VIDCON0:
+ if ((val & FIMD_VIDCON0_ENVID_MASK) == FIMD_VIDCON0_ENVID_MASK) {
+ exynos4210_fimd_enable(s, true);
+ } else {
+ if ((val & FIMD_VIDCON0_ENVID) == 0) {
+ exynos4210_fimd_enable(s, false);
+ }
+ }
+ s->vidcon[0] = val;
+ break;
+ case FIMD_VIDCON1:
+ /* Leave read-only bits as is */
+ val = (val & (~FIMD_VIDCON1_ROMASK)) |
+ (s->vidcon[1] & FIMD_VIDCON1_ROMASK);
+ s->vidcon[1] = val;
+ break;
+ case FIMD_VIDCON2 ... FIMD_VIDCON3:
+ s->vidcon[(offset) >> 2] = val;
+ break;
+ case FIMD_VIDTCON_START ... FIMD_VIDTCON_END:
+ s->vidtcon[(offset - FIMD_VIDTCON_START) >> 2] = val;
+ break;
+ case FIMD_WINCON_START ... FIMD_WINCON_END:
+ w = (offset - FIMD_WINCON_START) >> 2;
+ /* Window's current buffer ID */
+ i = fimd_get_buffer_id(&s->window[w]);
+ old_value = s->window[w].wincon;
+ val = (val & ~FIMD_WINCON_ROMASK) |
+ (s->window[w].wincon & FIMD_WINCON_ROMASK);
+ if (w == 0) {
+ /* Window 0 wincon ALPHA_MUL bit must always be 0 */
+ val &= ~FIMD_WINCON_ALPHA_MUL;
+ }
+ exynos4210_fimd_trace_bppmode(s, w, val);
+ switch (val & FIMD_WINCON_BUFSELECT) {
+ case FIMD_WINCON_BUF0_SEL:
+ val &= ~FIMD_WINCON_BUFSTATUS;
+ break;
+ case FIMD_WINCON_BUF1_SEL:
+ val = (val & ~FIMD_WINCON_BUFSTAT_H) | FIMD_WINCON_BUFSTAT_L;
+ break;
+ case FIMD_WINCON_BUF2_SEL:
+ if (val & FIMD_WINCON_BUFMODE) {
+ val = (val & ~FIMD_WINCON_BUFSTAT_L) | FIMD_WINCON_BUFSTAT_H;
+ }
+ break;
+ default:
+ break;
+ }
+ s->window[w].wincon = val;
+ exynos4210_fimd_update_win_bppmode(s, w);
+ fimd_update_get_alpha(s, w);
+ if ((i != fimd_get_buffer_id(&s->window[w])) ||
+ (!(old_value & FIMD_WINCON_ENWIN) && (s->window[w].wincon &
+ FIMD_WINCON_ENWIN))) {
+ fimd_update_memory_section(s, w);
+ }
+ break;
+ case FIMD_SHADOWCON:
+ old_value = s->shadowcon;
+ s->shadowcon = val;
+ for (w = 0; w < NUM_OF_WINDOWS; w++) {
+ if (FIMD_WINDOW_PROTECTED(old_value, w) &&
+ !FIMD_WINDOW_PROTECTED(s->shadowcon, w)) {
+ fimd_update_memory_section(s, w);
+ }
+ }
+ break;
+ case FIMD_WINCHMAP:
+ s->winchmap = val;
+ break;
+ case FIMD_VIDOSD_START ... FIMD_VIDOSD_END:
+ w = (offset - FIMD_VIDOSD_START) >> 4;
+ i = ((offset - FIMD_VIDOSD_START) & 0xF) >> 2;
+ switch (i) {
+ case 0:
+ old_value = s->window[w].lefttop_y;
+ s->window[w].lefttop_x = (val >> FIMD_VIDOSD_HOR_SHIFT) &
+ FIMD_VIDOSD_COORD_MASK;
+ s->window[w].lefttop_y = (val >> FIMD_VIDOSD_VER_SHIFT) &
+ FIMD_VIDOSD_COORD_MASK;
+ if (s->window[w].lefttop_y != old_value) {
+ fimd_update_memory_section(s, w);
+ }
+ break;
+ case 1:
+ old_value = s->window[w].rightbot_y;
+ s->window[w].rightbot_x = (val >> FIMD_VIDOSD_HOR_SHIFT) &
+ FIMD_VIDOSD_COORD_MASK;
+ s->window[w].rightbot_y = (val >> FIMD_VIDOSD_VER_SHIFT) &
+ FIMD_VIDOSD_COORD_MASK;
+ if (s->window[w].rightbot_y != old_value) {
+ fimd_update_memory_section(s, w);
+ }
+ break;
+ case 2:
+ if (w == 0) {
+ s->window[w].osdsize = val;
+ } else {
+ s->window[w].alpha_val[0] =
+ unpack_upper_4((val & FIMD_VIDOSD_ALPHA_AEN0) >>
+ FIMD_VIDOSD_AEN0_SHIFT) |
+ (s->window[w].alpha_val[0] & FIMD_VIDALPHA_ALPHA_LOWER);
+ s->window[w].alpha_val[1] =
+ unpack_upper_4(val & FIMD_VIDOSD_ALPHA_AEN1) |
+ (s->window[w].alpha_val[1] & FIMD_VIDALPHA_ALPHA_LOWER);
+ }
+ break;
+ case 3:
+ if (w != 1 && w != 2) {
+ DPRINT_ERROR("Bad write offset 0x%08x\n", offset);
+ return;
+ }
+ s->window[w].osdsize = val;
+ break;
+ }
+ break;
+ case FIMD_VIDWADD0_START ... FIMD_VIDWADD0_END:
+ w = (offset - FIMD_VIDWADD0_START) >> 3;
+ i = ((offset - FIMD_VIDWADD0_START) >> 2) & 1;
+ if (i == fimd_get_buffer_id(&s->window[w]) &&
+ s->window[w].buf_start[i] != val) {
+ s->window[w].buf_start[i] = val;
+ fimd_update_memory_section(s, w);
+ break;
+ }
+ s->window[w].buf_start[i] = val;
+ break;
+ case FIMD_VIDWADD1_START ... FIMD_VIDWADD1_END:
+ w = (offset - FIMD_VIDWADD1_START) >> 3;
+ i = ((offset - FIMD_VIDWADD1_START) >> 2) & 1;
+ s->window[w].buf_end[i] = val;
+ break;
+ case FIMD_VIDWADD2_START ... FIMD_VIDWADD2_END:
+ w = (offset - FIMD_VIDWADD2_START) >> 2;
+ if (((val & FIMD_VIDWADD2_PAGEWIDTH) != s->window[w].virtpage_width) ||
+ (((val >> FIMD_VIDWADD2_OFFSIZE_SHIFT) & FIMD_VIDWADD2_OFFSIZE) !=
+ s->window[w].virtpage_offsize)) {
+ s->window[w].virtpage_width = val & FIMD_VIDWADD2_PAGEWIDTH;
+ s->window[w].virtpage_offsize =
+ (val >> FIMD_VIDWADD2_OFFSIZE_SHIFT) & FIMD_VIDWADD2_OFFSIZE;
+ fimd_update_memory_section(s, w);
+ }
+ break;
+ case FIMD_VIDINTCON0:
+ s->vidintcon[0] = val;
+ break;
+ case FIMD_VIDINTCON1:
+ s->vidintcon[1] &= ~(val & 7);
+ exynos4210_fimd_update_irq(s);
+ break;
+ case FIMD_WKEYCON_START ... FIMD_WKEYCON_END:
+ w = ((offset - FIMD_WKEYCON_START) >> 3) + 1;
+ i = ((offset - FIMD_WKEYCON_START) >> 2) & 1;
+ s->window[w].keycon[i] = val;
+ break;
+ case FIMD_WKEYALPHA_START ... FIMD_WKEYALPHA_END:
+ w = ((offset - FIMD_WKEYALPHA_START) >> 2) + 1;
+ s->window[w].keyalpha = val;
+ break;
+ case FIMD_DITHMODE:
+ s->dithmode = val;
+ break;
+ case FIMD_WINMAP_START ... FIMD_WINMAP_END:
+ w = (offset - FIMD_WINMAP_START) >> 2;
+ old_value = s->window[w].winmap;
+ s->window[w].winmap = val;
+ if ((val & FIMD_WINMAP_EN) ^ (old_value & FIMD_WINMAP_EN)) {
+ exynos4210_fimd_invalidate(s);
+ exynos4210_fimd_update_win_bppmode(s, w);
+ exynos4210_fimd_trace_bppmode(s, w, 0xFFFFFFFF);
+ exynos4210_fimd_update(s);
+ }
+ break;
+ case FIMD_WPALCON_HIGH ... FIMD_WPALCON_LOW:
+ i = (offset - FIMD_WPALCON_HIGH) >> 2;
+ s->wpalcon[i] = val;
+ if (s->wpalcon[1] & FIMD_WPALCON_UPDATEEN) {
+ for (w = 0; w < NUM_OF_WINDOWS; w++) {
+ exynos4210_fimd_update_win_bppmode(s, w);
+ fimd_update_get_alpha(s, w);
+ }
+ }
+ break;
+ case FIMD_TRIGCON:
+ val = (val & ~FIMD_TRIGCON_ROMASK) | (s->trigcon & FIMD_TRIGCON_ROMASK);
+ s->trigcon = val;
+ break;
+ case FIMD_I80IFCON_START ... FIMD_I80IFCON_END:
+ s->i80ifcon[(offset - FIMD_I80IFCON_START) >> 2] = val;
+ break;
+ case FIMD_COLORGAINCON:
+ s->colorgaincon = val;
+ break;
+ case FIMD_LDI_CMDCON0 ... FIMD_LDI_CMDCON1:
+ s->ldi_cmdcon[(offset - FIMD_LDI_CMDCON0) >> 2] = val;
+ break;
+ case FIMD_SIFCCON0 ... FIMD_SIFCCON2:
+ i = (offset - FIMD_SIFCCON0) >> 2;
+ if (i != 2) {
+ s->sifccon[i] = val;
+ }
+ break;
+ case FIMD_HUECOEFCR_START ... FIMD_HUECOEFCR_END:
+ i = (offset - FIMD_HUECOEFCR_START) >> 2;
+ s->huecoef_cr[i] = val;
+ break;
+ case FIMD_HUECOEFCB_START ... FIMD_HUECOEFCB_END:
+ i = (offset - FIMD_HUECOEFCB_START) >> 2;
+ s->huecoef_cb[i] = val;
+ break;
+ case FIMD_HUEOFFSET:
+ s->hueoffset = val;
+ break;
+ case FIMD_VIDWALPHA_START ... FIMD_VIDWALPHA_END:
+ w = ((offset - FIMD_VIDWALPHA_START) >> 3);
+ i = ((offset - FIMD_VIDWALPHA_START) >> 2) & 1;
+ if (w == 0) {
+ s->window[w].alpha_val[i] = val;
+ } else {
+ s->window[w].alpha_val[i] = (val & FIMD_VIDALPHA_ALPHA_LOWER) |
+ (s->window[w].alpha_val[i] & FIMD_VIDALPHA_ALPHA_UPPER);
+ }
+ break;
+ case FIMD_BLENDEQ_START ... FIMD_BLENDEQ_END:
+ s->window[(offset - FIMD_BLENDEQ_START) >> 2].blendeq = val;
+ break;
+ case FIMD_BLENDCON:
+ old_value = s->blendcon;
+ s->blendcon = val;
+ if ((s->blendcon & FIMD_ALPHA_8BIT) != (old_value & FIMD_ALPHA_8BIT)) {
+ for (w = 0; w < NUM_OF_WINDOWS; w++) {
+ fimd_update_get_alpha(s, w);
+ }
+ }
+ break;
+ case FIMD_WRTQOSCON_START ... FIMD_WRTQOSCON_END:
+ s->window[(offset - FIMD_WRTQOSCON_START) >> 2].rtqoscon = val;
+ break;
+ case FIMD_I80IFCMD_START ... FIMD_I80IFCMD_END:
+ s->i80ifcmd[(offset - FIMD_I80IFCMD_START) >> 2] = val;
+ break;
+ case FIMD_VIDW0ADD0_B2 ... FIMD_VIDW4ADD0_B2:
+ if (offset & 0x0004) {
+ DPRINT_ERROR("bad write offset 0x%08x\n", offset);
+ break;
+ }
+ w = (offset - FIMD_VIDW0ADD0_B2) >> 3;
+ if (fimd_get_buffer_id(&s->window[w]) == 2 &&
+ s->window[w].buf_start[2] != val) {
+ s->window[w].buf_start[2] = val;
+ fimd_update_memory_section(s, w);
+ break;
+ }
+ s->window[w].buf_start[2] = val;
+ break;
+ case FIMD_SHD_ADD0_START ... FIMD_SHD_ADD0_END:
+ if (offset & 0x0004) {
+ DPRINT_ERROR("bad write offset 0x%08x\n", offset);
+ break;
+ }
+ s->window[(offset - FIMD_SHD_ADD0_START) >> 3].shadow_buf_start = val;
+ break;
+ case FIMD_SHD_ADD1_START ... FIMD_SHD_ADD1_END:
+ if (offset & 0x0004) {
+ DPRINT_ERROR("bad write offset 0x%08x\n", offset);
+ break;
+ }
+ s->window[(offset - FIMD_SHD_ADD1_START) >> 3].shadow_buf_end = val;
+ break;
+ case FIMD_SHD_ADD2_START ... FIMD_SHD_ADD2_END:
+ s->window[(offset - FIMD_SHD_ADD2_START) >> 2].shadow_buf_size = val;
+ break;
+ case FIMD_PAL_MEM_START ... FIMD_PAL_MEM_END:
+ w = (offset - FIMD_PAL_MEM_START) >> 10;
+ i = ((offset - FIMD_PAL_MEM_START) >> 2) & 0xFF;
+ s->window[w].palette[i] = val;
+ break;
+ case FIMD_PALMEM_AL_START ... FIMD_PALMEM_AL_END:
+ /* Palette memory aliases for windows 0 and 1 */
+ w = (offset - FIMD_PALMEM_AL_START) >> 10;
+ i = ((offset - FIMD_PALMEM_AL_START) >> 2) & 0xFF;
+ s->window[w].palette[i] = val;
+ break;
+ default:
+ DPRINT_ERROR("bad write offset 0x%08x\n", offset);
+ break;
+ }
+}
+
+static uint64_t exynos4210_fimd_read(void *opaque, target_phys_addr_t offset,
+ unsigned size)
+{
+ Exynos4210fimdState *s = (Exynos4210fimdState *)opaque;
+ int w, i;
+ uint32_t ret = 0;
+
+ DPRINT_L2("read offset 0x%08x\n", offset);
+
+ switch (offset) {
+ case FIMD_VIDCON0 ... FIMD_VIDCON3:
+ return s->vidcon[(offset - FIMD_VIDCON0) >> 2];
+ case FIMD_VIDTCON_START ... FIMD_VIDTCON_END:
+ return s->vidtcon[(offset - FIMD_VIDTCON_START) >> 2];
+ case FIMD_WINCON_START ... FIMD_WINCON_END:
+ return s->window[(offset - FIMD_WINCON_START) >> 2].wincon;
+ case FIMD_SHADOWCON:
+ return s->shadowcon;
+ case FIMD_WINCHMAP:
+ return s->winchmap;
+ case FIMD_VIDOSD_START ... FIMD_VIDOSD_END:
+ w = (offset - FIMD_VIDOSD_START) >> 4;
+ i = ((offset - FIMD_VIDOSD_START) & 0xF) >> 2;
+ switch (i) {
+ case 0:
+ ret = ((s->window[w].lefttop_x & FIMD_VIDOSD_COORD_MASK) <<
+ FIMD_VIDOSD_HOR_SHIFT) |
+ (s->window[w].lefttop_y & FIMD_VIDOSD_COORD_MASK);
+ break;
+ case 1:
+ ret = ((s->window[w].rightbot_x & FIMD_VIDOSD_COORD_MASK) <<
+ FIMD_VIDOSD_HOR_SHIFT) |
+ (s->window[w].rightbot_y & FIMD_VIDOSD_COORD_MASK);
+ break;
+ case 2:
+ if (w == 0) {
+ ret = s->window[w].osdsize;
+ } else {
+ ret = (pack_upper_4(s->window[w].alpha_val[0]) <<
+ FIMD_VIDOSD_AEN0_SHIFT) |
+ pack_upper_4(s->window[w].alpha_val[1]);
+ }
+ break;
+ case 3:
+ if (w != 1 && w != 2) {
+ DPRINT_ERROR("bad read offset 0x%08x\n", offset);
+ return 0xBAADBAAD;
+ }
+ ret = s->window[w].osdsize;
+ break;
+ }
+ return ret;
+ case FIMD_VIDWADD0_START ... FIMD_VIDWADD0_END:
+ w = (offset - FIMD_VIDWADD0_START) >> 3;
+ i = ((offset - FIMD_VIDWADD0_START) >> 2) & 1;
+ return s->window[w].buf_start[i];
+ case FIMD_VIDWADD1_START ... FIMD_VIDWADD1_END:
+ w = (offset - FIMD_VIDWADD1_START) >> 3;
+ i = ((offset - FIMD_VIDWADD1_START) >> 2) & 1;
+ return s->window[w].buf_end[i];
+ case FIMD_VIDWADD2_START ... FIMD_VIDWADD2_END:
+ w = (offset - FIMD_VIDWADD2_START) >> 2;
+ return s->window[w].virtpage_width | (s->window[w].virtpage_offsize <<
+ FIMD_VIDWADD2_OFFSIZE_SHIFT);
+ case FIMD_VIDINTCON0 ... FIMD_VIDINTCON1:
+ return s->vidintcon[(offset - FIMD_VIDINTCON0) >> 2];
+ case FIMD_WKEYCON_START ... FIMD_WKEYCON_END:
+ w = ((offset - FIMD_WKEYCON_START) >> 3) + 1;
+ i = ((offset - FIMD_WKEYCON_START) >> 2) & 1;
+ return s->window[w].keycon[i];
+ case FIMD_WKEYALPHA_START ... FIMD_WKEYALPHA_END:
+ w = ((offset - FIMD_WKEYALPHA_START) >> 2) + 1;
+ return s->window[w].keyalpha;
+ case FIMD_DITHMODE:
+ return s->dithmode;
+ case FIMD_WINMAP_START ... FIMD_WINMAP_END:
+ return s->window[(offset - FIMD_WINMAP_START) >> 2].winmap;
+ case FIMD_WPALCON_HIGH ... FIMD_WPALCON_LOW:
+ return s->wpalcon[(offset - FIMD_WPALCON_HIGH) >> 2];
+ case FIMD_TRIGCON:
+ return s->trigcon;
+ case FIMD_I80IFCON_START ... FIMD_I80IFCON_END:
+ return s->i80ifcon[(offset - FIMD_I80IFCON_START) >> 2];
+ case FIMD_COLORGAINCON:
+ return s->colorgaincon;
+ case FIMD_LDI_CMDCON0 ... FIMD_LDI_CMDCON1:
+ return s->ldi_cmdcon[(offset - FIMD_LDI_CMDCON0) >> 2];
+ case FIMD_SIFCCON0 ... FIMD_SIFCCON2:
+ i = (offset - FIMD_SIFCCON0) >> 2;
+ return s->sifccon[i];
+ case FIMD_HUECOEFCR_START ... FIMD_HUECOEFCR_END:
+ i = (offset - FIMD_HUECOEFCR_START) >> 2;
+ return s->huecoef_cr[i];
+ case FIMD_HUECOEFCB_START ... FIMD_HUECOEFCB_END:
+ i = (offset - FIMD_HUECOEFCB_START) >> 2;
+ return s->huecoef_cb[i];
+ case FIMD_HUEOFFSET:
+ return s->hueoffset;
+ case FIMD_VIDWALPHA_START ... FIMD_VIDWALPHA_END:
+ w = ((offset - FIMD_VIDWALPHA_START) >> 3);
+ i = ((offset - FIMD_VIDWALPHA_START) >> 2) & 1;
+ return s->window[w].alpha_val[i] &
+ (w == 0 ? 0xFFFFFF : FIMD_VIDALPHA_ALPHA_LOWER);
+ case FIMD_BLENDEQ_START ... FIMD_BLENDEQ_END:
+ return s->window[(offset - FIMD_BLENDEQ_START) >> 2].blendeq;
+ case FIMD_BLENDCON:
+ return s->blendcon;
+ case FIMD_WRTQOSCON_START ... FIMD_WRTQOSCON_END:
+ return s->window[(offset - FIMD_WRTQOSCON_START) >> 2].rtqoscon;
+ case FIMD_I80IFCMD_START ... FIMD_I80IFCMD_END:
+ return s->i80ifcmd[(offset - FIMD_I80IFCMD_START) >> 2];
+ case FIMD_VIDW0ADD0_B2 ... FIMD_VIDW4ADD0_B2:
+ if (offset & 0x0004) {
+ break;
+ }
+ return s->window[(offset - FIMD_VIDW0ADD0_B2) >> 3].buf_start[2];
+ case FIMD_SHD_ADD0_START ... FIMD_SHD_ADD0_END:
+ if (offset & 0x0004) {
+ break;
+ }
+ return s->window[(offset - FIMD_SHD_ADD0_START) >> 3].shadow_buf_start;
+ case FIMD_SHD_ADD1_START ... FIMD_SHD_ADD1_END:
+ if (offset & 0x0004) {
+ break;
+ }
+ return s->window[(offset - FIMD_SHD_ADD1_START) >> 3].shadow_buf_end;
+ case FIMD_SHD_ADD2_START ... FIMD_SHD_ADD2_END:
+ return s->window[(offset - FIMD_SHD_ADD2_START) >> 2].shadow_buf_size;
+ case FIMD_PAL_MEM_START ... FIMD_PAL_MEM_END:
+ w = (offset - FIMD_PAL_MEM_START) >> 10;
+ i = ((offset - FIMD_PAL_MEM_START) >> 2) & 0xFF;
+ return s->window[w].palette[i];
+ case FIMD_PALMEM_AL_START ... FIMD_PALMEM_AL_END:
+ /* Palette aliases for win 0,1 */
+ w = (offset - FIMD_PALMEM_AL_START) >> 10;
+ i = ((offset - FIMD_PALMEM_AL_START) >> 2) & 0xFF;
+ return s->window[w].palette[i];
+ }
+
+ DPRINT_ERROR("bad read offset 0x%08x\n", offset);
+ return 0xBAADBAAD;
+}
+
+static const MemoryRegionOps exynos4210_fimd_mmio_ops = {
+ .read = exynos4210_fimd_read,
+ .write = exynos4210_fimd_write,
+ .valid = {
+ .min_access_size = 4,
+ .max_access_size = 4,
+ .unaligned = false
+ },
+ .endianness = DEVICE_NATIVE_ENDIAN,
+};
+
+static int exynos4210_fimd_load(void *opaque, int version_id)
+{
+ Exynos4210fimdState *s = (Exynos4210fimdState *)opaque;
+ int w;
+
+ if (version_id != 1) {
+ return -EINVAL;
+ }
+
+ for (w = 0; w < NUM_OF_WINDOWS; w++) {
+ exynos4210_fimd_update_win_bppmode(s, w);
+ fimd_update_get_alpha(s, w);
+ fimd_update_memory_section(s, w);
+ }
+
+ /* Redraw the whole screen */
+ exynos4210_update_resolution(s);
+ exynos4210_fimd_invalidate(s);
+ exynos4210_fimd_enable(s, (s->vidcon[0] & FIMD_VIDCON0_ENVID_MASK) ==
+ FIMD_VIDCON0_ENVID_MASK);
+ return 0;
+}
+
+static const VMStateDescription exynos4210_fimd_window_vmstate = {
+ .name = "exynos4210.fimd_window",
+ .version_id = 1,
+ .minimum_version_id = 1,
+ .fields = (VMStateField[]) {
+ VMSTATE_UINT32(wincon, Exynos4210fimdWindow),
+ VMSTATE_UINT32_ARRAY(buf_start, Exynos4210fimdWindow, 3),
+ VMSTATE_UINT32_ARRAY(buf_end, Exynos4210fimdWindow, 3),
+ VMSTATE_UINT32_ARRAY(keycon, Exynos4210fimdWindow, 2),
+ VMSTATE_UINT32(keyalpha, Exynos4210fimdWindow),
+ VMSTATE_UINT32(winmap, Exynos4210fimdWindow),
+ VMSTATE_UINT32(blendeq, Exynos4210fimdWindow),
+ VMSTATE_UINT32(rtqoscon, Exynos4210fimdWindow),
+ VMSTATE_UINT32_ARRAY(palette, Exynos4210fimdWindow, 256),
+ VMSTATE_UINT32(shadow_buf_start, Exynos4210fimdWindow),
+ VMSTATE_UINT32(shadow_buf_end, Exynos4210fimdWindow),
+ VMSTATE_UINT32(shadow_buf_size, Exynos4210fimdWindow),
+ VMSTATE_UINT16(lefttop_x, Exynos4210fimdWindow),
+ VMSTATE_UINT16(lefttop_y, Exynos4210fimdWindow),
+ VMSTATE_UINT16(rightbot_x, Exynos4210fimdWindow),
+ VMSTATE_UINT16(rightbot_y, Exynos4210fimdWindow),
+ VMSTATE_UINT32(osdsize, Exynos4210fimdWindow),
+ VMSTATE_UINT32_ARRAY(alpha_val, Exynos4210fimdWindow, 2),
+ VMSTATE_UINT16(virtpage_width, Exynos4210fimdWindow),
+ VMSTATE_UINT16(virtpage_offsize, Exynos4210fimdWindow),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
+static const VMStateDescription exynos4210_fimd_vmstate = {
+ .name = "exynos4210.fimd",
+ .version_id = 1,
+ .minimum_version_id = 1,
+ .post_load = exynos4210_fimd_load,
+ .fields = (VMStateField[]) {
+ VMSTATE_UINT32_ARRAY(vidcon, Exynos4210fimdState, 4),
+ VMSTATE_UINT32_ARRAY(vidtcon, Exynos4210fimdState, 4),
+ VMSTATE_UINT32(shadowcon, Exynos4210fimdState),
+ VMSTATE_UINT32(winchmap, Exynos4210fimdState),
+ VMSTATE_UINT32_ARRAY(vidintcon, Exynos4210fimdState, 2),
+ VMSTATE_UINT32(dithmode, Exynos4210fimdState),
+ VMSTATE_UINT32_ARRAY(wpalcon, Exynos4210fimdState, 2),
+ VMSTATE_UINT32(trigcon, Exynos4210fimdState),
+ VMSTATE_UINT32_ARRAY(i80ifcon, Exynos4210fimdState, 4),
+ VMSTATE_UINT32(colorgaincon, Exynos4210fimdState),
+ VMSTATE_UINT32_ARRAY(ldi_cmdcon, Exynos4210fimdState, 2),
+ VMSTATE_UINT32_ARRAY(sifccon, Exynos4210fimdState, 3),
+ VMSTATE_UINT32_ARRAY(huecoef_cr, Exynos4210fimdState, 4),
+ VMSTATE_UINT32_ARRAY(huecoef_cb, Exynos4210fimdState, 4),
+ VMSTATE_UINT32(hueoffset, Exynos4210fimdState),
+ VMSTATE_UINT32_ARRAY(i80ifcmd, Exynos4210fimdState, 12),
+ VMSTATE_UINT32(blendcon, Exynos4210fimdState),
+ VMSTATE_STRUCT_ARRAY(window, Exynos4210fimdState, 5, 1,
+ exynos4210_fimd_window_vmstate, Exynos4210fimdWindow),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
+static int exynos4210_fimd_init(SysBusDevice *dev)
+{
+ Exynos4210fimdState *s = FROM_SYSBUS(Exynos4210fimdState, dev);
+
+ s->ifb = NULL;
+
+ sysbus_init_irq(dev, &s->irq[0]);
+ sysbus_init_irq(dev, &s->irq[1]);
+ sysbus_init_irq(dev, &s->irq[2]);
+
+ memory_region_init_io(&s->iomem, &exynos4210_fimd_mmio_ops, s,
+ "exynos4210.fimd", FIMD_REGS_SIZE);
+ sysbus_init_mmio(dev, &s->iomem);
+ s->console = graphic_console_init(exynos4210_fimd_update,
+ exynos4210_fimd_invalidate, NULL, NULL, s);
+
+ return 0;
+}
+
+static void exynos4210_fimd_class_init(ObjectClass *klass, void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(klass);
+ SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass);
+
+ dc->vmsd = &exynos4210_fimd_vmstate;
+ dc->reset = exynos4210_fimd_reset;
+ k->init = exynos4210_fimd_init;
+}
+
+static TypeInfo exynos4210_fimd_info = {
+ .name = "exynos4210.fimd",
+ .parent = TYPE_SYS_BUS_DEVICE,
+ .instance_size = sizeof(Exynos4210fimdState),
+ .class_init = exynos4210_fimd_class_init,
+};
+
+static void exynos4210_fimd_register_types(void)
+{
+ type_register_static(&exynos4210_fimd_info);
+}
+
+type_init(exynos4210_fimd_register_types)
diff --git a/hw/exynos4210_gic.c b/hw/exynos4210_gic.c
new file mode 100644
index 0000000000..ec13140f9f
--- /dev/null
+++ b/hw/exynos4210_gic.c
@@ -0,0 +1,458 @@
+/*
+ * Samsung exynos4210 GIC implementation. Based on hw/arm_gic.c
+ *
+ * Copyright (c) 2000 - 2011 Samsung Electronics Co., Ltd.
+ * All rights reserved.
+ *
+ * Evgeny Voevodin <e.voevodin@samsung.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ * See the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "sysbus.h"
+#include "qemu-common.h"
+#include "irq.h"
+#include "exynos4210.h"
+
+enum ExtGicId {
+ EXT_GIC_ID_MDMA_LCD0 = 66,
+ EXT_GIC_ID_PDMA0,
+ EXT_GIC_ID_PDMA1,
+ EXT_GIC_ID_TIMER0,
+ EXT_GIC_ID_TIMER1,
+ EXT_GIC_ID_TIMER2,
+ EXT_GIC_ID_TIMER3,
+ EXT_GIC_ID_TIMER4,
+ EXT_GIC_ID_MCT_L0,
+ EXT_GIC_ID_WDT,
+ EXT_GIC_ID_RTC_ALARM,
+ EXT_GIC_ID_RTC_TIC,
+ EXT_GIC_ID_GPIO_XB,
+ EXT_GIC_ID_GPIO_XA,
+ EXT_GIC_ID_MCT_L1,
+ EXT_GIC_ID_IEM_APC,
+ EXT_GIC_ID_IEM_IEC,
+ EXT_GIC_ID_NFC,
+ EXT_GIC_ID_UART0,
+ EXT_GIC_ID_UART1,
+ EXT_GIC_ID_UART2,
+ EXT_GIC_ID_UART3,
+ EXT_GIC_ID_UART4,
+ EXT_GIC_ID_MCT_G0,
+ EXT_GIC_ID_I2C0,
+ EXT_GIC_ID_I2C1,
+ EXT_GIC_ID_I2C2,
+ EXT_GIC_ID_I2C3,
+ EXT_GIC_ID_I2C4,
+ EXT_GIC_ID_I2C5,
+ EXT_GIC_ID_I2C6,
+ EXT_GIC_ID_I2C7,
+ EXT_GIC_ID_SPI0,
+ EXT_GIC_ID_SPI1,
+ EXT_GIC_ID_SPI2,
+ EXT_GIC_ID_MCT_G1,
+ EXT_GIC_ID_USB_HOST,
+ EXT_GIC_ID_USB_DEVICE,
+ EXT_GIC_ID_MODEMIF,
+ EXT_GIC_ID_HSMMC0,
+ EXT_GIC_ID_HSMMC1,
+ EXT_GIC_ID_HSMMC2,
+ EXT_GIC_ID_HSMMC3,
+ EXT_GIC_ID_SDMMC,
+ EXT_GIC_ID_MIPI_CSI_4LANE,
+ EXT_GIC_ID_MIPI_DSI_4LANE,
+ EXT_GIC_ID_MIPI_CSI_2LANE,
+ EXT_GIC_ID_MIPI_DSI_2LANE,
+ EXT_GIC_ID_ONENAND_AUDI,
+ EXT_GIC_ID_ROTATOR,
+ EXT_GIC_ID_FIMC0,
+ EXT_GIC_ID_FIMC1,
+ EXT_GIC_ID_FIMC2,
+ EXT_GIC_ID_FIMC3,
+ EXT_GIC_ID_JPEG,
+ EXT_GIC_ID_2D,
+ EXT_GIC_ID_PCIe,
+ EXT_GIC_ID_MIXER,
+ EXT_GIC_ID_HDMI,
+ EXT_GIC_ID_HDMI_I2C,
+ EXT_GIC_ID_MFC,
+ EXT_GIC_ID_TVENC,
+};
+
+enum ExtInt {
+ EXT_GIC_ID_EXTINT0 = 48,
+ EXT_GIC_ID_EXTINT1,
+ EXT_GIC_ID_EXTINT2,
+ EXT_GIC_ID_EXTINT3,
+ EXT_GIC_ID_EXTINT4,
+ EXT_GIC_ID_EXTINT5,
+ EXT_GIC_ID_EXTINT6,
+ EXT_GIC_ID_EXTINT7,
+ EXT_GIC_ID_EXTINT8,
+ EXT_GIC_ID_EXTINT9,
+ EXT_GIC_ID_EXTINT10,
+ EXT_GIC_ID_EXTINT11,
+ EXT_GIC_ID_EXTINT12,
+ EXT_GIC_ID_EXTINT13,
+ EXT_GIC_ID_EXTINT14,
+ EXT_GIC_ID_EXTINT15
+};
+
+/*
+ * External GIC sources which are not from External Interrupt Combiner or
+ * External Interrupts are starting from EXYNOS4210_MAX_EXT_COMBINER_OUT_IRQ,
+ * which is INTG16 in Internal Interrupt Combiner.
+ */
+
+static uint32_t
+combiner_grp_to_gic_id[64-EXYNOS4210_MAX_EXT_COMBINER_OUT_IRQ][8] = {
+ /* int combiner groups 16-19 */
+ { }, { }, { }, { },
+ /* int combiner group 20 */
+ { 0, EXT_GIC_ID_MDMA_LCD0 },
+ /* int combiner group 21 */
+ { EXT_GIC_ID_PDMA0, EXT_GIC_ID_PDMA1 },
+ /* int combiner group 22 */
+ { EXT_GIC_ID_TIMER0, EXT_GIC_ID_TIMER1, EXT_GIC_ID_TIMER2,
+ EXT_GIC_ID_TIMER3, EXT_GIC_ID_TIMER4 },
+ /* int combiner group 23 */
+ { EXT_GIC_ID_RTC_ALARM, EXT_GIC_ID_RTC_TIC },
+ /* int combiner group 24 */
+ { EXT_GIC_ID_GPIO_XB, EXT_GIC_ID_GPIO_XA },
+ /* int combiner group 25 */
+ { EXT_GIC_ID_IEM_APC, EXT_GIC_ID_IEM_IEC },
+ /* int combiner group 26 */
+ { EXT_GIC_ID_UART0, EXT_GIC_ID_UART1, EXT_GIC_ID_UART2, EXT_GIC_ID_UART3,
+ EXT_GIC_ID_UART4 },
+ /* int combiner group 27 */
+ { EXT_GIC_ID_I2C0, EXT_GIC_ID_I2C1, EXT_GIC_ID_I2C2, EXT_GIC_ID_I2C3,
+ EXT_GIC_ID_I2C4, EXT_GIC_ID_I2C5, EXT_GIC_ID_I2C6,
+ EXT_GIC_ID_I2C7 },
+ /* int combiner group 28 */
+ { EXT_GIC_ID_SPI0, EXT_GIC_ID_SPI1, EXT_GIC_ID_SPI2 },
+ /* int combiner group 29 */
+ { EXT_GIC_ID_HSMMC0, EXT_GIC_ID_HSMMC1, EXT_GIC_ID_HSMMC2,
+ EXT_GIC_ID_HSMMC3, EXT_GIC_ID_SDMMC },
+ /* int combiner group 30 */
+ { EXT_GIC_ID_MIPI_CSI_4LANE, EXT_GIC_ID_MIPI_CSI_2LANE },
+ /* int combiner group 31 */
+ { EXT_GIC_ID_MIPI_DSI_4LANE, EXT_GIC_ID_MIPI_DSI_2LANE },
+ /* int combiner group 32 */
+ { EXT_GIC_ID_FIMC0, EXT_GIC_ID_FIMC1 },
+ /* int combiner group 33 */
+ { EXT_GIC_ID_FIMC2, EXT_GIC_ID_FIMC3 },
+ /* int combiner group 34 */
+ { EXT_GIC_ID_ONENAND_AUDI, EXT_GIC_ID_NFC },
+ /* int combiner group 35 */
+ { 0, 0, 0, EXT_GIC_ID_MCT_L1, EXT_GIC_ID_MCT_G0, EXT_GIC_ID_MCT_G1 },
+ /* int combiner group 36 */
+ { EXT_GIC_ID_MIXER },
+ /* int combiner group 37 */
+ { EXT_GIC_ID_EXTINT4, EXT_GIC_ID_EXTINT5, EXT_GIC_ID_EXTINT6,
+ EXT_GIC_ID_EXTINT7 },
+ /* groups 38-50 */
+ { }, { }, { }, { }, { }, { }, { }, { }, { }, { }, { }, { }, { },
+ /* int combiner group 51 */
+ { EXT_GIC_ID_MCT_L0, 0, 0, 0, EXT_GIC_ID_MCT_G0, EXT_GIC_ID_MCT_G1 },
+ /* group 52 */
+ { },
+ /* int combiner group 53 */
+ { EXT_GIC_ID_WDT, 0, 0, 0, EXT_GIC_ID_MCT_G0, EXT_GIC_ID_MCT_G1 },
+ /* groups 54-63 */
+ { }, { }, { }, { }, { }, { }, { }, { }, { }, { }
+};
+
+#define EXYNOS4210_GIC_NIRQ 160
+#define NCPU EXYNOS4210_NCPUS
+
+#define EXYNOS4210_EXT_GIC_CPU_REGION_SIZE 0x10000
+#define EXYNOS4210_EXT_GIC_DIST_REGION_SIZE 0x10000
+
+#define EXYNOS4210_EXT_GIC_PER_CPU_OFFSET 0x8000
+#define EXYNOS4210_EXT_GIC_CPU_GET_OFFSET(n) \
+ ((n) * EXYNOS4210_EXT_GIC_PER_CPU_OFFSET)
+#define EXYNOS4210_EXT_GIC_DIST_GET_OFFSET(n) \
+ ((n) * EXYNOS4210_EXT_GIC_PER_CPU_OFFSET)
+
+#define EXYNOS4210_GIC_CPU_REGION_SIZE 0x100
+#define EXYNOS4210_GIC_DIST_REGION_SIZE 0x1000
+
+static void exynos4210_irq_handler(void *opaque, int irq, int level)
+{
+ Exynos4210Irq *s = (Exynos4210Irq *)opaque;
+
+ /* Bypass */
+ qemu_set_irq(s->board_irqs[irq], level);
+
+ return;
+}
+
+/*
+ * Initialize exynos4210 IRQ subsystem stub.
+ */
+qemu_irq *exynos4210_init_irq(Exynos4210Irq *s)
+{
+ return qemu_allocate_irqs(exynos4210_irq_handler, s,
+ EXYNOS4210_MAX_INT_COMBINER_IN_IRQ);
+}
+
+/*
+ * Initialize board IRQs.
+ * These IRQs contain splitted Int/External Combiner and External Gic IRQs.
+ */
+void exynos4210_init_board_irqs(Exynos4210Irq *s)
+{
+ uint32_t grp, bit, irq_id, n;
+
+ for (n = 0; n < EXYNOS4210_MAX_EXT_COMBINER_IN_IRQ; n++) {
+ s->board_irqs[n] = qemu_irq_split(s->int_combiner_irq[n],
+ s->ext_combiner_irq[n]);
+
+ irq_id = 0;
+ if (n == EXYNOS4210_COMBINER_GET_IRQ_NUM(1, 4) ||
+ n == EXYNOS4210_COMBINER_GET_IRQ_NUM(12, 4)) {
+ /* MCT_G0 is passed to External GIC */
+ irq_id = EXT_GIC_ID_MCT_G0;
+ }
+ if (n == EXYNOS4210_COMBINER_GET_IRQ_NUM(1, 5) ||
+ n == EXYNOS4210_COMBINER_GET_IRQ_NUM(12, 5)) {
+ /* MCT_G1 is passed to External and GIC */
+ irq_id = EXT_GIC_ID_MCT_G1;
+ }
+ if (irq_id) {
+ s->board_irqs[n] = qemu_irq_split(s->int_combiner_irq[n],
+ s->ext_gic_irq[irq_id-32]);
+ }
+
+ }
+ for (; n < EXYNOS4210_MAX_INT_COMBINER_IN_IRQ; n++) {
+ /* these IDs are passed to Internal Combiner and External GIC */
+ grp = EXYNOS4210_COMBINER_GET_GRP_NUM(n);
+ bit = EXYNOS4210_COMBINER_GET_BIT_NUM(n);
+ irq_id = combiner_grp_to_gic_id[grp -
+ EXYNOS4210_MAX_EXT_COMBINER_OUT_IRQ][bit];
+
+ if (irq_id) {
+ s->board_irqs[n] = qemu_irq_split(s->int_combiner_irq[n],
+ s->ext_gic_irq[irq_id-32]);
+ }
+ }
+}
+
+/*
+ * Get IRQ number from exynos4210 IRQ subsystem stub.
+ * To identify IRQ source use internal combiner group and bit number
+ * grp - group number
+ * bit - bit number inside group
+ */
+uint32_t exynos4210_get_irq(uint32_t grp, uint32_t bit)
+{
+ return EXYNOS4210_COMBINER_GET_IRQ_NUM(grp, bit);
+}
+
+/********* GIC part *********/
+
+static inline int
+gic_get_current_cpu(void)
+{
+ return cpu_single_env->cpu_index;
+}
+
+#include "arm_gic.c"
+
+typedef struct {
+ gic_state gic;
+ MemoryRegion cpu_container;
+ MemoryRegion dist_container;
+ MemoryRegion cpu_alias[NCPU];
+ MemoryRegion dist_alias[NCPU];
+ uint32_t num_cpu;
+} Exynos4210GicState;
+
+static int exynos4210_gic_init(SysBusDevice *dev)
+{
+ Exynos4210GicState *s = FROM_SYSBUSGIC(Exynos4210GicState, dev);
+ uint32_t i;
+ const char cpu_prefix[] = "exynos4210-gic-alias_cpu";
+ const char dist_prefix[] = "exynos4210-gic-alias_dist";
+ char cpu_alias_name[sizeof(cpu_prefix) + 3];
+ char dist_alias_name[sizeof(cpu_prefix) + 3];
+
+ gic_init(&s->gic, s->num_cpu, EXYNOS4210_GIC_NIRQ);
+
+ memory_region_init(&s->cpu_container, "exynos4210-cpu-container",
+ EXYNOS4210_EXT_GIC_CPU_REGION_SIZE);
+ memory_region_init(&s->dist_container, "exynos4210-dist-container",
+ EXYNOS4210_EXT_GIC_DIST_REGION_SIZE);
+
+ for (i = 0; i < s->num_cpu; i++) {
+ /* Map CPU interface per SMP Core */
+ sprintf(cpu_alias_name, "%s%x", cpu_prefix, i);
+ memory_region_init_alias(&s->cpu_alias[i],
+ cpu_alias_name,
+ &s->gic.cpuiomem[0],
+ 0,
+ EXYNOS4210_GIC_CPU_REGION_SIZE);
+ memory_region_add_subregion(&s->cpu_container,
+ EXYNOS4210_EXT_GIC_CPU_GET_OFFSET(i), &s->cpu_alias[i]);
+
+ /* Map Distributor per SMP Core */
+ sprintf(dist_alias_name, "%s%x", dist_prefix, i);
+ memory_region_init_alias(&s->dist_alias[i],
+ dist_alias_name,
+ &s->gic.iomem,
+ 0,
+ EXYNOS4210_GIC_DIST_REGION_SIZE);
+ memory_region_add_subregion(&s->dist_container,
+ EXYNOS4210_EXT_GIC_DIST_GET_OFFSET(i), &s->dist_alias[i]);
+ }
+
+ sysbus_init_mmio(dev, &s->cpu_container);
+ sysbus_init_mmio(dev, &s->dist_container);
+
+ gic_cpu_write(&s->gic, 1, 0, 1);
+
+ return 0;
+}
+
+static Property exynos4210_gic_properties[] = {
+ DEFINE_PROP_UINT32("num-cpu", Exynos4210GicState, num_cpu, 1),
+ DEFINE_PROP_END_OF_LIST(),
+};
+
+static void exynos4210_gic_class_init(ObjectClass *klass, void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(klass);
+ SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass);
+
+ k->init = exynos4210_gic_init;
+ dc->props = exynos4210_gic_properties;
+}
+
+static TypeInfo exynos4210_gic_info = {
+ .name = "exynos4210.gic",
+ .parent = TYPE_SYS_BUS_DEVICE,
+ .instance_size = sizeof(Exynos4210GicState),
+ .class_init = exynos4210_gic_class_init,
+};
+
+static void exynos4210_gic_register_types(void)
+{
+ type_register_static(&exynos4210_gic_info);
+}
+
+type_init(exynos4210_gic_register_types)
+
+/*
+ * IRQGate struct.
+ * IRQ Gate represents OR gate between GICs to pass IRQ to PIC.
+ */
+typedef struct {
+ SysBusDevice busdev;
+
+ qemu_irq pic_irq[NCPU]; /* output IRQs to PICs */
+ uint32_t gpio_level[EXYNOS4210_IRQ_GATE_NINPUTS]; /* Input levels */
+} Exynos4210IRQGateState;
+
+static const VMStateDescription vmstate_exynos4210_irq_gate = {
+ .name = "exynos4210.irq_gate",
+ .version_id = 1,
+ .minimum_version_id = 1,
+ .minimum_version_id_old = 1,
+ .fields = (VMStateField[]) {
+ VMSTATE_UINT32_ARRAY(gpio_level, Exynos4210IRQGateState,
+ EXYNOS4210_IRQ_GATE_NINPUTS),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
+/* Process a change in an external IRQ input. */
+static void exynos4210_irq_gate_handler(void *opaque, int irq, int level)
+{
+ Exynos4210IRQGateState *s =
+ (Exynos4210IRQGateState *)opaque;
+ uint32_t odd, even;
+
+ if (irq & 1) {
+ odd = irq;
+ even = irq & ~1;
+ } else {
+ even = irq;
+ odd = irq | 1;
+ }
+
+ assert(irq < EXYNOS4210_IRQ_GATE_NINPUTS);
+ s->gpio_level[irq] = level;
+
+ if (s->gpio_level[odd] >= 1 || s->gpio_level[even] >= 1) {
+ qemu_irq_raise(s->pic_irq[even >> 1]);
+ } else {
+ qemu_irq_lower(s->pic_irq[even >> 1]);
+ }
+
+ return;
+}
+
+static void exynos4210_irq_gate_reset(DeviceState *d)
+{
+ Exynos4210IRQGateState *s = (Exynos4210IRQGateState *)d;
+
+ memset(&s->gpio_level, 0, sizeof(s->gpio_level));
+}
+
+/*
+ * IRQ Gate initialization.
+ */
+static int exynos4210_irq_gate_init(SysBusDevice *dev)
+{
+ unsigned int i;
+ Exynos4210IRQGateState *s =
+ FROM_SYSBUS(Exynos4210IRQGateState, dev);
+
+ /* Allocate general purpose input signals and connect a handler to each of
+ * them */
+ qdev_init_gpio_in(&s->busdev.qdev, exynos4210_irq_gate_handler,
+ EXYNOS4210_IRQ_GATE_NINPUTS);
+
+ /* Connect SysBusDev irqs to device specific irqs */
+ for (i = 0; i < NCPU; i++) {
+ sysbus_init_irq(dev, &s->pic_irq[i]);
+ }
+
+ return 0;
+}
+
+static void exynos4210_irq_gate_class_init(ObjectClass *klass, void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(klass);
+ SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass);
+
+ k->init = exynos4210_irq_gate_init;
+ dc->reset = exynos4210_irq_gate_reset;
+ dc->vmsd = &vmstate_exynos4210_irq_gate;
+}
+
+static TypeInfo exynos4210_irq_gate_info = {
+ .name = "exynos4210.irq_gate",
+ .parent = TYPE_SYS_BUS_DEVICE,
+ .instance_size = sizeof(Exynos4210IRQGateState),
+ .class_init = exynos4210_irq_gate_class_init,
+};
+
+static void exynos4210_irq_gate_register_types(void)
+{
+ type_register_static(&exynos4210_irq_gate_info);
+}
+
+type_init(exynos4210_irq_gate_register_types)
diff --git a/hw/exynos4210_mct.c b/hw/exynos4210_mct.c
new file mode 100644
index 0000000000..01e3fb8a3b
--- /dev/null
+++ b/hw/exynos4210_mct.c
@@ -0,0 +1,1488 @@
+/*
+ * Samsung exynos4210 Multi Core timer
+ *
+ * Copyright (c) 2000 - 2011 Samsung Electronics Co., Ltd.
+ * All rights reserved.
+ *
+ * Evgeny Voevodin <e.voevodin@samsung.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ * See the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+/*
+ * Global Timer:
+ *
+ * Consists of two timers. First represents Free Running Counter and second
+ * is used to measure interval from FRC to nearest comparator.
+ *
+ * 0 UINT64_MAX
+ * | timer0 |
+ * | <-------------------------------------------------------------- |
+ * | --------------------------------------------frc---------------> |
+ * |______________________________________________|__________________|
+ * CMP0 CMP1 CMP2 | CMP3
+ * __| |_
+ * | timer1 |
+ * | -------------> |
+ * frc CMPx
+ *
+ * Problem: when implementing global timer as is, overflow arises.
+ * next_time = cur_time + period * count;
+ * period and count are 64 bits width.
+ * Lets arm timer for MCT_GT_COUNTER_STEP count and update internal G_CNT
+ * register during each event.
+ *
+ * Problem: both timers need to be implemented using MCT_XT_COUNTER_STEP because
+ * local timer contains two counters: TCNT and ICNT. TCNT == 0 -> ICNT--.
+ * IRQ is generated when ICNT riches zero. Implementation where TCNT == 0
+ * generates IRQs suffers from too frequently events. Better to have one
+ * uint64_t counter equal to TCNT*ICNT and arm ptimer.c for a minimum(TCNT*ICNT,
+ * MCT_GT_COUNTER_STEP); (yes, if target tunes ICNT * TCNT to be too low values,
+ * there is no way to avoid frequently events).
+ */
+
+#include "sysbus.h"
+#include "qemu-timer.h"
+#include "qemu-common.h"
+#include "ptimer.h"
+
+#include "exynos4210.h"
+
+//#define DEBUG_MCT
+
+#ifdef DEBUG_MCT
+#define DPRINTF(fmt, ...) \
+ do { fprintf(stdout, "MCT: [%24s:%5d] " fmt, __func__, __LINE__, \
+ ## __VA_ARGS__); } while (0)
+#else
+#define DPRINTF(fmt, ...) do {} while (0)
+#endif
+
+#define MCT_CFG 0x000
+#define G_CNT_L 0x100
+#define G_CNT_U 0x104
+#define G_CNT_WSTAT 0x110
+#define G_COMP0_L 0x200
+#define G_COMP0_U 0x204
+#define G_COMP0_ADD_INCR 0x208
+#define G_COMP1_L 0x210
+#define G_COMP1_U 0x214
+#define G_COMP1_ADD_INCR 0x218
+#define G_COMP2_L 0x220
+#define G_COMP2_U 0x224
+#define G_COMP2_ADD_INCR 0x228
+#define G_COMP3_L 0x230
+#define G_COMP3_U 0x234
+#define G_COMP3_ADD_INCR 0x238
+#define G_TCON 0x240
+#define G_INT_CSTAT 0x244
+#define G_INT_ENB 0x248
+#define G_WSTAT 0x24C
+#define L0_TCNTB 0x300
+#define L0_TCNTO 0x304
+#define L0_ICNTB 0x308
+#define L0_ICNTO 0x30C
+#define L0_FRCNTB 0x310
+#define L0_FRCNTO 0x314
+#define L0_TCON 0x320
+#define L0_INT_CSTAT 0x330
+#define L0_INT_ENB 0x334
+#define L0_WSTAT 0x340
+#define L1_TCNTB 0x400
+#define L1_TCNTO 0x404
+#define L1_ICNTB 0x408
+#define L1_ICNTO 0x40C
+#define L1_FRCNTB 0x410
+#define L1_FRCNTO 0x414
+#define L1_TCON 0x420
+#define L1_INT_CSTAT 0x430
+#define L1_INT_ENB 0x434
+#define L1_WSTAT 0x440
+
+#define MCT_CFG_GET_PRESCALER(x) ((x) & 0xFF)
+#define MCT_CFG_GET_DIVIDER(x) (1 << ((x) >> 8 & 7))
+
+#define GET_G_COMP_IDX(offset) (((offset) - G_COMP0_L) / 0x10)
+#define GET_G_COMP_ADD_INCR_IDX(offset) (((offset) - G_COMP0_ADD_INCR) / 0x10)
+
+#define G_COMP_L(x) (G_COMP0_L + (x) * 0x10)
+#define G_COMP_U(x) (G_COMP0_U + (x) * 0x10)
+
+#define G_COMP_ADD_INCR(x) (G_COMP0_ADD_INCR + (x) * 0x10)
+
+/* MCT bits */
+#define G_TCON_COMP_ENABLE(x) (1 << 2 * (x))
+#define G_TCON_AUTO_ICREMENT(x) (1 << (2 * (x) + 1))
+#define G_TCON_TIMER_ENABLE (1 << 8)
+
+#define G_INT_ENABLE(x) (1 << (x))
+#define G_INT_CSTAT_COMP(x) (1 << (x))
+
+#define G_CNT_WSTAT_L 1
+#define G_CNT_WSTAT_U 2
+
+#define G_WSTAT_COMP_L(x) (1 << 4 * (x))
+#define G_WSTAT_COMP_U(x) (1 << ((4 * (x)) + 1))
+#define G_WSTAT_COMP_ADDINCR(x) (1 << ((4 * (x)) + 2))
+#define G_WSTAT_TCON_WRITE (1 << 16)
+
+#define GET_L_TIMER_IDX(offset) ((((offset) & 0xF00) - L0_TCNTB) / 0x100)
+#define GET_L_TIMER_CNT_REG_IDX(offset, lt_i) \
+ (((offset) - (L0_TCNTB + 0x100 * (lt_i))) >> 2)
+
+#define L_ICNTB_MANUAL_UPDATE (1 << 31)
+
+#define L_TCON_TICK_START (1)
+#define L_TCON_INT_START (1 << 1)
+#define L_TCON_INTERVAL_MODE (1 << 2)
+#define L_TCON_FRC_START (1 << 3)
+
+#define L_INT_CSTAT_INTCNT (1 << 0)
+#define L_INT_CSTAT_FRCCNT (1 << 1)
+
+#define L_INT_INTENB_ICNTEIE (1 << 0)
+#define L_INT_INTENB_FRCEIE (1 << 1)
+
+#define L_WSTAT_TCNTB_WRITE (1 << 0)
+#define L_WSTAT_ICNTB_WRITE (1 << 1)
+#define L_WSTAT_FRCCNTB_WRITE (1 << 2)
+#define L_WSTAT_TCON_WRITE (1 << 3)
+
+enum LocalTimerRegCntIndexes {
+ L_REG_CNT_TCNTB,
+ L_REG_CNT_TCNTO,
+ L_REG_CNT_ICNTB,
+ L_REG_CNT_ICNTO,
+ L_REG_CNT_FRCCNTB,
+ L_REG_CNT_FRCCNTO,
+
+ L_REG_CNT_AMOUNT
+};
+
+#define MCT_NIRQ 6
+#define MCT_SFR_SIZE 0x444
+
+#define MCT_GT_CMP_NUM 4
+
+#define MCT_GT_MAX_VAL UINT64_MAX
+
+#define MCT_GT_COUNTER_STEP 0x100000000ULL
+#define MCT_LT_COUNTER_STEP 0x100000000ULL
+#define MCT_LT_CNT_LOW_LIMIT 0x100
+
+/* global timer */
+typedef struct {
+ qemu_irq irq[MCT_GT_CMP_NUM];
+
+ struct gregs {
+ uint64_t cnt;
+ uint32_t cnt_wstat;
+ uint32_t tcon;
+ uint32_t int_cstat;
+ uint32_t int_enb;
+ uint32_t wstat;
+ uint64_t comp[MCT_GT_CMP_NUM];
+ uint32_t comp_add_incr[MCT_GT_CMP_NUM];
+ } reg;
+
+ uint64_t count; /* Value FRC was armed with */
+ int32_t curr_comp; /* Current comparator FRC is running to */
+
+ ptimer_state *ptimer_frc; /* FRC timer */
+
+} Exynos4210MCTGT;
+
+/* local timer */
+typedef struct {
+ int id; /* timer id */
+ qemu_irq irq; /* local timer irq */
+
+ struct tick_timer {
+ uint32_t cnt_run; /* cnt timer is running */
+ uint32_t int_run; /* int timer is running */
+
+ uint32_t last_icnto;
+ uint32_t last_tcnto;
+ uint32_t tcntb; /* initial value for TCNTB */
+ uint32_t icntb; /* initial value for ICNTB */
+
+ /* for step mode */
+ uint64_t distance; /* distance to count to the next event */
+ uint64_t progress; /* progress when counting by steps */
+ uint64_t count; /* count to arm timer with */
+
+ ptimer_state *ptimer_tick; /* timer for tick counter */
+ } tick_timer;
+
+ /* use ptimer.c to represent count down timer */
+
+ ptimer_state *ptimer_frc; /* timer for free running counter */
+
+ /* registers */
+ struct lregs {
+ uint32_t cnt[L_REG_CNT_AMOUNT];
+ uint32_t tcon;
+ uint32_t int_cstat;
+ uint32_t int_enb;
+ uint32_t wstat;
+ } reg;
+
+} Exynos4210MCTLT;
+
+typedef struct Exynos4210MCTState {
+ SysBusDevice busdev;
+ MemoryRegion iomem;
+
+ /* Registers */
+ uint32_t reg_mct_cfg;
+
+ Exynos4210MCTLT l_timer[2];
+ Exynos4210MCTGT g_timer;
+
+ uint32_t freq; /* all timers tick frequency, TCLK */
+} Exynos4210MCTState;
+
+/*** VMState ***/
+static const VMStateDescription vmstate_tick_timer = {
+ .name = "exynos4210.mct.tick_timer",
+ .version_id = 1,
+ .minimum_version_id = 1,
+ .minimum_version_id_old = 1,
+ .fields = (VMStateField[]) {
+ VMSTATE_UINT32(cnt_run, struct tick_timer),
+ VMSTATE_UINT32(int_run, struct tick_timer),
+ VMSTATE_UINT32(last_icnto, struct tick_timer),
+ VMSTATE_UINT32(last_tcnto, struct tick_timer),
+ VMSTATE_UINT32(tcntb, struct tick_timer),
+ VMSTATE_UINT32(icntb, struct tick_timer),
+ VMSTATE_UINT64(distance, struct tick_timer),
+ VMSTATE_UINT64(progress, struct tick_timer),
+ VMSTATE_UINT64(count, struct tick_timer),
+ VMSTATE_PTIMER(ptimer_tick, struct tick_timer),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
+static const VMStateDescription vmstate_lregs = {
+ .name = "exynos4210.mct.lregs",
+ .version_id = 1,
+ .minimum_version_id = 1,
+ .minimum_version_id_old = 1,
+ .fields = (VMStateField[]) {
+ VMSTATE_UINT32_ARRAY(cnt, struct lregs, L_REG_CNT_AMOUNT),
+ VMSTATE_UINT32(tcon, struct lregs),
+ VMSTATE_UINT32(int_cstat, struct lregs),
+ VMSTATE_UINT32(int_enb, struct lregs),
+ VMSTATE_UINT32(wstat, struct lregs),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
+static const VMStateDescription vmstate_exynos4210_mct_lt = {
+ .name = "exynos4210.mct.lt",
+ .version_id = 1,
+ .minimum_version_id = 1,
+ .minimum_version_id_old = 1,
+ .fields = (VMStateField[]) {
+ VMSTATE_INT32(id, Exynos4210MCTLT),
+ VMSTATE_STRUCT(tick_timer, Exynos4210MCTLT, 0,
+ vmstate_tick_timer,
+ struct tick_timer),
+ VMSTATE_PTIMER(ptimer_frc, Exynos4210MCTLT),
+ VMSTATE_STRUCT(reg, Exynos4210MCTLT, 0,
+ vmstate_lregs,
+ struct lregs),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
+static const VMStateDescription vmstate_gregs = {
+ .name = "exynos4210.mct.lregs",
+ .version_id = 1,
+ .minimum_version_id = 1,
+ .minimum_version_id_old = 1,
+ .fields = (VMStateField[]) {
+ VMSTATE_UINT64(cnt, struct gregs),
+ VMSTATE_UINT32(cnt_wstat, struct gregs),
+ VMSTATE_UINT32(tcon, struct gregs),
+ VMSTATE_UINT32(int_cstat, struct gregs),
+ VMSTATE_UINT32(int_enb, struct gregs),
+ VMSTATE_UINT32(wstat, struct gregs),
+ VMSTATE_UINT64_ARRAY(comp, struct gregs, MCT_GT_CMP_NUM),
+ VMSTATE_UINT32_ARRAY(comp_add_incr, struct gregs,
+ MCT_GT_CMP_NUM),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
+static const VMStateDescription vmstate_exynos4210_mct_gt = {
+ .name = "exynos4210.mct.lt",
+ .version_id = 1,
+ .minimum_version_id = 1,
+ .minimum_version_id_old = 1,
+ .fields = (VMStateField[]) {
+ VMSTATE_STRUCT(reg, Exynos4210MCTGT, 0, vmstate_gregs,
+ struct gregs),
+ VMSTATE_UINT64(count, Exynos4210MCTGT),
+ VMSTATE_INT32(curr_comp, Exynos4210MCTGT),
+ VMSTATE_PTIMER(ptimer_frc, Exynos4210MCTGT),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
+static const VMStateDescription vmstate_exynos4210_mct_state = {
+ .name = "exynos4210.mct",
+ .version_id = 1,
+ .minimum_version_id = 1,
+ .minimum_version_id_old = 1,
+ .fields = (VMStateField[]) {
+ VMSTATE_UINT32(reg_mct_cfg, Exynos4210MCTState),
+ VMSTATE_STRUCT_ARRAY(l_timer, Exynos4210MCTState, 2, 0,
+ vmstate_exynos4210_mct_lt, Exynos4210MCTLT),
+ VMSTATE_STRUCT(g_timer, Exynos4210MCTState, 0,
+ vmstate_exynos4210_mct_gt, Exynos4210MCTGT),
+ VMSTATE_UINT32(freq, Exynos4210MCTState),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
+static void exynos4210_mct_update_freq(Exynos4210MCTState *s);
+
+/*
+ * Set counter of FRC global timer.
+ */
+static void exynos4210_gfrc_set_count(Exynos4210MCTGT *s, uint64_t count)
+{
+ s->count = count;
+ DPRINTF("global timer frc set count 0x%llx\n", count);
+ ptimer_set_count(s->ptimer_frc, count);
+}
+
+/*
+ * Get counter of FRC global timer.
+ */
+static uint64_t exynos4210_gfrc_get_count(Exynos4210MCTGT *s)
+{
+ uint64_t count = 0;
+ count = ptimer_get_count(s->ptimer_frc);
+ if (!count) {
+ /* Timer event was generated and s->reg.cnt holds adequate value */
+ return s->reg.cnt;
+ }
+ count = s->count - count;
+ return s->reg.cnt + count;
+}
+
+/*
+ * Stop global FRC timer
+ */
+static void exynos4210_gfrc_stop(Exynos4210MCTGT *s)
+{
+ DPRINTF("global timer frc stop\n");
+
+ ptimer_stop(s->ptimer_frc);
+}
+
+/*
+ * Start global FRC timer
+ */
+static void exynos4210_gfrc_start(Exynos4210MCTGT *s)
+{
+ DPRINTF("global timer frc start\n");
+
+ ptimer_run(s->ptimer_frc, 1);
+}
+
+/*
+ * Find next nearest Comparator. If current Comparator value equals to other
+ * Comparator value, skip them both
+ */
+static int32_t exynos4210_gcomp_find(Exynos4210MCTState *s)
+{
+ int res;
+ int i;
+ int enabled;
+ uint64_t min;
+ int min_comp_i;
+ uint64_t gfrc;
+ uint64_t distance;
+ uint64_t distance_min;
+ int comp_i;
+
+ /* get gfrc count */
+ gfrc = exynos4210_gfrc_get_count(&s->g_timer);
+
+ min = UINT64_MAX;
+ distance_min = UINT64_MAX;
+ comp_i = MCT_GT_CMP_NUM;
+ min_comp_i = MCT_GT_CMP_NUM;
+ enabled = 0;
+
+ /* lookup for nearest comparator */
+ for (i = 0; i < MCT_GT_CMP_NUM; i++) {
+
+ if (s->g_timer.reg.tcon & G_TCON_COMP_ENABLE(i)) {
+
+ enabled = 1;
+
+ if (s->g_timer.reg.comp[i] > gfrc) {
+ /* Comparator is upper then FRC */
+ distance = s->g_timer.reg.comp[i] - gfrc;
+
+ if (distance <= distance_min) {
+ distance_min = distance;
+ comp_i = i;
+ }
+ } else {
+ /* Comparator is below FRC, find the smallest */
+
+ if (s->g_timer.reg.comp[i] <= min) {
+ min = s->g_timer.reg.comp[i];
+ min_comp_i = i;
+ }
+ }
+ }
+ }
+
+ if (!enabled) {
+ /* All Comparators disabled */
+ res = -1;
+ } else if (comp_i < MCT_GT_CMP_NUM) {
+ /* Found upper Comparator */
+ res = comp_i;
+ } else {
+ /* All Comparators are below or equal to FRC */
+ res = min_comp_i;
+ }
+
+ DPRINTF("found comparator %d: comp 0x%llx distance 0x%llx, gfrc 0x%llx\n",
+ res,
+ s->g_timer.reg.comp[res],
+ distance_min,
+ gfrc);
+
+ return res;
+}
+
+/*
+ * Get distance to nearest Comparator
+ */
+static uint64_t exynos4210_gcomp_get_distance(Exynos4210MCTState *s, int32_t id)
+{
+ if (id == -1) {
+ /* no enabled Comparators, choose max distance */
+ return MCT_GT_COUNTER_STEP;
+ }
+ if (s->g_timer.reg.comp[id] - s->g_timer.reg.cnt < MCT_GT_COUNTER_STEP) {
+ return s->g_timer.reg.comp[id] - s->g_timer.reg.cnt;
+ } else {
+ return MCT_GT_COUNTER_STEP;
+ }
+}
+
+/*
+ * Restart global FRC timer
+ */
+static void exynos4210_gfrc_restart(Exynos4210MCTState *s)
+{
+ uint64_t distance;
+
+ exynos4210_gfrc_stop(&s->g_timer);
+
+ s->g_timer.curr_comp = exynos4210_gcomp_find(s);
+
+ distance = exynos4210_gcomp_get_distance(s, s->g_timer.curr_comp);
+
+ if (distance > MCT_GT_COUNTER_STEP || !distance) {
+ distance = MCT_GT_COUNTER_STEP;
+ }
+
+ exynos4210_gfrc_set_count(&s->g_timer, distance);
+ exynos4210_gfrc_start(&s->g_timer);
+}
+
+/*
+ * Raise global timer CMP IRQ
+ */
+static void exynos4210_gcomp_raise_irq(void *opaque, uint32_t id)
+{
+ Exynos4210MCTGT *s = opaque;
+
+ /* If CSTAT is pending and IRQ is enabled */
+ if ((s->reg.int_cstat & G_INT_CSTAT_COMP(id)) &&
+ (s->reg.int_enb & G_INT_ENABLE(id))) {
+ DPRINTF("gcmp timer[%d] IRQ\n", id);
+ qemu_irq_raise(s->irq[id]);
+ }
+}
+
+/*
+ * Lower global timer CMP IRQ
+ */
+static void exynos4210_gcomp_lower_irq(void *opaque, uint32_t id)
+{
+ Exynos4210MCTGT *s = opaque;
+ qemu_irq_lower(s->irq[id]);
+}
+
+/*
+ * Global timer FRC event handler.
+ * Each event occurs when internal counter reaches counter + MCT_GT_COUNTER_STEP
+ * Every time we arm global FRC timer to count for MCT_GT_COUNTER_STEP value
+ */
+static void exynos4210_gfrc_event(void *opaque)
+{
+ Exynos4210MCTState *s = (Exynos4210MCTState *)opaque;
+ int i;
+ uint64_t distance;
+
+ DPRINTF("\n");
+
+ s->g_timer.reg.cnt += s->g_timer.count;
+
+ /* Process all comparators */
+ for (i = 0; i < MCT_GT_CMP_NUM; i++) {
+
+ if (s->g_timer.reg.cnt == s->g_timer.reg.comp[i]) {
+ /* reached nearest comparator */
+
+ s->g_timer.reg.int_cstat |= G_INT_CSTAT_COMP(i);
+
+ /* Auto increment */
+ if (s->g_timer.reg.tcon & G_TCON_AUTO_ICREMENT(i)) {
+ s->g_timer.reg.comp[i] += s->g_timer.reg.comp_add_incr[i];
+ }
+
+ /* IRQ */
+ exynos4210_gcomp_raise_irq(&s->g_timer, i);
+ }
+ }
+
+ /* Reload FRC to reach nearest comparator */
+ s->g_timer.curr_comp = exynos4210_gcomp_find(s);
+ distance = exynos4210_gcomp_get_distance(s, s->g_timer.curr_comp);
+ if (distance > MCT_GT_COUNTER_STEP) {
+ distance = MCT_GT_COUNTER_STEP;
+ }
+ exynos4210_gfrc_set_count(&s->g_timer, distance);
+
+ exynos4210_gfrc_start(&s->g_timer);
+
+ return;
+}
+
+/*
+ * Get counter of FRC local timer.
+ */
+static uint64_t exynos4210_lfrc_get_count(Exynos4210MCTLT *s)
+{
+ return ptimer_get_count(s->ptimer_frc);
+}
+
+/*
+ * Set counter of FRC local timer.
+ */
+static void exynos4210_lfrc_update_count(Exynos4210MCTLT *s)
+{
+ if (!s->reg.cnt[L_REG_CNT_FRCCNTB]) {
+ ptimer_set_count(s->ptimer_frc, MCT_LT_COUNTER_STEP);
+ } else {
+ ptimer_set_count(s->ptimer_frc, s->reg.cnt[L_REG_CNT_FRCCNTB]);
+ }
+}
+
+/*
+ * Start local FRC timer
+ */
+static void exynos4210_lfrc_start(Exynos4210MCTLT *s)
+{
+ ptimer_run(s->ptimer_frc, 1);
+}
+
+/*
+ * Stop local FRC timer
+ */
+static void exynos4210_lfrc_stop(Exynos4210MCTLT *s)
+{
+ ptimer_stop(s->ptimer_frc);
+}
+
+/*
+ * Local timer free running counter tick handler
+ */
+static void exynos4210_lfrc_event(void *opaque)
+{
+ Exynos4210MCTLT * s = (Exynos4210MCTLT *)opaque;
+
+ /* local frc expired */
+
+ DPRINTF("\n");
+
+ s->reg.int_cstat |= L_INT_CSTAT_FRCCNT;
+
+ /* update frc counter */
+ exynos4210_lfrc_update_count(s);
+
+ /* raise irq */
+ if (s->reg.int_enb & L_INT_INTENB_FRCEIE) {
+ qemu_irq_raise(s->irq);
+ }
+
+ /* we reached here, this means that timer is enabled */
+ exynos4210_lfrc_start(s);
+}
+
+static uint32_t exynos4210_ltick_int_get_cnto(struct tick_timer *s);
+static uint32_t exynos4210_ltick_cnt_get_cnto(struct tick_timer *s);
+static void exynos4210_ltick_recalc_count(struct tick_timer *s);
+
+/*
+ * Action on enabling local tick int timer
+ */
+static void exynos4210_ltick_int_start(struct tick_timer *s)
+{
+ if (!s->int_run) {
+ s->int_run = 1;
+ }
+}
+
+/*
+ * Action on disabling local tick int timer
+ */
+static void exynos4210_ltick_int_stop(struct tick_timer *s)
+{
+ if (s->int_run) {
+ s->last_icnto = exynos4210_ltick_int_get_cnto(s);
+ s->int_run = 0;
+ }
+}
+
+/*
+ * Get count for INT timer
+ */
+static uint32_t exynos4210_ltick_int_get_cnto(struct tick_timer *s)
+{
+ uint32_t icnto;
+ uint64_t remain;
+ uint64_t count;
+ uint64_t counted;
+ uint64_t cur_progress;
+
+ count = ptimer_get_count(s->ptimer_tick);
+ if (count) {
+ /* timer is still counting, called not from event */
+ counted = s->count - ptimer_get_count(s->ptimer_tick);
+ cur_progress = s->progress + counted;
+ } else {
+ /* timer expired earlier */
+ cur_progress = s->progress;
+ }
+
+ remain = s->distance - cur_progress;
+
+ if (!s->int_run) {
+ /* INT is stopped. */
+ icnto = s->last_icnto;
+ } else {
+ /* Both are counting */
+ icnto = remain / s->tcntb;
+ }
+
+ return icnto;
+}
+
+/*
+ * Start local tick cnt timer.
+ */
+static void exynos4210_ltick_cnt_start(struct tick_timer *s)
+{
+ if (!s->cnt_run) {
+
+ exynos4210_ltick_recalc_count(s);
+ ptimer_set_count(s->ptimer_tick, s->count);
+ ptimer_run(s->ptimer_tick, 1);
+
+ s->cnt_run = 1;
+ }
+}
+
+/*
+ * Stop local tick cnt timer.
+ */
+static void exynos4210_ltick_cnt_stop(struct tick_timer *s)
+{
+ if (s->cnt_run) {
+
+ s->last_tcnto = exynos4210_ltick_cnt_get_cnto(s);
+
+ if (s->int_run) {
+ exynos4210_ltick_int_stop(s);
+ }
+
+ ptimer_stop(s->ptimer_tick);
+
+ s->cnt_run = 0;
+ }
+}
+
+/*
+ * Get counter for CNT timer
+ */
+static uint32_t exynos4210_ltick_cnt_get_cnto(struct tick_timer *s)
+{
+ uint32_t tcnto;
+ uint32_t icnto;
+ uint64_t remain;
+ uint64_t counted;
+ uint64_t count;
+ uint64_t cur_progress;
+
+ count = ptimer_get_count(s->ptimer_tick);
+ if (count) {
+ /* timer is still counting, called not from event */
+ counted = s->count - ptimer_get_count(s->ptimer_tick);
+ cur_progress = s->progress + counted;
+ } else {
+ /* timer expired earlier */
+ cur_progress = s->progress;
+ }
+
+ remain = s->distance - cur_progress;
+
+ if (!s->cnt_run) {
+ /* Both are stopped. */
+ tcnto = s->last_tcnto;
+ } else if (!s->int_run) {
+ /* INT counter is stopped, progress is by CNT timer */
+ tcnto = remain % s->tcntb;
+ } else {
+ /* Both are counting */
+ icnto = remain / s->tcntb;
+ if (icnto) {
+ tcnto = remain % (icnto * s->tcntb);
+ } else {
+ tcnto = remain % s->tcntb;
+ }
+ }
+
+ return tcnto;
+}
+
+/*
+ * Set new values of counters for CNT and INT timers
+ */
+static void exynos4210_ltick_set_cntb(struct tick_timer *s, uint32_t new_cnt,
+ uint32_t new_int)
+{
+ uint32_t cnt_stopped = 0;
+ uint32_t int_stopped = 0;
+
+ if (s->cnt_run) {
+ exynos4210_ltick_cnt_stop(s);
+ cnt_stopped = 1;
+ }
+
+ if (s->int_run) {
+ exynos4210_ltick_int_stop(s);
+ int_stopped = 1;
+ }
+
+ s->tcntb = new_cnt + 1;
+ s->icntb = new_int + 1;
+
+ if (cnt_stopped) {
+ exynos4210_ltick_cnt_start(s);
+ }
+ if (int_stopped) {
+ exynos4210_ltick_int_start(s);
+ }
+
+}
+
+/*
+ * Calculate new counter value for tick timer
+ */
+static void exynos4210_ltick_recalc_count(struct tick_timer *s)
+{
+ uint64_t to_count;
+
+ if ((s->cnt_run && s->last_tcnto) || (s->int_run && s->last_icnto)) {
+ /*
+ * one or both timers run and not counted to the end;
+ * distance is not passed, recalculate with last_tcnto * last_icnto
+ */
+
+ if (s->last_tcnto) {
+ to_count = s->last_tcnto * s->last_icnto;
+ } else {
+ to_count = s->last_icnto;
+ }
+ } else {
+ /* distance is passed, recalculate with tcnto * icnto */
+ if (s->icntb) {
+ s->distance = s->tcntb * s->icntb;
+ } else {
+ s->distance = s->tcntb;
+ }
+
+ to_count = s->distance;
+ s->progress = 0;
+ }
+
+ if (to_count > MCT_LT_COUNTER_STEP) {
+ /* count by step */
+ s->count = MCT_LT_COUNTER_STEP;
+ } else {
+ s->count = to_count;
+ }
+}
+
+/*
+ * Initialize tick_timer
+ */
+static void exynos4210_ltick_timer_init(struct tick_timer *s)
+{
+ exynos4210_ltick_int_stop(s);
+ exynos4210_ltick_cnt_stop(s);
+
+ s->count = 0;
+ s->distance = 0;
+ s->progress = 0;
+ s->icntb = 0;
+ s->tcntb = 0;
+}
+
+/*
+ * tick_timer event.
+ * Raises when abstract tick_timer expires.
+ */
+static void exynos4210_ltick_timer_event(struct tick_timer *s)
+{
+ s->progress += s->count;
+}
+
+/*
+ * Local timer tick counter handler.
+ * Don't use reloaded timers. If timer counter = zero
+ * then handler called but after handler finished no
+ * timer reload occurs.
+ */
+static void exynos4210_ltick_event(void *opaque)
+{
+ Exynos4210MCTLT * s = (Exynos4210MCTLT *)opaque;
+ uint32_t tcnto;
+ uint32_t icnto;
+#ifdef DEBUG_MCT
+ static uint64_t time1[2] = {0};
+ static uint64_t time2[2] = {0};
+#endif
+
+ /* Call tick_timer event handler, it will update it's tcntb and icntb */
+ exynos4210_ltick_timer_event(&s->tick_timer);
+
+ /* get tick_timer cnt */
+ tcnto = exynos4210_ltick_cnt_get_cnto(&s->tick_timer);
+
+ /* get tick_timer int */
+ icnto = exynos4210_ltick_int_get_cnto(&s->tick_timer);
+
+ /* raise IRQ if needed */
+ if (!icnto && s->reg.tcon & L_TCON_INT_START) {
+ /* INT counter enabled and expired */
+
+ s->reg.int_cstat |= L_INT_CSTAT_INTCNT;
+
+ /* raise interrupt if enabled */
+ if (s->reg.int_enb & L_INT_INTENB_ICNTEIE) {
+#ifdef DEBUG_MCT
+ time2[s->id] = qemu_get_clock_ns(vm_clock);
+ DPRINTF("local timer[%d] IRQ: %llx\n", s->id,
+ time2[s->id] - time1[s->id]);
+ time1[s->id] = time2[s->id];
+#endif
+ qemu_irq_raise(s->irq);
+ }
+
+ /* reload ICNTB */
+ if (s->reg.tcon & L_TCON_INTERVAL_MODE) {
+ exynos4210_ltick_set_cntb(&s->tick_timer,
+ s->reg.cnt[L_REG_CNT_TCNTB],
+ s->reg.cnt[L_REG_CNT_ICNTB]);
+ }
+ } else {
+ /* reload TCNTB */
+ if (!tcnto) {
+ exynos4210_ltick_set_cntb(&s->tick_timer,
+ s->reg.cnt[L_REG_CNT_TCNTB],
+ icnto);
+ }
+ }
+
+ /* start tick_timer cnt */
+ exynos4210_ltick_cnt_start(&s->tick_timer);
+
+ /* start tick_timer int */
+ exynos4210_ltick_int_start(&s->tick_timer);
+}
+
+/* update timer frequency */
+static void exynos4210_mct_update_freq(Exynos4210MCTState *s)
+{
+ uint32_t freq = s->freq;
+ s->freq = 24000000 /
+ ((MCT_CFG_GET_PRESCALER(s->reg_mct_cfg)+1) *
+ MCT_CFG_GET_DIVIDER(s->reg_mct_cfg));
+
+ if (freq != s->freq) {
+ DPRINTF("freq=%dHz\n", s->freq);
+
+ /* global timer */
+ ptimer_set_freq(s->g_timer.ptimer_frc, s->freq);
+
+ /* local timer */
+ ptimer_set_freq(s->l_timer[0].tick_timer.ptimer_tick, s->freq);
+ ptimer_set_freq(s->l_timer[0].ptimer_frc, s->freq);
+ ptimer_set_freq(s->l_timer[1].tick_timer.ptimer_tick, s->freq);
+ ptimer_set_freq(s->l_timer[1].ptimer_frc, s->freq);
+ }
+}
+
+/* set defaul_timer values for all fields */
+static void exynos4210_mct_reset(DeviceState *d)
+{
+ Exynos4210MCTState *s = (Exynos4210MCTState *)d;
+ uint32_t i;
+
+ s->reg_mct_cfg = 0;
+
+ /* global timer */
+ memset(&s->g_timer.reg, 0, sizeof(s->g_timer.reg));
+ exynos4210_gfrc_stop(&s->g_timer);
+
+ /* local timer */
+ memset(s->l_timer[0].reg.cnt, 0, sizeof(s->l_timer[0].reg.cnt));
+ memset(s->l_timer[1].reg.cnt, 0, sizeof(s->l_timer[1].reg.cnt));
+ for (i = 0; i < 2; i++) {
+ s->l_timer[i].reg.int_cstat = 0;
+ s->l_timer[i].reg.int_enb = 0;
+ s->l_timer[i].reg.tcon = 0;
+ s->l_timer[i].reg.wstat = 0;
+ s->l_timer[i].tick_timer.count = 0;
+ s->l_timer[i].tick_timer.distance = 0;
+ s->l_timer[i].tick_timer.progress = 0;
+ ptimer_stop(s->l_timer[i].ptimer_frc);
+
+ exynos4210_ltick_timer_init(&s->l_timer[i].tick_timer);
+ }
+
+ exynos4210_mct_update_freq(s);
+
+}
+
+/* Multi Core Timer read */
+static uint64_t exynos4210_mct_read(void *opaque, target_phys_addr_t offset,
+ unsigned size)
+{
+ Exynos4210MCTState *s = (Exynos4210MCTState *)opaque;
+ int index;
+ int shift;
+ uint64_t count;
+ uint32_t value;
+ int lt_i;
+
+ switch (offset) {
+
+ case MCT_CFG:
+ value = s->reg_mct_cfg;
+ break;
+
+ case G_CNT_L: case G_CNT_U:
+ shift = 8 * (offset & 0x4);
+ count = exynos4210_gfrc_get_count(&s->g_timer);
+ value = UINT32_MAX & (count >> shift);
+ DPRINTF("read FRC=0x%llx\n", count);
+ break;
+
+ case G_CNT_WSTAT:
+ value = s->g_timer.reg.cnt_wstat;
+ break;
+
+ case G_COMP_L(0): case G_COMP_L(1): case G_COMP_L(2): case G_COMP_L(3):
+ case G_COMP_U(0): case G_COMP_U(1): case G_COMP_U(2): case G_COMP_U(3):
+ index = GET_G_COMP_IDX(offset);
+ shift = 8 * (offset & 0x4);
+ value = UINT32_MAX & (s->g_timer.reg.comp[index] >> shift);
+ break;
+
+ case G_TCON:
+ value = s->g_timer.reg.tcon;
+ break;
+
+ case G_INT_CSTAT:
+ value = s->g_timer.reg.int_cstat;
+ break;
+
+ case G_INT_ENB:
+ value = s->g_timer.reg.int_enb;
+ break;
+ break;
+ case G_WSTAT:
+ value = s->g_timer.reg.wstat;
+ break;
+
+ case G_COMP0_ADD_INCR: case G_COMP1_ADD_INCR:
+ case G_COMP2_ADD_INCR: case G_COMP3_ADD_INCR:
+ value = s->g_timer.reg.comp_add_incr[GET_G_COMP_ADD_INCR_IDX(offset)];
+ break;
+
+ /* Local timers */
+ case L0_TCNTB: case L0_ICNTB: case L0_FRCNTB:
+ case L1_TCNTB: case L1_ICNTB: case L1_FRCNTB:
+ lt_i = GET_L_TIMER_IDX(offset);
+ index = GET_L_TIMER_CNT_REG_IDX(offset, lt_i);
+ value = s->l_timer[lt_i].reg.cnt[index];
+ break;
+
+ case L0_TCNTO: case L1_TCNTO:
+ lt_i = GET_L_TIMER_IDX(offset);
+
+ value = exynos4210_ltick_cnt_get_cnto(&s->l_timer[lt_i].tick_timer);
+ DPRINTF("local timer[%d] read TCNTO %x\n", lt_i, value);
+ break;
+
+ case L0_ICNTO: case L1_ICNTO:
+ lt_i = GET_L_TIMER_IDX(offset);
+
+ value = exynos4210_ltick_int_get_cnto(&s->l_timer[lt_i].tick_timer);
+ DPRINTF("local timer[%d] read ICNTO %x\n", lt_i, value);
+ break;
+
+ case L0_FRCNTO: case L1_FRCNTO:
+ lt_i = GET_L_TIMER_IDX(offset);
+
+ value = exynos4210_lfrc_get_count(&s->l_timer[lt_i]);
+
+ break;
+
+ case L0_TCON: case L1_TCON:
+ lt_i = ((offset & 0xF00) - L0_TCNTB) / 0x100;
+ value = s->l_timer[lt_i].reg.tcon;
+ break;
+
+ case L0_INT_CSTAT: case L1_INT_CSTAT:
+ lt_i = ((offset & 0xF00) - L0_TCNTB) / 0x100;
+ value = s->l_timer[lt_i].reg.int_cstat;
+ break;
+
+ case L0_INT_ENB: case L1_INT_ENB:
+ lt_i = ((offset & 0xF00) - L0_TCNTB) / 0x100;
+ value = s->l_timer[lt_i].reg.int_enb;
+ break;
+
+ case L0_WSTAT: case L1_WSTAT:
+ lt_i = ((offset & 0xF00) - L0_TCNTB) / 0x100;
+ value = s->l_timer[lt_i].reg.wstat;
+ break;
+
+ default:
+ hw_error("exynos4210.mct: bad read offset "
+ TARGET_FMT_plx "\n", offset);
+ break;
+ }
+ return value;
+}
+
+/* MCT write */
+static void exynos4210_mct_write(void *opaque, target_phys_addr_t offset,
+ uint64_t value, unsigned size)
+{
+ Exynos4210MCTState *s = (Exynos4210MCTState *)opaque;
+ int index; /* index in buffer which represents register set */
+ int shift;
+ int lt_i;
+ uint64_t new_frc;
+ uint32_t i;
+ uint32_t old_val;
+#ifdef DEBUG_MCT
+ static uint32_t icntb_max[2] = {0};
+ static uint32_t icntb_min[2] = {UINT32_MAX, UINT32_MAX};
+ static uint32_t tcntb_max[2] = {0};
+ static uint32_t tcntb_min[2] = {UINT32_MAX, UINT32_MAX};
+#endif
+
+ new_frc = s->g_timer.reg.cnt;
+
+ switch (offset) {
+
+ case MCT_CFG:
+ s->reg_mct_cfg = value;
+ exynos4210_mct_update_freq(s);
+ break;
+
+ case G_CNT_L:
+ case G_CNT_U:
+ if (offset == G_CNT_L) {
+
+ DPRINTF("global timer write to reg.cntl %llx\n", value);
+
+ new_frc = (s->g_timer.reg.cnt & (uint64_t)UINT32_MAX << 32) + value;
+ s->g_timer.reg.cnt_wstat |= G_CNT_WSTAT_L;
+ }
+ if (offset == G_CNT_U) {
+
+ DPRINTF("global timer write to reg.cntu %llx\n", value);
+
+ new_frc = (s->g_timer.reg.cnt & UINT32_MAX) +
+ ((uint64_t)value << 32);
+ s->g_timer.reg.cnt_wstat |= G_CNT_WSTAT_U;
+ }
+
+ s->g_timer.reg.cnt = new_frc;
+ exynos4210_gfrc_restart(s);
+ break;
+
+ case G_CNT_WSTAT:
+ s->g_timer.reg.cnt_wstat &= ~(value);
+ break;
+
+ case G_COMP_L(0): case G_COMP_L(1): case G_COMP_L(2): case G_COMP_L(3):
+ case G_COMP_U(0): case G_COMP_U(1): case G_COMP_U(2): case G_COMP_U(3):
+ index = GET_G_COMP_IDX(offset);
+ shift = 8 * (offset & 0x4);
+ s->g_timer.reg.comp[index] =
+ (s->g_timer.reg.comp[index] &
+ (((uint64_t)UINT32_MAX << 32) >> shift)) +
+ (value << shift);
+
+ DPRINTF("comparator %d write 0x%llx val << %d\n", index, value, shift);
+
+ if (offset&0x4) {
+ s->g_timer.reg.wstat |= G_WSTAT_COMP_U(index);
+ } else {
+ s->g_timer.reg.wstat |= G_WSTAT_COMP_L(index);
+ }
+
+ exynos4210_gfrc_restart(s);
+ break;
+
+ case G_TCON:
+ old_val = s->g_timer.reg.tcon;
+ s->g_timer.reg.tcon = value;
+ s->g_timer.reg.wstat |= G_WSTAT_TCON_WRITE;
+
+ DPRINTF("global timer write to reg.g_tcon %llx\n", value);
+
+ /* Start FRC if transition from disabled to enabled */
+ if ((value & G_TCON_TIMER_ENABLE) > (old_val &
+ G_TCON_TIMER_ENABLE)) {
+ exynos4210_gfrc_start(&s->g_timer);
+ }
+ if ((value & G_TCON_TIMER_ENABLE) < (old_val &
+ G_TCON_TIMER_ENABLE)) {
+ exynos4210_gfrc_stop(&s->g_timer);
+ }
+
+ /* Start CMP if transition from disabled to enabled */
+ for (i = 0; i < MCT_GT_CMP_NUM; i++) {
+ if ((value & G_TCON_COMP_ENABLE(i)) != (old_val &
+ G_TCON_COMP_ENABLE(i))) {
+ exynos4210_gfrc_restart(s);
+ }
+ }
+ break;
+
+ case G_INT_CSTAT:
+ s->g_timer.reg.int_cstat &= ~(value);
+ for (i = 0; i < MCT_GT_CMP_NUM; i++) {
+ if (value & G_INT_CSTAT_COMP(i)) {
+ exynos4210_gcomp_lower_irq(&s->g_timer, i);
+ }
+ }
+ break;
+
+ case G_INT_ENB:
+
+ /* Raise IRQ if transition from disabled to enabled and CSTAT pending */
+ for (i = 0; i < MCT_GT_CMP_NUM; i++) {
+ if ((value & G_INT_ENABLE(i)) > (s->g_timer.reg.tcon &
+ G_INT_ENABLE(i))) {
+ if (s->g_timer.reg.int_cstat & G_INT_CSTAT_COMP(i)) {
+ exynos4210_gcomp_raise_irq(&s->g_timer, i);
+ }
+ }
+
+ if ((value & G_INT_ENABLE(i)) < (s->g_timer.reg.tcon &
+ G_INT_ENABLE(i))) {
+ exynos4210_gcomp_lower_irq(&s->g_timer, i);
+ }
+ }
+
+ DPRINTF("global timer INT enable %llx\n", value);
+ s->g_timer.reg.int_enb = value;
+ break;
+
+ case G_WSTAT:
+ s->g_timer.reg.wstat &= ~(value);
+ break;
+
+ case G_COMP0_ADD_INCR: case G_COMP1_ADD_INCR:
+ case G_COMP2_ADD_INCR: case G_COMP3_ADD_INCR:
+ index = GET_G_COMP_ADD_INCR_IDX(offset);
+ s->g_timer.reg.comp_add_incr[index] = value;
+ s->g_timer.reg.wstat |= G_WSTAT_COMP_ADDINCR(index);
+ break;
+
+ /* Local timers */
+ case L0_TCON: case L1_TCON:
+ lt_i = GET_L_TIMER_IDX(offset);
+ old_val = s->l_timer[lt_i].reg.tcon;
+
+ s->l_timer[lt_i].reg.wstat |= L_WSTAT_TCON_WRITE;
+ s->l_timer[lt_i].reg.tcon = value;
+
+ /* Stop local CNT */
+ if ((value & L_TCON_TICK_START) <
+ (old_val & L_TCON_TICK_START)) {
+ DPRINTF("local timer[%d] stop cnt\n", lt_i);
+ exynos4210_ltick_cnt_stop(&s->l_timer[lt_i].tick_timer);
+ }
+
+ /* Stop local INT */
+ if ((value & L_TCON_INT_START) <
+ (old_val & L_TCON_INT_START)) {
+ DPRINTF("local timer[%d] stop int\n", lt_i);
+ exynos4210_ltick_int_stop(&s->l_timer[lt_i].tick_timer);
+ }
+
+ /* Start local CNT */
+ if ((value & L_TCON_TICK_START) >
+ (old_val & L_TCON_TICK_START)) {
+ DPRINTF("local timer[%d] start cnt\n", lt_i);
+ exynos4210_ltick_cnt_start(&s->l_timer[lt_i].tick_timer);
+ }
+
+ /* Start local INT */
+ if ((value & L_TCON_INT_START) >
+ (old_val & L_TCON_INT_START)) {
+ DPRINTF("local timer[%d] start int\n", lt_i);
+ exynos4210_ltick_int_start(&s->l_timer[lt_i].tick_timer);
+ }
+
+ /* Start or Stop local FRC if TCON changed */
+ if ((value & L_TCON_FRC_START) >
+ (s->l_timer[lt_i].reg.tcon & L_TCON_FRC_START)) {
+ DPRINTF("local timer[%d] start frc\n", lt_i);
+ exynos4210_lfrc_start(&s->l_timer[lt_i]);
+ }
+ if ((value & L_TCON_FRC_START) <
+ (s->l_timer[lt_i].reg.tcon & L_TCON_FRC_START)) {
+ DPRINTF("local timer[%d] stop frc\n", lt_i);
+ exynos4210_lfrc_stop(&s->l_timer[lt_i]);
+ }
+ break;
+
+ case L0_TCNTB: case L1_TCNTB:
+
+ lt_i = GET_L_TIMER_IDX(offset);
+ index = GET_L_TIMER_CNT_REG_IDX(offset, lt_i);
+
+ /*
+ * TCNTB is updated to internal register only after CNT expired.
+ * Due to this we should reload timer to nearest moment when CNT is
+ * expired and then in event handler update tcntb to new TCNTB value.
+ */
+ exynos4210_ltick_set_cntb(&s->l_timer[lt_i].tick_timer, value,
+ s->l_timer[lt_i].tick_timer.icntb);
+
+ s->l_timer[lt_i].reg.wstat |= L_WSTAT_TCNTB_WRITE;
+ s->l_timer[lt_i].reg.cnt[L_REG_CNT_TCNTB] = value;
+
+#ifdef DEBUG_MCT
+ if (tcntb_min[lt_i] > value) {
+ tcntb_min[lt_i] = value;
+ }
+ if (tcntb_max[lt_i] < value) {
+ tcntb_max[lt_i] = value;
+ }
+ DPRINTF("local timer[%d] TCNTB write %llx; max=%x, min=%x\n",
+ lt_i, value, tcntb_max[lt_i], tcntb_min[lt_i]);
+#endif
+ break;
+
+ case L0_ICNTB: case L1_ICNTB:
+
+ lt_i = GET_L_TIMER_IDX(offset);
+ index = GET_L_TIMER_CNT_REG_IDX(offset, lt_i);
+
+ s->l_timer[lt_i].reg.wstat |= L_WSTAT_ICNTB_WRITE;
+ s->l_timer[lt_i].reg.cnt[L_REG_CNT_ICNTB] = value &
+ ~L_ICNTB_MANUAL_UPDATE;
+
+ /*
+ * We need to avoid too small values for TCNTB*ICNTB. If not, IRQ event
+ * could raise too fast disallowing QEMU to execute target code.
+ */
+ if (s->l_timer[lt_i].reg.cnt[L_REG_CNT_ICNTB] *
+ s->l_timer[lt_i].reg.cnt[L_REG_CNT_TCNTB] < MCT_LT_CNT_LOW_LIMIT) {
+ if (!s->l_timer[lt_i].reg.cnt[L_REG_CNT_TCNTB]) {
+ s->l_timer[lt_i].reg.cnt[L_REG_CNT_ICNTB] =
+ MCT_LT_CNT_LOW_LIMIT;
+ } else {
+ s->l_timer[lt_i].reg.cnt[L_REG_CNT_ICNTB] =
+ MCT_LT_CNT_LOW_LIMIT /
+ s->l_timer[lt_i].reg.cnt[L_REG_CNT_TCNTB];
+ }
+ }
+
+ if (value & L_ICNTB_MANUAL_UPDATE) {
+ exynos4210_ltick_set_cntb(&s->l_timer[lt_i].tick_timer,
+ s->l_timer[lt_i].tick_timer.tcntb,
+ s->l_timer[lt_i].reg.cnt[L_REG_CNT_ICNTB]);
+ }
+
+#ifdef DEBUG_MCT
+ if (icntb_min[lt_i] > value) {
+ icntb_min[lt_i] = value;
+ }
+ if (icntb_max[lt_i] < value) {
+ icntb_max[lt_i] = value;
+ }
+DPRINTF("local timer[%d] ICNTB write %llx; max=%x, min=%x\n\n",
+ lt_i, value, icntb_max[lt_i], icntb_min[lt_i]);
+#endif
+break;
+
+ case L0_FRCNTB: case L1_FRCNTB:
+
+ lt_i = GET_L_TIMER_IDX(offset);
+ index = GET_L_TIMER_CNT_REG_IDX(offset, lt_i);
+
+ DPRINTF("local timer[%d] FRCNTB write %llx\n", lt_i, value);
+
+ s->l_timer[lt_i].reg.wstat |= L_WSTAT_FRCCNTB_WRITE;
+ s->l_timer[lt_i].reg.cnt[L_REG_CNT_FRCCNTB] = value;
+
+ break;
+
+ case L0_TCNTO: case L1_TCNTO:
+ case L0_ICNTO: case L1_ICNTO:
+ case L0_FRCNTO: case L1_FRCNTO:
+ fprintf(stderr, "\n[exynos4210.mct: write to RO register "
+ TARGET_FMT_plx "]\n\n", offset);
+ break;
+
+ case L0_INT_CSTAT: case L1_INT_CSTAT:
+ lt_i = GET_L_TIMER_IDX(offset);
+
+ DPRINTF("local timer[%d] CSTAT write %llx\n", lt_i, value);
+
+ s->l_timer[lt_i].reg.int_cstat &= ~value;
+ if (!s->l_timer[lt_i].reg.int_cstat) {
+ qemu_irq_lower(s->l_timer[lt_i].irq);
+ }
+ break;
+
+ case L0_INT_ENB: case L1_INT_ENB:
+ lt_i = GET_L_TIMER_IDX(offset);
+ old_val = s->l_timer[lt_i].reg.int_enb;
+
+ /* Raise Local timer IRQ if cstat is pending */
+ if ((value & L_INT_INTENB_ICNTEIE) > (old_val & L_INT_INTENB_ICNTEIE)) {
+ if (s->l_timer[lt_i].reg.int_cstat & L_INT_CSTAT_INTCNT) {
+ qemu_irq_raise(s->l_timer[lt_i].irq);
+ }
+ }
+
+ s->l_timer[lt_i].reg.int_enb = value;
+
+ break;
+
+ case L0_WSTAT: case L1_WSTAT:
+ lt_i = GET_L_TIMER_IDX(offset);
+
+ s->l_timer[lt_i].reg.wstat &= ~value;
+ break;
+
+ default:
+ hw_error("exynos4210.mct: bad write offset "
+ TARGET_FMT_plx "\n", offset);
+ break;
+ }
+}
+
+static const MemoryRegionOps exynos4210_mct_ops = {
+ .read = exynos4210_mct_read,
+ .write = exynos4210_mct_write,
+ .endianness = DEVICE_NATIVE_ENDIAN,
+};
+
+/* MCT init */
+static int exynos4210_mct_init(SysBusDevice *dev)
+{
+ int i;
+ Exynos4210MCTState *s = FROM_SYSBUS(Exynos4210MCTState, dev);
+ QEMUBH *bh[2];
+
+ /* Global timer */
+ bh[0] = qemu_bh_new(exynos4210_gfrc_event, s);
+ s->g_timer.ptimer_frc = ptimer_init(bh[0]);
+ memset(&s->g_timer.reg, 0, sizeof(struct gregs));
+
+ /* Local timers */
+ for (i = 0; i < 2; i++) {
+ bh[0] = qemu_bh_new(exynos4210_ltick_event, &s->l_timer[i]);
+ bh[1] = qemu_bh_new(exynos4210_lfrc_event, &s->l_timer[i]);
+ s->l_timer[i].tick_timer.ptimer_tick = ptimer_init(bh[0]);
+ s->l_timer[i].ptimer_frc = ptimer_init(bh[1]);
+ s->l_timer[i].id = i;
+ }
+
+ /* IRQs */
+ for (i = 0; i < MCT_GT_CMP_NUM; i++) {
+ sysbus_init_irq(dev, &s->g_timer.irq[i]);
+ }
+ for (i = 0; i < 2; i++) {
+ sysbus_init_irq(dev, &s->l_timer[i].irq);
+ }
+
+ memory_region_init_io(&s->iomem, &exynos4210_mct_ops, s, "exynos4210-mct",
+ MCT_SFR_SIZE);
+ sysbus_init_mmio(dev, &s->iomem);
+
+ return 0;
+}
+
+static void exynos4210_mct_class_init(ObjectClass *klass, void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(klass);
+ SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass);
+
+ k->init = exynos4210_mct_init;
+ dc->reset = exynos4210_mct_reset;
+ dc->vmsd = &vmstate_exynos4210_mct_state;
+}
+
+static TypeInfo exynos4210_mct_info = {
+ .name = "exynos4210.mct",
+ .parent = TYPE_SYS_BUS_DEVICE,
+ .instance_size = sizeof(Exynos4210MCTState),
+ .class_init = exynos4210_mct_class_init,
+};
+
+static void exynos4210_mct_register_types(void)
+{
+ type_register_static(&exynos4210_mct_info);
+}
+
+type_init(exynos4210_mct_register_types)
diff --git a/hw/exynos4210_pmu.c b/hw/exynos4210_pmu.c
new file mode 100644
index 0000000000..c12d7501cc
--- /dev/null
+++ b/hw/exynos4210_pmu.c
@@ -0,0 +1,499 @@
+/*
+ * Exynos4210 Power Management Unit (PMU) Emulation
+ *
+ * Copyright (C) 2011 Samsung Electronics Co Ltd.
+ * Maksim Kozlov <m.kozlov@samsung.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+/*
+ * This model implements PMU registers just as a bulk of memory. Currently,
+ * the only reason this device exists is that secondary CPU boot loader
+ * uses PMU INFORM5 register as a holding pen.
+ */
+
+#include "sysbus.h"
+
+#ifndef DEBUG_PMU
+#define DEBUG_PMU 0
+#endif
+
+#ifndef DEBUG_PMU_EXTEND
+#define DEBUG_PMU_EXTEND 0
+#endif
+
+#if DEBUG_PMU
+#define PRINT_DEBUG(fmt, args...) \
+ do { \
+ fprintf(stderr, " [%s:%d] "fmt, __func__, __LINE__, ##args); \
+ } while (0)
+
+#if DEBUG_PMU_EXTEND
+#define PRINT_DEBUG_EXTEND(fmt, args...) \
+ do { \
+ fprintf(stderr, " [%s:%d] "fmt, __func__, __LINE__, ##args); \
+ } while (0)
+#else
+#define PRINT_DEBUG_EXTEND(fmt, args...) do {} while (0)
+#endif /* EXTEND */
+
+#else
+#define PRINT_DEBUG(fmt, args...) do {} while (0)
+#define PRINT_DEBUG_EXTEND(fmt, args...) do {} while (0)
+#endif
+
+/*
+ * Offsets for PMU registers
+ */
+#define OM_STAT 0x0000 /* OM status register */
+#define RTC_CLKO_SEL 0x000C /* Controls RTCCLKOUT */
+#define GNSS_RTC_OUT_CTRL 0x0010 /* Controls GNSS_RTC_OUT */
+/* Decides whether system-level low-power mode is used. */
+#define SYSTEM_POWER_DOWN_CTRL 0x0200
+/* Sets control options for CENTRAL_SEQ */
+#define SYSTEM_POWER_DOWN_OPTION 0x0208
+#define SWRESET 0x0400 /* Generate software reset */
+#define RST_STAT 0x0404 /* Reset status register */
+#define WAKEUP_STAT 0x0600 /* Wakeup status register */
+#define EINT_WAKEUP_MASK 0x0604 /* Configure External INTerrupt mask */
+#define WAKEUP_MASK 0x0608 /* Configure wakeup source mask */
+#define HDMI_PHY_CONTROL 0x0700 /* HDMI PHY control register */
+#define USBDEVICE_PHY_CONTROL 0x0704 /* USB Device PHY control register */
+#define USBHOST_PHY_CONTROL 0x0708 /* USB HOST PHY control register */
+#define DAC_PHY_CONTROL 0x070C /* DAC control register */
+#define MIPI_PHY0_CONTROL 0x0710 /* MIPI PHY control register */
+#define MIPI_PHY1_CONTROL 0x0714 /* MIPI PHY control register */
+#define ADC_PHY_CONTROL 0x0718 /* TS-ADC control register */
+#define PCIe_PHY_CONTROL 0x071C /* TS-PCIe control register */
+#define SATA_PHY_CONTROL 0x0720 /* TS-SATA control register */
+#define INFORM0 0x0800 /* Information register 0 */
+#define INFORM1 0x0804 /* Information register 1 */
+#define INFORM2 0x0808 /* Information register 2 */
+#define INFORM3 0x080C /* Information register 3 */
+#define INFORM4 0x0810 /* Information register 4 */
+#define INFORM5 0x0814 /* Information register 5 */
+#define INFORM6 0x0818 /* Information register 6 */
+#define INFORM7 0x081C /* Information register 7 */
+#define PMU_DEBUG 0x0A00 /* PMU debug register */
+/* Registers to set system-level low-power option */
+#define ARM_CORE0_SYS_PWR_REG 0x1000
+#define ARM_CORE1_SYS_PWR_REG 0x1010
+#define ARM_COMMON_SYS_PWR_REG 0x1080
+#define ARM_CPU_L2_0_SYS_PWR_REG 0x10C0
+#define ARM_CPU_L2_1_SYS_PWR_REG 0x10C4
+#define CMU_ACLKSTOP_SYS_PWR_REG 0x1100
+#define CMU_SCLKSTOP_SYS_PWR_REG 0x1104
+#define CMU_RESET_SYS_PWR_REG 0x110C
+#define APLL_SYSCLK_SYS_PWR_REG 0x1120
+#define MPLL_SYSCLK_SYS_PWR_REG 0x1124
+#define VPLL_SYSCLK_SYS_PWR_REG 0x1128
+#define EPLL_SYSCLK_SYS_PWR_REG 0x112C
+#define CMU_CLKSTOP_GPS_ALIVE_SYS_PWR_REG 0x1138
+#define CMU_RESET_GPS_ALIVE_SYS_PWR_REG 0x113C
+#define CMU_CLKSTOP_CAM_SYS_PWR_REG 0x1140
+#define CMU_CLKSTOP_TV_SYS_PWR_REG 0x1144
+#define CMU_CLKSTOP_MFC_SYS_PWR_REG 0x1148
+#define CMU_CLKSTOP_G3D_SYS_PWR_REG 0x114C
+#define CMU_CLKSTOP_LCD0_SYS_PWR_REG 0x1150
+#define CMU_CLKSTOP_LCD1_SYS_PWR_REG 0x1154
+#define CMU_CLKSTOP_MAUDIO_SYS_PWR_REG 0x1158
+#define CMU_CLKSTOP_GPS_SYS_PWR_REG 0x115C
+#define CMU_RESET_CAM_SYS_PWR_REG 0x1160
+#define CMU_RESET_TV_SYS_PWR_REG 0x1164
+#define CMU_RESET_MFC_SYS_PWR_REG 0x1168
+#define CMU_RESET_G3D_SYS_PWR_REG 0x116C
+#define CMU_RESET_LCD0_SYS_PWR_REG 0x1170
+#define CMU_RESET_LCD1_SYS_PWR_REG 0x1174
+#define CMU_RESET_MAUDIO_SYS_PWR_REG 0x1178
+#define CMU_RESET_GPS_SYS_PWR_REG 0x117C
+#define TOP_BUS_SYS_PWR_REG 0x1180
+#define TOP_RETENTION_SYS_PWR_REG 0x1184
+#define TOP_PWR_SYS_PWR_REG 0x1188
+#define LOGIC_RESET_SYS_PWR_REG 0x11A0
+#define OneNANDXL_MEM_SYS_PWR_REG 0x11C0
+#define MODEMIF_MEM_SYS_PWR_REG 0x11C4
+#define USBDEVICE_MEM_SYS_PWR_REG 0x11CC
+#define SDMMC_MEM_SYS_PWR_REG 0x11D0
+#define CSSYS_MEM_SYS_PWR_REG 0x11D4
+#define SECSS_MEM_SYS_PWR_REG 0x11D8
+#define PCIe_MEM_SYS_PWR_REG 0x11E0
+#define SATA_MEM_SYS_PWR_REG 0x11E4
+#define PAD_RETENTION_DRAM_SYS_PWR_REG 0x1200
+#define PAD_RETENTION_MAUDIO_SYS_PWR_REG 0x1204
+#define PAD_RETENTION_GPIO_SYS_PWR_REG 0x1220
+#define PAD_RETENTION_UART_SYS_PWR_REG 0x1224
+#define PAD_RETENTION_MMCA_SYS_PWR_REG 0x1228
+#define PAD_RETENTION_MMCB_SYS_PWR_REG 0x122C
+#define PAD_RETENTION_EBIA_SYS_PWR_REG 0x1230
+#define PAD_RETENTION_EBIB_SYS_PWR_REG 0x1234
+#define PAD_ISOLATION_SYS_PWR_REG 0x1240
+#define PAD_ALV_SEL_SYS_PWR_REG 0x1260
+#define XUSBXTI_SYS_PWR_REG 0x1280
+#define XXTI_SYS_PWR_REG 0x1284
+#define EXT_REGULATOR_SYS_PWR_REG 0x12C0
+#define GPIO_MODE_SYS_PWR_REG 0x1300
+#define GPIO_MODE_MAUDIO_SYS_PWR_REG 0x1340
+#define CAM_SYS_PWR_REG 0x1380
+#define TV_SYS_PWR_REG 0x1384
+#define MFC_SYS_PWR_REG 0x1388
+#define G3D_SYS_PWR_REG 0x138C
+#define LCD0_SYS_PWR_REG 0x1390
+#define LCD1_SYS_PWR_REG 0x1394
+#define MAUDIO_SYS_PWR_REG 0x1398
+#define GPS_SYS_PWR_REG 0x139C
+#define GPS_ALIVE_SYS_PWR_REG 0x13A0
+#define ARM_CORE0_CONFIGURATION 0x2000 /* Configure power mode of ARM_CORE0 */
+#define ARM_CORE0_STATUS 0x2004 /* Check power mode of ARM_CORE0 */
+#define ARM_CORE0_OPTION 0x2008 /* Sets control options for ARM_CORE0 */
+#define ARM_CORE1_CONFIGURATION 0x2080 /* Configure power mode of ARM_CORE1 */
+#define ARM_CORE1_STATUS 0x2084 /* Check power mode of ARM_CORE1 */
+#define ARM_CORE1_OPTION 0x2088 /* Sets control options for ARM_CORE0 */
+#define ARM_COMMON_OPTION 0x2408 /* Sets control options for ARM_COMMON */
+/* Configure power mode of ARM_CPU_L2_0 */
+#define ARM_CPU_L2_0_CONFIGURATION 0x2600
+#define ARM_CPU_L2_0_STATUS 0x2604 /* Check power mode of ARM_CPU_L2_0 */
+/* Configure power mode of ARM_CPU_L2_1 */
+#define ARM_CPU_L2_1_CONFIGURATION 0x2620
+#define ARM_CPU_L2_1_STATUS 0x2624 /* Check power mode of ARM_CPU_L2_1 */
+/* Sets control options for PAD_RETENTION_MAUDIO */
+#define PAD_RETENTION_MAUDIO_OPTION 0x3028
+/* Sets control options for PAD_RETENTION_GPIO */
+#define PAD_RETENTION_GPIO_OPTION 0x3108
+/* Sets control options for PAD_RETENTION_UART */
+#define PAD_RETENTION_UART_OPTION 0x3128
+/* Sets control options for PAD_RETENTION_MMCA */
+#define PAD_RETENTION_MMCA_OPTION 0x3148
+/* Sets control options for PAD_RETENTION_MMCB */
+#define PAD_RETENTION_MMCB_OPTION 0x3168
+/* Sets control options for PAD_RETENTION_EBIA */
+#define PAD_RETENTION_EBIA_OPTION 0x3188
+/* Sets control options for PAD_RETENTION_EBIB */
+#define PAD_RETENTION_EBIB_OPTION 0x31A8
+#define PS_HOLD_CONTROL 0x330C /* PS_HOLD control register */
+#define XUSBXTI_CONFIGURATION 0x3400 /* Configure the pad of XUSBXTI */
+#define XUSBXTI_STATUS 0x3404 /* Check the pad of XUSBXTI */
+/* Sets time required for XUSBXTI to be stabilized */
+#define XUSBXTI_DURATION 0x341C
+#define XXTI_CONFIGURATION 0x3420 /* Configure the pad of XXTI */
+#define XXTI_STATUS 0x3424 /* Check the pad of XXTI */
+/* Sets time required for XXTI to be stabilized */
+#define XXTI_DURATION 0x343C
+/* Sets time required for EXT_REGULATOR to be stabilized */
+#define EXT_REGULATOR_DURATION 0x361C
+#define CAM_CONFIGURATION 0x3C00 /* Configure power mode of CAM */
+#define CAM_STATUS 0x3C04 /* Check power mode of CAM */
+#define CAM_OPTION 0x3C08 /* Sets control options for CAM */
+#define TV_CONFIGURATION 0x3C20 /* Configure power mode of TV */
+#define TV_STATUS 0x3C24 /* Check power mode of TV */
+#define TV_OPTION 0x3C28 /* Sets control options for TV */
+#define MFC_CONFIGURATION 0x3C40 /* Configure power mode of MFC */
+#define MFC_STATUS 0x3C44 /* Check power mode of MFC */
+#define MFC_OPTION 0x3C48 /* Sets control options for MFC */
+#define G3D_CONFIGURATION 0x3C60 /* Configure power mode of G3D */
+#define G3D_STATUS 0x3C64 /* Check power mode of G3D */
+#define G3D_OPTION 0x3C68 /* Sets control options for G3D */
+#define LCD0_CONFIGURATION 0x3C80 /* Configure power mode of LCD0 */
+#define LCD0_STATUS 0x3C84 /* Check power mode of LCD0 */
+#define LCD0_OPTION 0x3C88 /* Sets control options for LCD0 */
+#define LCD1_CONFIGURATION 0x3CA0 /* Configure power mode of LCD1 */
+#define LCD1_STATUS 0x3CA4 /* Check power mode of LCD1 */
+#define LCD1_OPTION 0x3CA8 /* Sets control options for LCD1 */
+#define GPS_CONFIGURATION 0x3CE0 /* Configure power mode of GPS */
+#define GPS_STATUS 0x3CE4 /* Check power mode of GPS */
+#define GPS_OPTION 0x3CE8 /* Sets control options for GPS */
+#define GPS_ALIVE_CONFIGURATION 0x3D00 /* Configure power mode of GPS */
+#define GPS_ALIVE_STATUS 0x3D04 /* Check power mode of GPS */
+#define GPS_ALIVE_OPTION 0x3D08 /* Sets control options for GPS */
+
+#define EXYNOS4210_PMU_REGS_MEM_SIZE 0x3d0c
+
+typedef struct Exynos4210PmuReg {
+ const char *name; /* for debug only */
+ uint32_t offset;
+ uint32_t reset_value;
+} Exynos4210PmuReg;
+
+static const Exynos4210PmuReg exynos4210_pmu_regs[] = {
+ {"OM_STAT", OM_STAT, 0x00000000},
+ {"RTC_CLKO_SEL", RTC_CLKO_SEL, 0x00000000},
+ {"GNSS_RTC_OUT_CTRL", GNSS_RTC_OUT_CTRL, 0x00000001},
+ {"SYSTEM_POWER_DOWN_CTRL", SYSTEM_POWER_DOWN_CTRL, 0x00010000},
+ {"SYSTEM_POWER_DOWN_OPTION", SYSTEM_POWER_DOWN_OPTION, 0x03030000},
+ {"SWRESET", SWRESET, 0x00000000},
+ {"RST_STAT", RST_STAT, 0x00000000},
+ {"WAKEUP_STAT", WAKEUP_STAT, 0x00000000},
+ {"EINT_WAKEUP_MASK", EINT_WAKEUP_MASK, 0x00000000},
+ {"WAKEUP_MASK", WAKEUP_MASK, 0x00000000},
+ {"HDMI_PHY_CONTROL", HDMI_PHY_CONTROL, 0x00960000},
+ {"USBDEVICE_PHY_CONTROL", USBDEVICE_PHY_CONTROL, 0x00000000},
+ {"USBHOST_PHY_CONTROL", USBHOST_PHY_CONTROL, 0x00000000},
+ {"DAC_PHY_CONTROL", DAC_PHY_CONTROL, 0x00000000},
+ {"MIPI_PHY0_CONTROL", MIPI_PHY0_CONTROL, 0x00000000},
+ {"MIPI_PHY1_CONTROL", MIPI_PHY1_CONTROL, 0x00000000},
+ {"ADC_PHY_CONTROL", ADC_PHY_CONTROL, 0x00000001},
+ {"PCIe_PHY_CONTROL", PCIe_PHY_CONTROL, 0x00000000},
+ {"SATA_PHY_CONTROL", SATA_PHY_CONTROL, 0x00000000},
+ {"INFORM0", INFORM0, 0x00000000},
+ {"INFORM1", INFORM1, 0x00000000},
+ {"INFORM2", INFORM2, 0x00000000},
+ {"INFORM3", INFORM3, 0x00000000},
+ {"INFORM4", INFORM4, 0x00000000},
+ {"INFORM5", INFORM5, 0x00000000},
+ {"INFORM6", INFORM6, 0x00000000},
+ {"INFORM7", INFORM7, 0x00000000},
+ {"PMU_DEBUG", PMU_DEBUG, 0x00000000},
+ {"ARM_CORE0_SYS_PWR_REG", ARM_CORE0_SYS_PWR_REG, 0xFFFFFFFF},
+ {"ARM_CORE1_SYS_PWR_REG", ARM_CORE1_SYS_PWR_REG, 0xFFFFFFFF},
+ {"ARM_COMMON_SYS_PWR_REG", ARM_COMMON_SYS_PWR_REG, 0xFFFFFFFF},
+ {"ARM_CPU_L2_0_SYS_PWR_REG", ARM_CPU_L2_0_SYS_PWR_REG, 0xFFFFFFFF},
+ {"ARM_CPU_L2_1_SYS_PWR_REG", ARM_CPU_L2_1_SYS_PWR_REG, 0xFFFFFFFF},
+ {"CMU_ACLKSTOP_SYS_PWR_REG", CMU_ACLKSTOP_SYS_PWR_REG, 0xFFFFFFFF},
+ {"CMU_SCLKSTOP_SYS_PWR_REG", CMU_SCLKSTOP_SYS_PWR_REG, 0xFFFFFFFF},
+ {"CMU_RESET_SYS_PWR_REG", CMU_RESET_SYS_PWR_REG, 0xFFFFFFFF},
+ {"APLL_SYSCLK_SYS_PWR_REG", APLL_SYSCLK_SYS_PWR_REG, 0xFFFFFFFF},
+ {"MPLL_SYSCLK_SYS_PWR_REG", MPLL_SYSCLK_SYS_PWR_REG, 0xFFFFFFFF},
+ {"VPLL_SYSCLK_SYS_PWR_REG", VPLL_SYSCLK_SYS_PWR_REG, 0xFFFFFFFF},
+ {"EPLL_SYSCLK_SYS_PWR_REG", EPLL_SYSCLK_SYS_PWR_REG, 0xFFFFFFFF},
+ {"CMU_CLKSTOP_GPS_ALIVE_SYS_PWR_REG", CMU_CLKSTOP_GPS_ALIVE_SYS_PWR_REG,
+ 0xFFFFFFFF},
+ {"CMU_RESET_GPS_ALIVE_SYS_PWR_REG", CMU_RESET_GPS_ALIVE_SYS_PWR_REG,
+ 0xFFFFFFFF},
+ {"CMU_CLKSTOP_CAM_SYS_PWR_REG", CMU_CLKSTOP_CAM_SYS_PWR_REG, 0xFFFFFFFF},
+ {"CMU_CLKSTOP_TV_SYS_PWR_REG", CMU_CLKSTOP_TV_SYS_PWR_REG, 0xFFFFFFFF},
+ {"CMU_CLKSTOP_MFC_SYS_PWR_REG", CMU_CLKSTOP_MFC_SYS_PWR_REG, 0xFFFFFFFF},
+ {"CMU_CLKSTOP_G3D_SYS_PWR_REG", CMU_CLKSTOP_G3D_SYS_PWR_REG, 0xFFFFFFFF},
+ {"CMU_CLKSTOP_LCD0_SYS_PWR_REG", CMU_CLKSTOP_LCD0_SYS_PWR_REG, 0xFFFFFFFF},
+ {"CMU_CLKSTOP_LCD1_SYS_PWR_REG", CMU_CLKSTOP_LCD1_SYS_PWR_REG, 0xFFFFFFFF},
+ {"CMU_CLKSTOP_MAUDIO_SYS_PWR_REG", CMU_CLKSTOP_MAUDIO_SYS_PWR_REG,
+ 0xFFFFFFFF},
+ {"CMU_CLKSTOP_GPS_SYS_PWR_REG", CMU_CLKSTOP_GPS_SYS_PWR_REG, 0xFFFFFFFF},
+ {"CMU_RESET_CAM_SYS_PWR_REG", CMU_RESET_CAM_SYS_PWR_REG, 0xFFFFFFFF},
+ {"CMU_RESET_TV_SYS_PWR_REG", CMU_RESET_TV_SYS_PWR_REG, 0xFFFFFFFF},
+ {"CMU_RESET_MFC_SYS_PWR_REG", CMU_RESET_MFC_SYS_PWR_REG, 0xFFFFFFFF},
+ {"CMU_RESET_G3D_SYS_PWR_REG", CMU_RESET_G3D_SYS_PWR_REG, 0xFFFFFFFF},
+ {"CMU_RESET_LCD0_SYS_PWR_REG", CMU_RESET_LCD0_SYS_PWR_REG, 0xFFFFFFFF},
+ {"CMU_RESET_LCD1_SYS_PWR_REG", CMU_RESET_LCD1_SYS_PWR_REG, 0xFFFFFFFF},
+ {"CMU_RESET_MAUDIO_SYS_PWR_REG", CMU_RESET_MAUDIO_SYS_PWR_REG, 0xFFFFFFFF},
+ {"CMU_RESET_GPS_SYS_PWR_REG", CMU_RESET_GPS_SYS_PWR_REG, 0xFFFFFFFF},
+ {"TOP_BUS_SYS_PWR_REG", TOP_BUS_SYS_PWR_REG, 0xFFFFFFFF},
+ {"TOP_RETENTION_SYS_PWR_REG", TOP_RETENTION_SYS_PWR_REG, 0xFFFFFFFF},
+ {"TOP_PWR_SYS_PWR_REG", TOP_PWR_SYS_PWR_REG, 0xFFFFFFFF},
+ {"LOGIC_RESET_SYS_PWR_REG", LOGIC_RESET_SYS_PWR_REG, 0xFFFFFFFF},
+ {"OneNANDXL_MEM_SYS_PWR_REG", OneNANDXL_MEM_SYS_PWR_REG, 0xFFFFFFFF},
+ {"MODEMIF_MEM_SYS_PWR_REG", MODEMIF_MEM_SYS_PWR_REG, 0xFFFFFFFF},
+ {"USBDEVICE_MEM_SYS_PWR_REG", USBDEVICE_MEM_SYS_PWR_REG, 0xFFFFFFFF},
+ {"SDMMC_MEM_SYS_PWR_REG", SDMMC_MEM_SYS_PWR_REG, 0xFFFFFFFF},
+ {"CSSYS_MEM_SYS_PWR_REG", CSSYS_MEM_SYS_PWR_REG, 0xFFFFFFFF},
+ {"SECSS_MEM_SYS_PWR_REG", SECSS_MEM_SYS_PWR_REG, 0xFFFFFFFF},
+ {"PCIe_MEM_SYS_PWR_REG", PCIe_MEM_SYS_PWR_REG, 0xFFFFFFFF},
+ {"SATA_MEM_SYS_PWR_REG", SATA_MEM_SYS_PWR_REG, 0xFFFFFFFF},
+ {"PAD_RETENTION_DRAM_SYS_PWR_REG", PAD_RETENTION_DRAM_SYS_PWR_REG,
+ 0xFFFFFFFF},
+ {"PAD_RETENTION_MAUDIO_SYS_PWR_REG", PAD_RETENTION_MAUDIO_SYS_PWR_REG,
+ 0xFFFFFFFF},
+ {"PAD_RETENTION_GPIO_SYS_PWR_REG", PAD_RETENTION_GPIO_SYS_PWR_REG,
+ 0xFFFFFFFF},
+ {"PAD_RETENTION_UART_SYS_PWR_REG", PAD_RETENTION_UART_SYS_PWR_REG,
+ 0xFFFFFFFF},
+ {"PAD_RETENTION_MMCA_SYS_PWR_REG", PAD_RETENTION_MMCA_SYS_PWR_REG,
+ 0xFFFFFFFF},
+ {"PAD_RETENTION_MMCB_SYS_PWR_REG", PAD_RETENTION_MMCB_SYS_PWR_REG,
+ 0xFFFFFFFF},
+ {"PAD_RETENTION_EBIA_SYS_PWR_REG", PAD_RETENTION_EBIA_SYS_PWR_REG,
+ 0xFFFFFFFF},
+ {"PAD_RETENTION_EBIB_SYS_PWR_REG", PAD_RETENTION_EBIB_SYS_PWR_REG,
+ 0xFFFFFFFF},
+ {"PAD_ISOLATION_SYS_PWR_REG", PAD_ISOLATION_SYS_PWR_REG, 0xFFFFFFFF},
+ {"PAD_ALV_SEL_SYS_PWR_REG", PAD_ALV_SEL_SYS_PWR_REG, 0xFFFFFFFF},
+ {"XUSBXTI_SYS_PWR_REG", XUSBXTI_SYS_PWR_REG, 0xFFFFFFFF},
+ {"XXTI_SYS_PWR_REG", XXTI_SYS_PWR_REG, 0xFFFFFFFF},
+ {"EXT_REGULATOR_SYS_PWR_REG", EXT_REGULATOR_SYS_PWR_REG, 0xFFFFFFFF},
+ {"GPIO_MODE_SYS_PWR_REG", GPIO_MODE_SYS_PWR_REG, 0xFFFFFFFF},
+ {"GPIO_MODE_MAUDIO_SYS_PWR_REG", GPIO_MODE_MAUDIO_SYS_PWR_REG, 0xFFFFFFFF},
+ {"CAM_SYS_PWR_REG", CAM_SYS_PWR_REG, 0xFFFFFFFF},
+ {"TV_SYS_PWR_REG", TV_SYS_PWR_REG, 0xFFFFFFFF},
+ {"MFC_SYS_PWR_REG", MFC_SYS_PWR_REG, 0xFFFFFFFF},
+ {"G3D_SYS_PWR_REG", G3D_SYS_PWR_REG, 0xFFFFFFFF},
+ {"LCD0_SYS_PWR_REG", LCD0_SYS_PWR_REG, 0xFFFFFFFF},
+ {"LCD1_SYS_PWR_REG", LCD1_SYS_PWR_REG, 0xFFFFFFFF},
+ {"MAUDIO_SYS_PWR_REG", MAUDIO_SYS_PWR_REG, 0xFFFFFFFF},
+ {"GPS_SYS_PWR_REG", GPS_SYS_PWR_REG, 0xFFFFFFFF},
+ {"GPS_ALIVE_SYS_PWR_REG", GPS_ALIVE_SYS_PWR_REG, 0xFFFFFFFF},
+ {"ARM_CORE0_CONFIGURATION", ARM_CORE0_CONFIGURATION, 0x00000003},
+ {"ARM_CORE0_STATUS", ARM_CORE0_STATUS, 0x00030003},
+ {"ARM_CORE0_OPTION", ARM_CORE0_OPTION, 0x01010001},
+ {"ARM_CORE1_CONFIGURATION", ARM_CORE1_CONFIGURATION, 0x00000003},
+ {"ARM_CORE1_STATUS", ARM_CORE1_STATUS, 0x00030003},
+ {"ARM_CORE1_OPTION", ARM_CORE1_OPTION, 0x01010001},
+ {"ARM_COMMON_OPTION", ARM_COMMON_OPTION, 0x00000001},
+ {"ARM_CPU_L2_0_CONFIGURATION", ARM_CPU_L2_0_CONFIGURATION, 0x00000003},
+ {"ARM_CPU_L2_0_STATUS", ARM_CPU_L2_0_STATUS, 0x00000003},
+ {"ARM_CPU_L2_1_CONFIGURATION", ARM_CPU_L2_1_CONFIGURATION, 0x00000003},
+ {"ARM_CPU_L2_1_STATUS", ARM_CPU_L2_1_STATUS, 0x00000003},
+ {"PAD_RETENTION_MAUDIO_OPTION", PAD_RETENTION_MAUDIO_OPTION, 0x00000000},
+ {"PAD_RETENTION_GPIO_OPTION", PAD_RETENTION_GPIO_OPTION, 0x00000000},
+ {"PAD_RETENTION_UART_OPTION", PAD_RETENTION_UART_OPTION, 0x00000000},
+ {"PAD_RETENTION_MMCA_OPTION", PAD_RETENTION_MMCA_OPTION, 0x00000000},
+ {"PAD_RETENTION_MMCB_OPTION", PAD_RETENTION_MMCB_OPTION, 0x00000000},
+ {"PAD_RETENTION_EBIA_OPTION", PAD_RETENTION_EBIA_OPTION, 0x00000000},
+ {"PAD_RETENTION_EBIB_OPTION", PAD_RETENTION_EBIB_OPTION, 0x00000000},
+ {"PS_HOLD_CONTROL", PS_HOLD_CONTROL, 0x00005200},
+ {"XUSBXTI_CONFIGURATION", XUSBXTI_CONFIGURATION, 0x00000001},
+ {"XUSBXTI_STATUS", XUSBXTI_STATUS, 0x00000001},
+ {"XUSBXTI_DURATION", XUSBXTI_DURATION, 0xFFF00000},
+ {"XXTI_CONFIGURATION", XXTI_CONFIGURATION, 0x00000001},
+ {"XXTI_STATUS", XXTI_STATUS, 0x00000001},
+ {"XXTI_DURATION", XXTI_DURATION, 0xFFF00000},
+ {"EXT_REGULATOR_DURATION", EXT_REGULATOR_DURATION, 0xFFF03FFF},
+ {"CAM_CONFIGURATION", CAM_CONFIGURATION, 0x00000007},
+ {"CAM_STATUS", CAM_STATUS, 0x00060007},
+ {"CAM_OPTION", CAM_OPTION, 0x00000001},
+ {"TV_CONFIGURATION", TV_CONFIGURATION, 0x00000007},
+ {"TV_STATUS", TV_STATUS, 0x00060007},
+ {"TV_OPTION", TV_OPTION, 0x00000001},
+ {"MFC_CONFIGURATION", MFC_CONFIGURATION, 0x00000007},
+ {"MFC_STATUS", MFC_STATUS, 0x00060007},
+ {"MFC_OPTION", MFC_OPTION, 0x00000001},
+ {"G3D_CONFIGURATION", G3D_CONFIGURATION, 0x00000007},
+ {"G3D_STATUS", G3D_STATUS, 0x00060007},
+ {"G3D_OPTION", G3D_OPTION, 0x00000001},
+ {"LCD0_CONFIGURATION", LCD0_CONFIGURATION, 0x00000007},
+ {"LCD0_STATUS", LCD0_STATUS, 0x00060007},
+ {"LCD0_OPTION", LCD0_OPTION, 0x00000001},
+ {"LCD1_CONFIGURATION", LCD1_CONFIGURATION, 0x00000007},
+ {"LCD1_STATUS", LCD1_STATUS, 0x00060007},
+ {"LCD1_OPTION", LCD1_OPTION, 0x00000001},
+ {"GPS_CONFIGURATION", GPS_CONFIGURATION, 0x00000007},
+ {"GPS_STATUS", GPS_STATUS, 0x00060007},
+ {"GPS_OPTION", GPS_OPTION, 0x00000001},
+ {"GPS_ALIVE_CONFIGURATION", GPS_ALIVE_CONFIGURATION, 0x00000007},
+ {"GPS_ALIVE_STATUS", GPS_ALIVE_STATUS, 0x00060007},
+ {"GPS_ALIVE_OPTION", GPS_ALIVE_OPTION, 0x00000001},
+};
+
+#define PMU_NUM_OF_REGISTERS \
+ (sizeof(exynos4210_pmu_regs) / sizeof(Exynos4210PmuReg))
+
+typedef struct Exynos4210PmuState {
+ SysBusDevice busdev;
+ MemoryRegion iomem;
+ uint32_t reg[PMU_NUM_OF_REGISTERS];
+} Exynos4210PmuState;
+
+static uint64_t exynos4210_pmu_read(void *opaque, target_phys_addr_t offset,
+ unsigned size)
+{
+ Exynos4210PmuState *s = (Exynos4210PmuState *)opaque;
+ unsigned i;
+ const Exynos4210PmuReg *reg_p = exynos4210_pmu_regs;
+
+ for (i = 0; i < PMU_NUM_OF_REGISTERS; i++) {
+ if (reg_p->offset == offset) {
+ PRINT_DEBUG_EXTEND("%s [0x%04x] -> 0x%04x\n", reg_p->name,
+ (uint32_t)offset, s->reg[i]);
+ return s->reg[i];
+ }
+ reg_p++;
+ }
+ PRINT_DEBUG("QEMU PMU ERROR: bad read offset 0x%04x\n", (uint32_t)offset);
+ return 0;
+}
+
+static void exynos4210_pmu_write(void *opaque, target_phys_addr_t offset,
+ uint64_t val, unsigned size)
+{
+ Exynos4210PmuState *s = (Exynos4210PmuState *)opaque;
+ unsigned i;
+ const Exynos4210PmuReg *reg_p = exynos4210_pmu_regs;
+
+ for (i = 0; i < PMU_NUM_OF_REGISTERS; i++) {
+ if (reg_p->offset == offset) {
+ PRINT_DEBUG_EXTEND("%s <0x%04x> <- 0x%04x\n", reg_p->name,
+ (uint32_t)offset, (uint32_t)val);
+ s->reg[i] = val;
+ return;
+ }
+ reg_p++;
+ }
+ PRINT_DEBUG("QEMU PMU ERROR: bad write offset 0x%04x\n", (uint32_t)offset);
+}
+
+static const MemoryRegionOps exynos4210_pmu_ops = {
+ .read = exynos4210_pmu_read,
+ .write = exynos4210_pmu_write,
+ .endianness = DEVICE_NATIVE_ENDIAN,
+ .valid = {
+ .min_access_size = 4,
+ .max_access_size = 4,
+ .unaligned = false
+ }
+};
+
+static void exynos4210_pmu_reset(DeviceState *dev)
+{
+ Exynos4210PmuState *s =
+ container_of(dev, Exynos4210PmuState, busdev.qdev);
+ unsigned i;
+
+ /* Set default values for registers */
+ for (i = 0; i < PMU_NUM_OF_REGISTERS; i++) {
+ s->reg[i] = exynos4210_pmu_regs[i].reset_value;
+ }
+}
+
+static int exynos4210_pmu_init(SysBusDevice *dev)
+{
+ Exynos4210PmuState *s = FROM_SYSBUS(Exynos4210PmuState, dev);
+
+ /* memory mapping */
+ memory_region_init_io(&s->iomem, &exynos4210_pmu_ops, s, "exynos4210.pmu",
+ EXYNOS4210_PMU_REGS_MEM_SIZE);
+ sysbus_init_mmio(dev, &s->iomem);
+ return 0;
+}
+
+static const VMStateDescription exynos4210_pmu_vmstate = {
+ .name = "exynos4210.pmu",
+ .version_id = 1,
+ .minimum_version_id = 1,
+ .fields = (VMStateField[]) {
+ VMSTATE_UINT32_ARRAY(reg, Exynos4210PmuState, PMU_NUM_OF_REGISTERS),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
+static void exynos4210_pmu_class_init(ObjectClass *klass, void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(klass);
+ SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass);
+
+ k->init = exynos4210_pmu_init;
+ dc->reset = exynos4210_pmu_reset;
+ dc->vmsd = &exynos4210_pmu_vmstate;
+}
+
+static TypeInfo exynos4210_pmu_info = {
+ .name = "exynos4210.pmu",
+ .parent = TYPE_SYS_BUS_DEVICE,
+ .instance_size = sizeof(Exynos4210PmuState),
+ .class_init = exynos4210_pmu_class_init,
+};
+
+static void exynos4210_pmu_register(void)
+{
+ type_register_static(&exynos4210_pmu_info);
+}
+
+type_init(exynos4210_pmu_register)
diff --git a/hw/exynos4210_pwm.c b/hw/exynos4210_pwm.c
new file mode 100644
index 0000000000..6243e59c48
--- /dev/null
+++ b/hw/exynos4210_pwm.c
@@ -0,0 +1,422 @@
+/*
+ * Samsung exynos4210 Pulse Width Modulation Timer
+ *
+ * Copyright (c) 2000 - 2011 Samsung Electronics Co., Ltd.
+ * All rights reserved.
+ *
+ * Evgeny Voevodin <e.voevodin@samsung.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ * See the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "sysbus.h"
+#include "qemu-timer.h"
+#include "qemu-common.h"
+#include "ptimer.h"
+
+#include "exynos4210.h"
+
+//#define DEBUG_PWM
+
+#ifdef DEBUG_PWM
+#define DPRINTF(fmt, ...) \
+ do { fprintf(stdout, "PWM: [%24s:%5d] " fmt, __func__, __LINE__, \
+ ## __VA_ARGS__); } while (0)
+#else
+#define DPRINTF(fmt, ...) do {} while (0)
+#endif
+
+#define EXYNOS4210_PWM_TIMERS_NUM 5
+#define EXYNOS4210_PWM_REG_MEM_SIZE 0x50
+
+#define TCFG0 0x0000
+#define TCFG1 0x0004
+#define TCON 0x0008
+#define TCNTB0 0x000C
+#define TCMPB0 0x0010
+#define TCNTO0 0x0014
+#define TCNTB1 0x0018
+#define TCMPB1 0x001C
+#define TCNTO1 0x0020
+#define TCNTB2 0x0024
+#define TCMPB2 0x0028
+#define TCNTO2 0x002C
+#define TCNTB3 0x0030
+#define TCMPB3 0x0034
+#define TCNTO3 0x0038
+#define TCNTB4 0x003C
+#define TCNTO4 0x0040
+#define TINT_CSTAT 0x0044
+
+#define TCNTB(x) (0xC * (x))
+#define TCMPB(x) (0xC * (x) + 1)
+#define TCNTO(x) (0xC * (x) + 2)
+
+#define GET_PRESCALER(reg, x) (((reg) & (0xFF << (8 * (x)))) >> 8 * (x))
+#define GET_DIVIDER(reg, x) (1 << (((reg) & (0xF << (4 * (x)))) >> (4 * (x))))
+
+/*
+ * Attention! Timer4 doesn't have OUTPUT_INVERTER,
+ * so Auto Reload bit is not accessible by macros!
+ */
+#define TCON_TIMER_BASE(x) (((x) ? 1 : 0) * 4 + 4 * (x))
+#define TCON_TIMER_START(x) (1 << (TCON_TIMER_BASE(x) + 0))
+#define TCON_TIMER_MANUAL_UPD(x) (1 << (TCON_TIMER_BASE(x) + 1))
+#define TCON_TIMER_OUTPUT_INV(x) (1 << (TCON_TIMER_BASE(x) + 2))
+#define TCON_TIMER_AUTO_RELOAD(x) (1 << (TCON_TIMER_BASE(x) + 3))
+#define TCON_TIMER4_AUTO_RELOAD (1 << 22)
+
+#define TINT_CSTAT_STATUS(x) (1 << (5 + (x)))
+#define TINT_CSTAT_ENABLE(x) (1 << (x))
+
+/* timer struct */
+typedef struct {
+ uint32_t id; /* timer id */
+ qemu_irq irq; /* local timer irq */
+ uint32_t freq; /* timer frequency */
+
+ /* use ptimer.c to represent count down timer */
+ ptimer_state *ptimer; /* timer */
+
+ /* registers */
+ uint32_t reg_tcntb; /* counter register buffer */
+ uint32_t reg_tcmpb; /* compare register buffer */
+
+ struct Exynos4210PWMState *parent;
+
+} Exynos4210PWM;
+
+
+typedef struct Exynos4210PWMState {
+ SysBusDevice busdev;
+ MemoryRegion iomem;
+
+ uint32_t reg_tcfg[2];
+ uint32_t reg_tcon;
+ uint32_t reg_tint_cstat;
+
+ Exynos4210PWM timer[EXYNOS4210_PWM_TIMERS_NUM];
+
+} Exynos4210PWMState;
+
+/*** VMState ***/
+static const VMStateDescription vmstate_exynos4210_pwm = {
+ .name = "exynos4210.pwm.pwm",
+ .version_id = 1,
+ .minimum_version_id = 1,
+ .minimum_version_id_old = 1,
+ .fields = (VMStateField[]) {
+ VMSTATE_UINT32(id, Exynos4210PWM),
+ VMSTATE_UINT32(freq, Exynos4210PWM),
+ VMSTATE_PTIMER(ptimer, Exynos4210PWM),
+ VMSTATE_UINT32(reg_tcntb, Exynos4210PWM),
+ VMSTATE_UINT32(reg_tcmpb, Exynos4210PWM),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
+static const VMStateDescription vmstate_exynos4210_pwm_state = {
+ .name = "exynos4210.pwm",
+ .version_id = 1,
+ .minimum_version_id = 1,
+ .minimum_version_id_old = 1,
+ .fields = (VMStateField[]) {
+ VMSTATE_UINT32_ARRAY(reg_tcfg, Exynos4210PWMState, 2),
+ VMSTATE_UINT32(reg_tcon, Exynos4210PWMState),
+ VMSTATE_UINT32(reg_tint_cstat, Exynos4210PWMState),
+ VMSTATE_STRUCT_ARRAY(timer, Exynos4210PWMState,
+ EXYNOS4210_PWM_TIMERS_NUM, 0,
+ vmstate_exynos4210_pwm, Exynos4210PWM),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
+/*
+ * PWM update frequency
+ */
+static void exynos4210_pwm_update_freq(Exynos4210PWMState *s, uint32_t id)
+{
+ uint32_t freq;
+ freq = s->timer[id].freq;
+ if (id > 1) {
+ s->timer[id].freq = 24000000 /
+ ((GET_PRESCALER(s->reg_tcfg[0], 1) + 1) *
+ (GET_DIVIDER(s->reg_tcfg[1], id)));
+ } else {
+ s->timer[id].freq = 24000000 /
+ ((GET_PRESCALER(s->reg_tcfg[0], 0) + 1) *
+ (GET_DIVIDER(s->reg_tcfg[1], id)));
+ }
+
+ if (freq != s->timer[id].freq) {
+ ptimer_set_freq(s->timer[id].ptimer, s->timer[id].freq);
+ DPRINTF("freq=%dHz\n", s->timer[id].freq);
+ }
+}
+
+/*
+ * Counter tick handler
+ */
+static void exynos4210_pwm_tick(void *opaque)
+{
+ Exynos4210PWM *s = (Exynos4210PWM *)opaque;
+ Exynos4210PWMState *p = (Exynos4210PWMState *)s->parent;
+ uint32_t id = s->id;
+ bool cmp;
+
+ DPRINTF("timer %d tick\n", id);
+
+ /* set irq status */
+ p->reg_tint_cstat |= TINT_CSTAT_STATUS(id);
+
+ /* raise IRQ */
+ if (p->reg_tint_cstat & TINT_CSTAT_ENABLE(id)) {
+ DPRINTF("timer %d IRQ\n", id);
+ qemu_irq_raise(p->timer[id].irq);
+ }
+
+ /* reload timer */
+ if (id != 4) {
+ cmp = p->reg_tcon & TCON_TIMER_AUTO_RELOAD(id);
+ } else {
+ cmp = p->reg_tcon & TCON_TIMER4_AUTO_RELOAD;
+ }
+
+ if (cmp) {
+ DPRINTF("auto reload timer %d count to %x\n", id,
+ p->timer[id].reg_tcntb);
+ ptimer_set_count(p->timer[id].ptimer, p->timer[id].reg_tcntb);
+ ptimer_run(p->timer[id].ptimer, 1);
+ } else {
+ /* stop timer, set status to STOP, see Basic Timer Operation */
+ p->reg_tcon = ~TCON_TIMER_START(id);
+ ptimer_stop(p->timer[id].ptimer);
+ }
+}
+
+/*
+ * PWM Read
+ */
+static uint64_t exynos4210_pwm_read(void *opaque, target_phys_addr_t offset,
+ unsigned size)
+{
+ Exynos4210PWMState *s = (Exynos4210PWMState *)opaque;
+ uint32_t value = 0;
+ int index;
+
+ switch (offset) {
+ case TCFG0: case TCFG1:
+ index = (offset - TCFG0) >> 2;
+ value = s->reg_tcfg[index];
+ break;
+
+ case TCON:
+ value = s->reg_tcon;
+ break;
+
+ case TCNTB0: case TCNTB1:
+ case TCNTB2: case TCNTB3: case TCNTB4:
+ index = (offset - TCNTB0) / 0xC;
+ value = s->timer[index].reg_tcntb;
+ break;
+
+ case TCMPB0: case TCMPB1:
+ case TCMPB2: case TCMPB3:
+ index = (offset - TCMPB0) / 0xC;
+ value = s->timer[index].reg_tcmpb;
+ break;
+
+ case TCNTO0: case TCNTO1:
+ case TCNTO2: case TCNTO3: case TCNTO4:
+ index = (offset == TCNTO4) ? 4 : (offset - TCNTO0) / 0xC;
+ value = ptimer_get_count(s->timer[index].ptimer);
+ break;
+
+ case TINT_CSTAT:
+ value = s->reg_tint_cstat;
+ break;
+
+ default:
+ fprintf(stderr,
+ "[exynos4210.pwm: bad read offset " TARGET_FMT_plx "]\n",
+ offset);
+ break;
+ }
+ return value;
+}
+
+/*
+ * PWM Write
+ */
+static void exynos4210_pwm_write(void *opaque, target_phys_addr_t offset,
+ uint64_t value, unsigned size)
+{
+ Exynos4210PWMState *s = (Exynos4210PWMState *)opaque;
+ int index;
+ uint32_t new_val;
+ int i;
+
+ switch (offset) {
+ case TCFG0: case TCFG1:
+ index = (offset - TCFG0) >> 2;
+ s->reg_tcfg[index] = value;
+
+ /* update timers frequencies */
+ for (i = 0; i < EXYNOS4210_PWM_TIMERS_NUM; i++) {
+ exynos4210_pwm_update_freq(s, s->timer[i].id);
+ }
+ break;
+
+ case TCON:
+ for (i = 0; i < EXYNOS4210_PWM_TIMERS_NUM; i++) {
+ if ((value & TCON_TIMER_MANUAL_UPD(i)) >
+ (s->reg_tcon & TCON_TIMER_MANUAL_UPD(i))) {
+ /*
+ * TCNTB and TCMPB are loaded into TCNT and TCMP.
+ * Update timers.
+ */
+
+ /* this will start timer to run, this ok, because
+ * during processing start bit timer will be stopped
+ * if needed */
+ ptimer_set_count(s->timer[i].ptimer, s->timer[i].reg_tcntb);
+ DPRINTF("set timer %d count to %x\n", i,
+ s->timer[i].reg_tcntb);
+ }
+
+ if ((value & TCON_TIMER_START(i)) >
+ (s->reg_tcon & TCON_TIMER_START(i))) {
+ /* changed to start */
+ ptimer_run(s->timer[i].ptimer, 1);
+ DPRINTF("run timer %d\n", i);
+ }
+
+ if ((value & TCON_TIMER_START(i)) <
+ (s->reg_tcon & TCON_TIMER_START(i))) {
+ /* changed to stop */
+ ptimer_stop(s->timer[i].ptimer);
+ DPRINTF("stop timer %d\n", i);
+ }
+ }
+ s->reg_tcon = value;
+ break;
+
+ case TCNTB0: case TCNTB1:
+ case TCNTB2: case TCNTB3: case TCNTB4:
+ index = (offset - TCNTB0) / 0xC;
+ s->timer[index].reg_tcntb = value;
+ break;
+
+ case TCMPB0: case TCMPB1:
+ case TCMPB2: case TCMPB3:
+ index = (offset - TCMPB0) / 0xC;
+ s->timer[index].reg_tcmpb = value;
+ break;
+
+ case TINT_CSTAT:
+ new_val = (s->reg_tint_cstat & 0x3E0) + (0x1F & value);
+ new_val &= ~(0x3E0 & value);
+
+ for (i = 0; i < EXYNOS4210_PWM_TIMERS_NUM; i++) {
+ if ((new_val & TINT_CSTAT_STATUS(i)) <
+ (s->reg_tint_cstat & TINT_CSTAT_STATUS(i))) {
+ qemu_irq_lower(s->timer[i].irq);
+ }
+ }
+
+ s->reg_tint_cstat = new_val;
+ break;
+
+ default:
+ fprintf(stderr,
+ "[exynos4210.pwm: bad write offset " TARGET_FMT_plx "]\n",
+ offset);
+ break;
+
+ }
+}
+
+/*
+ * Set default values to timer fields and registers
+ */
+static void exynos4210_pwm_reset(DeviceState *d)
+{
+ Exynos4210PWMState *s = (Exynos4210PWMState *)d;
+ int i;
+ s->reg_tcfg[0] = 0x0101;
+ s->reg_tcfg[1] = 0x0;
+ s->reg_tcon = 0;
+ s->reg_tint_cstat = 0;
+ for (i = 0; i < EXYNOS4210_PWM_TIMERS_NUM; i++) {
+ s->timer[i].reg_tcmpb = 0;
+ s->timer[i].reg_tcntb = 0;
+
+ exynos4210_pwm_update_freq(s, s->timer[i].id);
+ ptimer_stop(s->timer[i].ptimer);
+ }
+}
+
+static const MemoryRegionOps exynos4210_pwm_ops = {
+ .read = exynos4210_pwm_read,
+ .write = exynos4210_pwm_write,
+ .endianness = DEVICE_NATIVE_ENDIAN,
+};
+
+/*
+ * PWM timer initialization
+ */
+static int exynos4210_pwm_init(SysBusDevice *dev)
+{
+ Exynos4210PWMState *s = FROM_SYSBUS(Exynos4210PWMState, dev);
+ int i;
+ QEMUBH *bh;
+
+ for (i = 0; i < EXYNOS4210_PWM_TIMERS_NUM; i++) {
+ bh = qemu_bh_new(exynos4210_pwm_tick, &s->timer[i]);
+ sysbus_init_irq(dev, &s->timer[i].irq);
+ s->timer[i].ptimer = ptimer_init(bh);
+ s->timer[i].id = i;
+ s->timer[i].parent = s;
+ }
+
+ memory_region_init_io(&s->iomem, &exynos4210_pwm_ops, s, "exynos4210-pwm",
+ EXYNOS4210_PWM_REG_MEM_SIZE);
+ sysbus_init_mmio(dev, &s->iomem);
+
+ return 0;
+}
+
+static void exynos4210_pwm_class_init(ObjectClass *klass, void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(klass);
+ SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass);
+
+ k->init = exynos4210_pwm_init;
+ dc->reset = exynos4210_pwm_reset;
+ dc->vmsd = &vmstate_exynos4210_pwm_state;
+}
+
+static TypeInfo exynos4210_pwm_info = {
+ .name = "exynos4210.pwm",
+ .parent = TYPE_SYS_BUS_DEVICE,
+ .instance_size = sizeof(Exynos4210PWMState),
+ .class_init = exynos4210_pwm_class_init,
+};
+
+static void exynos4210_pwm_register_types(void)
+{
+ type_register_static(&exynos4210_pwm_info);
+}
+
+type_init(exynos4210_pwm_register_types)
diff --git a/hw/exynos4210_uart.c b/hw/exynos4210_uart.c
new file mode 100644
index 0000000000..73a9c18f30
--- /dev/null
+++ b/hw/exynos4210_uart.c
@@ -0,0 +1,676 @@
+/*
+ * Exynos4210 UART Emulation
+ *
+ * Copyright (C) 2011 Samsung Electronics Co Ltd.
+ * Maksim Kozlov, <m.kozlov@samsung.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "sysbus.h"
+#include "sysemu.h"
+#include "qemu-char.h"
+
+#include "exynos4210.h"
+
+#undef DEBUG_UART
+#undef DEBUG_UART_EXTEND
+#undef DEBUG_IRQ
+#undef DEBUG_Rx_DATA
+#undef DEBUG_Tx_DATA
+
+#define DEBUG_UART 0
+#define DEBUG_UART_EXTEND 0
+#define DEBUG_IRQ 0
+#define DEBUG_Rx_DATA 0
+#define DEBUG_Tx_DATA 0
+
+#if DEBUG_UART
+#define PRINT_DEBUG(fmt, args...) \
+ do { \
+ fprintf(stderr, " [%s:%d] "fmt, __func__, __LINE__, ##args); \
+ } while (0)
+
+#if DEBUG_UART_EXTEND
+#define PRINT_DEBUG_EXTEND(fmt, args...) \
+ do { \
+ fprintf(stderr, " [%s:%d] "fmt, __func__, __LINE__, ##args); \
+ } while (0)
+#else
+#define PRINT_DEBUG_EXTEND(fmt, args...) \
+ do {} while (0)
+#endif /* EXTEND */
+
+#else
+#define PRINT_DEBUG(fmt, args...) \
+ do {} while (0)
+#define PRINT_DEBUG_EXTEND(fmt, args...) \
+ do {} while (0)
+#endif
+
+#define PRINT_ERROR(fmt, args...) \
+ do { \
+ fprintf(stderr, " [%s:%d] "fmt, __func__, __LINE__, ##args); \
+ } while (0)
+
+/*
+ * Offsets for UART registers relative to SFR base address
+ * for UARTn
+ *
+ */
+#define ULCON 0x0000 /* Line Control */
+#define UCON 0x0004 /* Control */
+#define UFCON 0x0008 /* FIFO Control */
+#define UMCON 0x000C /* Modem Control */
+#define UTRSTAT 0x0010 /* Tx/Rx Status */
+#define UERSTAT 0x0014 /* UART Error Status */
+#define UFSTAT 0x0018 /* FIFO Status */
+#define UMSTAT 0x001C /* Modem Status */
+#define UTXH 0x0020 /* Transmit Buffer */
+#define URXH 0x0024 /* Receive Buffer */
+#define UBRDIV 0x0028 /* Baud Rate Divisor */
+#define UFRACVAL 0x002C /* Divisor Fractional Value */
+#define UINTP 0x0030 /* Interrupt Pending */
+#define UINTSP 0x0034 /* Interrupt Source Pending */
+#define UINTM 0x0038 /* Interrupt Mask */
+
+/*
+ * for indexing register in the uint32_t array
+ *
+ * 'reg' - register offset (see offsets definitions above)
+ *
+ */
+#define I_(reg) (reg / sizeof(uint32_t))
+
+typedef struct Exynos4210UartReg {
+ const char *name; /* the only reason is the debug output */
+ target_phys_addr_t offset;
+ uint32_t reset_value;
+} Exynos4210UartReg;
+
+static Exynos4210UartReg exynos4210_uart_regs[] = {
+ {"ULCON", ULCON, 0x00000000},
+ {"UCON", UCON, 0x00003000},
+ {"UFCON", UFCON, 0x00000000},
+ {"UMCON", UMCON, 0x00000000},
+ {"UTRSTAT", UTRSTAT, 0x00000006}, /* RO */
+ {"UERSTAT", UERSTAT, 0x00000000}, /* RO */
+ {"UFSTAT", UFSTAT, 0x00000000}, /* RO */
+ {"UMSTAT", UMSTAT, 0x00000000}, /* RO */
+ {"UTXH", UTXH, 0x5c5c5c5c}, /* WO, undefined reset value*/
+ {"URXH", URXH, 0x00000000}, /* RO */
+ {"UBRDIV", UBRDIV, 0x00000000},
+ {"UFRACVAL", UFRACVAL, 0x00000000},
+ {"UINTP", UINTP, 0x00000000},
+ {"UINTSP", UINTSP, 0x00000000},
+ {"UINTM", UINTM, 0x00000000},
+};
+
+#define EXYNOS4210_UART_REGS_MEM_SIZE 0x3C
+
+/* UART FIFO Control */
+#define UFCON_FIFO_ENABLE 0x1
+#define UFCON_Rx_FIFO_RESET 0x2
+#define UFCON_Tx_FIFO_RESET 0x4
+#define UFCON_Tx_FIFO_TRIGGER_LEVEL_SHIFT 8
+#define UFCON_Tx_FIFO_TRIGGER_LEVEL (7 << UFCON_Tx_FIFO_TRIGGER_LEVEL_SHIFT)
+#define UFCON_Rx_FIFO_TRIGGER_LEVEL_SHIFT 4
+#define UFCON_Rx_FIFO_TRIGGER_LEVEL (7 << UFCON_Rx_FIFO_TRIGGER_LEVEL_SHIFT)
+
+/* Uart FIFO Status */
+#define UFSTAT_Rx_FIFO_COUNT 0xff
+#define UFSTAT_Rx_FIFO_FULL 0x100
+#define UFSTAT_Rx_FIFO_ERROR 0x200
+#define UFSTAT_Tx_FIFO_COUNT_SHIFT 16
+#define UFSTAT_Tx_FIFO_COUNT (0xff << UFSTAT_Tx_FIFO_COUNT_SHIFT)
+#define UFSTAT_Tx_FIFO_FULL_SHIFT 24
+#define UFSTAT_Tx_FIFO_FULL (1 << UFSTAT_Tx_FIFO_FULL_SHIFT)
+
+/* UART Interrupt Source Pending */
+#define UINTSP_RXD 0x1 /* Receive interrupt */
+#define UINTSP_ERROR 0x2 /* Error interrupt */
+#define UINTSP_TXD 0x4 /* Transmit interrupt */
+#define UINTSP_MODEM 0x8 /* Modem interrupt */
+
+/* UART Line Control */
+#define ULCON_IR_MODE_SHIFT 6
+#define ULCON_PARITY_SHIFT 3
+#define ULCON_STOP_BIT_SHIFT 1
+
+/* UART Tx/Rx Status */
+#define UTRSTAT_TRANSMITTER_EMPTY 0x4
+#define UTRSTAT_Tx_BUFFER_EMPTY 0x2
+#define UTRSTAT_Rx_BUFFER_DATA_READY 0x1
+
+/* UART Error Status */
+#define UERSTAT_OVERRUN 0x1
+#define UERSTAT_PARITY 0x2
+#define UERSTAT_FRAME 0x4
+#define UERSTAT_BREAK 0x8
+
+typedef struct {
+ uint8_t *data;
+ uint32_t sp, rp; /* store and retrieve pointers */
+ uint32_t size;
+} Exynos4210UartFIFO;
+
+typedef struct {
+ SysBusDevice busdev;
+ MemoryRegion iomem;
+
+ uint32_t reg[EXYNOS4210_UART_REGS_MEM_SIZE / sizeof(uint32_t)];
+ Exynos4210UartFIFO rx;
+ Exynos4210UartFIFO tx;
+
+ CharDriverState *chr;
+ qemu_irq irq;
+
+ uint32_t channel;
+
+} Exynos4210UartState;
+
+
+#if DEBUG_UART
+/* Used only for debugging inside PRINT_DEBUG_... macros */
+static const char *exynos4210_uart_regname(target_phys_addr_t offset)
+{
+
+ int regs_number = sizeof(exynos4210_uart_regs) / sizeof(Exynos4210UartReg);
+ int i;
+
+ for (i = 0; i < regs_number; i++) {
+ if (offset == exynos4210_uart_regs[i].offset) {
+ return exynos4210_uart_regs[i].name;
+ }
+ }
+
+ return NULL;
+}
+#endif
+
+
+static void fifo_store(Exynos4210UartFIFO *q, uint8_t ch)
+{
+ q->data[q->sp] = ch;
+ q->sp = (q->sp + 1) % q->size;
+}
+
+static uint8_t fifo_retrieve(Exynos4210UartFIFO *q)
+{
+ uint8_t ret = q->data[q->rp];
+ q->rp = (q->rp + 1) % q->size;
+ return ret;
+}
+
+static int fifo_elements_number(Exynos4210UartFIFO *q)
+{
+ if (q->sp < q->rp) {
+ return q->size - q->rp + q->sp;
+ }
+
+ return q->sp - q->rp;
+}
+
+static int fifo_empty_elements_number(Exynos4210UartFIFO *q)
+{
+ return q->size - fifo_elements_number(q);
+}
+
+static void fifo_reset(Exynos4210UartFIFO *q)
+{
+ if (q->data != NULL) {
+ g_free(q->data);
+ q->data = NULL;
+ }
+
+ q->data = (uint8_t *)g_malloc0(q->size);
+
+ q->sp = 0;
+ q->rp = 0;
+}
+
+static uint32_t exynos4210_uart_Tx_FIFO_trigger_level(Exynos4210UartState *s)
+{
+ uint32_t level = 0;
+ uint32_t reg;
+
+ reg = (s->reg[I_(UFCON)] && UFCON_Tx_FIFO_TRIGGER_LEVEL) >>
+ UFCON_Tx_FIFO_TRIGGER_LEVEL_SHIFT;
+
+ switch (s->channel) {
+ case 0:
+ level = reg * 32;
+ break;
+ case 1:
+ case 4:
+ level = reg * 8;
+ break;
+ case 2:
+ case 3:
+ level = reg * 2;
+ break;
+ default:
+ level = 0;
+ PRINT_ERROR("Wrong UART channel number: %d\n", s->channel);
+ }
+
+ return level;
+}
+
+static void exynos4210_uart_update_irq(Exynos4210UartState *s)
+{
+ /*
+ * The Tx interrupt is always requested if the number of data in the
+ * transmit FIFO is smaller than the trigger level.
+ */
+ if (s->reg[I_(UFCON)] && UFCON_FIFO_ENABLE) {
+
+ uint32_t count = (s->reg[I_(UFSTAT)] && UFSTAT_Tx_FIFO_COUNT) >>
+ UFSTAT_Tx_FIFO_COUNT_SHIFT;
+
+ if (count <= exynos4210_uart_Tx_FIFO_trigger_level(s)) {
+ s->reg[I_(UINTSP)] |= UINTSP_TXD;
+ }
+ }
+
+ s->reg[I_(UINTP)] = s->reg[I_(UINTSP)] & ~s->reg[I_(UINTM)];
+
+ if (s->reg[I_(UINTP)]) {
+ qemu_irq_raise(s->irq);
+
+#if DEBUG_IRQ
+ fprintf(stderr, "UART%d: IRQ has been raised: %08x\n",
+ s->channel, s->reg[I_(UINTP)]);
+#endif
+
+ } else {
+ qemu_irq_lower(s->irq);
+ }
+}
+
+static void exynos4210_uart_update_parameters(Exynos4210UartState *s)
+{
+ int speed, parity, data_bits, stop_bits, frame_size;
+ QEMUSerialSetParams ssp;
+ uint64_t uclk_rate;
+
+ if (s->reg[I_(UBRDIV)] == 0) {
+ return;
+ }
+
+ frame_size = 1; /* start bit */
+ if (s->reg[I_(ULCON)] & 0x20) {
+ frame_size++; /* parity bit */
+ if (s->reg[I_(ULCON)] & 0x28) {
+ parity = 'E';
+ } else {
+ parity = 'O';
+ }
+ } else {
+ parity = 'N';
+ }
+
+ if (s->reg[I_(ULCON)] & 0x4) {
+ stop_bits = 2;
+ } else {
+ stop_bits = 1;
+ }
+
+ data_bits = (s->reg[I_(ULCON)] & 0x3) + 5;
+
+ frame_size += data_bits + stop_bits;
+
+ uclk_rate = 24000000;
+
+ speed = uclk_rate / ((16 * (s->reg[I_(UBRDIV)]) & 0xffff) +
+ (s->reg[I_(UFRACVAL)] & 0x7) + 16);
+
+ ssp.speed = speed;
+ ssp.parity = parity;
+ ssp.data_bits = data_bits;
+ ssp.stop_bits = stop_bits;
+
+ qemu_chr_fe_ioctl(s->chr, CHR_IOCTL_SERIAL_SET_PARAMS, &ssp);
+
+ PRINT_DEBUG("UART%d: speed: %d, parity: %c, data: %d, stop: %d\n",
+ s->channel, speed, parity, data_bits, stop_bits);
+}
+
+static void exynos4210_uart_write(void *opaque, target_phys_addr_t offset,
+ uint64_t val, unsigned size)
+{
+ Exynos4210UartState *s = (Exynos4210UartState *)opaque;
+ uint8_t ch;
+
+ PRINT_DEBUG_EXTEND("UART%d: <0x%04x> %s <- 0x%08llx\n", s->channel,
+ offset, exynos4210_uart_regname(offset), (long long unsigned int)val);
+
+ switch (offset) {
+ case ULCON:
+ case UBRDIV:
+ case UFRACVAL:
+ s->reg[I_(offset)] = val;
+ exynos4210_uart_update_parameters(s);
+ break;
+ case UFCON:
+ s->reg[I_(UFCON)] = val;
+ if (val & UFCON_Rx_FIFO_RESET) {
+ fifo_reset(&s->rx);
+ s->reg[I_(UFCON)] &= ~UFCON_Rx_FIFO_RESET;
+ PRINT_DEBUG("UART%d: Rx FIFO Reset\n", s->channel);
+ }
+ if (val & UFCON_Tx_FIFO_RESET) {
+ fifo_reset(&s->tx);
+ s->reg[I_(UFCON)] &= ~UFCON_Tx_FIFO_RESET;
+ PRINT_DEBUG("UART%d: Tx FIFO Reset\n", s->channel);
+ }
+ break;
+
+ case UTXH:
+ if (s->chr) {
+ s->reg[I_(UTRSTAT)] &= ~(UTRSTAT_TRANSMITTER_EMPTY |
+ UTRSTAT_Tx_BUFFER_EMPTY);
+ ch = (uint8_t)val;
+ qemu_chr_fe_write(s->chr, &ch, 1);
+#if DEBUG_Tx_DATA
+ fprintf(stderr, "%c", ch);
+#endif
+ s->reg[I_(UTRSTAT)] |= UTRSTAT_TRANSMITTER_EMPTY |
+ UTRSTAT_Tx_BUFFER_EMPTY;
+ s->reg[I_(UINTSP)] |= UINTSP_TXD;
+ exynos4210_uart_update_irq(s);
+ }
+ break;
+
+ case UINTP:
+ s->reg[I_(UINTP)] &= ~val;
+ s->reg[I_(UINTSP)] &= ~val;
+ PRINT_DEBUG("UART%d: UINTP [%04x] have been cleared: %08x\n",
+ s->channel, offset, s->reg[I_(UINTP)]);
+ exynos4210_uart_update_irq(s);
+ break;
+ case UTRSTAT:
+ case UERSTAT:
+ case UFSTAT:
+ case UMSTAT:
+ case URXH:
+ PRINT_DEBUG("UART%d: Trying to write into RO register: %s [%04x]\n",
+ s->channel, exynos4210_uart_regname(offset), offset);
+ break;
+ case UINTSP:
+ s->reg[I_(UINTSP)] &= ~val;
+ break;
+ case UINTM:
+ s->reg[I_(UINTM)] = val;
+ exynos4210_uart_update_irq(s);
+ break;
+ case UCON:
+ case UMCON:
+ default:
+ s->reg[I_(offset)] = val;
+ break;
+ }
+}
+static uint64_t exynos4210_uart_read(void *opaque, target_phys_addr_t offset,
+ unsigned size)
+{
+ Exynos4210UartState *s = (Exynos4210UartState *)opaque;
+ uint32_t res;
+
+ switch (offset) {
+ case UERSTAT: /* Read Only */
+ res = s->reg[I_(UERSTAT)];
+ s->reg[I_(UERSTAT)] = 0;
+ return res;
+ case UFSTAT: /* Read Only */
+ s->reg[I_(UFSTAT)] = fifo_elements_number(&s->rx) & 0xff;
+ if (fifo_empty_elements_number(&s->rx) == 0) {
+ s->reg[I_(UFSTAT)] |= UFSTAT_Rx_FIFO_FULL;
+ s->reg[I_(UFSTAT)] &= ~0xff;
+ }
+ return s->reg[I_(UFSTAT)];
+ case URXH:
+ if (s->reg[I_(UFCON)] & UFCON_FIFO_ENABLE) {
+ if (fifo_elements_number(&s->rx)) {
+ res = fifo_retrieve(&s->rx);
+#if DEBUG_Rx_DATA
+ fprintf(stderr, "%c", res);
+#endif
+ if (!fifo_elements_number(&s->rx)) {
+ s->reg[I_(UTRSTAT)] &= ~UTRSTAT_Rx_BUFFER_DATA_READY;
+ } else {
+ s->reg[I_(UTRSTAT)] |= UTRSTAT_Rx_BUFFER_DATA_READY;
+ }
+ } else {
+ s->reg[I_(UINTSP)] |= UINTSP_ERROR;
+ exynos4210_uart_update_irq(s);
+ res = 0;
+ }
+ } else {
+ s->reg[I_(UTRSTAT)] &= ~UTRSTAT_Rx_BUFFER_DATA_READY;
+ res = s->reg[I_(URXH)];
+ }
+ return res;
+ case UTXH:
+ PRINT_DEBUG("UART%d: Trying to read from WO register: %s [%04x]\n",
+ s->channel, exynos4210_uart_regname(offset), offset);
+ break;
+ default:
+ return s->reg[I_(offset)];
+ }
+
+ return 0;
+}
+
+static const MemoryRegionOps exynos4210_uart_ops = {
+ .read = exynos4210_uart_read,
+ .write = exynos4210_uart_write,
+ .endianness = DEVICE_NATIVE_ENDIAN,
+ .valid = {
+ .max_access_size = 4,
+ .unaligned = false
+ },
+};
+
+static int exynos4210_uart_can_receive(void *opaque)
+{
+ Exynos4210UartState *s = (Exynos4210UartState *)opaque;
+
+ return fifo_empty_elements_number(&s->rx);
+}
+
+
+static void exynos4210_uart_receive(void *opaque, const uint8_t *buf, int size)
+{
+ Exynos4210UartState *s = (Exynos4210UartState *)opaque;
+ int i;
+
+ if (s->reg[I_(UFCON)] & UFCON_FIFO_ENABLE) {
+ if (fifo_empty_elements_number(&s->rx) < size) {
+ for (i = 0; i < fifo_empty_elements_number(&s->rx); i++) {
+ fifo_store(&s->rx, buf[i]);
+ }
+ s->reg[I_(UINTSP)] |= UINTSP_ERROR;
+ s->reg[I_(UTRSTAT)] |= UTRSTAT_Rx_BUFFER_DATA_READY;
+ } else {
+ for (i = 0; i < size; i++) {
+ fifo_store(&s->rx, buf[i]);
+ }
+ s->reg[I_(UTRSTAT)] |= UTRSTAT_Rx_BUFFER_DATA_READY;
+ }
+ /* XXX: Around here we maybe should check Rx trigger level */
+ s->reg[I_(UINTSP)] |= UINTSP_RXD;
+ } else {
+ s->reg[I_(URXH)] = buf[0];
+ s->reg[I_(UINTSP)] |= UINTSP_RXD;
+ s->reg[I_(UTRSTAT)] |= UTRSTAT_Rx_BUFFER_DATA_READY;
+ }
+
+ exynos4210_uart_update_irq(s);
+}
+
+
+static void exynos4210_uart_event(void *opaque, int event)
+{
+ Exynos4210UartState *s = (Exynos4210UartState *)opaque;
+
+ if (event == CHR_EVENT_BREAK) {
+ /* When the RxDn is held in logic 0, then a null byte is pushed into the
+ * fifo */
+ fifo_store(&s->rx, '\0');
+ s->reg[I_(UERSTAT)] |= UERSTAT_BREAK;
+ exynos4210_uart_update_irq(s);
+ }
+}
+
+
+static void exynos4210_uart_reset(DeviceState *dev)
+{
+ Exynos4210UartState *s =
+ container_of(dev, Exynos4210UartState, busdev.qdev);
+ int regs_number = sizeof(exynos4210_uart_regs)/sizeof(Exynos4210UartReg);
+ int i;
+
+ for (i = 0; i < regs_number; i++) {
+ s->reg[I_(exynos4210_uart_regs[i].offset)] =
+ exynos4210_uart_regs[i].reset_value;
+ }
+
+ fifo_reset(&s->rx);
+ fifo_reset(&s->tx);
+
+ PRINT_DEBUG("UART%d: Rx FIFO size: %d\n", s->channel, s->rx.size);
+}
+
+static const VMStateDescription vmstate_exynos4210_uart_fifo = {
+ .name = "exynos4210.uart.fifo",
+ .version_id = 1,
+ .minimum_version_id = 1,
+ .minimum_version_id_old = 1,
+ .fields = (VMStateField[]) {
+ VMSTATE_UINT32(sp, Exynos4210UartFIFO),
+ VMSTATE_UINT32(rp, Exynos4210UartFIFO),
+ VMSTATE_VBUFFER_UINT32(data, Exynos4210UartFIFO, 1, NULL, 0, size),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
+static const VMStateDescription vmstate_exynos4210_uart = {
+ .name = "exynos4210.uart",
+ .version_id = 1,
+ .minimum_version_id = 1,
+ .minimum_version_id_old = 1,
+ .fields = (VMStateField[]) {
+ VMSTATE_STRUCT(rx, Exynos4210UartState, 1,
+ vmstate_exynos4210_uart_fifo, Exynos4210UartFIFO),
+ VMSTATE_UINT32_ARRAY(reg, Exynos4210UartState,
+ EXYNOS4210_UART_REGS_MEM_SIZE / sizeof(uint32_t)),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
+DeviceState *exynos4210_uart_create(target_phys_addr_t addr,
+ int fifo_size,
+ int channel,
+ CharDriverState *chr,
+ qemu_irq irq)
+{
+ DeviceState *dev;
+ SysBusDevice *bus;
+
+ const char chr_name[] = "serial";
+ char label[ARRAY_SIZE(chr_name) + 1];
+
+ dev = qdev_create(NULL, "exynos4210.uart");
+
+ if (!chr) {
+ if (channel >= MAX_SERIAL_PORTS) {
+ hw_error("Only %d serial ports are supported by QEMU.\n",
+ MAX_SERIAL_PORTS);
+ }
+ chr = serial_hds[channel];
+ if (!chr) {
+ snprintf(label, ARRAY_SIZE(label), "%s%d", chr_name, channel);
+ chr = qemu_chr_new(label, "null", NULL);
+ if (!(chr)) {
+ hw_error("Can't assign serial port to UART%d.\n", channel);
+ }
+ }
+ }
+
+ qdev_prop_set_chr(dev, "chardev", chr);
+ qdev_prop_set_uint32(dev, "channel", channel);
+ qdev_prop_set_uint32(dev, "rx-size", fifo_size);
+ qdev_prop_set_uint32(dev, "tx-size", fifo_size);
+
+ bus = sysbus_from_qdev(dev);
+ qdev_init_nofail(dev);
+ if (addr != (target_phys_addr_t)-1) {
+ sysbus_mmio_map(bus, 0, addr);
+ }
+ sysbus_connect_irq(bus, 0, irq);
+
+ return dev;
+}
+
+static int exynos4210_uart_init(SysBusDevice *dev)
+{
+ Exynos4210UartState *s = FROM_SYSBUS(Exynos4210UartState, dev);
+
+ /* memory mapping */
+ memory_region_init_io(&s->iomem, &exynos4210_uart_ops, s, "exynos4210.uart",
+ EXYNOS4210_UART_REGS_MEM_SIZE);
+ sysbus_init_mmio(dev, &s->iomem);
+
+ sysbus_init_irq(dev, &s->irq);
+
+ qemu_chr_add_handlers(s->chr, exynos4210_uart_can_receive,
+ exynos4210_uart_receive, exynos4210_uart_event, s);
+
+ return 0;
+}
+
+static Property exynos4210_uart_properties[] = {
+ DEFINE_PROP_CHR("chardev", Exynos4210UartState, chr),
+ DEFINE_PROP_UINT32("channel", Exynos4210UartState, channel, 0),
+ DEFINE_PROP_UINT32("rx-size", Exynos4210UartState, rx.size, 16),
+ DEFINE_PROP_UINT32("tx-size", Exynos4210UartState, tx.size, 16),
+ DEFINE_PROP_END_OF_LIST(),
+};
+
+static void exynos4210_uart_class_init(ObjectClass *klass, void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(klass);
+ SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass);
+
+ k->init = exynos4210_uart_init;
+ dc->reset = exynos4210_uart_reset;
+ dc->props = exynos4210_uart_properties;
+ dc->vmsd = &vmstate_exynos4210_uart;
+}
+
+static TypeInfo exynos4210_uart_info = {
+ .name = "exynos4210.uart",
+ .parent = TYPE_SYS_BUS_DEVICE,
+ .instance_size = sizeof(Exynos4210UartState),
+ .class_init = exynos4210_uart_class_init,
+};
+
+static void exynos4210_uart_register(void)
+{
+ type_register_static(&exynos4210_uart_info);
+}
+
+type_init(exynos4210_uart_register)
diff --git a/hw/exynos4_boards.c b/hw/exynos4_boards.c
new file mode 100644
index 0000000000..553a02b910
--- /dev/null
+++ b/hw/exynos4_boards.c
@@ -0,0 +1,177 @@
+/*
+ * Samsung exynos4 SoC based boards emulation
+ *
+ * Copyright (c) 2011 Samsung Electronics Co., Ltd. All rights reserved.
+ * Maksim Kozlov <m.kozlov@samsung.com>
+ * Evgeny Voevodin <e.voevodin@samsung.com>
+ * Igor Mitsyanko <i.mitsyanko@samsung.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "sysemu.h"
+#include "sysbus.h"
+#include "net.h"
+#include "arm-misc.h"
+#include "exec-memory.h"
+#include "exynos4210.h"
+#include "boards.h"
+
+#undef DEBUG
+
+//#define DEBUG
+
+#ifdef DEBUG
+ #undef PRINT_DEBUG
+ #define PRINT_DEBUG(fmt, args...) \
+ do { \
+ fprintf(stderr, " [%s:%d] "fmt, __func__, __LINE__, ##args); \
+ } while (0)
+#else
+ #define PRINT_DEBUG(fmt, args...) do {} while (0)
+#endif
+
+#define SMDK_LAN9118_BASE_ADDR 0x05000000
+
+typedef enum Exynos4BoardType {
+ EXYNOS4_BOARD_NURI,
+ EXYNOS4_BOARD_SMDKC210,
+ EXYNOS4_NUM_OF_BOARDS
+} Exynos4BoardType;
+
+static int exynos4_board_id[EXYNOS4_NUM_OF_BOARDS] = {
+ [EXYNOS4_BOARD_NURI] = 0xD33,
+ [EXYNOS4_BOARD_SMDKC210] = 0xB16,
+};
+
+static int exynos4_board_smp_bootreg_addr[EXYNOS4_NUM_OF_BOARDS] = {
+ [EXYNOS4_BOARD_NURI] = EXYNOS4210_SECOND_CPU_BOOTREG,
+ [EXYNOS4_BOARD_SMDKC210] = EXYNOS4210_SECOND_CPU_BOOTREG,
+};
+
+static unsigned long exynos4_board_ram_size[EXYNOS4_NUM_OF_BOARDS] = {
+ [EXYNOS4_BOARD_NURI] = 0x40000000,
+ [EXYNOS4_BOARD_SMDKC210] = 0x40000000,
+};
+
+static struct arm_boot_info exynos4_board_binfo = {
+ .loader_start = EXYNOS4210_BASE_BOOT_ADDR,
+ .smp_loader_start = EXYNOS4210_SMP_BOOT_ADDR,
+ .nb_cpus = EXYNOS4210_NCPUS,
+};
+
+static QEMUMachine exynos4_machines[EXYNOS4_NUM_OF_BOARDS];
+
+static void lan9215_init(uint32_t base, qemu_irq irq)
+{
+ DeviceState *dev;
+ SysBusDevice *s;
+
+ /* This should be a 9215 but the 9118 is close enough */
+ if (nd_table[0].vlan) {
+ qemu_check_nic_model(&nd_table[0], "lan9118");
+ dev = qdev_create(NULL, "lan9118");
+ qdev_set_nic_properties(dev, &nd_table[0]);
+ qdev_prop_set_uint32(dev, "mode_16bit", 1);
+ qdev_init_nofail(dev);
+ s = sysbus_from_qdev(dev);
+ sysbus_mmio_map(s, 0, base);
+ sysbus_connect_irq(s, 0, irq);
+ }
+}
+
+static Exynos4210State *exynos4_boards_init_common(
+ const char *kernel_filename,
+ const char *kernel_cmdline,
+ const char *initrd_filename,
+ Exynos4BoardType board_type)
+{
+ if (smp_cpus != EXYNOS4210_NCPUS) {
+ fprintf(stderr, "%s board supports only %d CPU cores. Ignoring smp_cpus"
+ " value.\n",
+ exynos4_machines[board_type].name,
+ exynos4_machines[board_type].max_cpus);
+ }
+
+ exynos4_board_binfo.ram_size = exynos4_board_ram_size[board_type];
+ exynos4_board_binfo.board_id = exynos4_board_id[board_type];
+ exynos4_board_binfo.smp_bootreg_addr =
+ exynos4_board_smp_bootreg_addr[board_type];
+ exynos4_board_binfo.kernel_filename = kernel_filename;
+ exynos4_board_binfo.initrd_filename = initrd_filename;
+ exynos4_board_binfo.kernel_cmdline = kernel_cmdline;
+ exynos4_board_binfo.gic_cpu_if_addr =
+ EXYNOS4210_SMP_PRIVATE_BASE_ADDR + 0x100;
+
+ PRINT_DEBUG("\n ram_size: %luMiB [0x%08lx]\n"
+ " kernel_filename: %s\n"
+ " kernel_cmdline: %s\n"
+ " initrd_filename: %s\n",
+ exynos4_board_ram_size[board_type] / 1048576,
+ exynos4_board_ram_size[board_type],
+ kernel_filename,
+ kernel_cmdline,
+ initrd_filename);
+
+ return exynos4210_init(get_system_memory(),
+ exynos4_board_ram_size[board_type]);
+}
+
+static void nuri_init(ram_addr_t ram_size,
+ const char *boot_device,
+ const char *kernel_filename, const char *kernel_cmdline,
+ const char *initrd_filename, const char *cpu_model)
+{
+ exynos4_boards_init_common(kernel_filename, kernel_cmdline,
+ initrd_filename, EXYNOS4_BOARD_NURI);
+
+ arm_load_kernel(first_cpu, &exynos4_board_binfo);
+}
+
+static void smdkc210_init(ram_addr_t ram_size,
+ const char *boot_device,
+ const char *kernel_filename, const char *kernel_cmdline,
+ const char *initrd_filename, const char *cpu_model)
+{
+ Exynos4210State *s = exynos4_boards_init_common(kernel_filename,
+ kernel_cmdline, initrd_filename, EXYNOS4_BOARD_SMDKC210);
+
+ lan9215_init(SMDK_LAN9118_BASE_ADDR,
+ qemu_irq_invert(s->irq_table[exynos4210_get_irq(37, 1)]));
+ arm_load_kernel(first_cpu, &exynos4_board_binfo);
+}
+
+static QEMUMachine exynos4_machines[EXYNOS4_NUM_OF_BOARDS] = {
+ [EXYNOS4_BOARD_NURI] = {
+ .name = "nuri",
+ .desc = "Samsung NURI board (Exynos4210)",
+ .init = nuri_init,
+ .max_cpus = EXYNOS4210_NCPUS,
+ },
+ [EXYNOS4_BOARD_SMDKC210] = {
+ .name = "smdkc210",
+ .desc = "Samsung SMDKC210 board (Exynos4210)",
+ .init = smdkc210_init,
+ .max_cpus = EXYNOS4210_NCPUS,
+ },
+};
+
+static void exynos4_machine_init(void)
+{
+ qemu_register_machine(&exynos4_machines[EXYNOS4_BOARD_NURI]);
+ qemu_register_machine(&exynos4_machines[EXYNOS4_BOARD_SMDKC210]);
+}
+
+machine_init(exynos4_machine_init);
diff --git a/hw/fdc.c b/hw/fdc.c
index f575a2c9f5..a0236b7295 100644
--- a/hw/fdc.c
+++ b/hw/fdc.c
@@ -62,12 +62,15 @@
#define FD_SECTOR_SC 2 /* Sector size code */
#define FD_RESET_SENSEI_COUNT 4 /* Number of sense interrupts on RESET */
+typedef struct FDCtrl FDCtrl;
+
/* Floppy disk drive emulation */
typedef enum FDiskFlags {
FDISK_DBL_SIDES = 0x01,
} FDiskFlags;
typedef struct FDrive {
+ FDCtrl *fdctrl;
BlockDriverState *bs;
/* Drive status */
FDriveType drive;
@@ -83,6 +86,7 @@ typedef struct FDrive {
uint16_t bps; /* Bytes per sector */
uint8_t ro; /* Is read-only */
uint8_t media_changed; /* Is media changed */
+ uint8_t media_rate; /* Data rate of medium */
} FDrive;
static void fd_init(FDrive *drv)
@@ -95,16 +99,19 @@ static void fd_init(FDrive *drv)
drv->max_track = 0;
}
+#define NUM_SIDES(drv) ((drv)->flags & FDISK_DBL_SIDES ? 2 : 1)
+
static int fd_sector_calc(uint8_t head, uint8_t track, uint8_t sect,
- uint8_t last_sect)
+ uint8_t last_sect, uint8_t num_sides)
{
- return (((track * 2) + head) * last_sect) + sect - 1;
+ return (((track * num_sides) + head) * last_sect) + sect - 1;
}
/* Returns current position, in sectors, for given drive */
static int fd_sector(FDrive *drv)
{
- return fd_sector_calc(drv->head, drv->track, drv->sect, drv->last_sect);
+ return fd_sector_calc(drv->head, drv->track, drv->sect, drv->last_sect,
+ NUM_SIDES(drv));
}
/* Seek to a new position:
@@ -135,7 +142,7 @@ static int fd_seek(FDrive *drv, uint8_t head, uint8_t track, uint8_t sect,
drv->max_track, drv->last_sect);
return 3;
}
- sector = fd_sector_calc(head, track, sect, drv->last_sect);
+ sector = fd_sector_calc(head, track, sect, drv->last_sect, NUM_SIDES(drv));
ret = 0;
if (sector != fd_sector(drv)) {
#if 0
@@ -169,12 +176,13 @@ static void fd_revalidate(FDrive *drv)
{
int nb_heads, max_track, last_sect, ro;
FDriveType drive;
+ FDriveRate rate;
FLOPPY_DPRINTF("revalidate\n");
if (drv->bs != NULL && bdrv_is_inserted(drv->bs)) {
ro = bdrv_is_read_only(drv->bs);
bdrv_get_floppy_geometry_hint(drv->bs, &nb_heads, &max_track,
- &last_sect, drv->drive, &drive);
+ &last_sect, drv->drive, &drive, &rate);
if (nb_heads != 0 && max_track != 0 && last_sect != 0) {
FLOPPY_DPRINTF("User defined disk (%d %d %d)",
nb_heads - 1, max_track, last_sect);
@@ -191,6 +199,7 @@ static void fd_revalidate(FDrive *drv)
drv->last_sect = last_sect;
drv->ro = ro;
drv->drive = drive;
+ drv->media_rate = rate;
} else {
FLOPPY_DPRINTF("No disk in drive\n");
drv->last_sect = 0;
@@ -202,13 +211,12 @@ static void fd_revalidate(FDrive *drv)
/********************************************************/
/* Intel 82078 floppy disk controller emulation */
-typedef struct FDCtrl FDCtrl;
-
static void fdctrl_reset(FDCtrl *fdctrl, int do_irq);
static void fdctrl_reset_fifo(FDCtrl *fdctrl);
static int fdctrl_transfer_handler (void *opaque, int nchan,
int dma_pos, int dma_len);
static void fdctrl_raise_irq(FDCtrl *fdctrl, uint8_t status0);
+static FDrive *get_cur_drv(FDCtrl *fdctrl);
static uint32_t fdctrl_read_statusA(FDCtrl *fdctrl);
static uint32_t fdctrl_read_statusB(FDCtrl *fdctrl);
@@ -221,6 +229,7 @@ static void fdctrl_write_rate(FDCtrl *fdctrl, uint32_t value);
static uint32_t fdctrl_read_data(FDCtrl *fdctrl);
static void fdctrl_write_data(FDCtrl *fdctrl, uint32_t value);
static uint32_t fdctrl_read_dir(FDCtrl *fdctrl);
+static void fdctrl_write_ccr(FDCtrl *fdctrl, uint32_t value);
enum {
FD_DIR_WRITE = 0,
@@ -245,6 +254,7 @@ enum {
FD_REG_DSR = 0x04,
FD_REG_FIFO = 0x05,
FD_REG_DIR = 0x07,
+ FD_REG_CCR = 0x07,
};
enum {
@@ -297,6 +307,8 @@ enum {
};
enum {
+ FD_SR1_MA = 0x01, /* Missing address mark */
+ FD_SR1_NW = 0x02, /* Not writable */
FD_SR1_EC = 0x80, /* End of cylinder */
};
@@ -413,6 +425,7 @@ struct FDCtrl {
int sun4m;
FDrive drives[MAX_FD];
int reset_sensei;
+ uint32_t check_media_rate;
/* Timers state */
uint8_t timer0;
uint8_t timer1;
@@ -487,6 +500,9 @@ static void fdctrl_write (void *opaque, uint32_t reg, uint32_t value)
case FD_REG_FIFO:
fdctrl_write_data(fdctrl, value);
break;
+ case FD_REG_CCR:
+ fdctrl_write_ccr(fdctrl, value);
+ break;
default:
break;
}
@@ -538,6 +554,24 @@ static const VMStateDescription vmstate_fdrive_media_changed = {
}
};
+static bool fdrive_media_rate_needed(void *opaque)
+{
+ FDrive *drive = opaque;
+
+ return drive->fdctrl->check_media_rate;
+}
+
+static const VMStateDescription vmstate_fdrive_media_rate = {
+ .name = "fdrive/media_rate",
+ .version_id = 1,
+ .minimum_version_id = 1,
+ .minimum_version_id_old = 1,
+ .fields = (VMStateField[]) {
+ VMSTATE_UINT8(media_rate, FDrive),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
static const VMStateDescription vmstate_fdrive = {
.name = "fdrive",
.version_id = 1,
@@ -554,6 +588,9 @@ static const VMStateDescription vmstate_fdrive = {
.vmsd = &vmstate_fdrive_media_changed,
.needed = &fdrive_media_changed_needed,
} , {
+ .vmsd = &vmstate_fdrive_media_rate,
+ .needed = &fdrive_media_rate_needed,
+ } , {
/* empty */
}
}
@@ -877,6 +914,23 @@ static void fdctrl_write_rate(FDCtrl *fdctrl, uint32_t value)
fdctrl->dsr = value;
}
+/* Configuration control register: 0x07 (write) */
+static void fdctrl_write_ccr(FDCtrl *fdctrl, uint32_t value)
+{
+ /* Reset mode */
+ if (!(fdctrl->dor & FD_DOR_nRESET)) {
+ FLOPPY_DPRINTF("Floppy controller in RESET state !\n");
+ return;
+ }
+ FLOPPY_DPRINTF("configuration control register set to 0x%02x\n", value);
+
+ /* Only the rate selection bits used in AT mode, and we
+ * store those in the DSR.
+ */
+ fdctrl->dsr = (fdctrl->dsr & ~FD_DSR_DRATEMASK) |
+ (value & FD_DSR_DRATEMASK);
+}
+
static int fdctrl_media_changed(FDrive *drv)
{
int ret;
@@ -903,14 +957,9 @@ static uint32_t fdctrl_read_dir(FDCtrl *fdctrl)
{
uint32_t retval = 0;
- if (fdctrl_media_changed(drv0(fdctrl))
- || fdctrl_media_changed(drv1(fdctrl))
-#if MAX_FD == 4
- || fdctrl_media_changed(drv2(fdctrl))
- || fdctrl_media_changed(drv3(fdctrl))
-#endif
- )
+ if (fdctrl_media_changed(get_cur_drv(fdctrl))) {
retval |= FD_DIR_DSKCHG;
+ }
if (retval != 0) {
FLOPPY_DPRINTF("Floppy digital input register: 0x%02x\n", retval);
}
@@ -1019,7 +1068,8 @@ static void fdctrl_start_transfer(FDCtrl *fdctrl, int direction)
ks = fdctrl->fifo[4];
FLOPPY_DPRINTF("Start transfer at %d %d %02x %02x (%d)\n",
GET_CUR_DRV(fdctrl), kh, kt, ks,
- fd_sector_calc(kh, kt, ks, cur_drv->last_sect));
+ fd_sector_calc(kh, kt, ks, cur_drv->last_sect,
+ NUM_SIDES(cur_drv)));
switch (fd_seek(cur_drv, kh, kt, ks, fdctrl->config & FD_CONFIG_EIS)) {
case 2:
/* sect too big */
@@ -1049,6 +1099,19 @@ static void fdctrl_start_transfer(FDCtrl *fdctrl, int direction)
break;
}
+ /* Check the data rate. If the programmed data rate does not match
+ * the currently inserted medium, the operation has to fail. */
+ if (fdctrl->check_media_rate &&
+ (fdctrl->dsr & FD_DSR_DRATEMASK) != cur_drv->media_rate) {
+ FLOPPY_DPRINTF("data rate mismatch (fdc=%d, media=%d)\n",
+ fdctrl->dsr & FD_DSR_DRATEMASK, cur_drv->media_rate);
+ fdctrl_stop_transfer(fdctrl, FD_SR0_ABNTERM, FD_SR1_MA, 0x00);
+ fdctrl->fifo[3] = kt;
+ fdctrl->fifo[4] = kh;
+ fdctrl->fifo[5] = ks;
+ return;
+ }
+
/* Set the FIFO state */
fdctrl->data_dir = direction;
fdctrl->data_pos = 0;
@@ -1175,6 +1238,16 @@ static int fdctrl_transfer_handler (void *opaque, int nchan,
break;
case FD_DIR_WRITE:
/* WRITE commands */
+ if (cur_drv->ro) {
+ /* Handle readonly medium early, no need to do DMA, touch the
+ * LED or attempt any writes. A real floppy doesn't attempt
+ * to write to readonly media either. */
+ fdctrl_stop_transfer(fdctrl,
+ FD_SR0_ABNTERM | FD_SR0_SEEK, FD_SR1_NW,
+ 0x00);
+ goto transfer_error;
+ }
+
DMA_read_memory (nchan, fdctrl->fifo + rel_pos,
fdctrl->data_pos, len);
if (bdrv_write(cur_drv->bs, fd_sector(cur_drv),
@@ -1289,7 +1362,8 @@ static void fdctrl_format_sector(FDCtrl *fdctrl)
ks = fdctrl->fifo[8];
FLOPPY_DPRINTF("format sector at %d %d %02x %02x (%d)\n",
GET_CUR_DRV(fdctrl), kh, kt, ks,
- fd_sector_calc(kh, kt, ks, cur_drv->last_sect));
+ fd_sector_calc(kh, kt, ks, cur_drv->last_sect,
+ NUM_SIDES(cur_drv)));
switch (fd_seek(cur_drv, kh, kt, ks, fdctrl->config & FD_CONFIG_EIS)) {
case 2:
/* sect too big */
@@ -1343,7 +1417,7 @@ static void fdctrl_handle_lock(FDCtrl *fdctrl, int direction)
{
fdctrl->lock = (fdctrl->fifo[0] & 0x80) ? 1 : 0;
fdctrl->fifo[0] = fdctrl->lock << 4;
- fdctrl_set_fifo(fdctrl, 1, fdctrl->lock);
+ fdctrl_set_fifo(fdctrl, 1, 0);
}
static void fdctrl_handle_dumpreg(FDCtrl *fdctrl, int direction)
@@ -1375,7 +1449,7 @@ static void fdctrl_handle_version(FDCtrl *fdctrl, int direction)
{
/* Controller's version */
fdctrl->fifo[0] = fdctrl->version;
- fdctrl_set_fifo(fdctrl, 1, 1);
+ fdctrl_set_fifo(fdctrl, 1, 0);
}
static void fdctrl_handle_partid(FDCtrl *fdctrl, int direction)
@@ -1434,14 +1508,13 @@ static void fdctrl_handle_save(FDCtrl *fdctrl, int direction)
fdctrl->fifo[12] = fdctrl->pwrd;
fdctrl->fifo[13] = 0;
fdctrl->fifo[14] = 0;
- fdctrl_set_fifo(fdctrl, 15, 1);
+ fdctrl_set_fifo(fdctrl, 15, 0);
}
static void fdctrl_handle_readid(FDCtrl *fdctrl, int direction)
{
FDrive *cur_drv = get_cur_drv(fdctrl);
- /* XXX: should set main status register to busy */
cur_drv->head = (fdctrl->fifo[1] >> 2) & 1;
qemu_mod_timer(fdctrl->result_timer,
qemu_get_clock_ns(vm_clock) + (get_ticks_per_sec() / 50));
@@ -1545,13 +1618,16 @@ static void fdctrl_handle_seek(FDCtrl *fdctrl, int direction)
SET_CUR_DRV(fdctrl, fdctrl->fifo[1] & FD_DOR_SELMASK);
cur_drv = get_cur_drv(fdctrl);
fdctrl_reset_fifo(fdctrl);
+ /* The seek command just sends step pulses to the drive and doesn't care if
+ * there is a medium inserted of if it's banging the head against the drive.
+ */
if (fdctrl->fifo[2] > cur_drv->max_track) {
- fdctrl_raise_irq(fdctrl, FD_SR0_ABNTERM | FD_SR0_SEEK);
+ cur_drv->track = cur_drv->max_track;
} else {
cur_drv->track = fdctrl->fifo[2];
- /* Raise Interrupt */
- fdctrl_raise_irq(fdctrl, FD_SR0_SEEK);
}
+ /* Raise Interrupt */
+ fdctrl_raise_irq(fdctrl, FD_SR0_SEEK);
}
static void fdctrl_handle_perpendicular_mode(FDCtrl *fdctrl, int direction)
@@ -1576,7 +1652,7 @@ static void fdctrl_handle_powerdown_mode(FDCtrl *fdctrl, int direction)
{
fdctrl->pwrd = fdctrl->fifo[1];
fdctrl->fifo[0] = fdctrl->fifo[1];
- fdctrl_set_fifo(fdctrl, 1, 1);
+ fdctrl_set_fifo(fdctrl, 1, 0);
}
static void fdctrl_handle_option(FDCtrl *fdctrl, int direction)
@@ -1595,7 +1671,7 @@ static void fdctrl_handle_drive_specification_command(FDCtrl *fdctrl, int direct
fdctrl->fifo[0] = fdctrl->fifo[1];
fdctrl->fifo[2] = 0;
fdctrl->fifo[3] = 0;
- fdctrl_set_fifo(fdctrl, 4, 1);
+ fdctrl_set_fifo(fdctrl, 4, 0);
} else {
fdctrl_reset_fifo(fdctrl);
}
@@ -1603,7 +1679,7 @@ static void fdctrl_handle_drive_specification_command(FDCtrl *fdctrl, int direct
/* ERROR */
fdctrl->fifo[0] = 0x80 |
(cur_drv->head << 2) | GET_CUR_DRV(fdctrl);
- fdctrl_set_fifo(fdctrl, 1, 1);
+ fdctrl_set_fifo(fdctrl, 1, 0);
}
}
@@ -1729,6 +1805,7 @@ static void fdctrl_write_data(FDCtrl *fdctrl, uint32_t value)
pos = command_to_handler[value & 0xff];
FLOPPY_DPRINTF("%s command\n", handlers[pos].name);
fdctrl->data_len = handlers[pos].parameters + 1;
+ fdctrl->msr |= FD_MSR_CMDBUSY;
}
FLOPPY_DPRINTF("%s: %02x\n", __func__, value);
@@ -1760,7 +1837,15 @@ static void fdctrl_result_timer(void *opaque)
if (cur_drv->last_sect != 0) {
cur_drv->sect = (cur_drv->sect % cur_drv->last_sect) + 1;
}
- fdctrl_stop_transfer(fdctrl, 0x00, 0x00, 0x00);
+ /* READ_ID can't automatically succeed! */
+ if (fdctrl->check_media_rate &&
+ (fdctrl->dsr & FD_DSR_DRATEMASK) != cur_drv->media_rate) {
+ FLOPPY_DPRINTF("read id rate mismatch (fdc=%d, media=%d)\n",
+ fdctrl->dsr & FD_DSR_DRATEMASK, cur_drv->media_rate);
+ fdctrl_stop_transfer(fdctrl, FD_SR0_ABNTERM, FD_SR1_MA, 0x00);
+ } else {
+ fdctrl_stop_transfer(fdctrl, 0x00, 0x00, 0x00);
+ }
}
static void fdctrl_change_cb(void *opaque, bool load)
@@ -1782,6 +1867,7 @@ static int fdctrl_connect_drives(FDCtrl *fdctrl)
for (i = 0; i < MAX_FD; i++) {
drive = &fdctrl->drives[i];
+ drive->fdctrl = fdctrl;
if (drive->bs) {
if (bdrv_get_on_error(drive->bs, 0) != BLOCK_ERR_STOP_ENOSPC) {
@@ -1964,6 +2050,8 @@ static Property isa_fdc_properties[] = {
DEFINE_PROP_DRIVE("driveB", FDCtrlISABus, state.drives[1].bs),
DEFINE_PROP_INT32("bootindexA", FDCtrlISABus, bootindexA, -1),
DEFINE_PROP_INT32("bootindexB", FDCtrlISABus, bootindexB, -1),
+ DEFINE_PROP_BIT("check_media_rate", FDCtrlISABus, state.check_media_rate,
+ 0, true),
DEFINE_PROP_END_OF_LIST(),
};
@@ -2043,11 +2131,11 @@ static TypeInfo sun4m_fdc_info = {
.class_init = sun4m_fdc_class_init,
};
-static void fdc_register_devices(void)
+static void fdc_register_types(void)
{
type_register_static(&isa_fdc_info);
type_register_static(&sysbus_fdc_info);
type_register_static(&sun4m_fdc_info);
}
-device_init(fdc_register_devices)
+type_init(fdc_register_types)
diff --git a/hw/fmopl.c b/hw/fmopl.c
index 734d2f4aae..f0a023477d 100644
--- a/hw/fmopl.c
+++ b/hw/fmopl.c
@@ -733,7 +733,7 @@ INLINE void CSMKeyControll(OPL_CH *CH)
}
/* ---------- opl initialize ---------- */
-static void OPL_initalize(FM_OPL *OPL)
+static void OPL_initialize(FM_OPL *OPL)
{
int fn;
@@ -1239,7 +1239,7 @@ FM_OPL *OPLCreate(int type, int clock, int rate)
OPL->rate = rate;
OPL->max_ch = max_ch;
/* init grobal tables */
- OPL_initalize(OPL);
+ OPL_initialize(OPL);
/* reset chip */
OPLResetChip(OPL);
#ifdef OPL_OUTPUT_LOG
diff --git a/hw/fw_cfg.c b/hw/fw_cfg.c
index 6b2f7d1986..7b3b5769a2 100644
--- a/hw/fw_cfg.c
+++ b/hw/fw_cfg.c
@@ -556,9 +556,9 @@ static TypeInfo fw_cfg_info = {
.class_init = fw_cfg_class_init,
};
-static void fw_cfg_register_devices(void)
+static void fw_cfg_register_types(void)
{
type_register_static(&fw_cfg_info);
}
-device_init(fw_cfg_register_devices)
+type_init(fw_cfg_register_types)
diff --git a/hw/g364fb.c b/hw/g364fb.c
index 66d0044c06..3a0b68fbae 100644
--- a/hw/g364fb.c
+++ b/hw/g364fb.c
@@ -289,7 +289,7 @@ static void g364fb_reset(G364State *s)
g364fb_invalidate_display(s);
}
-static void g364fb_screen_dump(void *opaque, const char *filename)
+static void g364fb_screen_dump(void *opaque, const char *filename, bool cswitch)
{
G364State *s = opaque;
int y, x;
@@ -574,9 +574,9 @@ static TypeInfo g364fb_sysbus_info = {
.class_init = g364fb_sysbus_class_init,
};
-static void g364fb_register(void)
+static void g364fb_register_types(void)
{
type_register_static(&g364fb_sysbus_info);
}
-device_init(g364fb_register);
+type_init(g364fb_register_types)
diff --git a/hw/grackle_pci.c b/hw/grackle_pci.c
index 8122baf482..81ff3a339a 100644
--- a/hw/grackle_pci.c
+++ b/hw/grackle_pci.c
@@ -157,10 +157,10 @@ static TypeInfo grackle_pci_host_info = {
.class_init = pci_grackle_class_init,
};
-static void grackle_register_devices(void)
+static void grackle_register_types(void)
{
type_register_static(&grackle_pci_info);
type_register_static(&grackle_pci_host_info);
}
-device_init(grackle_register_devices)
+type_init(grackle_register_types)
diff --git a/hw/grlib_apbuart.c b/hw/grlib_apbuart.c
index 89de2d85ae..73fc9894db 100644
--- a/hw/grlib_apbuart.c
+++ b/hw/grlib_apbuart.c
@@ -263,9 +263,9 @@ static TypeInfo grlib_gptimer_info = {
.class_init = grlib_gptimer_class_init,
};
-static void grlib_gptimer_register(void)
+static void grlib_gptimer_register_types(void)
{
type_register_static(&grlib_gptimer_info);
}
-device_init(grlib_gptimer_register)
+type_init(grlib_gptimer_register_types)
diff --git a/hw/grlib_gptimer.c b/hw/grlib_gptimer.c
index fb0b236746..41770a9e6c 100644
--- a/hw/grlib_gptimer.c
+++ b/hw/grlib_gptimer.c
@@ -396,9 +396,9 @@ static TypeInfo grlib_gptimer_info = {
.class_init = grlib_gptimer_class_init,
};
-static void grlib_gptimer_register(void)
+static void grlib_gptimer_register_types(void)
{
type_register_static(&grlib_gptimer_info);
}
-device_init(grlib_gptimer_register)
+type_init(grlib_gptimer_register_types)
diff --git a/hw/grlib_irqmp.c b/hw/grlib_irqmp.c
index 1e5ad826f5..0f6e65cf20 100644
--- a/hw/grlib_irqmp.c
+++ b/hw/grlib_irqmp.c
@@ -377,9 +377,9 @@ static TypeInfo grlib_irqmp_info = {
.class_init = grlib_irqmp_class_init,
};
-static void grlib_irqmp_register(void)
+static void grlib_irqmp_register_types(void)
{
type_register_static(&grlib_irqmp_info);
}
-device_init(grlib_irqmp_register)
+type_init(grlib_irqmp_register_types)
diff --git a/hw/gt64xxx.c b/hw/gt64xxx.c
index e8cd59c3a4..a2d0e5a2c3 100644
--- a/hw/gt64xxx.c
+++ b/hw/gt64xxx.c
@@ -1168,10 +1168,10 @@ static TypeInfo gt64120_info = {
.class_init = gt64120_class_init,
};
-static void gt64120_pci_register_devices(void)
+static void gt64120_pci_register_types(void)
{
type_register_static(&gt64120_info);
type_register_static(&gt64120_pci_info);
}
-device_init(gt64120_pci_register_devices)
+type_init(gt64120_pci_register_types)
diff --git a/hw/gus.c b/hw/gus.c
index 20547075ec..840d098d6a 100644
--- a/hw/gus.c
+++ b/hw/gus.c
@@ -324,8 +324,9 @@ static TypeInfo gus_info = {
.class_init = gus_class_initfn,
};
-static void gus_register (void)
+static void gus_register_types (void)
{
type_register_static (&gus_info);
}
-device_init (gus_register)
+
+type_init (gus_register_types)
diff --git a/hw/hda-audio.c b/hw/hda-audio.c
index 152f8e6f13..8995519573 100644
--- a/hw/hda-audio.c
+++ b/hw/hda-audio.c
@@ -948,9 +948,10 @@ static TypeInfo hda_audio_duplex_info = {
.class_init = hda_audio_duplex_class_init,
};
-static void hda_audio_register(void)
+static void hda_audio_register_types(void)
{
type_register_static(&hda_audio_output_info);
type_register_static(&hda_audio_duplex_info);
}
-device_init(hda_audio_register);
+
+type_init(hda_audio_register_types)
diff --git a/hw/highbank.c b/hw/highbank.c
index 684178ee3e..489c00e5b9 100644
--- a/hw/highbank.c
+++ b/hw/highbank.c
@@ -19,7 +19,6 @@
#include "sysbus.h"
#include "arm-misc.h"
-#include "primecell.h"
#include "devices.h"
#include "loader.h"
#include "net.h"
@@ -177,12 +176,12 @@ static TypeInfo highbank_regs_info = {
.class_init = highbank_regs_class_init,
};
-static void highbank_regs_register_device(void)
+static void highbank_regs_register_types(void)
{
type_register_static(&highbank_regs_info);
}
-device_init(highbank_regs_register_device)
+type_init(highbank_regs_register_types)
static struct arm_boot_info highbank_binfo;
diff --git a/hw/hpet.c b/hw/hpet.c
index b6ace4e696..fd3ddca7f7 100644
--- a/hw/hpet.c
+++ b/hw/hpet.c
@@ -31,6 +31,7 @@
#include "hpet_emul.h"
#include "sysbus.h"
#include "mc146818rtc.h"
+#include "i8254.h"
//#define HPET_DEBUG
#ifdef HPET_DEBUG
@@ -64,6 +65,7 @@ typedef struct HPETState {
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];
@@ -240,6 +242,24 @@ static int hpet_post_load(void *opaque, int version_id)
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,
@@ -273,6 +293,14 @@ static const VMStateDescription vmstate_hpet = {
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 */
+ }
}
};
@@ -546,12 +574,15 @@ static void hpet_ram_write(void *opaque, target_phys_addr_t addr,
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;
@@ -605,7 +636,6 @@ 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];
@@ -622,29 +652,30 @@ static void hpet_reset(DeviceState *d)
timer->wrap_flag = 0;
}
+ qemu_set_irq(s->pit_enabled, 1);
s->hpet_counter = 0ULL;
s->hpet_offset = 0ULL;
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();
- }
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;
- count = 1;
+
+ /* 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);
+ }
}
}
@@ -687,7 +718,8 @@ static int hpet_init(SysBusDevice *dev)
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_rtc_irq, 1);
+ qdev_init_gpio_in(&dev->qdev, hpet_handle_legacy_irq, 2);
+ qdev_init_gpio_out(&dev->qdev, &s->pit_enabled, 1);
/* HPET Area */
memory_region_init_io(&s->iomem, &hpet_ram_ops, s, "hpet", 0x400);
@@ -720,9 +752,9 @@ static TypeInfo hpet_device_info = {
.class_init = hpet_device_class_init,
};
-static void hpet_register_device(void)
+static void hpet_register_types(void)
{
type_register_static(&hpet_device_info);
}
-device_init(hpet_register_device)
+type_init(hpet_register_types)
diff --git a/hw/hpet_emul.h b/hw/hpet_emul.h
index 6128702533..757f79fdd2 100644
--- a/hw/hpet_emul.h
+++ b/hw/hpet_emul.h
@@ -22,6 +22,9 @@
#define HPET_NUM_IRQ_ROUTES 32
+#define HPET_LEGACY_PIT_INT 0
+#define HPET_LEGACY_RTC_INT 1
+
#define HPET_CFG_ENABLE 0x001
#define HPET_CFG_LEGACY 0x002
diff --git a/hw/i2c.c b/hw/i2c.c
index 8ae4aaae1d..23dfccba14 100644
--- a/hw/i2c.c
+++ b/hw/i2c.c
@@ -230,9 +230,9 @@ static TypeInfo i2c_slave_type_info = {
.class_init = i2c_slave_class_init,
};
-static void i2c_slave_register_devices(void)
+static void i2c_slave_register_types(void)
{
type_register_static(&i2c_slave_type_info);
}
-device_init(i2c_slave_register_devices);
+type_init(i2c_slave_register_types)
diff --git a/hw/i82374.c b/hw/i82374.c
index 220e8cccb6..67298a39f5 100644
--- a/hw/i82374.c
+++ b/hw/i82374.c
@@ -157,9 +157,9 @@ static TypeInfo i82374_isa_info = {
.class_init = i82374_class_init,
};
-static void i82374_register_devices(void)
+static void i82374_register_types(void)
{
type_register_static(&i82374_isa_info);
}
-device_init(i82374_register_devices)
+type_init(i82374_register_types)
diff --git a/hw/i82378.c b/hw/i82378.c
index 9c3efe8b95..faad1a365b 100644
--- a/hw/i82378.c
+++ b/hw/i82378.c
@@ -19,6 +19,8 @@
#include "pci.h"
#include "pc.h"
+#include "i8254.h"
+#include "pcspk.h"
//#define DEBUG_I82378
@@ -191,10 +193,10 @@ static void i82378_init(DeviceState *dev, I82378State *s)
isa_bus_irqs(isabus, s->i8259);
/* 1 82C54 (pit) */
- pit = pit_init(isabus, 0x40, 0);
+ pit = pit_init(isabus, 0x40, 0, NULL);
/* speaker */
- pcspk_init(pit);
+ pcspk_init(isabus, pit);
/* 2 82C37 (dma) */
DMA_init(1, &s->out[1]);
@@ -267,9 +269,9 @@ static TypeInfo pci_i82378_info = {
.class_init = pci_i82378_class_init,
};
-static void i82378_register_devices(void)
+static void i82378_register_types(void)
{
type_register_static(&pci_i82378_info);
}
-device_init(i82378_register_devices)
+type_init(i82378_register_types)
diff --git a/hw/i8254.c b/hw/i8254.c
index 522fed8809..f30396af88 100644
--- a/hw/i8254.c
+++ b/hw/i8254.c
@@ -25,6 +25,7 @@
#include "pc.h"
#include "isa.h"
#include "qemu-timer.h"
+#include "i8254.h"
//#define DEBUG_PIT
@@ -51,18 +52,16 @@ typedef struct PITChannelState {
int64_t next_transition_time;
QEMUTimer *irq_timer;
qemu_irq irq;
+ uint32_t irq_disabled;
} PITChannelState;
typedef struct PITState {
ISADevice dev;
MemoryRegion ioports;
- uint32_t irq;
uint32_t iobase;
PITChannelState channels[3];
} PITState;
-static PITState pit_state;
-
static void pit_irq_timer_update(PITChannelState *s, int64_t current_time);
static int pit_get_count(PITChannelState *s)
@@ -91,7 +90,7 @@ static int pit_get_count(PITChannelState *s)
}
/* get pit output bit */
-static int pit_get_out1(PITChannelState *s, int64_t current_time)
+static int pit_get_out(PITChannelState *s, int64_t current_time)
{
uint64_t d;
int out;
@@ -123,13 +122,6 @@ static int pit_get_out1(PITChannelState *s, int64_t current_time)
return out;
}
-int pit_get_out(ISADevice *dev, int channel, int64_t current_time)
-{
- PITState *pit = DO_UPCAST(PITState, dev, dev);
- PITChannelState *s = &pit->channels[channel];
- return pit_get_out1(s, current_time);
-}
-
/* return -1 if no transition will occur. */
static int64_t pit_get_next_transition_time(PITChannelState *s,
int64_t current_time)
@@ -216,25 +208,15 @@ void pit_set_gate(ISADevice *dev, int channel, int val)
s->gate = val;
}
-int pit_get_gate(ISADevice *dev, int channel)
+void pit_get_channel_info(ISADevice *dev, int channel, PITChannelInfo *info)
{
PITState *pit = DO_UPCAST(PITState, dev, dev);
PITChannelState *s = &pit->channels[channel];
- return s->gate;
-}
-int pit_get_initial_count(ISADevice *dev, int channel)
-{
- PITState *pit = DO_UPCAST(PITState, dev, dev);
- PITChannelState *s = &pit->channels[channel];
- return s->count;
-}
-
-int pit_get_mode(ISADevice *dev, int channel)
-{
- PITState *pit = DO_UPCAST(PITState, dev, dev);
- PITChannelState *s = &pit->channels[channel];
- return s->mode;
+ info->gate = s->gate;
+ info->mode = s->mode;
+ info->initial_count = s->count;
+ info->out = pit_get_out(s, qemu_get_clock_ns(vm_clock));
}
static inline void pit_load_count(PITChannelState *s, int val)
@@ -275,7 +257,9 @@ static void pit_ioport_write(void *opaque, uint32_t addr, uint32_t val)
if (!(val & 0x10) && !s->status_latched) {
/* status latch */
/* XXX: add BCD and null count */
- s->status = (pit_get_out1(s, qemu_get_clock_ns(vm_clock)) << 7) |
+ s->status =
+ (pit_get_out(s,
+ qemu_get_clock_ns(vm_clock)) << 7) |
(s->rw_mode << 4) |
(s->mode << 1) |
s->bcd;
@@ -378,10 +362,11 @@ static void pit_irq_timer_update(PITChannelState *s, int64_t current_time)
int64_t expire_time;
int irq_level;
- if (!s->irq_timer)
+ if (!s->irq_timer || s->irq_disabled) {
return;
+ }
expire_time = pit_get_next_transition_time(s, current_time);
- irq_level = pit_get_out1(s, current_time);
+ irq_level = pit_get_out(s, current_time);
qemu_set_irq(s->irq, irq_level);
#ifdef DEBUG_PIT
printf("irq_level=%d next_delay=%f\n",
@@ -450,6 +435,7 @@ static int pit_load_old(QEMUFile *f, void *opaque, int version_id)
qemu_get_8s(f, &s->bcd);
qemu_get_8s(f, &s->gate);
s->count_load_time=qemu_get_be64(f);
+ s->irq_disabled = 0;
if (s->irq_timer) {
s->next_transition_time=qemu_get_be64(f);
qemu_get_timer(f, s->irq_timer);
@@ -460,11 +446,12 @@ static int pit_load_old(QEMUFile *f, void *opaque, int version_id)
static const VMStateDescription vmstate_pit = {
.name = "i8254",
- .version_id = 2,
+ .version_id = 3,
.minimum_version_id = 2,
.minimum_version_id_old = 1,
.load_state_old = pit_load_old,
.fields = (VMStateField []) {
+ VMSTATE_UINT32_V(channels[0].irq_disabled, PITState, 3),
VMSTATE_STRUCT_ARRAY(channels, PITState, 3, 2, vmstate_pit_channel, PITChannelState),
VMSTATE_TIMER(channels[0].irq_timer, PITState),
VMSTATE_END_OF_LIST()
@@ -481,30 +468,30 @@ static void pit_reset(DeviceState *dev)
s = &pit->channels[i];
s->mode = 3;
s->gate = (i != 2);
- pit_load_count(s, 0);
+ s->count_load_time = qemu_get_clock_ns(vm_clock);
+ s->count = 0x10000;
+ if (i == 0 && !s->irq_disabled) {
+ s->next_transition_time =
+ pit_get_next_transition_time(s, s->count_load_time);
+ qemu_mod_timer(s->irq_timer, s->next_transition_time);
+ }
}
}
-/* When HPET is operating in legacy mode, i8254 timer0 is disabled */
-void hpet_pit_disable(void) {
- PITChannelState *s;
- s = &pit_state.channels[0];
- if (s->irq_timer)
- qemu_del_timer(s->irq_timer);
-}
-
-/* When HPET is reset or leaving legacy mode, it must reenable i8254
- * timer 0
- */
-
-void hpet_pit_enable(void)
+/* When HPET is operating in legacy mode, suppress the ignored timer IRQ,
+ * reenable it when legacy mode is left again. */
+static void pit_irq_control(void *opaque, int n, int enable)
{
- PITState *pit = &pit_state;
- PITChannelState *s;
- s = &pit->channels[0];
- s->mode = 3;
- s->gate = 1;
- pit_load_count(s, 0);
+ PITState *pit = opaque;
+ PITChannelState *s = &pit->channels[0];
+
+ if (enable) {
+ s->irq_disabled = 0;
+ pit_irq_timer_update(s, qemu_get_clock_ns(vm_clock));
+ } else {
+ s->irq_disabled = 1;
+ qemu_del_timer(s->irq_timer);
+ }
}
static const MemoryRegionPortio pit_portio[] = {
@@ -525,18 +512,19 @@ static int pit_initfn(ISADevice *dev)
s = &pit->channels[0];
/* the timer 0 is connected to an IRQ */
s->irq_timer = qemu_new_timer_ns(vm_clock, pit_irq_timer, s);
- s->irq = isa_get_irq(dev, pit->irq);
+ qdev_init_gpio_out(&dev->qdev, &s->irq, 1);
memory_region_init_io(&pit->ioports, &pit_ioport_ops, pit, "pit", 4);
isa_register_ioport(dev, &pit->ioports, pit->iobase);
+ qdev_init_gpio_in(&dev->qdev, pit_irq_control, 1);
+
qdev_set_legacy_instance_id(&dev->qdev, pit->iobase, 2);
return 0;
}
static Property pit_properties[] = {
- DEFINE_PROP_UINT32("irq", PITState, irq, -1),
DEFINE_PROP_HEX32("iobase", PITState, iobase, -1),
DEFINE_PROP_END_OF_LIST(),
};
@@ -559,8 +547,9 @@ static TypeInfo pit_info = {
.class_init = pit_class_initfn,
};
-static void pit_register(void)
+static void pit_register_types(void)
{
type_register_static(&pit_info);
}
-device_init(pit_register)
+
+type_init(pit_register_types)
diff --git a/hw/i8254.h b/hw/i8254.h
new file mode 100644
index 0000000000..a1d2e9835b
--- /dev/null
+++ b/hw/i8254.h
@@ -0,0 +1,57 @@
+/*
+ * QEMU 8253/8254 interval timer emulation
+ *
+ * Copyright (c) 2003-2004 Fabrice Bellard
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#ifndef HW_I8254_H
+#define HW_I8254_H
+
+#include "hw.h"
+#include "isa.h"
+
+#define PIT_FREQ 1193182
+
+typedef struct PITChannelInfo {
+ int gate;
+ int mode;
+ int initial_count;
+ int out;
+} PITChannelInfo;
+
+static inline ISADevice *pit_init(ISABus *bus, int base, int isa_irq,
+ qemu_irq alt_irq)
+{
+ ISADevice *dev;
+
+ dev = isa_create(bus, "isa-pit");
+ qdev_prop_set_uint32(&dev->qdev, "iobase", base);
+ qdev_init_nofail(&dev->qdev);
+ qdev_connect_gpio_out(&dev->qdev, 0,
+ isa_irq >= 0 ? isa_get_irq(dev, isa_irq) : alt_irq);
+
+ return dev;
+}
+
+void pit_set_gate(ISADevice *dev, int channel, int val);
+void pit_get_channel_info(ISADevice *dev, int channel, PITChannelInfo *info);
+
+#endif /* !HW_I8254_H */
diff --git a/hw/i8259.c b/hw/i8259.c
index 7ae53805d7..53daf78652 100644
--- a/hw/i8259.c
+++ b/hw/i8259.c
@@ -231,8 +231,8 @@ static void pic_reset(DeviceState *dev)
{
PICCommonState *s = DO_UPCAST(PICCommonState, dev.qdev, dev);
- pic_init_reset(s);
s->elcr = 0;
+ pic_init_reset(s);
}
static void pic_ioport_write(void *opaque, target_phys_addr_t addr64,
@@ -488,9 +488,9 @@ static TypeInfo i8259_info = {
.class_init = i8259_class_init,
};
-static void pic_register(void)
+static void pic_register_types(void)
{
type_register_static(&i8259_info);
}
-device_init(pic_register)
+type_init(pic_register_types)
diff --git a/hw/i8259_common.c b/hw/i8259_common.c
index 9f150bc6d0..ab3d98b2a1 100644
--- a/hw/i8259_common.c
+++ b/hw/i8259_common.c
@@ -28,7 +28,7 @@
void pic_reset_common(PICCommonState *s)
{
s->last_irr = 0;
- s->irr = 0;
+ s->irr &= s->elcr;
s->imr = 0;
s->isr = 0;
s->priority_add = 0;
@@ -153,9 +153,9 @@ static TypeInfo pic_common_type = {
.abstract = true,
};
-static void register_devices(void)
+static void register_types(void)
{
type_register_static(&pic_common_type);
}
-device_init(register_devices);
+type_init(register_types);
diff --git a/hw/ide/ahci.c b/hw/ide/ahci.c
index c87a6ca515..041ce1e89f 100644
--- a/hw/ide/ahci.c
+++ b/hw/ide/ahci.c
@@ -146,6 +146,7 @@ static void ahci_check_irq(AHCIState *s)
DPRINTF(-1, "check irq %#x\n", s->control_regs.irqstatus);
+ s->control_regs.irqstatus = 0;
for (i = 0; i < s->ports; i++) {
AHCIPortRegs *pr = &s->dev[i].port_regs;
if (pr->irq_stat & pr->irq_mask) {
@@ -216,6 +217,7 @@ static void ahci_port_write(AHCIState *s, int port, int offset, uint32_t val)
break;
case PORT_IRQ_STAT:
pr->irq_stat &= ~val;
+ ahci_check_irq(s);
break;
case PORT_IRQ_MASK:
pr->irq_mask = val & 0xfdc000ff;
@@ -426,55 +428,6 @@ static void ahci_reg_init(AHCIState *s)
}
}
-static uint32_t read_from_sglist(uint8_t *buffer, uint32_t len,
- QEMUSGList *sglist)
-{
- uint32_t i = 0;
- uint32_t total = 0, once;
- ScatterGatherEntry *cur_prd;
- uint32_t sgcount;
-
- cur_prd = sglist->sg;
- sgcount = sglist->nsg;
- for (i = 0; len && sgcount; i++) {
- once = MIN(cur_prd->len, len);
- cpu_physical_memory_read(cur_prd->base, buffer, once);
- cur_prd++;
- sgcount--;
- len -= once;
- buffer += once;
- total += once;
- }
-
- return total;
-}
-
-static uint32_t write_to_sglist(uint8_t *buffer, uint32_t len,
- QEMUSGList *sglist)
-{
- uint32_t i = 0;
- uint32_t total = 0, once;
- ScatterGatherEntry *cur_prd;
- uint32_t sgcount;
-
- DPRINTF(-1, "total: 0x%x bytes\n", len);
-
- cur_prd = sglist->sg;
- sgcount = sglist->nsg;
- for (i = 0; len && sgcount; i++) {
- once = MIN(cur_prd->len, len);
- DPRINTF(-1, "write 0x%x bytes to 0x%lx\n", once, (long)cur_prd->base);
- cpu_physical_memory_write(cur_prd->base, buffer, once);
- cur_prd++;
- sgcount--;
- len -= once;
- buffer += once;
- total += once;
- }
-
- return total;
-}
-
static void check_cmd(AHCIState *s, int port)
{
AHCIPortRegs *pr = &s->dev[port].port_regs;
@@ -560,6 +513,11 @@ static void ahci_reset_port(AHCIState *s, int port)
ncq_tfs->aiocb = NULL;
}
+ /* Maybe we just finished the request thanks to bdrv_aio_cancel() */
+ if (!ncq_tfs->used) {
+ continue;
+ }
+
qemu_sglist_destroy(&ncq_tfs->sglist);
ncq_tfs->used = 0;
}
@@ -795,9 +753,8 @@ static void process_ncq_command(AHCIState *s, int port, uint8_t *cmd_fis,
DPRINTF(port, "tag %d aio read %"PRId64"\n",
ncq_tfs->tag, ncq_tfs->lba);
- bdrv_acct_start(ncq_tfs->drive->port.ifs[0].bs, &ncq_tfs->acct,
- (ncq_tfs->sector_count-1) * BDRV_SECTOR_SIZE,
- BDRV_ACCT_READ);
+ dma_acct_start(ncq_tfs->drive->port.ifs[0].bs, &ncq_tfs->acct,
+ &ncq_tfs->sglist, BDRV_ACCT_READ);
ncq_tfs->aiocb = dma_bdrv_read(ncq_tfs->drive->port.ifs[0].bs,
&ncq_tfs->sglist, ncq_tfs->lba,
ncq_cb, ncq_tfs);
@@ -809,9 +766,8 @@ static void process_ncq_command(AHCIState *s, int port, uint8_t *cmd_fis,
DPRINTF(port, "tag %d aio write %"PRId64"\n",
ncq_tfs->tag, ncq_tfs->lba);
- bdrv_acct_start(ncq_tfs->drive->port.ifs[0].bs, &ncq_tfs->acct,
- (ncq_tfs->sector_count-1) * BDRV_SECTOR_SIZE,
- BDRV_ACCT_WRITE);
+ dma_acct_start(ncq_tfs->drive->port.ifs[0].bs, &ncq_tfs->acct,
+ &ncq_tfs->sglist, BDRV_ACCT_WRITE);
ncq_tfs->aiocb = dma_bdrv_write(ncq_tfs->drive->port.ifs[0].bs,
&ncq_tfs->sglist, ncq_tfs->lba,
ncq_cb, ncq_tfs);
@@ -1016,12 +972,12 @@ static int ahci_start_transfer(IDEDMA *dma)
is_write ? "writ" : "read", size, is_atapi ? "atapi" : "ata",
has_sglist ? "" : "o");
- if (is_write && has_sglist && (s->data_ptr < s->data_end)) {
- read_from_sglist(s->data_ptr, size, &s->sg);
- }
-
- if (!is_write && has_sglist && (s->data_ptr < s->data_end)) {
- write_to_sglist(s->data_ptr, size, &s->sg);
+ if (has_sglist && size) {
+ if (is_write) {
+ dma_buf_write(s->data_ptr, size, &s->sg);
+ } else {
+ dma_buf_read(s->data_ptr, size, &s->sg);
+ }
}
/* update number of transferred bytes */
@@ -1060,14 +1016,9 @@ static int ahci_dma_prepare_buf(IDEDMA *dma, int is_write)
{
AHCIDevice *ad = DO_UPCAST(AHCIDevice, dma, dma);
IDEState *s = &ad->port.ifs[0];
- int i;
ahci_populate_sglist(ad, &s->sg);
-
- s->io_buffer_size = 0;
- for (i = 0; i < s->sg.nsg; i++) {
- s->io_buffer_size += s->sg.sg[i].len;
- }
+ s->io_buffer_size = s->sg.size;
DPRINTF(ad->port_no, "len=%#x\n", s->io_buffer_size);
return s->io_buffer_size != 0;
@@ -1085,9 +1036,9 @@ static int ahci_dma_rw_buf(IDEDMA *dma, int is_write)
}
if (is_write) {
- write_to_sglist(p, l, &s->sg);
+ dma_buf_read(p, l, &s->sg);
} else {
- read_from_sglist(p, l, &s->sg);
+ dma_buf_write(p, l, &s->sg);
}
/* update number of transferred bytes */
@@ -1261,9 +1212,9 @@ static TypeInfo sysbus_ahci_info = {
.class_init = sysbus_ahci_class_init,
};
-static void sysbus_ahci_register(void)
+static void sysbus_ahci_register_types(void)
{
type_register_static(&sysbus_ahci_info);
}
-device_init(sysbus_ahci_register);
+type_init(sysbus_ahci_register_types)
diff --git a/hw/ide/atapi.c b/hw/ide/atapi.c
index 0adb27b799..5919cf52d8 100644
--- a/hw/ide/atapi.c
+++ b/hw/ide/atapi.c
@@ -883,8 +883,11 @@ static void cmd_start_stop_unit(IDEState *s, uint8_t* buf)
ide_atapi_cmd_error(s, sense, ASC_MEDIA_REMOVAL_PREVENTED);
return;
}
- bdrv_eject(s->bs, !start);
- s->tray_open = !start;
+
+ if (s->tray_open != !start) {
+ bdrv_eject(s->bs, !start);
+ s->tray_open = !start;
+ }
}
ide_atapi_cmd_ok(s);
diff --git a/hw/ide/cmd646.c b/hw/ide/cmd646.c
index a119500f40..743ec02406 100644
--- a/hw/ide/cmd646.c
+++ b/hw/ide/cmd646.c
@@ -351,8 +351,9 @@ static TypeInfo cmd646_ide_info = {
.class_init = cmd646_ide_class_init,
};
-static void cmd646_ide_register(void)
+static void cmd646_ide_register_types(void)
{
type_register_static(&cmd646_ide_info);
}
-device_init(cmd646_ide_register);
+
+type_init(cmd646_ide_register_types)
diff --git a/hw/ide/core.c b/hw/ide/core.c
index 56b219b504..4d568acc9c 100644
--- a/hw/ide/core.c
+++ b/hw/ide/core.c
@@ -519,7 +519,7 @@ static int ide_handle_rw_error(IDEState *s, int error, int op)
BlockErrorAction action = bdrv_get_on_error(s->bs, is_read);
if (action == BLOCK_ERR_IGNORE) {
- bdrv_mon_event(s->bs, BDRV_ACTION_IGNORE, is_read);
+ bdrv_emit_qmp_error_event(s->bs, BDRV_ACTION_IGNORE, is_read);
return 0;
}
@@ -527,7 +527,7 @@ static int ide_handle_rw_error(IDEState *s, int error, int op)
|| action == BLOCK_ERR_STOP_ANY) {
s->bus->dma->ops->set_unit(s->bus->dma, s->unit);
s->bus->error_status = op;
- bdrv_mon_event(s->bs, BDRV_ACTION_STOP, is_read);
+ bdrv_emit_qmp_error_event(s->bs, BDRV_ACTION_STOP, is_read);
vm_stop(RUN_STATE_IO_ERROR);
bdrv_iostatus_set_err(s->bs, error);
} else {
@@ -537,7 +537,7 @@ static int ide_handle_rw_error(IDEState *s, int error, int op)
} else {
ide_rw_error(s);
}
- bdrv_mon_event(s->bs, BDRV_ACTION_REPORT, is_read);
+ bdrv_emit_qmp_error_event(s->bs, BDRV_ACTION_REPORT, is_read);
}
return 1;
@@ -1068,6 +1068,9 @@ void ide_exec_cmd(IDEBus *bus, uint32_t val)
ide_set_signature(s); /* odd, but ATA4 8.27.5.2 requires it */
goto abort_cmd;
}
+ if (!s->bs) {
+ goto abort_cmd;
+ }
ide_cmd_lba48_transform(s, lba48);
s->req_nb_sectors = 1;
ide_sector_read(s);
@@ -1078,6 +1081,9 @@ void ide_exec_cmd(IDEBus *bus, uint32_t val)
case WIN_WRITE_ONCE:
case CFA_WRITE_SECT_WO_ERASE:
case WIN_WRITE_VERIFY:
+ if (!s->bs) {
+ goto abort_cmd;
+ }
ide_cmd_lba48_transform(s, lba48);
s->error = 0;
s->status = SEEK_STAT | READY_STAT;
@@ -1088,8 +1094,12 @@ void ide_exec_cmd(IDEBus *bus, uint32_t val)
case WIN_MULTREAD_EXT:
lba48 = 1;
case WIN_MULTREAD:
- if (!s->mult_sectors)
+ if (!s->bs) {
+ goto abort_cmd;
+ }
+ if (!s->mult_sectors) {
goto abort_cmd;
+ }
ide_cmd_lba48_transform(s, lba48);
s->req_nb_sectors = s->mult_sectors;
ide_sector_read(s);
@@ -1098,8 +1108,12 @@ void ide_exec_cmd(IDEBus *bus, uint32_t val)
lba48 = 1;
case WIN_MULTWRITE:
case CFA_WRITE_MULTI_WO_ERASE:
- if (!s->mult_sectors)
+ if (!s->bs) {
goto abort_cmd;
+ }
+ if (!s->mult_sectors) {
+ goto abort_cmd;
+ }
ide_cmd_lba48_transform(s, lba48);
s->error = 0;
s->status = SEEK_STAT | READY_STAT;
@@ -1114,8 +1128,9 @@ void ide_exec_cmd(IDEBus *bus, uint32_t val)
lba48 = 1;
case WIN_READDMA:
case WIN_READDMA_ONCE:
- if (!s->bs)
+ if (!s->bs) {
goto abort_cmd;
+ }
ide_cmd_lba48_transform(s, lba48);
ide_sector_start_dma(s, IDE_DMA_READ);
break;
@@ -1123,8 +1138,9 @@ void ide_exec_cmd(IDEBus *bus, uint32_t val)
lba48 = 1;
case WIN_WRITEDMA:
case WIN_WRITEDMA_ONCE:
- if (!s->bs)
+ if (!s->bs) {
goto abort_cmd;
+ }
ide_cmd_lba48_transform(s, lba48);
ide_sector_start_dma(s, IDE_DMA_WRITE);
s->media_changed = 1;
@@ -2077,15 +2093,6 @@ static bool ide_drive_pio_state_needed(void *opaque)
|| (s->bus->error_status & BM_STATUS_PIO_RETRY);
}
-static int ide_tray_state_post_load(void *opaque, int version_id)
-{
- IDEState *s = opaque;
-
- bdrv_eject(s->bs, s->tray_open);
- bdrv_lock_medium(s->bs, s->tray_locked);
- return 0;
-}
-
static bool ide_tray_state_needed(void *opaque)
{
IDEState *s = opaque;
@@ -2125,7 +2132,6 @@ static const VMStateDescription vmstate_ide_tray_state = {
.version_id = 1,
.minimum_version_id = 1,
.minimum_version_id_old = 1,
- .post_load = ide_tray_state_post_load,
.fields = (VMStateField[]) {
VMSTATE_BOOL(tray_open, IDEState),
VMSTATE_BOOL(tray_locked, IDEState),
diff --git a/hw/ide/ich.c b/hw/ide/ich.c
index 5cdaa990ea..560ae37618 100644
--- a/hw/ide/ich.c
+++ b/hw/ide/ich.c
@@ -168,8 +168,9 @@ static TypeInfo ich_ahci_info = {
.class_init = ich_ahci_class_init,
};
-static void ich_ahci_register(void)
+static void ich_ahci_register_types(void)
{
type_register_static(&ich_ahci_info);
}
-device_init(ich_ahci_register);
+
+type_init(ich_ahci_register_types)
diff --git a/hw/ide/isa.c b/hw/ide/isa.c
index a0bcb43eba..8ab2718eea 100644
--- a/hw/ide/isa.c
+++ b/hw/ide/isa.c
@@ -118,9 +118,9 @@ static TypeInfo isa_ide_info = {
.class_init = isa_ide_class_initfn,
};
-static void isa_ide_register_devices(void)
+static void isa_ide_register_types(void)
{
type_register_static(&isa_ide_info);
}
-device_init(isa_ide_register_devices)
+type_init(isa_ide_register_types)
diff --git a/hw/ide/pci.c b/hw/ide/pci.c
index 246dd5704b..88c0942e34 100644
--- a/hw/ide/pci.c
+++ b/hw/ide/pci.c
@@ -336,7 +336,7 @@ static uint64_t bmdma_addr_read(void *opaque, target_phys_addr_t addr,
data = (bm->addr >> (addr * 8)) & mask;
#ifdef DEBUG_IDE
- printf("%s: 0x%08x\n", __func__, (unsigned)*data);
+ printf("%s: 0x%08x\n", __func__, (unsigned)data);
#endif
return data;
}
diff --git a/hw/ide/piix.c b/hw/ide/piix.c
index bf4465bb49..1030fcc31c 100644
--- a/hw/ide/piix.c
+++ b/hw/ide/piix.c
@@ -53,7 +53,7 @@ static uint64_t bmdma_read(void *opaque, target_phys_addr_t addr, unsigned size)
break;
}
#ifdef DEBUG_IDE
- printf("bmdma: readb 0x%02x : 0x%02x\n", addr, val);
+ printf("bmdma: readb 0x%02x : 0x%02x\n", (uint8_t)addr, val);
#endif
return val;
}
@@ -68,7 +68,7 @@ static void bmdma_write(void *opaque, target_phys_addr_t addr,
}
#ifdef DEBUG_IDE
- printf("bmdma: writeb 0x%02x : 0x%02x\n", addr, val);
+ printf("bmdma: writeb 0x%02x : 0x%02x\n", (uint8_t)addr, (uint8_t)val);
#endif
switch(addr & 3) {
case 0:
@@ -299,10 +299,11 @@ static TypeInfo piix4_ide_info = {
.class_init = piix4_ide_class_init,
};
-static void piix_ide_register(void)
+static void piix_ide_register_types(void)
{
type_register_static(&piix3_ide_info);
type_register_static(&piix3_ide_xen_info);
type_register_static(&piix4_ide_info);
}
-device_init(piix_ide_register);
+
+type_init(piix_ide_register_types)
diff --git a/hw/ide/qdev.c b/hw/ide/qdev.c
index 1640616906..f6a48961c5 100644
--- a/hw/ide/qdev.c
+++ b/hw/ide/qdev.c
@@ -257,11 +257,12 @@ static TypeInfo ide_device_type_info = {
.class_init = ide_device_class_init,
};
-static void ide_dev_register(void)
+static void ide_register_types(void)
{
type_register_static(&ide_hd_info);
type_register_static(&ide_cd_info);
type_register_static(&ide_drive_info);
type_register_static(&ide_device_type_info);
}
-device_init(ide_dev_register);
+
+type_init(ide_register_types)
diff --git a/hw/ide/via.c b/hw/ide/via.c
index b4ca6f27b3..2886bc6dfb 100644
--- a/hw/ide/via.c
+++ b/hw/ide/via.c
@@ -234,8 +234,9 @@ static TypeInfo via_ide_info = {
.class_init = via_ide_class_init,
};
-static void via_ide_register(void)
+static void via_ide_register_types(void)
{
type_register_static(&via_ide_info);
}
-device_init(via_ide_register);
+
+type_init(via_ide_register_types)
diff --git a/hw/integratorcp.c b/hw/integratorcp.c
index 6dbd649908..5b06c81c9b 100644
--- a/hw/integratorcp.c
+++ b/hw/integratorcp.c
@@ -8,7 +8,6 @@
*/
#include "sysbus.h"
-#include "primecell.h"
#include "devices.h"
#include "boards.h"
#include "arm-misc.h"
@@ -553,10 +552,10 @@ static TypeInfo icp_pic_info = {
.class_init = icp_pic_class_init,
};
-static void integratorcp_register_devices(void)
+static void integratorcp_register_types(void)
{
type_register_static(&icp_pic_info);
type_register_static(&core_info);
}
-device_init(integratorcp_register_devices)
+type_init(integratorcp_register_types)
diff --git a/hw/intel-hda.c b/hw/intel-hda.c
index 83c42d58b6..bb11af286a 100644
--- a/hw/intel-hda.c
+++ b/hw/intel-hda.c
@@ -1287,12 +1287,13 @@ static TypeInfo hda_codec_device_type_info = {
.class_init = hda_codec_device_class_init,
};
-static void intel_hda_register(void)
+static void intel_hda_register_types(void)
{
type_register_static(&intel_hda_info);
type_register_static(&hda_codec_device_type_info);
}
-device_init(intel_hda_register);
+
+type_init(intel_hda_register_types)
/*
* create intel hda controller with codec attached to it,
diff --git a/hw/ioapic.c b/hw/ioapic.c
index 79549f8ed1..3fee0114d9 100644
--- a/hw/ioapic.c
+++ b/hw/ioapic.c
@@ -251,9 +251,9 @@ static TypeInfo ioapic_info = {
.class_init = ioapic_class_init,
};
-static void ioapic_register_devices(void)
+static void ioapic_register_types(void)
{
type_register_static(&ioapic_info);
}
-device_init(ioapic_register_devices)
+type_init(ioapic_register_types)
diff --git a/hw/ioapic_common.c b/hw/ioapic_common.c
index f932700d12..653eef2ce1 100644
--- a/hw/ioapic_common.c
+++ b/hw/ioapic_common.c
@@ -112,10 +112,9 @@ static TypeInfo ioapic_common_type = {
.abstract = true,
};
-static void register_devices(void)
+static void register_types(void)
{
type_register_static(&ioapic_common_type);
}
-device_init(register_devices);
-
+type_init(register_types)
diff --git a/hw/ioh3420.c b/hw/ioh3420.c
index 1c60123d43..1632d31c19 100644
--- a/hw/ioh3420.c
+++ b/hw/ioh3420.c
@@ -237,12 +237,12 @@ static TypeInfo ioh3420_info = {
.class_init = ioh3420_class_init,
};
-static void ioh3420_register(void)
+static void ioh3420_register_types(void)
{
type_register_static(&ioh3420_info);
}
-device_init(ioh3420_register);
+type_init(ioh3420_register_types)
/*
* Local variables:
diff --git a/hw/isa-bus.c b/hw/isa-bus.c
index d03f82824c..5a43f03a7c 100644
--- a/hw/isa-bus.c
+++ b/hw/isa-bus.c
@@ -210,7 +210,7 @@ static TypeInfo isa_device_type_info = {
.class_init = isa_device_class_init,
};
-static void isabus_register_devices(void)
+static void isabus_register_types(void)
{
type_register_static(&isabus_bridge_info);
type_register_static(&isa_device_type_info);
@@ -235,4 +235,4 @@ MemoryRegion *isa_address_space(ISADevice *dev)
return get_system_memory();
}
-device_init(isabus_register_devices)
+type_init(isabus_register_types)
diff --git a/hw/ivshmem.c b/hw/ivshmem.c
index 6f017d474c..64e1cd968e 100644
--- a/hw/ivshmem.c
+++ b/hw/ivshmem.c
@@ -798,9 +798,9 @@ static TypeInfo ivshmem_info = {
.class_init = ivshmem_class_init,
};
-static void ivshmem_register_devices(void)
+static void ivshmem_register_types(void)
{
type_register_static(&ivshmem_info);
}
-device_init(ivshmem_register_devices)
+type_init(ivshmem_register_types)
diff --git a/hw/jazz_led.c b/hw/jazz_led.c
index f8a218252c..648652302a 100644
--- a/hw/jazz_led.c
+++ b/hw/jazz_led.c
@@ -1,7 +1,7 @@
/*
* QEMU JAZZ LED emulator.
*
- * Copyright (c) 2007 Hervé Poussineau
+ * Copyright (c) 2007-2012 Herve Poussineau
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
@@ -22,131 +22,53 @@
* THE SOFTWARE.
*/
-#include "hw.h"
-#include "mips.h"
#include "console.h"
#include "pixel_ops.h"
-
-//#define DEBUG_LED
-
-#ifdef DEBUG_LED
-#define DPRINTF(fmt, ...) \
-do { printf("jazz led: " fmt , ## __VA_ARGS__); } while (0)
-#else
-#define DPRINTF(fmt, ...) do {} while (0)
-#endif
-#define BADF(fmt, ...) \
-do { fprintf(stderr, "jazz led ERROR: " fmt , ## __VA_ARGS__);} while (0)
+#include "trace.h"
+#include "sysbus.h"
typedef enum {
REDRAW_NONE = 0, REDRAW_SEGMENTS = 1, REDRAW_BACKGROUND = 2,
} screen_state_t;
typedef struct LedState {
+ SysBusDevice busdev;
MemoryRegion iomem;
uint8_t segments;
DisplayState *ds;
screen_state_t state;
} LedState;
-static uint32_t led_readb(void *opaque, target_phys_addr_t addr)
+static uint64_t jazz_led_read(void *opaque, target_phys_addr_t addr,
+ unsigned int size)
{
LedState *s = opaque;
- uint32_t val;
-
- switch (addr) {
- case 0:
- val = s->segments;
- break;
- default:
- BADF("invalid read at [" TARGET_FMT_plx "]\n", addr);
- val = 0;
- }
+ uint8_t val;
- DPRINTF("read addr=" TARGET_FMT_plx " val=0x%02x\n", addr, val);
+ val = s->segments;
+ trace_jazz_led_read(addr, val);
return val;
}
-static uint32_t led_readw(void *opaque, target_phys_addr_t addr)
-{
- uint32_t v;
-#ifdef TARGET_WORDS_BIGENDIAN
- v = led_readb(opaque, addr) << 8;
- v |= led_readb(opaque, addr + 1);
-#else
- v = led_readb(opaque, addr);
- v |= led_readb(opaque, addr + 1) << 8;
-#endif
- return v;
-}
-
-static uint32_t led_readl(void *opaque, target_phys_addr_t addr)
-{
- uint32_t v;
-#ifdef TARGET_WORDS_BIGENDIAN
- v = led_readb(opaque, addr) << 24;
- v |= led_readb(opaque, addr + 1) << 16;
- v |= led_readb(opaque, addr + 2) << 8;
- v |= led_readb(opaque, addr + 3);
-#else
- v = led_readb(opaque, addr);
- v |= led_readb(opaque, addr + 1) << 8;
- v |= led_readb(opaque, addr + 2) << 16;
- v |= led_readb(opaque, addr + 3) << 24;
-#endif
- return v;
-}
-
-static void led_writeb(void *opaque, target_phys_addr_t addr, uint32_t val)
+static void jazz_led_write(void *opaque, target_phys_addr_t addr,
+ uint64_t val, unsigned int size)
{
LedState *s = opaque;
+ uint8_t new_val = val & 0xff;
- DPRINTF("write addr=" TARGET_FMT_plx " val=0x%02x\n", addr, val);
+ trace_jazz_led_write(addr, new_val);
- switch (addr) {
- case 0:
- s->segments = val;
- s->state |= REDRAW_SEGMENTS;
- break;
- default:
- BADF("invalid write of 0x%08x at [" TARGET_FMT_plx "]\n", val, addr);
- break;
- }
-}
-
-static void led_writew(void *opaque, target_phys_addr_t addr, uint32_t val)
-{
-#ifdef TARGET_WORDS_BIGENDIAN
- led_writeb(opaque, addr, (val >> 8) & 0xff);
- led_writeb(opaque, addr + 1, val & 0xff);
-#else
- led_writeb(opaque, addr, val & 0xff);
- led_writeb(opaque, addr + 1, (val >> 8) & 0xff);
-#endif
-}
-
-static void led_writel(void *opaque, target_phys_addr_t addr, uint32_t val)
-{
-#ifdef TARGET_WORDS_BIGENDIAN
- led_writeb(opaque, addr, (val >> 24) & 0xff);
- led_writeb(opaque, addr + 1, (val >> 16) & 0xff);
- led_writeb(opaque, addr + 2, (val >> 8) & 0xff);
- led_writeb(opaque, addr + 3, val & 0xff);
-#else
- led_writeb(opaque, addr, val & 0xff);
- led_writeb(opaque, addr + 1, (val >> 8) & 0xff);
- led_writeb(opaque, addr + 2, (val >> 16) & 0xff);
- led_writeb(opaque, addr + 3, (val >> 24) & 0xff);
-#endif
+ s->segments = new_val;
+ s->state |= REDRAW_SEGMENTS;
}
static const MemoryRegionOps led_ops = {
- .old_mmio = {
- .read = { led_readb, led_readw, led_readl, },
- .write = { led_writeb, led_writew, led_writel, },
- },
+ .read = jazz_led_read,
+ .write = jazz_led_write,
.endianness = DEVICE_NATIVE_ENDIAN,
+ .impl.min_access_size = 1,
+ .impl.max_access_size = 1,
};
/***********************************************************/
@@ -283,11 +205,6 @@ static void jazz_led_invalidate_display(void *opaque)
s->state |= REDRAW_SEGMENTS | REDRAW_BACKGROUND;
}
-static void jazz_led_screen_dump(void *opaque, const char *filename)
-{
- printf("jazz_led_screen_dump() not implemented\n");
-}
-
static void jazz_led_text_update(void *opaque, console_ch_t *chardata)
{
LedState *s = opaque;
@@ -304,20 +221,71 @@ static void jazz_led_text_update(void *opaque, console_ch_t *chardata)
dpy_update(s->ds, 0, 0, 2, 1);
}
-void jazz_led_init(MemoryRegion *address_space, target_phys_addr_t base)
+static int jazz_led_post_load(void *opaque, int version_id)
{
- LedState *s;
+ /* force refresh */
+ jazz_led_invalidate_display(opaque);
- s = g_malloc0(sizeof(LedState));
+ return 0;
+}
- s->state = REDRAW_SEGMENTS | REDRAW_BACKGROUND;
+static const VMStateDescription vmstate_jazz_led = {
+ .name = "jazz-led",
+ .version_id = 0,
+ .minimum_version_id = 0,
+ .minimum_version_id_old = 0,
+ .post_load = jazz_led_post_load,
+ .fields = (VMStateField[]) {
+ VMSTATE_UINT8(segments, LedState),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
+static int jazz_led_init(SysBusDevice *dev)
+{
+ LedState *s = FROM_SYSBUS(LedState, dev);
memory_region_init_io(&s->iomem, &led_ops, s, "led", 1);
- memory_region_add_subregion(address_space, base, &s->iomem);
+ sysbus_init_mmio(dev, &s->iomem);
s->ds = graphic_console_init(jazz_led_update_display,
jazz_led_invalidate_display,
- jazz_led_screen_dump,
+ NULL,
jazz_led_text_update, s);
+
+ return 0;
+}
+
+static void jazz_led_reset(DeviceState *d)
+{
+ LedState *s = DO_UPCAST(LedState, busdev.qdev, d);
+
+ s->segments = 0;
+ s->state = REDRAW_SEGMENTS | REDRAW_BACKGROUND;
qemu_console_resize(s->ds, 60, 80);
}
+
+static void jazz_led_class_init(ObjectClass *klass, void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(klass);
+ SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass);
+
+ k->init = jazz_led_init;
+ dc->desc = "Jazz LED display",
+ dc->vmsd = &vmstate_jazz_led;
+ dc->reset = jazz_led_reset;
+}
+
+static TypeInfo jazz_led_info = {
+ .name = "jazz-led",
+ .parent = TYPE_SYS_BUS_DEVICE,
+ .instance_size = sizeof(LedState),
+ .class_init = jazz_led_class_init,
+};
+
+static void jazz_led_register(void)
+{
+ type_register_static(&jazz_led_info);
+}
+
+type_init(jazz_led_register);
diff --git a/hw/kvm/apic.c b/hw/kvm/apic.c
index dfc2ab3b03..9ca68f81aa 100644
--- a/hw/kvm/apic.c
+++ b/hw/kvm/apic.c
@@ -92,6 +92,35 @@ static void kvm_apic_set_tpr(APICCommonState *s, uint8_t val)
s->tpr = (val & 0x0f) << 4;
}
+static uint8_t kvm_apic_get_tpr(APICCommonState *s)
+{
+ return s->tpr >> 4;
+}
+
+static void kvm_apic_enable_tpr_reporting(APICCommonState *s, bool enable)
+{
+ struct kvm_tpr_access_ctl ctl = {
+ .enabled = enable
+ };
+
+ kvm_vcpu_ioctl(s->cpu_env, KVM_TPR_ACCESS_REPORTING, &ctl);
+}
+
+static void kvm_apic_vapic_base_update(APICCommonState *s)
+{
+ struct kvm_vapic_addr vapid_addr = {
+ .vapic_addr = s->vapic_paddr,
+ };
+ int ret;
+
+ ret = kvm_vcpu_ioctl(s->cpu_env, KVM_SET_VAPIC_ADDR, &vapid_addr);
+ if (ret < 0) {
+ fprintf(stderr, "KVM: setting VAPIC address failed (%s)\n",
+ strerror(-ret));
+ abort();
+ }
+}
+
static void do_inject_external_nmi(void *data)
{
APICCommonState *s = data;
@@ -129,6 +158,9 @@ static void kvm_apic_class_init(ObjectClass *klass, void *data)
k->init = kvm_apic_init;
k->set_base = kvm_apic_set_base;
k->set_tpr = kvm_apic_set_tpr;
+ k->get_tpr = kvm_apic_get_tpr;
+ k->enable_tpr_reporting = kvm_apic_enable_tpr_reporting;
+ k->vapic_base_update = kvm_apic_vapic_base_update;
k->external_nmi = kvm_apic_external_nmi;
}
@@ -139,9 +171,9 @@ static TypeInfo kvm_apic_info = {
.class_init = kvm_apic_class_init,
};
-static void kvm_apic_register_device(void)
+static void kvm_apic_register_types(void)
{
type_register_static(&kvm_apic_info);
}
-device_init(kvm_apic_register_device)
+type_init(kvm_apic_register_types)
diff --git a/hw/kvm/clock.c b/hw/kvm/clock.c
index d5a53869b3..2157340326 100644
--- a/hw/kvm/clock.c
+++ b/hw/kvm/clock.c
@@ -119,11 +119,11 @@ void kvmclock_create(void)
}
}
-static void kvmclock_register_device(void)
+static void kvmclock_register_types(void)
{
if (kvm_enabled()) {
type_register_static(&kvmclock_info);
}
}
-device_init(kvmclock_register_device);
+type_init(kvmclock_register_types)
diff --git a/hw/kvm/i8259.c b/hw/kvm/i8259.c
index 14bd427518..94d1b9aa95 100644
--- a/hw/kvm/i8259.c
+++ b/hw/kvm/i8259.c
@@ -84,8 +84,8 @@ static void kvm_pic_reset(DeviceState *dev)
{
PICCommonState *s = DO_UPCAST(PICCommonState, dev.qdev, dev);
- pic_reset_common(s);
s->elcr = 0;
+ pic_reset_common(s);
kvm_pic_put(s);
}
@@ -130,9 +130,9 @@ static TypeInfo kvm_i8259_info = {
.class_init = kvm_i8259_class_init,
};
-static void kvm_pic_register(void)
+static void kvm_pic_register_types(void)
{
type_register_static(&kvm_i8259_info);
}
-device_init(kvm_pic_register)
+type_init(kvm_pic_register_types)
diff --git a/hw/kvm/ioapic.c b/hw/kvm/ioapic.c
index b316933a96..3ae3175403 100644
--- a/hw/kvm/ioapic.c
+++ b/hw/kvm/ioapic.c
@@ -117,9 +117,9 @@ static TypeInfo kvm_ioapic_info = {
.class_init = kvm_ioapic_class_init,
};
-static void kvm_ioapic_register_device(void)
+static void kvm_ioapic_register_types(void)
{
type_register_static(&kvm_ioapic_info);
}
-device_init(kvm_ioapic_register_device)
+type_init(kvm_ioapic_register_types)
diff --git a/hw/kvmvapic.c b/hw/kvmvapic.c
new file mode 100644
index 0000000000..36ccfbcdbd
--- /dev/null
+++ b/hw/kvmvapic.c
@@ -0,0 +1,805 @@
+/*
+ * TPR optimization for 32-bit Windows guests (XP and Server 2003)
+ *
+ * Copyright (C) 2007-2008 Qumranet Technologies
+ * Copyright (C) 2012 Jan Kiszka, Siemens AG
+ *
+ * This work is licensed under the terms of the GNU GPL version 2, or
+ * (at your option) any later version. See the COPYING file in the
+ * top-level directory.
+ */
+#include "sysemu.h"
+#include "cpus.h"
+#include "kvm.h"
+#include "apic_internal.h"
+
+#define APIC_DEFAULT_ADDRESS 0xfee00000
+
+#define VAPIC_IO_PORT 0x7e
+
+#define VAPIC_CPU_SHIFT 7
+
+#define ROM_BLOCK_SIZE 512
+#define ROM_BLOCK_MASK (~(ROM_BLOCK_SIZE - 1))
+
+typedef enum VAPICMode {
+ VAPIC_INACTIVE = 0,
+ VAPIC_ACTIVE = 1,
+ VAPIC_STANDBY = 2,
+} VAPICMode;
+
+typedef struct VAPICHandlers {
+ uint32_t set_tpr;
+ uint32_t set_tpr_eax;
+ uint32_t get_tpr[8];
+ uint32_t get_tpr_stack;
+} QEMU_PACKED VAPICHandlers;
+
+typedef struct GuestROMState {
+ char signature[8];
+ uint32_t vaddr;
+ uint32_t fixup_start;
+ uint32_t fixup_end;
+ uint32_t vapic_vaddr;
+ uint32_t vapic_size;
+ uint32_t vcpu_shift;
+ uint32_t real_tpr_addr;
+ VAPICHandlers up;
+ VAPICHandlers mp;
+} QEMU_PACKED GuestROMState;
+
+typedef struct VAPICROMState {
+ SysBusDevice busdev;
+ MemoryRegion io;
+ MemoryRegion rom;
+ uint32_t state;
+ uint32_t rom_state_paddr;
+ uint32_t rom_state_vaddr;
+ uint32_t vapic_paddr;
+ uint32_t real_tpr_addr;
+ GuestROMState rom_state;
+ size_t rom_size;
+ bool rom_mapped_writable;
+} VAPICROMState;
+
+#define TPR_INSTR_ABS_MODRM 0x1
+#define TPR_INSTR_MATCH_MODRM_REG 0x2
+
+typedef struct TPRInstruction {
+ uint8_t opcode;
+ uint8_t modrm_reg;
+ unsigned int flags;
+ TPRAccess access;
+ size_t length;
+ off_t addr_offset;
+} TPRInstruction;
+
+/* must be sorted by length, shortest first */
+static const TPRInstruction tpr_instr[] = {
+ { /* mov abs to eax */
+ .opcode = 0xa1,
+ .access = TPR_ACCESS_READ,
+ .length = 5,
+ .addr_offset = 1,
+ },
+ { /* mov eax to abs */
+ .opcode = 0xa3,
+ .access = TPR_ACCESS_WRITE,
+ .length = 5,
+ .addr_offset = 1,
+ },
+ { /* mov r32 to r/m32 */
+ .opcode = 0x89,
+ .flags = TPR_INSTR_ABS_MODRM,
+ .access = TPR_ACCESS_WRITE,
+ .length = 6,
+ .addr_offset = 2,
+ },
+ { /* mov r/m32 to r32 */
+ .opcode = 0x8b,
+ .flags = TPR_INSTR_ABS_MODRM,
+ .access = TPR_ACCESS_READ,
+ .length = 6,
+ .addr_offset = 2,
+ },
+ { /* push r/m32 */
+ .opcode = 0xff,
+ .modrm_reg = 6,
+ .flags = TPR_INSTR_ABS_MODRM | TPR_INSTR_MATCH_MODRM_REG,
+ .access = TPR_ACCESS_READ,
+ .length = 6,
+ .addr_offset = 2,
+ },
+ { /* mov imm32, r/m32 (c7/0) */
+ .opcode = 0xc7,
+ .modrm_reg = 0,
+ .flags = TPR_INSTR_ABS_MODRM | TPR_INSTR_MATCH_MODRM_REG,
+ .access = TPR_ACCESS_WRITE,
+ .length = 10,
+ .addr_offset = 2,
+ },
+};
+
+static void read_guest_rom_state(VAPICROMState *s)
+{
+ cpu_physical_memory_rw(s->rom_state_paddr, (void *)&s->rom_state,
+ sizeof(GuestROMState), 0);
+}
+
+static void write_guest_rom_state(VAPICROMState *s)
+{
+ cpu_physical_memory_rw(s->rom_state_paddr, (void *)&s->rom_state,
+ sizeof(GuestROMState), 1);
+}
+
+static void update_guest_rom_state(VAPICROMState *s)
+{
+ read_guest_rom_state(s);
+
+ s->rom_state.real_tpr_addr = cpu_to_le32(s->real_tpr_addr);
+ s->rom_state.vcpu_shift = cpu_to_le32(VAPIC_CPU_SHIFT);
+
+ write_guest_rom_state(s);
+}
+
+static int find_real_tpr_addr(VAPICROMState *s, CPUState *env)
+{
+ target_phys_addr_t paddr;
+ target_ulong addr;
+
+ if (s->state == VAPIC_ACTIVE) {
+ return 0;
+ }
+ /*
+ * If there is no prior TPR access instruction we could analyze (which is
+ * the case after resume from hibernation), we need to scan the possible
+ * virtual address space for the APIC mapping.
+ */
+ for (addr = 0xfffff000; addr >= 0x80000000; addr -= TARGET_PAGE_SIZE) {
+ paddr = cpu_get_phys_page_debug(env, addr);
+ if (paddr != APIC_DEFAULT_ADDRESS) {
+ continue;
+ }
+ s->real_tpr_addr = addr + 0x80;
+ update_guest_rom_state(s);
+ return 0;
+ }
+ return -1;
+}
+
+static uint8_t modrm_reg(uint8_t modrm)
+{
+ return (modrm >> 3) & 7;
+}
+
+static bool is_abs_modrm(uint8_t modrm)
+{
+ return (modrm & 0xc7) == 0x05;
+}
+
+static bool opcode_matches(uint8_t *opcode, const TPRInstruction *instr)
+{
+ return opcode[0] == instr->opcode &&
+ (!(instr->flags & TPR_INSTR_ABS_MODRM) || is_abs_modrm(opcode[1])) &&
+ (!(instr->flags & TPR_INSTR_MATCH_MODRM_REG) ||
+ modrm_reg(opcode[1]) == instr->modrm_reg);
+}
+
+static int evaluate_tpr_instruction(VAPICROMState *s, CPUState *env,
+ target_ulong *pip, TPRAccess access)
+{
+ const TPRInstruction *instr;
+ target_ulong ip = *pip;
+ uint8_t opcode[2];
+ uint32_t real_tpr_addr;
+ int i;
+
+ if ((ip & 0xf0000000ULL) != 0x80000000ULL &&
+ (ip & 0xf0000000ULL) != 0xe0000000ULL) {
+ return -1;
+ }
+
+ /*
+ * Early Windows 2003 SMP initialization contains a
+ *
+ * mov imm32, r/m32
+ *
+ * instruction that is patched by TPR optimization. The problem is that
+ * RSP, used by the patched instruction, is zero, so the guest gets a
+ * double fault and dies.
+ */
+ if (env->regs[R_ESP] == 0) {
+ return -1;
+ }
+
+ if (kvm_enabled() && !kvm_irqchip_in_kernel()) {
+ /*
+ * KVM without kernel-based TPR access reporting will pass an IP that
+ * points after the accessing instruction. So we need to look backward
+ * to find the reason.
+ */
+ for (i = 0; i < ARRAY_SIZE(tpr_instr); i++) {
+ instr = &tpr_instr[i];
+ if (instr->access != access) {
+ continue;
+ }
+ if (cpu_memory_rw_debug(env, ip - instr->length, opcode,
+ sizeof(opcode), 0) < 0) {
+ return -1;
+ }
+ if (opcode_matches(opcode, instr)) {
+ ip -= instr->length;
+ goto instruction_ok;
+ }
+ }
+ return -1;
+ } else {
+ if (cpu_memory_rw_debug(env, ip, opcode, sizeof(opcode), 0) < 0) {
+ return -1;
+ }
+ for (i = 0; i < ARRAY_SIZE(tpr_instr); i++) {
+ instr = &tpr_instr[i];
+ if (opcode_matches(opcode, instr)) {
+ goto instruction_ok;
+ }
+ }
+ return -1;
+ }
+
+instruction_ok:
+ /*
+ * Grab the virtual TPR address from the instruction
+ * and update the cached values.
+ */
+ if (cpu_memory_rw_debug(env, ip + instr->addr_offset,
+ (void *)&real_tpr_addr,
+ sizeof(real_tpr_addr), 0) < 0) {
+ return -1;
+ }
+ real_tpr_addr = le32_to_cpu(real_tpr_addr);
+ if ((real_tpr_addr & 0xfff) != 0x80) {
+ return -1;
+ }
+ s->real_tpr_addr = real_tpr_addr;
+ update_guest_rom_state(s);
+
+ *pip = ip;
+ return 0;
+}
+
+static int update_rom_mapping(VAPICROMState *s, CPUState *env, target_ulong ip)
+{
+ target_phys_addr_t paddr;
+ uint32_t rom_state_vaddr;
+ uint32_t pos, patch, offset;
+
+ /* nothing to do if already activated */
+ if (s->state == VAPIC_ACTIVE) {
+ return 0;
+ }
+
+ /* bail out if ROM init code was not executed (missing ROM?) */
+ if (s->state == VAPIC_INACTIVE) {
+ return -1;
+ }
+
+ /* find out virtual address of the ROM */
+ rom_state_vaddr = s->rom_state_paddr + (ip & 0xf0000000);
+ paddr = cpu_get_phys_page_debug(env, rom_state_vaddr);
+ if (paddr == -1) {
+ return -1;
+ }
+ paddr += rom_state_vaddr & ~TARGET_PAGE_MASK;
+ if (paddr != s->rom_state_paddr) {
+ return -1;
+ }
+ read_guest_rom_state(s);
+ if (memcmp(s->rom_state.signature, "kvm aPiC", 8) != 0) {
+ return -1;
+ }
+ s->rom_state_vaddr = rom_state_vaddr;
+
+ /* fixup addresses in ROM if needed */
+ if (rom_state_vaddr == le32_to_cpu(s->rom_state.vaddr)) {
+ return 0;
+ }
+ for (pos = le32_to_cpu(s->rom_state.fixup_start);
+ pos < le32_to_cpu(s->rom_state.fixup_end);
+ pos += 4) {
+ cpu_physical_memory_rw(paddr + pos - s->rom_state.vaddr,
+ (void *)&offset, sizeof(offset), 0);
+ offset = le32_to_cpu(offset);
+ cpu_physical_memory_rw(paddr + offset, (void *)&patch,
+ sizeof(patch), 0);
+ patch = le32_to_cpu(patch);
+ patch += rom_state_vaddr - le32_to_cpu(s->rom_state.vaddr);
+ patch = cpu_to_le32(patch);
+ cpu_physical_memory_rw(paddr + offset, (void *)&patch,
+ sizeof(patch), 1);
+ }
+ read_guest_rom_state(s);
+ s->vapic_paddr = paddr + le32_to_cpu(s->rom_state.vapic_vaddr) -
+ le32_to_cpu(s->rom_state.vaddr);
+
+ return 0;
+}
+
+/*
+ * Tries to read the unique processor number from the Kernel Processor Control
+ * Region (KPCR) of 32-bit Windows XP and Server 2003. Returns -1 if the KPCR
+ * cannot be accessed or is considered invalid. This also ensures that we are
+ * not patching the wrong guest.
+ */
+static int get_kpcr_number(CPUState *env)
+{
+ struct kpcr {
+ uint8_t fill1[0x1c];
+ uint32_t self;
+ uint8_t fill2[0x31];
+ uint8_t number;
+ } QEMU_PACKED kpcr;
+
+ if (cpu_memory_rw_debug(env, env->segs[R_FS].base,
+ (void *)&kpcr, sizeof(kpcr), 0) < 0 ||
+ kpcr.self != env->segs[R_FS].base) {
+ return -1;
+ }
+ return kpcr.number;
+}
+
+static int vapic_enable(VAPICROMState *s, CPUState *env)
+{
+ int cpu_number = get_kpcr_number(env);
+ target_phys_addr_t vapic_paddr;
+ static const uint8_t enabled = 1;
+
+ if (cpu_number < 0) {
+ return -1;
+ }
+ vapic_paddr = s->vapic_paddr +
+ (((target_phys_addr_t)cpu_number) << VAPIC_CPU_SHIFT);
+ cpu_physical_memory_rw(vapic_paddr + offsetof(VAPICState, enabled),
+ (void *)&enabled, sizeof(enabled), 1);
+ apic_enable_vapic(env->apic_state, vapic_paddr);
+
+ s->state = VAPIC_ACTIVE;
+
+ return 0;
+}
+
+static void patch_byte(CPUState *env, target_ulong addr, uint8_t byte)
+{
+ cpu_memory_rw_debug(env, addr, &byte, 1, 1);
+}
+
+static void patch_call(VAPICROMState *s, CPUState *env, target_ulong ip,
+ uint32_t target)
+{
+ uint32_t offset;
+
+ offset = cpu_to_le32(target - ip - 5);
+ patch_byte(env, ip, 0xe8); /* call near */
+ cpu_memory_rw_debug(env, ip + 1, (void *)&offset, sizeof(offset), 1);
+}
+
+static void patch_instruction(VAPICROMState *s, CPUState *env, target_ulong ip)
+{
+ target_phys_addr_t paddr;
+ VAPICHandlers *handlers;
+ uint8_t opcode[2];
+ uint32_t imm32;
+
+ if (smp_cpus == 1) {
+ handlers = &s->rom_state.up;
+ } else {
+ handlers = &s->rom_state.mp;
+ }
+
+ pause_all_vcpus();
+
+ cpu_memory_rw_debug(env, ip, opcode, sizeof(opcode), 0);
+
+ switch (opcode[0]) {
+ case 0x89: /* mov r32 to r/m32 */
+ patch_byte(env, ip, 0x50 + modrm_reg(opcode[1])); /* push reg */
+ patch_call(s, env, ip + 1, handlers->set_tpr);
+ break;
+ case 0x8b: /* mov r/m32 to r32 */
+ patch_byte(env, ip, 0x90);
+ patch_call(s, env, ip + 1, handlers->get_tpr[modrm_reg(opcode[1])]);
+ break;
+ case 0xa1: /* mov abs to eax */
+ patch_call(s, env, ip, handlers->get_tpr[0]);
+ break;
+ case 0xa3: /* mov eax to abs */
+ patch_call(s, env, ip, handlers->set_tpr_eax);
+ break;
+ case 0xc7: /* mov imm32, r/m32 (c7/0) */
+ patch_byte(env, ip, 0x68); /* push imm32 */
+ cpu_memory_rw_debug(env, ip + 6, (void *)&imm32, sizeof(imm32), 0);
+ cpu_memory_rw_debug(env, ip + 1, (void *)&imm32, sizeof(imm32), 1);
+ patch_call(s, env, ip + 5, handlers->set_tpr);
+ break;
+ case 0xff: /* push r/m32 */
+ patch_byte(env, ip, 0x50); /* push eax */
+ patch_call(s, env, ip + 1, handlers->get_tpr_stack);
+ break;
+ default:
+ abort();
+ }
+
+ resume_all_vcpus();
+
+ paddr = cpu_get_phys_page_debug(env, ip);
+ paddr += ip & ~TARGET_PAGE_MASK;
+ tb_invalidate_phys_page_range(paddr, paddr + 1, 1);
+}
+
+void vapic_report_tpr_access(DeviceState *dev, void *cpu, target_ulong ip,
+ TPRAccess access)
+{
+ VAPICROMState *s = DO_UPCAST(VAPICROMState, busdev.qdev, dev);
+ CPUState *env = cpu;
+
+ cpu_synchronize_state(env);
+
+ if (evaluate_tpr_instruction(s, env, &ip, access) < 0) {
+ if (s->state == VAPIC_ACTIVE) {
+ vapic_enable(s, env);
+ }
+ return;
+ }
+ if (update_rom_mapping(s, env, ip) < 0) {
+ return;
+ }
+ if (vapic_enable(s, env) < 0) {
+ return;
+ }
+ patch_instruction(s, env, ip);
+}
+
+typedef struct VAPICEnableTPRReporting {
+ DeviceState *apic;
+ bool enable;
+} VAPICEnableTPRReporting;
+
+static void vapic_do_enable_tpr_reporting(void *data)
+{
+ VAPICEnableTPRReporting *info = data;
+
+ apic_enable_tpr_access_reporting(info->apic, info->enable);
+}
+
+static void vapic_enable_tpr_reporting(bool enable)
+{
+ VAPICEnableTPRReporting info = {
+ .enable = enable,
+ };
+ CPUState *env;
+
+ for (env = first_cpu; env != NULL; env = env->next_cpu) {
+ info.apic = env->apic_state;
+ run_on_cpu(env, vapic_do_enable_tpr_reporting, &info);
+ }
+}
+
+static void vapic_reset(DeviceState *dev)
+{
+ VAPICROMState *s = DO_UPCAST(VAPICROMState, busdev.qdev, dev);
+
+ if (s->state == VAPIC_ACTIVE) {
+ s->state = VAPIC_STANDBY;
+ }
+ vapic_enable_tpr_reporting(false);
+}
+
+/*
+ * Set the IRQ polling hypercalls to the supported variant:
+ * - vmcall if using KVM in-kernel irqchip
+ * - 32-bit VAPIC port write otherwise
+ */
+static int patch_hypercalls(VAPICROMState *s)
+{
+ target_phys_addr_t rom_paddr = s->rom_state_paddr & ROM_BLOCK_MASK;
+ static const uint8_t vmcall_pattern[] = { /* vmcall */
+ 0xb8, 0x1, 0, 0, 0, 0xf, 0x1, 0xc1
+ };
+ static const uint8_t outl_pattern[] = { /* nop; outl %eax,0x7e */
+ 0xb8, 0x1, 0, 0, 0, 0x90, 0xe7, 0x7e
+ };
+ uint8_t alternates[2];
+ const uint8_t *pattern;
+ const uint8_t *patch;
+ int patches = 0;
+ off_t pos;
+ uint8_t *rom;
+
+ rom = g_malloc(s->rom_size);
+ cpu_physical_memory_rw(rom_paddr, rom, s->rom_size, 0);
+
+ for (pos = 0; pos < s->rom_size - sizeof(vmcall_pattern); pos++) {
+ if (kvm_irqchip_in_kernel()) {
+ pattern = outl_pattern;
+ alternates[0] = outl_pattern[7];
+ alternates[1] = outl_pattern[7];
+ patch = &vmcall_pattern[5];
+ } else {
+ pattern = vmcall_pattern;
+ alternates[0] = vmcall_pattern[7];
+ alternates[1] = 0xd9; /* AMD's VMMCALL */
+ patch = &outl_pattern[5];
+ }
+ if (memcmp(rom + pos, pattern, 7) == 0 &&
+ (rom[pos + 7] == alternates[0] || rom[pos + 7] == alternates[1])) {
+ cpu_physical_memory_rw(rom_paddr + pos + 5, (uint8_t *)patch,
+ 3, 1);
+ /*
+ * Don't flush the tb here. Under ordinary conditions, the patched
+ * calls are miles away from the current IP. Under malicious
+ * conditions, the guest could trick us to crash.
+ */
+ }
+ }
+
+ g_free(rom);
+
+ if (patches != 0 && patches != 2) {
+ return -1;
+ }
+
+ return 0;
+}
+
+/*
+ * For TCG mode or the time KVM honors read-only memory regions, we need to
+ * enable write access to the option ROM so that variables can be updated by
+ * the guest.
+ */
+static void vapic_map_rom_writable(VAPICROMState *s)
+{
+ target_phys_addr_t rom_paddr = s->rom_state_paddr & ROM_BLOCK_MASK;
+ MemoryRegionSection section;
+ MemoryRegion *as;
+ size_t rom_size;
+ uint8_t *ram;
+
+ as = sysbus_address_space(&s->busdev);
+
+ if (s->rom_mapped_writable) {
+ memory_region_del_subregion(as, &s->rom);
+ memory_region_destroy(&s->rom);
+ }
+
+ /* grab RAM memory region (region @rom_paddr may still be pc.rom) */
+ section = memory_region_find(as, 0, 1);
+
+ /* read ROM size from RAM region */
+ ram = memory_region_get_ram_ptr(section.mr);
+ rom_size = ram[rom_paddr + 2] * ROM_BLOCK_SIZE;
+ s->rom_size = rom_size;
+
+ /* We need to round up to avoid creating subpages
+ * from which we cannot run code. */
+ rom_size = TARGET_PAGE_ALIGN(rom_size);
+
+ memory_region_init_alias(&s->rom, "kvmvapic-rom", section.mr, rom_paddr,
+ rom_size);
+ memory_region_add_subregion_overlap(as, rom_paddr, &s->rom, 1000);
+ s->rom_mapped_writable = true;
+}
+
+static int vapic_prepare(VAPICROMState *s)
+{
+ vapic_map_rom_writable(s);
+
+ if (patch_hypercalls(s) < 0) {
+ return -1;
+ }
+
+ vapic_enable_tpr_reporting(true);
+
+ return 0;
+}
+
+static void vapic_write(void *opaque, target_phys_addr_t addr, uint64_t data,
+ unsigned int size)
+{
+ CPUState *env = cpu_single_env;
+ target_phys_addr_t rom_paddr;
+ VAPICROMState *s = opaque;
+
+ cpu_synchronize_state(env);
+
+ /*
+ * The VAPIC supports two PIO-based hypercalls, both via port 0x7E.
+ * o 16-bit write access:
+ * Reports the option ROM initialization to the hypervisor. Written
+ * value is the offset of the state structure in the ROM.
+ * o 8-bit write access:
+ * Reactivates the VAPIC after a guest hibernation, i.e. after the
+ * option ROM content has been re-initialized by a guest power cycle.
+ * o 32-bit write access:
+ * Poll for pending IRQs, considering the current VAPIC state.
+ */
+ switch (size) {
+ case 2:
+ if (s->state == VAPIC_INACTIVE) {
+ rom_paddr = (env->segs[R_CS].base + env->eip) & ROM_BLOCK_MASK;
+ s->rom_state_paddr = rom_paddr + data;
+
+ s->state = VAPIC_STANDBY;
+ }
+ if (vapic_prepare(s) < 0) {
+ s->state = VAPIC_INACTIVE;
+ break;
+ }
+ break;
+ case 1:
+ if (kvm_enabled()) {
+ /*
+ * Disable triggering instruction in ROM by writing a NOP.
+ *
+ * We cannot do this in TCG mode as the reported IP is not
+ * accurate.
+ */
+ pause_all_vcpus();
+ patch_byte(env, env->eip - 2, 0x66);
+ patch_byte(env, env->eip - 1, 0x90);
+ resume_all_vcpus();
+ }
+
+ if (s->state == VAPIC_ACTIVE) {
+ break;
+ }
+ if (update_rom_mapping(s, env, env->eip) < 0) {
+ break;
+ }
+ if (find_real_tpr_addr(s, env) < 0) {
+ break;
+ }
+ vapic_enable(s, env);
+ break;
+ default:
+ case 4:
+ if (!kvm_irqchip_in_kernel()) {
+ apic_poll_irq(env->apic_state);
+ }
+ break;
+ }
+}
+
+static const MemoryRegionOps vapic_ops = {
+ .write = vapic_write,
+ .endianness = DEVICE_NATIVE_ENDIAN,
+};
+
+static int vapic_init(SysBusDevice *dev)
+{
+ VAPICROMState *s = FROM_SYSBUS(VAPICROMState, dev);
+
+ memory_region_init_io(&s->io, &vapic_ops, s, "kvmvapic", 2);
+ sysbus_add_io(dev, VAPIC_IO_PORT, &s->io);
+ sysbus_init_ioports(dev, VAPIC_IO_PORT, 2);
+
+ option_rom[nb_option_roms].name = "kvmvapic.bin";
+ option_rom[nb_option_roms].bootindex = -1;
+ nb_option_roms++;
+
+ return 0;
+}
+
+static void do_vapic_enable(void *data)
+{
+ VAPICROMState *s = data;
+
+ vapic_enable(s, first_cpu);
+}
+
+static int vapic_post_load(void *opaque, int version_id)
+{
+ VAPICROMState *s = opaque;
+ uint8_t *zero;
+
+ /*
+ * The old implementation of qemu-kvm did not provide the state
+ * VAPIC_STANDBY. Reconstruct it.
+ */
+ if (s->state == VAPIC_INACTIVE && s->rom_state_paddr != 0) {
+ s->state = VAPIC_STANDBY;
+ }
+
+ if (s->state != VAPIC_INACTIVE) {
+ if (vapic_prepare(s) < 0) {
+ return -1;
+ }
+ }
+ if (s->state == VAPIC_ACTIVE) {
+ if (smp_cpus == 1) {
+ run_on_cpu(first_cpu, do_vapic_enable, s);
+ } else {
+ zero = g_malloc0(s->rom_state.vapic_size);
+ cpu_physical_memory_rw(s->vapic_paddr, zero,
+ s->rom_state.vapic_size, 1);
+ g_free(zero);
+ }
+ }
+
+ return 0;
+}
+
+static const VMStateDescription vmstate_handlers = {
+ .name = "kvmvapic-handlers",
+ .version_id = 1,
+ .minimum_version_id = 1,
+ .minimum_version_id_old = 1,
+ .fields = (VMStateField[]) {
+ VMSTATE_UINT32(set_tpr, VAPICHandlers),
+ VMSTATE_UINT32(set_tpr_eax, VAPICHandlers),
+ VMSTATE_UINT32_ARRAY(get_tpr, VAPICHandlers, 8),
+ VMSTATE_UINT32(get_tpr_stack, VAPICHandlers),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
+static const VMStateDescription vmstate_guest_rom = {
+ .name = "kvmvapic-guest-rom",
+ .version_id = 1,
+ .minimum_version_id = 1,
+ .minimum_version_id_old = 1,
+ .fields = (VMStateField[]) {
+ VMSTATE_UNUSED(8), /* signature */
+ VMSTATE_UINT32(vaddr, GuestROMState),
+ VMSTATE_UINT32(fixup_start, GuestROMState),
+ VMSTATE_UINT32(fixup_end, GuestROMState),
+ VMSTATE_UINT32(vapic_vaddr, GuestROMState),
+ VMSTATE_UINT32(vapic_size, GuestROMState),
+ VMSTATE_UINT32(vcpu_shift, GuestROMState),
+ VMSTATE_UINT32(real_tpr_addr, GuestROMState),
+ VMSTATE_STRUCT(up, GuestROMState, 0, vmstate_handlers, VAPICHandlers),
+ VMSTATE_STRUCT(mp, GuestROMState, 0, vmstate_handlers, VAPICHandlers),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
+static const VMStateDescription vmstate_vapic = {
+ .name = "kvm-tpr-opt", /* compatible with qemu-kvm VAPIC */
+ .version_id = 1,
+ .minimum_version_id = 1,
+ .minimum_version_id_old = 1,
+ .post_load = vapic_post_load,
+ .fields = (VMStateField[]) {
+ VMSTATE_STRUCT(rom_state, VAPICROMState, 0, vmstate_guest_rom,
+ GuestROMState),
+ VMSTATE_UINT32(state, VAPICROMState),
+ VMSTATE_UINT32(real_tpr_addr, VAPICROMState),
+ VMSTATE_UINT32(rom_state_vaddr, VAPICROMState),
+ VMSTATE_UINT32(vapic_paddr, VAPICROMState),
+ VMSTATE_UINT32(rom_state_paddr, VAPICROMState),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
+static void vapic_class_init(ObjectClass *klass, void *data)
+{
+ SysBusDeviceClass *sc = SYS_BUS_DEVICE_CLASS(klass);
+ DeviceClass *dc = DEVICE_CLASS(klass);
+
+ dc->no_user = 1;
+ dc->reset = vapic_reset;
+ dc->vmsd = &vmstate_vapic;
+ sc->init = vapic_init;
+}
+
+static TypeInfo vapic_type = {
+ .name = "kvmvapic",
+ .parent = TYPE_SYS_BUS_DEVICE,
+ .instance_size = sizeof(VAPICROMState),
+ .class_init = vapic_class_init,
+};
+
+static void vapic_register(void)
+{
+ type_register_static(&vapic_type);
+}
+
+type_init(vapic_register);
diff --git a/hw/lan9118.c b/hw/lan9118.c
index 78777c7336..7b4fe87fca 100644
--- a/hw/lan9118.c
+++ b/hw/lan9118.c
@@ -235,11 +235,21 @@ typedef struct {
int32_t rxp_offset;
int32_t rxp_size;
int32_t rxp_pad;
+
+ uint32_t write_word_prev_offset;
+ uint32_t write_word_n;
+ uint16_t write_word_l;
+ uint16_t write_word_h;
+ uint32_t read_word_prev_offset;
+ uint32_t read_word_n;
+ uint32_t read_long;
+
+ uint32_t mode_16bit;
} lan9118_state;
static const VMStateDescription vmstate_lan9118 = {
.name = "lan9118",
- .version_id = 1,
+ .version_id = 2,
.minimum_version_id = 1,
.fields = (VMStateField[]) {
VMSTATE_PTIMER(timer, lan9118_state),
@@ -294,6 +304,14 @@ static const VMStateDescription vmstate_lan9118 = {
VMSTATE_INT32(rxp_offset, lan9118_state),
VMSTATE_INT32(rxp_size, lan9118_state),
VMSTATE_INT32(rxp_pad, lan9118_state),
+ VMSTATE_UINT32_V(write_word_prev_offset, lan9118_state, 2),
+ VMSTATE_UINT32_V(write_word_n, lan9118_state, 2),
+ VMSTATE_UINT16_V(write_word_l, lan9118_state, 2),
+ VMSTATE_UINT16_V(write_word_h, lan9118_state, 2),
+ VMSTATE_UINT32_V(read_word_prev_offset, lan9118_state, 2),
+ VMSTATE_UINT32_V(read_word_n, lan9118_state, 2),
+ VMSTATE_UINT32_V(read_long, lan9118_state, 2),
+ VMSTATE_UINT32_V(mode_16bit, lan9118_state, 2),
VMSTATE_END_OF_LIST()
}
};
@@ -390,7 +408,7 @@ static void lan9118_reset(DeviceState *d)
s->fifo_int = 0x48000000;
s->rx_cfg = 0;
s->tx_cfg = 0;
- s->hw_cfg = 0x00050000;
+ s->hw_cfg = s->mode_16bit ? 0x00050000 : 0x00050004;
s->pmt_ctrl &= 0x45;
s->gpio_cfg = 0;
s->txp->fifo_used = 0;
@@ -429,6 +447,9 @@ static void lan9118_reset(DeviceState *d)
s->mac_mii_data = 0;
s->mac_flow = 0;
+ s->read_word_n = 0;
+ s->write_word_n = 0;
+
phy_reset(s);
s->eeprom_writable = 0;
@@ -984,7 +1005,7 @@ static void lan9118_writel(void *opaque, target_phys_addr_t offset,
{
lan9118_state *s = (lan9118_state *)opaque;
offset &= 0xff;
-
+
//DPRINTF("Write reg 0x%02x = 0x%08x\n", (int)offset, val);
if (offset >= 0x20 && offset < 0x40) {
/* TX FIFO */
@@ -1034,7 +1055,7 @@ static void lan9118_writel(void *opaque, target_phys_addr_t offset,
/* SRST */
lan9118_reset(&s->busdev.qdev);
} else {
- s->hw_cfg = val & 0x003f300;
+ s->hw_cfg = (val & 0x003f300) | (s->hw_cfg & 0x4);
}
break;
case CSR_RX_DP_CTRL:
@@ -1113,6 +1134,46 @@ static void lan9118_writel(void *opaque, target_phys_addr_t offset,
lan9118_update(s);
}
+static void lan9118_writew(void *opaque, target_phys_addr_t offset,
+ uint32_t val)
+{
+ lan9118_state *s = (lan9118_state *)opaque;
+ offset &= 0xff;
+
+ if (s->write_word_prev_offset != (offset & ~0x3)) {
+ /* New offset, reset word counter */
+ s->write_word_n = 0;
+ s->write_word_prev_offset = offset & ~0x3;
+ }
+
+ if (offset & 0x2) {
+ s->write_word_h = val;
+ } else {
+ s->write_word_l = val;
+ }
+
+ //DPRINTF("Writew reg 0x%02x = 0x%08x\n", (int)offset, val);
+ s->write_word_n++;
+ if (s->write_word_n == 2) {
+ s->write_word_n = 0;
+ lan9118_writel(s, offset & ~3, s->write_word_l +
+ (s->write_word_h << 16), 4);
+ }
+}
+
+static void lan9118_16bit_mode_write(void *opaque, target_phys_addr_t offset,
+ uint64_t val, unsigned size)
+{
+ switch (size) {
+ case 2:
+ return lan9118_writew(opaque, offset, (uint32_t)val);
+ case 4:
+ return lan9118_writel(opaque, offset, val, size);
+ }
+
+ hw_error("lan9118_write: Bad size 0x%x\n", size);
+}
+
static uint64_t lan9118_readl(void *opaque, target_phys_addr_t offset,
unsigned size)
{
@@ -1149,7 +1210,7 @@ static uint64_t lan9118_readl(void *opaque, target_phys_addr_t offset,
case CSR_TX_CFG:
return s->tx_cfg;
case CSR_HW_CFG:
- return s->hw_cfg | 0x4;
+ return s->hw_cfg;
case CSR_RX_DP_CTRL:
return 0;
case CSR_RX_FIFO_INF:
@@ -1187,12 +1248,60 @@ static uint64_t lan9118_readl(void *opaque, target_phys_addr_t offset,
return 0;
}
+static uint32_t lan9118_readw(void *opaque, target_phys_addr_t offset)
+{
+ lan9118_state *s = (lan9118_state *)opaque;
+ uint32_t val;
+
+ if (s->read_word_prev_offset != (offset & ~0x3)) {
+ /* New offset, reset word counter */
+ s->read_word_n = 0;
+ s->read_word_prev_offset = offset & ~0x3;
+ }
+
+ s->read_word_n++;
+ if (s->read_word_n == 1) {
+ s->read_long = lan9118_readl(s, offset & ~3, 4);
+ } else {
+ s->read_word_n = 0;
+ }
+
+ if (offset & 2) {
+ val = s->read_long >> 16;
+ } else {
+ val = s->read_long & 0xFFFF;
+ }
+
+ //DPRINTF("Readw reg 0x%02x, val 0x%x\n", (int)offset, val);
+ return val;
+}
+
+static uint64_t lan9118_16bit_mode_read(void *opaque, target_phys_addr_t offset,
+ unsigned size)
+{
+ switch (size) {
+ case 2:
+ return lan9118_readw(opaque, offset);
+ case 4:
+ return lan9118_readl(opaque, offset, size);
+ }
+
+ hw_error("lan9118_read: Bad size 0x%x\n", size);
+ return 0;
+}
+
static const MemoryRegionOps lan9118_mem_ops = {
.read = lan9118_readl,
.write = lan9118_writel,
.endianness = DEVICE_NATIVE_ENDIAN,
};
+static const MemoryRegionOps lan9118_16bit_mem_ops = {
+ .read = lan9118_16bit_mode_read,
+ .write = lan9118_16bit_mode_write,
+ .endianness = DEVICE_NATIVE_ENDIAN,
+};
+
static void lan9118_cleanup(VLANClientState *nc)
{
lan9118_state *s = DO_UPCAST(NICState, nc, nc)->opaque;
@@ -1214,8 +1323,10 @@ static int lan9118_init1(SysBusDevice *dev)
lan9118_state *s = FROM_SYSBUS(lan9118_state, dev);
QEMUBH *bh;
int i;
+ const MemoryRegionOps *mem_ops =
+ s->mode_16bit ? &lan9118_16bit_mem_ops : &lan9118_mem_ops;
- memory_region_init_io(&s->mmio, &lan9118_mem_ops, s, "lan9118-mmio", 0x100);
+ memory_region_init_io(&s->mmio, mem_ops, s, "lan9118-mmio", 0x100);
sysbus_init_mmio(dev, &s->mmio);
sysbus_init_irq(dev, &s->irq);
qemu_macaddr_default_if_unset(&s->conf.macaddr);
@@ -1240,6 +1351,7 @@ static int lan9118_init1(SysBusDevice *dev)
static Property lan9118_properties[] = {
DEFINE_NIC_PROPERTIES(lan9118_state, conf),
+ DEFINE_PROP_UINT32("mode_16bit", lan9118_state, mode_16bit, 0),
DEFINE_PROP_END_OF_LIST(),
};
@@ -1261,7 +1373,7 @@ static TypeInfo lan9118_info = {
.class_init = lan9118_class_init,
};
-static void lan9118_register_devices(void)
+static void lan9118_register_types(void)
{
type_register_static(&lan9118_info);
}
@@ -1282,4 +1394,4 @@ void lan9118_init(NICInfo *nd, uint32_t base, qemu_irq irq)
sysbus_connect_irq(s, 0, irq);
}
-device_init(lan9118_register_devices)
+type_init(lan9118_register_types)
diff --git a/hw/lance.c b/hw/lance.c
index 519720bd9c..ce3d46c17b 100644
--- a/hw/lance.c
+++ b/hw/lance.c
@@ -162,8 +162,9 @@ static TypeInfo lance_info = {
.class_init = lance_class_init,
};
-static void lance_register_devices(void)
+static void lance_register_types(void)
{
type_register_static(&lance_info);
}
-device_init(lance_register_devices)
+
+type_init(lance_register_types)
diff --git a/hw/lm32_juart.c b/hw/lm32_juart.c
index 38dd28230d..f07ed3977f 100644
--- a/hw/lm32_juart.c
+++ b/hw/lm32_juart.c
@@ -151,9 +151,9 @@ static TypeInfo lm32_juart_info = {
.class_init = lm32_juart_class_init,
};
-static void lm32_juart_register(void)
+static void lm32_juart_register_types(void)
{
type_register_static(&lm32_juart_info);
}
-device_init(lm32_juart_register)
+type_init(lm32_juart_register_types)
diff --git a/hw/lm32_pic.c b/hw/lm32_pic.c
index 7be6d0d68e..32f65db7f1 100644
--- a/hw/lm32_pic.c
+++ b/hw/lm32_pic.c
@@ -191,9 +191,9 @@ static TypeInfo lm32_pic_info = {
.class_init = lm32_pic_class_init,
};
-static void lm32_pic_register(void)
+static void lm32_pic_register_types(void)
{
type_register_static(&lm32_pic_info);
}
-device_init(lm32_pic_register)
+type_init(lm32_pic_register_types)
diff --git a/hw/lm32_sys.c b/hw/lm32_sys.c
index ba6f4acbaa..bbe03c41d5 100644
--- a/hw/lm32_sys.c
+++ b/hw/lm32_sys.c
@@ -164,9 +164,9 @@ static TypeInfo lm32_sys_info = {
.class_init = lm32_sys_class_init,
};
-static void lm32_sys_register(void)
+static void lm32_sys_register_types(void)
{
type_register_static(&lm32_sys_info);
}
-device_init(lm32_sys_register)
+type_init(lm32_sys_register_types)
diff --git a/hw/lm32_timer.c b/hw/lm32_timer.c
index 3cb4e0a4f3..e9450a0ce1 100644
--- a/hw/lm32_timer.c
+++ b/hw/lm32_timer.c
@@ -222,9 +222,9 @@ static TypeInfo lm32_timer_info = {
.class_init = lm32_timer_class_init,
};
-static void lm32_timer_register(void)
+static void lm32_timer_register_types(void)
{
type_register_static(&lm32_timer_info);
}
-device_init(lm32_timer_register)
+type_init(lm32_timer_register_types)
diff --git a/hw/lm32_uart.c b/hw/lm32_uart.c
index 630ccb7131..57066e28c6 100644
--- a/hw/lm32_uart.c
+++ b/hw/lm32_uart.c
@@ -288,9 +288,9 @@ static TypeInfo lm32_uart_info = {
.class_init = lm32_uart_class_init,
};
-static void lm32_uart_register(void)
+static void lm32_uart_register_types(void)
{
type_register_static(&lm32_uart_info);
}
-device_init(lm32_uart_register)
+type_init(lm32_uart_register_types)
diff --git a/hw/lm832x.c b/hw/lm832x.c
index 895d306635..8e09f9bcc9 100644
--- a/hw/lm832x.c
+++ b/hw/lm832x.c
@@ -513,9 +513,9 @@ static TypeInfo lm8323_info = {
.class_init = lm8323_class_init,
};
-static void lm832x_register_devices(void)
+static void lm832x_register_types(void)
{
type_register_static(&lm8323_info);
}
-device_init(lm832x_register_devices)
+type_init(lm832x_register_types)
diff --git a/hw/lsi53c895a.c b/hw/lsi53c895a.c
index 9a7ffe3f42..edc09b7307 100644
--- a/hw/lsi53c895a.c
+++ b/hw/lsi53c895a.c
@@ -699,7 +699,7 @@ static int lsi_queue_req(LSIState *s, SCSIRequest *req, uint32_t len)
}
/* Callback to indicate that the SCSI layer has completed a command. */
-static void lsi_command_complete(SCSIRequest *req, uint32_t status)
+static void lsi_command_complete(SCSIRequest *req, uint32_t status, size_t resid)
{
LSIState *s = DO_UPCAST(LSIState, dev.qdev, req->bus->qbus.parent);
int out;
@@ -2142,9 +2142,9 @@ static TypeInfo lsi_info = {
.class_init = lsi_class_init,
};
-static void lsi53c895a_register_devices(void)
+static void lsi53c895a_register_types(void)
{
type_register_static(&lsi_info);
}
-device_init(lsi53c895a_register_devices);
+type_init(lsi53c895a_register_types)
diff --git a/hw/m48t59.c b/hw/m48t59.c
index c35867d0a1..60bbb00946 100644
--- a/hw/m48t59.c
+++ b/hw/m48t59.c
@@ -768,10 +768,10 @@ static TypeInfo m48t59_info = {
.class_init = m48t59_class_init,
};
-static void m48t59_register_devices(void)
+static void m48t59_register_types(void)
{
type_register_static(&m48t59_info);
type_register_static(&m48t59_isa_info);
}
-device_init(m48t59_register_devices)
+type_init(m48t59_register_types)
diff --git a/hw/macio.c b/hw/macio.c
index 3d648e934b..eb15b890b1 100644
--- a/hw/macio.c
+++ b/hw/macio.c
@@ -97,12 +97,12 @@ static TypeInfo macio_info = {
.class_init = macio_class_init,
};
-static void macio_register(void)
+static void macio_register_types(void)
{
type_register_static(&macio_info);
}
-device_init(macio_register);
+type_init(macio_register_types)
void macio_init (PCIBus *bus, int device_id, int is_oldworld,
MemoryRegion *pic_mem, MemoryRegion *dbdma_mem,
diff --git a/hw/marvell_88w8618_audio.c b/hw/marvell_88w8618_audio.c
index b628f1718d..f6f1937442 100644
--- a/hw/marvell_88w8618_audio.c
+++ b/hw/marvell_88w8618_audio.c
@@ -295,9 +295,9 @@ static TypeInfo mv88w8618_audio_info = {
.class_init = mv88w8618_audio_class_init,
};
-static void mv88w8618_register_devices(void)
+static void mv88w8618_register_types(void)
{
type_register_static(&mv88w8618_audio_info);
}
-device_init(mv88w8618_register_devices)
+type_init(mv88w8618_register_types)
diff --git a/hw/max111x.c b/hw/max111x.c
index 9d61aa98db..706d89f4fd 100644
--- a/hw/max111x.c
+++ b/hw/max111x.c
@@ -183,10 +183,10 @@ static TypeInfo max1111_info = {
.class_init = max1111_class_init,
};
-static void max111x_register_devices(void)
+static void max111x_register_types(void)
{
type_register_static(&max1110_info);
type_register_static(&max1111_info);
}
-device_init(max111x_register_devices)
+type_init(max111x_register_types)
diff --git a/hw/max7310.c b/hw/max7310.c
index 3a6bb961ef..1ed18ba876 100644
--- a/hw/max7310.c
+++ b/hw/max7310.c
@@ -205,9 +205,9 @@ static TypeInfo max7310_info = {
.class_init = max7310_class_init,
};
-static void max7310_register_devices(void)
+static void max7310_register_types(void)
{
type_register_static(&max7310_info);
}
-device_init(max7310_register_devices)
+type_init(max7310_register_types)
diff --git a/hw/mc146818rtc.c b/hw/mc146818rtc.c
index 4a43225707..8b5cf8c81f 100644
--- a/hw/mc146818rtc.c
+++ b/hw/mc146818rtc.c
@@ -25,10 +25,13 @@
#include "qemu-timer.h"
#include "sysemu.h"
#include "pc.h"
-#include "apic.h"
#include "isa.h"
#include "mc146818rtc.h"
+#ifdef TARGET_I386
+#include "apic.h"
+#endif
+
//#define DEBUG_CMOS
//#define DEBUG_COALESCED
@@ -102,6 +105,7 @@ typedef struct RTCState {
QEMUTimer *second_timer2;
Notifier clock_reset_notifier;
LostTickPolicy lost_tick_policy;
+ Notifier suspend_notifier;
} RTCState;
static void rtc_set_time(RTCState *s);
@@ -436,6 +440,7 @@ static void rtc_update_second2(void *opaque)
s->cmos_data[RTC_REG_C] |= REG_C_AF;
if (s->cmos_data[RTC_REG_B] & REG_B_AIE) {
+ qemu_system_wakeup_request(QEMU_WAKEUP_REASON_RTC);
qemu_irq_raise(s->irq);
s->cmos_data[RTC_REG_C] |= REG_C_IRQF;
}
@@ -596,6 +601,14 @@ static void rtc_notify_clock_reset(Notifier *notifier, void *data)
#endif
}
+/* set CMOS shutdown status register (index 0xF) as S3_resume(0xFE)
+ BIOS will read it and start S3 resume at POST Entry */
+static void rtc_notify_suspend(Notifier *notifier, void *data)
+{
+ RTCState *s = container_of(notifier, RTCState, suspend_notifier);
+ rtc_set_memory(&s->dev, 0xF, 0xFE);
+}
+
static void rtc_reset(void *opaque)
{
RTCState *s = opaque;
@@ -676,6 +689,9 @@ static int rtc_initfn(ISADevice *dev)
s->clock_reset_notifier.notify = rtc_notify_clock_reset;
qemu_register_clock_reset_notifier(rtc_clock, &s->clock_reset_notifier);
+ s->suspend_notifier.notify = rtc_notify_suspend;
+ qemu_register_suspend_notifier(&s->suspend_notifier);
+
s->next_second_time =
qemu_get_clock_ns(rtc_clock) + (get_ticks_per_sec() * 99) / 100;
qemu_mod_timer(s->second_timer2, s->next_second_time);
@@ -733,8 +749,9 @@ static TypeInfo mc146818rtc_info = {
.class_init = rtc_class_initfn,
};
-static void mc146818rtc_register(void)
+static void mc146818rtc_register_types(void)
{
type_register_static(&mc146818rtc_info);
}
-device_init(mc146818rtc_register)
+
+type_init(mc146818rtc_register_types)
diff --git a/hw/milkymist-ac97.c b/hw/milkymist-ac97.c
index 0881643a4c..4414f39734 100644
--- a/hw/milkymist-ac97.c
+++ b/hw/milkymist-ac97.c
@@ -336,9 +336,9 @@ static TypeInfo milkymist_ac97_info = {
.class_init = milkymist_ac97_class_init,
};
-static void milkymist_ac97_register(void)
+static void milkymist_ac97_register_types(void)
{
type_register_static(&milkymist_ac97_info);
}
-device_init(milkymist_ac97_register)
+type_init(milkymist_ac97_register_types)
diff --git a/hw/milkymist-hpdmc.c b/hw/milkymist-hpdmc.c
index b5122afd57..2da0293683 100644
--- a/hw/milkymist-hpdmc.c
+++ b/hw/milkymist-hpdmc.c
@@ -162,9 +162,9 @@ static TypeInfo milkymist_hpdmc_info = {
.class_init = milkymist_hpdmc_class_init,
};
-static void milkymist_hpdmc_register(void)
+static void milkymist_hpdmc_register_types(void)
{
type_register_static(&milkymist_hpdmc_info);
}
-device_init(milkymist_hpdmc_register)
+type_init(milkymist_hpdmc_register_types)
diff --git a/hw/milkymist-memcard.c b/hw/milkymist-memcard.c
index 3c1c68a2e7..3515c3cd9a 100644
--- a/hw/milkymist-memcard.c
+++ b/hw/milkymist-memcard.c
@@ -295,9 +295,9 @@ static TypeInfo milkymist_memcard_info = {
.class_init = milkymist_memcard_class_init,
};
-static void milkymist_memcard_register(void)
+static void milkymist_memcard_register_types(void)
{
type_register_static(&milkymist_memcard_info);
}
-device_init(milkymist_memcard_register)
+type_init(milkymist_memcard_register_types)
diff --git a/hw/milkymist-minimac2.c b/hw/milkymist-minimac2.c
index b9b553fc95..70bf336add 100644
--- a/hw/milkymist-minimac2.c
+++ b/hw/milkymist-minimac2.c
@@ -542,9 +542,9 @@ static TypeInfo milkymist_minimac2_info = {
.class_init = milkymist_minimac2_class_init,
};
-static void milkymist_minimac2_register(void)
+static void milkymist_minimac2_register_types(void)
{
type_register_static(&milkymist_minimac2_info);
}
-device_init(milkymist_minimac2_register)
+type_init(milkymist_minimac2_register_types)
diff --git a/hw/milkymist-pfpu.c b/hw/milkymist-pfpu.c
index 1b73a4686b..0f9ff4a13d 100644
--- a/hw/milkymist-pfpu.c
+++ b/hw/milkymist-pfpu.c
@@ -536,9 +536,9 @@ static TypeInfo milkymist_pfpu_info = {
.class_init = milkymist_pfpu_class_init,
};
-static void milkymist_pfpu_register(void)
+static void milkymist_pfpu_register_types(void)
{
type_register_static(&milkymist_pfpu_info);
}
-device_init(milkymist_pfpu_register)
+type_init(milkymist_pfpu_register_types)
diff --git a/hw/milkymist-softusb.c b/hw/milkymist-softusb.c
index 5d496cbdb0..ecc2be9225 100644
--- a/hw/milkymist-softusb.c
+++ b/hw/milkymist-softusb.c
@@ -323,9 +323,9 @@ static TypeInfo milkymist_softusb_info = {
.class_init = milkymist_softusb_class_init,
};
-static void milkymist_softusb_register(void)
+static void milkymist_softusb_register_types(void)
{
type_register_static(&milkymist_softusb_info);
}
-device_init(milkymist_softusb_register)
+type_init(milkymist_softusb_register_types)
diff --git a/hw/milkymist-sysctl.c b/hw/milkymist-sysctl.c
index 18171f6165..a88548e0aa 100644
--- a/hw/milkymist-sysctl.c
+++ b/hw/milkymist-sysctl.c
@@ -322,9 +322,9 @@ static TypeInfo milkymist_sysctl_info = {
.class_init = milkymist_sysctl_class_init,
};
-static void milkymist_sysctl_register(void)
+static void milkymist_sysctl_register_types(void)
{
type_register_static(&milkymist_sysctl_info);
}
-device_init(milkymist_sysctl_register)
+type_init(milkymist_sysctl_register_types)
diff --git a/hw/milkymist-tmu2.c b/hw/milkymist-tmu2.c
index 474eae0a4a..210ceede2a 100644
--- a/hw/milkymist-tmu2.c
+++ b/hw/milkymist-tmu2.c
@@ -482,9 +482,9 @@ static TypeInfo milkymist_tmu2_info = {
.class_init = milkymist_tmu2_class_init,
};
-static void milkymist_tmu2_register(void)
+static void milkymist_tmu2_register_types(void)
{
type_register_static(&milkymist_tmu2_info);
}
-device_init(milkymist_tmu2_register)
+type_init(milkymist_tmu2_register_types)
diff --git a/hw/milkymist-uart.c b/hw/milkymist-uart.c
index f9a229cf68..291fe3c57b 100644
--- a/hw/milkymist-uart.c
+++ b/hw/milkymist-uart.c
@@ -235,9 +235,9 @@ static TypeInfo milkymist_uart_info = {
.class_init = milkymist_uart_class_init,
};
-static void milkymist_uart_register(void)
+static void milkymist_uart_register_types(void)
{
type_register_static(&milkymist_uart_info);
}
-device_init(milkymist_uart_register)
+type_init(milkymist_uart_register_types)
diff --git a/hw/milkymist-vgafb.c b/hw/milkymist-vgafb.c
index 92ad02f9e2..69afd72d8a 100644
--- a/hw/milkymist-vgafb.c
+++ b/hw/milkymist-vgafb.c
@@ -323,9 +323,9 @@ static TypeInfo milkymist_vgafb_info = {
.class_init = milkymist_vgafb_class_init,
};
-static void milkymist_vgafb_register(void)
+static void milkymist_vgafb_register_types(void)
{
type_register_static(&milkymist_vgafb_info);
}
-device_init(milkymist_vgafb_register)
+type_init(milkymist_vgafb_register_types)
diff --git a/hw/mips.h b/hw/mips.h
index 22156fce53..a7e6d4cc62 100644
--- a/hw/mips.h
+++ b/hw/mips.h
@@ -10,9 +10,6 @@ PCIBus *gt64120_register(qemu_irq *pic);
/* bonito.c */
PCIBus *bonito_init(qemu_irq *pic);
-/* jazz_led.c */
-void jazz_led_init(MemoryRegion *address_space, target_phys_addr_t base);
-
/* rc4030.c */
typedef struct rc4030DMAState *rc4030_dma;
void rc4030_dma_memory_rw(void *opaque, target_phys_addr_t addr, uint8_t *buf, int len, int is_write);
diff --git a/hw/mips_fulong2e.c b/hw/mips_fulong2e.c
index 163a668533..e3ba9dd42d 100644
--- a/hw/mips_fulong2e.c
+++ b/hw/mips_fulong2e.c
@@ -40,6 +40,7 @@
#include "elf.h"
#include "vt82c686.h"
#include "mc146818rtc.h"
+#include "i8254.h"
#include "blockdev.h"
#include "exec-memory.h"
@@ -363,7 +364,7 @@ static void mips_fulong2e_init(ram_addr_t ram_size, const char *boot_device,
smbus_eeprom_init(smbus, 1, eeprom_spd, sizeof(eeprom_spd));
/* init other devices */
- pit = pit_init(isa_bus, 0x40, 0);
+ pit = pit_init(isa_bus, 0x40, 0, NULL);
cpu_exit_irq = qemu_allocate_irqs(cpu_request_exit, NULL, 1);
DMA_init(0, cpu_exit_irq);
diff --git a/hw/mips_jazz.c b/hw/mips_jazz.c
index 63165b9a38..2b4678e170 100644
--- a/hw/mips_jazz.c
+++ b/hw/mips_jazz.c
@@ -36,6 +36,8 @@
#include "mips-bios.h"
#include "loader.h"
#include "mc146818rtc.h"
+#include "i8254.h"
+#include "pcspk.h"
#include "blockdev.h"
#include "sysbus.h"
#include "exec-memory.h"
@@ -191,8 +193,8 @@ static void mips_jazz_init(MemoryRegion *address_space,
isa_bus_irqs(isa_bus, i8259);
cpu_exit_irq = qemu_allocate_irqs(cpu_request_exit, NULL, 1);
DMA_init(0, cpu_exit_irq);
- pit = pit_init(isa_bus, 0x40, 0);
- pcspk_init(pit);
+ pit = pit_init(isa_bus, 0x40, 0, NULL);
+ pcspk_init(isa_bus, pit);
/* ISA IO space at 0x90000000 */
isa_mmio_init(0x90000000, 0x01000000);
@@ -293,7 +295,7 @@ static void mips_jazz_init(MemoryRegion *address_space,
sysbus_mmio_map(sysbus, 0, 0x80009000);
/* LED indicator */
- jazz_led_init(address_space, 0x8000f000);
+ sysbus_create_simple("jazz-led", 0x8000f000, NULL);
}
static
diff --git a/hw/mips_malta.c b/hw/mips_malta.c
index d232630e66..b1563ed2a7 100644
--- a/hw/mips_malta.c
+++ b/hw/mips_malta.c
@@ -45,6 +45,7 @@
#include "loader.h"
#include "elf.h"
#include "mc146818rtc.h"
+#include "i8254.h"
#include "blockdev.h"
#include "exec-memory.h"
#include "sysbus.h" /* SysBusDevice */
@@ -966,10 +967,10 @@ void mips_malta_init (ram_addr_t ram_size,
pci_piix4_ide_init(pci_bus, hd, piix4_devfn + 1);
usb_uhci_piix4_init(pci_bus, piix4_devfn + 2);
smbus = piix4_pm_init(pci_bus, piix4_devfn + 3, 0x1100,
- isa_get_irq(NULL, 9), NULL, NULL, 0);
+ isa_get_irq(NULL, 9), NULL, 0);
/* TODO: Populate SPD eeprom data. */
smbus_eeprom_init(smbus, 8, NULL, 0);
- pit = pit_init(isa_bus, 0x40, 0);
+ pit = pit_init(isa_bus, 0x40, 0, NULL);
cpu_exit_irq = qemu_allocate_irqs(cpu_request_exit, NULL, 1);
DMA_init(0, cpu_exit_irq);
@@ -1029,7 +1030,7 @@ static QEMUMachine mips_malta_machine = {
.is_default = 1,
};
-static void mips_malta_device_init(void)
+static void mips_malta_register_types(void)
{
type_register_static(&mips_malta_device);
}
@@ -1039,5 +1040,5 @@ static void mips_malta_machine_init(void)
qemu_register_machine(&mips_malta_machine);
}
-device_init(mips_malta_device_init);
+type_init(mips_malta_register_types)
machine_init(mips_malta_machine_init);
diff --git a/hw/mips_r4k.c b/hw/mips_r4k.c
index 1c0615c1da..83401f0648 100644
--- a/hw/mips_r4k.c
+++ b/hw/mips_r4k.c
@@ -22,6 +22,7 @@
#include "loader.h"
#include "elf.h"
#include "mc146818rtc.h"
+#include "i8254.h"
#include "blockdev.h"
#include "exec-memory.h"
@@ -269,7 +270,7 @@ void mips_r4k_init (ram_addr_t ram_size,
isa_mmio_init(0x14000000, 0x00010000);
isa_mem_base = 0x10000000;
- pit = pit_init(isa_bus, 0x40, 0);
+ pit = pit_init(isa_bus, 0x40, 0, NULL);
for(i = 0; i < MAX_SERIAL_PORTS; i++) {
if (serial_hds[i]) {
diff --git a/hw/mipsnet.c b/hw/mipsnet.c
index a0e6c9fa1e..50d92f8f54 100644
--- a/hw/mipsnet.c
+++ b/hw/mipsnet.c
@@ -276,9 +276,9 @@ static TypeInfo mipsnet_info = {
.class_init = mipsnet_class_init,
};
-static void mipsnet_register_devices(void)
+static void mipsnet_register_types(void)
{
type_register_static(&mipsnet_info);
}
-device_init(mipsnet_register_devices)
+type_init(mipsnet_register_types)
diff --git a/hw/mpc8544_guts.c b/hw/mpc8544_guts.c
index 28cd60d89c..aeb2de7ccc 100644
--- a/hw/mpc8544_guts.c
+++ b/hw/mpc8544_guts.c
@@ -135,8 +135,9 @@ static TypeInfo mpc8544_guts_info = {
.class_init = mpc8544_guts_class_init,
};
-static void mpc8544_guts_register(void)
+static void mpc8544_guts_register_types(void)
{
type_register_static(&mpc8544_guts_info);
}
-device_init(mpc8544_guts_register);
+
+type_init(mpc8544_guts_register_types)
diff --git a/hw/msmouse.c b/hw/msmouse.c
index c3b57ea31c..9c492a4637 100644
--- a/hw/msmouse.c
+++ b/hw/msmouse.c
@@ -64,7 +64,7 @@ static void msmouse_chr_close (struct CharDriverState *chr)
g_free (chr);
}
-int qemu_chr_open_msmouse(QemuOpts *opts, CharDriverState **_chr)
+CharDriverState *qemu_chr_open_msmouse(QemuOpts *opts)
{
CharDriverState *chr;
@@ -74,6 +74,5 @@ int qemu_chr_open_msmouse(QemuOpts *opts, CharDriverState **_chr)
qemu_add_mouse_event_handler(msmouse_event, chr, 0, "QEMU Microsoft Mouse");
- *_chr = chr;
- return 0;
+ return chr;
}
diff --git a/hw/msmouse.h b/hw/msmouse.h
index 8b853b35bf..456cb21424 100644
--- a/hw/msmouse.h
+++ b/hw/msmouse.h
@@ -1,2 +1,2 @@
/* msmouse.c */
-int qemu_chr_open_msmouse(QemuOpts *opts, CharDriverState **_chr);
+CharDriverState *qemu_chr_open_msmouse(QemuOpts *opts);
diff --git a/hw/mst_fpga.c b/hw/mst_fpga.c
index 1729db01a4..024192d135 100644
--- a/hw/mst_fpga.c
+++ b/hw/mst_fpga.c
@@ -255,8 +255,9 @@ static TypeInfo mst_fpga_info = {
.class_init = mst_fpga_class_init,
};
-static void mst_fpga_register(void)
+static void mst_fpga_register_types(void)
{
type_register_static(&mst_fpga_info);
}
-device_init(mst_fpga_register);
+
+type_init(mst_fpga_register_types)
diff --git a/hw/musicpal.c b/hw/musicpal.c
index ac909248d4..187a1aef5e 100644
--- a/hw/musicpal.c
+++ b/hw/musicpal.c
@@ -1681,7 +1681,7 @@ static TypeInfo mv88w8618_wlan_info = {
.class_init = mv88w8618_wlan_class_init,
};
-static void musicpal_register_devices(void)
+static void musicpal_register_types(void)
{
type_register_static(&mv88w8618_pic_info);
type_register_static(&mv88w8618_pit_info);
@@ -1693,4 +1693,4 @@ static void musicpal_register_devices(void)
type_register_static(&musicpal_key_info);
}
-device_init(musicpal_register_devices)
+type_init(musicpal_register_types)
diff --git a/hw/nand.c b/hw/nand.c
index 5d947b1ced..e9501ae038 100644
--- a/hw/nand.c
+++ b/hw/nand.c
@@ -442,7 +442,7 @@ static TypeInfo nand_info = {
.class_init = nand_class_init,
};
-static void nand_create_device(void)
+static void nand_register_types(void)
{
type_register_static(&nand_info);
}
@@ -635,7 +635,7 @@ DeviceState *nand_init(BlockDriverState *bdrv, int manf_id, int chip_id)
return dev;
}
-device_init(nand_create_device)
+type_init(nand_register_types)
#else
diff --git a/hw/ne2000-isa.c b/hw/ne2000-isa.c
index 1352282152..a4a783ab89 100644
--- a/hw/ne2000-isa.c
+++ b/hw/ne2000-isa.c
@@ -104,9 +104,9 @@ static TypeInfo ne2000_isa_info = {
.class_init = isa_ne2000_class_initfn,
};
-static void ne2000_isa_register_devices(void)
+static void ne2000_isa_register_types(void)
{
type_register_static(&ne2000_isa_info);
}
-device_init(ne2000_isa_register_devices)
+type_init(ne2000_isa_register_types)
diff --git a/hw/ne2000.c b/hw/ne2000.c
index 080811ec4d..71452e1f2e 100644
--- a/hw/ne2000.c
+++ b/hw/ne2000.c
@@ -763,14 +763,6 @@ static int pci_ne2000_init(PCIDevice *pci_dev)
object_get_typename(OBJECT(pci_dev)), pci_dev->qdev.id, s);
qemu_format_nic_info_str(&s->nic->nc, s->c.macaddr.a);
- if (!pci_dev->qdev.hotplugged) {
- static int loaded = 0;
- if (!loaded) {
- rom_add_option("pxe-ne2k_pci.rom", -1);
- loaded = 1;
- }
- }
-
add_boot_device_path(s->c.bootindex, &pci_dev->qdev, "/ethernet-phy@0");
return 0;
@@ -798,6 +790,7 @@ static void ne2000_class_init(ObjectClass *klass, void *data)
k->init = pci_ne2000_init;
k->exit = pci_ne2000_exit;
+ k->romfile = "pxe-ne2k_pci.rom",
k->vendor_id = PCI_VENDOR_ID_REALTEK;
k->device_id = PCI_DEVICE_ID_REALTEK_8029;
k->class_id = PCI_CLASS_NETWORK_ETHERNET;
@@ -812,9 +805,9 @@ static TypeInfo ne2000_info = {
.class_init = ne2000_class_init,
};
-static void ne2000_register_devices(void)
+static void ne2000_register_types(void)
{
type_register_static(&ne2000_info);
}
-device_init(ne2000_register_devices)
+type_init(ne2000_register_types)
diff --git a/hw/nseries.c b/hw/nseries.c
index d429dbdebf..c5b31843dd 100644
--- a/hw/nseries.c
+++ b/hw/nseries.c
@@ -204,6 +204,8 @@ static void n8x0_i2c_setup(struct n800_s *s)
qdev_get_gpio_in(s->cpu->ih[0],
OMAP_INT_24XX_SYS_NIRQ));
+ qemu_system_powerdown = qdev_get_gpio_in(dev, 3);
+
/* Attach a TMP105 PM chip (A0 wired to ground) */
dev = i2c_create_slave(s->i2c, "tmp105", N8X0_TMP105_ADDR);
qdev_connect_gpio_out(dev, 0, tmp_irq);
diff --git a/hw/omap_gpio.c b/hw/omap_gpio.c
index 9a9a8e183f..201ff77c36 100644
--- a/hw/omap_gpio.c
+++ b/hw/omap_gpio.c
@@ -783,10 +783,10 @@ static TypeInfo omap2_gpio_info = {
.class_init = omap2_gpio_class_init,
};
-static void omap_gpio_register_device(void)
+static void omap_gpio_register_types(void)
{
type_register_static(&omap_gpio_info);
type_register_static(&omap2_gpio_info);
}
-device_init(omap_gpio_register_device)
+type_init(omap_gpio_register_types)
diff --git a/hw/omap_intc.c b/hw/omap_intc.c
index 5aa98a8fdb..5076e07ed5 100644
--- a/hw/omap_intc.c
+++ b/hw/omap_intc.c
@@ -640,10 +640,10 @@ static TypeInfo omap2_intc_info = {
.class_init = omap2_intc_class_init,
};
-static void omap_intc_register_device(void)
+static void omap_intc_register_types(void)
{
type_register_static(&omap_intc_info);
type_register_static(&omap2_intc_info);
}
-device_init(omap_intc_register_device)
+type_init(omap_intc_register_types)
diff --git a/hw/omap_lcdc.c b/hw/omap_lcdc.c
index f265306556..f172093876 100644
--- a/hw/omap_lcdc.c
+++ b/hw/omap_lcdc.c
@@ -264,9 +264,12 @@ static int ppm_save(const char *filename, uint8_t *data,
return 0;
}
-static void omap_screen_dump(void *opaque, const char *filename) {
+static void omap_screen_dump(void *opaque, const char *filename, bool cswitch)
+{
struct omap_lcd_panel_s *omap_lcd = opaque;
- omap_update_display(opaque);
+ if (cswitch) {
+ omap_update_display(opaque);
+ }
if (omap_lcd && ds_get_data(omap_lcd->state))
ppm_save(filename, ds_get_data(omap_lcd->state),
omap_lcd->width, omap_lcd->height,
diff --git a/hw/onenand.c b/hw/onenand.c
index 8744b04db3..db6af682c4 100644
--- a/hw/onenand.c
+++ b/hw/onenand.c
@@ -828,7 +828,7 @@ static TypeInfo onenand_info = {
.class_init = onenand_class_init,
};
-static void onenand_register_device(void)
+static void onenand_register_types(void)
{
type_register_static(&onenand_info);
}
@@ -838,4 +838,4 @@ void *onenand_raw_otp(DeviceState *onenand_device)
return FROM_SYSBUS(OneNANDState, sysbus_from_qdev(onenand_device))->otp;
}
-device_init(onenand_register_device)
+type_init(onenand_register_types)
diff --git a/hw/opencores_eth.c b/hw/opencores_eth.c
index 09f2757df3..9b036cb103 100644
--- a/hw/opencores_eth.c
+++ b/hw/opencores_eth.c
@@ -750,9 +750,9 @@ static TypeInfo open_eth_info = {
.class_init = open_eth_class_init,
};
-static void open_eth_register_devices(void)
+static void open_eth_register_types(void)
{
type_register_static(&open_eth_info);
}
-device_init(open_eth_register_devices)
+type_init(open_eth_register_types)
diff --git a/hw/parallel.c b/hw/parallel.c
index 484d727927..219f38436f 100644
--- a/hw/parallel.c
+++ b/hw/parallel.c
@@ -606,9 +606,9 @@ static TypeInfo parallel_isa_info = {
.class_init = parallel_isa_class_initfn,
};
-static void parallel_register_devices(void)
+static void parallel_register_types(void)
{
type_register_static(&parallel_isa_info);
}
-device_init(parallel_register_devices)
+type_init(parallel_register_types)
diff --git a/hw/pc.c b/hw/pc.c
index 7f3aa65d20..12c02f2044 100644
--- a/hw/pc.c
+++ b/hw/pc.c
@@ -36,6 +36,8 @@
#include "elf.h"
#include "multiboot.h"
#include "mc146818rtc.h"
+#include "i8254.h"
+#include "pcspk.h"
#include "msi.h"
#include "sysbus.h"
#include "sysemu.h"
@@ -58,10 +60,6 @@
#define DPRINTF(fmt, ...)
#endif
-#define BIOS_FILENAME "bios.bin"
-
-#define PC_MAX_BIOS_SIZE (4 * 1024 * 1024)
-
/* Leave a chunk of memory at the top of RAM for the BIOS ACPI tables. */
#define ACPI_DATA_SIZE 0x10000
#define BIOS_CFG_IOPORT 0x510
@@ -337,6 +335,7 @@ void pc_cmos_init(ram_addr_t ram_size, ram_addr_t above_4g_mem_size,
{
int val, nb, nb_heads, max_track, last_sect, i;
FDriveType fd_type[2] = { FDRIVE_DRV_NONE, FDRIVE_DRV_NONE };
+ FDriveRate rate;
BlockDriverState *fd[MAX_FD];
static pc_cmos_init_late_arg arg;
@@ -385,7 +384,7 @@ void pc_cmos_init(ram_addr_t ram_size, ram_addr_t above_4g_mem_size,
if (fd[i] && bdrv_is_inserted(fd[i])) {
bdrv_get_floppy_geometry_hint(fd[i], &nb_heads, &max_track,
&last_sect, FDRIVE_DRV_NONE,
- &fd_type[i]);
+ &fd_type[i], &rate);
}
}
}
@@ -514,11 +513,12 @@ static TypeInfo port92_info = {
.class_init = port92_class_initfn,
};
-static void port92_register(void)
+static void port92_register_types(void)
{
type_register_static(&port92_info);
}
-device_init(port92_register)
+
+type_init(port92_register_types)
static void handle_a20_line_change(void *opaque, int irq, int level)
{
@@ -889,7 +889,7 @@ static DeviceState *apic_init(void *env, uint8_t apic_id)
DeviceState *dev;
static int apic_mapped;
- if (kvm_enabled() && kvm_irqchip_in_kernel()) {
+ if (kvm_irqchip_in_kernel()) {
dev = qdev_create(NULL, "kvm-apic");
} else {
dev = qdev_create(NULL, "apic");
@@ -908,24 +908,13 @@ static DeviceState *apic_init(void *env, uint8_t apic_id)
}
/* KVM does not support MSI yet. */
- if (!kvm_enabled() || !kvm_irqchip_in_kernel()) {
+ if (!kvm_irqchip_in_kernel()) {
msi_supported = true;
}
return dev;
}
-/* set CMOS shutdown status register (index 0xF) as S3_resume(0xFE)
- BIOS will read it and start S3 resume at POST Entry */
-void pc_cmos_set_s3_resume(void *opaque, int irq, int level)
-{
- ISADevice *s = opaque;
-
- if (level) {
- rtc_set_memory(s, 0xF, 0xFE);
- }
-}
-
void pc_acpi_smi_interrupt(void *opaque, int irq, int level)
{
CPUState *s = opaque;
@@ -987,11 +976,9 @@ void pc_memory_init(MemoryRegion *system_memory,
MemoryRegion *rom_memory,
MemoryRegion **ram_memory)
{
- char *filename;
- int ret, linux_boot, i;
- MemoryRegion *ram, *bios, *isa_bios, *option_rom_mr;
+ int linux_boot, i;
+ MemoryRegion *ram, *option_rom_mr;
MemoryRegion *ram_below_4g, *ram_above_4g;
- int bios_size, isa_bios_size;
void *fw_cfg;
linux_boot = (kernel_filename != NULL);
@@ -1017,44 +1004,9 @@ void pc_memory_init(MemoryRegion *system_memory,
ram_above_4g);
}
- /* BIOS load */
- if (bios_name == NULL)
- bios_name = BIOS_FILENAME;
- filename = qemu_find_file(QEMU_FILE_TYPE_BIOS, bios_name);
- if (filename) {
- bios_size = get_image_size(filename);
- } else {
- bios_size = -1;
- }
- if (bios_size <= 0 ||
- (bios_size % 65536) != 0) {
- goto bios_error;
- }
- bios = g_malloc(sizeof(*bios));
- memory_region_init_ram(bios, "pc.bios", bios_size);
- vmstate_register_ram_global(bios);
- memory_region_set_readonly(bios, true);
- ret = rom_add_file_fixed(bios_name, (uint32_t)(-bios_size), -1);
- if (ret != 0) {
- bios_error:
- fprintf(stderr, "qemu: could not load PC BIOS '%s'\n", bios_name);
- exit(1);
- }
- if (filename) {
- g_free(filename);
- }
- /* map the last 128KB of the BIOS in ISA space */
- isa_bios_size = bios_size;
- if (isa_bios_size > (128 * 1024))
- isa_bios_size = 128 * 1024;
- isa_bios = g_malloc(sizeof(*isa_bios));
- memory_region_init_alias(isa_bios, "isa-bios", bios,
- bios_size - isa_bios_size, isa_bios_size);
- memory_region_add_subregion_overlap(rom_memory,
- 0x100000 - isa_bios_size,
- isa_bios,
- 1);
- memory_region_set_readonly(isa_bios, true);
+
+ /* Initialize PC system firmware */
+ pc_system_firmware_init(rom_memory);
option_rom_mr = g_malloc(sizeof(*option_rom_mr));
memory_region_init_ram(option_rom_mr, "pc.rom", PC_ROM_SIZE);
@@ -1064,11 +1016,6 @@ void pc_memory_init(MemoryRegion *system_memory,
option_rom_mr,
1);
- /* map all the bios at the top of memory */
- memory_region_add_subregion(rom_memory,
- (uint32_t)(-bios_size),
- bios);
-
fw_cfg = bochs_bios_init();
rom_set_fw(fw_cfg);
@@ -1137,6 +1084,9 @@ void pc_basic_device_init(ISABus *isa_bus, qemu_irq *gsi,
{
int i;
DriveInfo *fd[MAX_FD];
+ DeviceState *hpet = NULL;
+ int pit_isa_irq = 0;
+ qemu_irq pit_alt_irq = NULL;
qemu_irq rtc_irq = NULL;
qemu_irq *a20_line;
ISADevice *i8042, *port92, *vmmouse, *pit;
@@ -1147,21 +1097,27 @@ void pc_basic_device_init(ISABus *isa_bus, qemu_irq *gsi,
register_ioport_write(0xf0, 1, 1, ioportF0_write, NULL);
if (!no_hpet) {
- DeviceState *hpet = sysbus_try_create_simple("hpet", HPET_BASE, NULL);
+ hpet = sysbus_try_create_simple("hpet", HPET_BASE, NULL);
if (hpet) {
for (i = 0; i < GSI_NUM_PINS; i++) {
sysbus_connect_irq(sysbus_from_qdev(hpet), i, gsi[i]);
}
- rtc_irq = qdev_get_gpio_in(hpet, 0);
+ pit_isa_irq = -1;
+ pit_alt_irq = qdev_get_gpio_in(hpet, HPET_LEGACY_PIT_INT);
+ rtc_irq = qdev_get_gpio_in(hpet, HPET_LEGACY_RTC_INT);
}
}
*rtc_state = rtc_init(isa_bus, 2000, rtc_irq);
qemu_register_boot_set(pc_boot_set, *rtc_state);
- pit = pit_init(isa_bus, 0x40, 0);
- pcspk_init(pit);
+ pit = pit_init(isa_bus, 0x40, pit_isa_irq, pit_alt_irq);
+ if (hpet) {
+ /* connect PIT to output control line of the HPET */
+ qdev_connect_gpio_out(hpet, 0, qdev_get_gpio_in(&pit->qdev, 0));
+ }
+ pcspk_init(isa_bus, pit);
for(i = 0; i < MAX_SERIAL_PORTS; i++) {
if (serial_hds[i]) {
diff --git a/hw/pc.h b/hw/pc.h
index c666ec9827..74d3369a12 100644
--- a/hw/pc.h
+++ b/hw/pc.h
@@ -81,31 +81,6 @@ typedef struct GSIState {
void gsi_handler(void *opaque, int n, int level);
-/* i8254.c */
-
-#define PIT_FREQ 1193182
-
-static inline ISADevice *pit_init(ISABus *bus, int base, int irq)
-{
- ISADevice *dev;
-
- dev = isa_create(bus, "isa-pit");
- qdev_prop_set_uint32(&dev->qdev, "iobase", base);
- qdev_prop_set_uint32(&dev->qdev, "irq", irq);
- qdev_init_nofail(&dev->qdev);
-
- return dev;
-}
-
-void pit_set_gate(ISADevice *dev, int channel, int val);
-int pit_get_gate(ISADevice *dev, int channel);
-int pit_get_initial_count(ISADevice *dev, int channel);
-int pit_get_mode(ISADevice *dev, int channel);
-int pit_get_out(ISADevice *dev, int channel, int64_t current_time);
-
-void hpet_pit_disable(void);
-void hpet_pit_enable(void);
-
/* vmport.c */
static inline void vmport_init(ISABus *bus)
{
@@ -128,7 +103,6 @@ void i8042_setup_a20_line(ISADevice *dev, qemu_irq *a20_out);
extern int fd_bootchk;
void pc_register_ferr_irq(qemu_irq irq);
-void pc_cmos_set_s3_resume(void *opaque, int irq, int level);
void pc_acpi_smi_interrupt(void *opaque, int irq, int level);
void pc_cpus_init(const char *cpu_model);
@@ -167,17 +141,13 @@ int acpi_table_add(const char *table_desc);
/* acpi_piix.c */
i2c_bus *piix4_pm_init(PCIBus *bus, int devfn, uint32_t smb_io_base,
- qemu_irq sci_irq, qemu_irq cmos_s3, qemu_irq smi_irq,
+ qemu_irq sci_irq, qemu_irq smi_irq,
int kvm_enabled);
void piix4_smbus_register_device(SMBusDevice *dev, uint8_t addr);
/* hpet.c */
extern int no_hpet;
-/* pcspk.c */
-void pcspk_init(ISADevice *pit);
-int pcspk_audio_init(ISABus *bus);
-
/* piix_pci.c */
struct PCII440FXState;
typedef struct PCII440FXState PCII440FXState;
@@ -245,6 +215,9 @@ static inline bool isa_ne2000_init(ISABus *bus, int base, int irq, NICInfo *nd)
return true;
}
+/* pc_sysfw.c */
+void pc_system_firmware_init(MemoryRegion *rom_memory);
+
/* e820 types */
#define E820_RAM 1
#define E820_RESERVED 2
diff --git a/hw/pc_piix.c b/hw/pc_piix.c
index c06f1b544e..6c5c40f5df 100644
--- a/hw/pc_piix.c
+++ b/hw/pc_piix.c
@@ -104,7 +104,7 @@ static void ioapic_init(GSIState *gsi_state)
SysBusDevice *d;
unsigned int i;
- if (kvm_enabled() && kvm_irqchip_in_kernel()) {
+ if (kvm_irqchip_in_kernel()) {
dev = qdev_create(NULL, "kvm-ioapic");
} else {
dev = qdev_create(NULL, "ioapic");
@@ -139,7 +139,6 @@ static void pc_init1(MemoryRegion *system_memory,
qemu_irq *cpu_irq;
qemu_irq *gsi;
qemu_irq *i8259;
- qemu_irq *cmos_s3;
qemu_irq *smi_irq;
GSIState *gsi_state;
DriveInfo *hd[MAX_IDE_BUS * MAX_IDE_DEVS];
@@ -183,7 +182,7 @@ static void pc_init1(MemoryRegion *system_memory,
}
gsi_state = g_malloc0(sizeof(*gsi_state));
- if (kvm_enabled() && kvm_irqchip_in_kernel()) {
+ if (kvm_irqchip_in_kernel()) {
kvm_piix3_setup_irq_routing(pci_enabled);
gsi = qemu_allocate_irqs(kvm_piix3_gsi_handler, gsi_state,
GSI_NUM_PINS);
@@ -209,7 +208,7 @@ static void pc_init1(MemoryRegion *system_memory,
}
isa_bus_irqs(isa_bus, gsi);
- if (kvm_enabled() && kvm_irqchip_in_kernel()) {
+ if (kvm_irqchip_in_kernel()) {
i8259 = kvm_i8259_init(isa_bus);
} else if (xen_enabled()) {
i8259 = xen_interrupt_controller_init();
@@ -291,15 +290,10 @@ static void pc_init1(MemoryRegion *system_memory,
if (pci_enabled && acpi_enabled) {
i2c_bus *smbus;
- if (!xen_enabled()) {
- cmos_s3 = qemu_allocate_irqs(pc_cmos_set_s3_resume, rtc_state, 1);
- } else {
- cmos_s3 = qemu_allocate_irqs(xen_cmos_set_s3_resume, rtc_state, 1);
- }
smi_irq = qemu_allocate_irqs(pc_acpi_smi_interrupt, first_cpu, 1);
/* TODO: Populate SPD eeprom data. */
smbus = piix4_pm_init(pci_bus, piix3_devfn + 3, 0xb100,
- gsi[9], *cmos_s3, *smi_irq,
+ gsi[9], *smi_irq,
kvm_enabled());
smbus_eeprom_init(smbus, 8, NULL, 0);
}
@@ -371,8 +365,8 @@ static void pc_xen_hvm_init(ram_addr_t ram_size,
}
#endif
-static QEMUMachine pc_machine_v1_0 = {
- .name = "pc-1.0",
+static QEMUMachine pc_machine_v1_1 = {
+ .name = "pc-1.1",
.alias = "pc",
.desc = "Standard PC",
.init = pc_init_pci,
@@ -380,12 +374,42 @@ static QEMUMachine pc_machine_v1_0 = {
.is_default = 1,
};
+static QEMUMachine pc_machine_v1_0 = {
+ .name = "pc-1.0",
+ .desc = "Standard PC",
+ .init = pc_init_pci,
+ .max_cpus = 255,
+ .compat_props = (GlobalProperty[]) {
+ {
+ .driver = "pc-sysfw",
+ .property = "rom_only",
+ .value = stringify(1),
+ }, {
+ .driver = "isa-fdc",
+ .property = "check_media_rate",
+ .value = "off",
+ },
+ { /* end of list */ }
+ },
+};
+
static QEMUMachine pc_machine_v0_15 = {
.name = "pc-0.15",
.desc = "Standard PC",
.init = pc_init_pci,
.max_cpus = 255,
- .is_default = 1,
+ .compat_props = (GlobalProperty[]) {
+ {
+ .driver = "pc-sysfw",
+ .property = "rom_only",
+ .value = stringify(1),
+ }, {
+ .driver = "isa-fdc",
+ .property = "check_media_rate",
+ .value = "off",
+ },
+ { /* end of list */ }
+ },
};
static QEMUMachine pc_machine_v0_14 = {
@@ -418,6 +442,15 @@ static QEMUMachine pc_machine_v0_14 = {
.driver = "virtio-balloon-pci",
.property = "event_idx",
.value = "off",
+ },{
+ .driver = "isa-fdc",
+ .property = "check_media_rate",
+ .value = "off",
+ },
+ {
+ .driver = "pc-sysfw",
+ .property = "rom_only",
+ .value = stringify(1),
},
{ /* end of list */ }
},
@@ -465,6 +498,15 @@ static QEMUMachine pc_machine_v0_13 = {
.driver = "AC97",
.property = "use_broken_id",
.value = stringify(1),
+ },{
+ .driver = "isa-fdc",
+ .property = "check_media_rate",
+ .value = "off",
+ },
+ {
+ .driver = "pc-sysfw",
+ .property = "rom_only",
+ .value = stringify(1),
},
{ /* end of list */ }
},
@@ -516,6 +558,15 @@ static QEMUMachine pc_machine_v0_12 = {
.driver = "AC97",
.property = "use_broken_id",
.value = stringify(1),
+ },{
+ .driver = "isa-fdc",
+ .property = "check_media_rate",
+ .value = "off",
+ },
+ {
+ .driver = "pc-sysfw",
+ .property = "rom_only",
+ .value = stringify(1),
},
{ /* end of list */ }
}
@@ -575,6 +626,15 @@ static QEMUMachine pc_machine_v0_11 = {
.driver = "AC97",
.property = "use_broken_id",
.value = stringify(1),
+ },{
+ .driver = "isa-fdc",
+ .property = "check_media_rate",
+ .value = "off",
+ },
+ {
+ .driver = "pc-sysfw",
+ .property = "rom_only",
+ .value = stringify(1),
},
{ /* end of list */ }
}
@@ -646,6 +706,15 @@ static QEMUMachine pc_machine_v0_10 = {
.driver = "AC97",
.property = "use_broken_id",
.value = stringify(1),
+ },{
+ .driver = "isa-fdc",
+ .property = "check_media_rate",
+ .value = "off",
+ },
+ {
+ .driver = "pc-sysfw",
+ .property = "rom_only",
+ .value = stringify(1),
},
{ /* end of list */ }
},
@@ -656,6 +725,14 @@ static QEMUMachine isapc_machine = {
.desc = "ISA-only PC",
.init = pc_init_isa,
.max_cpus = 1,
+ .compat_props = (GlobalProperty[]) {
+ {
+ .driver = "pc-sysfw",
+ .property = "rom_only",
+ .value = stringify(1),
+ },
+ { /* end of list */ }
+ },
};
#ifdef CONFIG_XEN
@@ -670,6 +747,7 @@ static QEMUMachine xenfv_machine = {
static void pc_machine_init(void)
{
+ qemu_register_machine(&pc_machine_v1_1);
qemu_register_machine(&pc_machine_v1_0);
qemu_register_machine(&pc_machine_v0_15);
qemu_register_machine(&pc_machine_v0_14);
diff --git a/hw/pc_sysfw.c b/hw/pc_sysfw.c
new file mode 100644
index 0000000000..abf9004182
--- /dev/null
+++ b/hw/pc_sysfw.c
@@ -0,0 +1,254 @@
+/*
+ * QEMU PC System Firmware
+ *
+ * Copyright (c) 2003-2004 Fabrice Bellard
+ * Copyright (c) 2011-2012 Intel Corporation
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#include "sysbus.h"
+#include "hw.h"
+#include "pc.h"
+#include "hw/boards.h"
+#include "loader.h"
+#include "sysemu.h"
+#include "flash.h"
+#include "kvm.h"
+
+#define BIOS_FILENAME "bios.bin"
+
+typedef struct PcSysFwDevice {
+ SysBusDevice busdev;
+ uint8_t rom_only;
+} PcSysFwDevice;
+
+static void pc_isa_bios_init(MemoryRegion *rom_memory,
+ MemoryRegion *flash_mem,
+ int ram_size)
+{
+ int isa_bios_size;
+ MemoryRegion *isa_bios;
+ uint64_t flash_size;
+ void *flash_ptr, *isa_bios_ptr;
+
+ flash_size = memory_region_size(flash_mem);
+
+ /* map the last 128KB of the BIOS in ISA space */
+ isa_bios_size = flash_size;
+ if (isa_bios_size > (128 * 1024)) {
+ isa_bios_size = 128 * 1024;
+ }
+ isa_bios = g_malloc(sizeof(*isa_bios));
+ memory_region_init_ram(isa_bios, "isa-bios", isa_bios_size);
+ vmstate_register_ram_global(isa_bios);
+ memory_region_add_subregion_overlap(rom_memory,
+ 0x100000 - isa_bios_size,
+ isa_bios,
+ 1);
+
+ /* copy ISA rom image from top of flash memory */
+ flash_ptr = memory_region_get_ram_ptr(flash_mem);
+ isa_bios_ptr = memory_region_get_ram_ptr(isa_bios);
+ memcpy(isa_bios_ptr,
+ ((uint8_t*)flash_ptr) + (flash_size - isa_bios_size),
+ isa_bios_size);
+
+ memory_region_set_readonly(isa_bios, true);
+}
+
+static void pc_fw_add_pflash_drv(void)
+{
+ QemuOpts *opts;
+ QEMUMachine *machine;
+ char *filename;
+
+ if (bios_name == NULL) {
+ bios_name = BIOS_FILENAME;
+ }
+ filename = qemu_find_file(QEMU_FILE_TYPE_BIOS, bios_name);
+
+ opts = drive_add(IF_PFLASH, -1, filename, "readonly=on");
+ if (opts == NULL) {
+ return;
+ }
+
+ machine = find_default_machine();
+ if (machine == NULL) {
+ return;
+ }
+
+ drive_init(opts, machine->use_scsi);
+}
+
+static void pc_system_flash_init(MemoryRegion *rom_memory,
+ DriveInfo *pflash_drv)
+{
+ BlockDriverState *bdrv;
+ int64_t size;
+ target_phys_addr_t phys_addr;
+ int sector_bits, sector_size;
+ pflash_t *system_flash;
+ MemoryRegion *flash_mem;
+
+ bdrv = pflash_drv->bdrv;
+ size = bdrv_getlength(pflash_drv->bdrv);
+ sector_bits = 12;
+ sector_size = 1 << sector_bits;
+
+ if ((size % sector_size) != 0) {
+ fprintf(stderr,
+ "qemu: PC system firmware (pflash) must be a multiple of 0x%x\n",
+ sector_size);
+ exit(1);
+ }
+
+ phys_addr = 0x100000000ULL - size;
+ system_flash = pflash_cfi01_register(phys_addr, NULL, "system.flash", size,
+ bdrv, sector_size, size >> sector_bits,
+ 1, 0x0000, 0x0000, 0x0000, 0x0000, 0);
+ flash_mem = pflash_cfi01_get_memory(system_flash);
+
+ pc_isa_bios_init(rom_memory, flash_mem, size);
+}
+
+static void old_pc_system_rom_init(MemoryRegion *rom_memory)
+{
+ char *filename;
+ MemoryRegion *bios, *isa_bios;
+ int bios_size, isa_bios_size;
+ int ret;
+
+ /* BIOS load */
+ if (bios_name == NULL) {
+ bios_name = BIOS_FILENAME;
+ }
+ filename = qemu_find_file(QEMU_FILE_TYPE_BIOS, bios_name);
+ if (filename) {
+ bios_size = get_image_size(filename);
+ } else {
+ bios_size = -1;
+ }
+ if (bios_size <= 0 ||
+ (bios_size % 65536) != 0) {
+ goto bios_error;
+ }
+ bios = g_malloc(sizeof(*bios));
+ memory_region_init_ram(bios, "pc.bios", bios_size);
+ vmstate_register_ram_global(bios);
+ memory_region_set_readonly(bios, true);
+ ret = rom_add_file_fixed(bios_name, (uint32_t)(-bios_size), -1);
+ if (ret != 0) {
+ bios_error:
+ fprintf(stderr, "qemu: could not load PC BIOS '%s'\n", bios_name);
+ exit(1);
+ }
+ if (filename) {
+ g_free(filename);
+ }
+
+ /* map the last 128KB of the BIOS in ISA space */
+ isa_bios_size = bios_size;
+ if (isa_bios_size > (128 * 1024)) {
+ isa_bios_size = 128 * 1024;
+ }
+ isa_bios = g_malloc(sizeof(*isa_bios));
+ memory_region_init_alias(isa_bios, "isa-bios", bios,
+ bios_size - isa_bios_size, isa_bios_size);
+ memory_region_add_subregion_overlap(rom_memory,
+ 0x100000 - isa_bios_size,
+ isa_bios,
+ 1);
+ memory_region_set_readonly(isa_bios, true);
+
+ /* map all the bios at the top of memory */
+ memory_region_add_subregion(rom_memory,
+ (uint32_t)(-bios_size),
+ bios);
+}
+
+void pc_system_firmware_init(MemoryRegion *rom_memory)
+{
+ DriveInfo *pflash_drv;
+ PcSysFwDevice *sysfw_dev;
+
+ sysfw_dev = (PcSysFwDevice*) qdev_create(NULL, "pc-sysfw");
+
+ if (sysfw_dev->rom_only) {
+ old_pc_system_rom_init(rom_memory);
+ return;
+ }
+
+ pflash_drv = drive_get(IF_PFLASH, 0, 0);
+
+ /* Currently KVM cannot execute from device memory.
+ Use old rom based firmware initialization for KVM. */
+ if (kvm_enabled()) {
+ if (pflash_drv != NULL) {
+ fprintf(stderr, "qemu: pflash cannot be used with kvm enabled\n");
+ exit(1);
+ } else {
+ sysfw_dev->rom_only = 1;
+ old_pc_system_rom_init(rom_memory);
+ return;
+ }
+ }
+
+ /* If a pflash drive is not found, then create one using
+ the bios filename. */
+ if (pflash_drv == NULL) {
+ pc_fw_add_pflash_drv();
+ pflash_drv = drive_get(IF_PFLASH, 0, 0);
+ }
+
+ if (pflash_drv != NULL) {
+ pc_system_flash_init(rom_memory, pflash_drv);
+ } else {
+ fprintf(stderr, "qemu: PC system firmware (pflash) not available\n");
+ exit(1);
+ }
+}
+
+static Property pcsysfw_properties[] = {
+ DEFINE_PROP_UINT8("rom_only", PcSysFwDevice, rom_only, 0),
+ DEFINE_PROP_END_OF_LIST(),
+};
+
+static void pcsysfw_class_init (ObjectClass *klass, void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS (klass);
+
+ dc->desc = "PC System Firmware";
+ dc->props = pcsysfw_properties;
+}
+
+static TypeInfo pcsysfw_info = {
+ .name = "pc-sysfw",
+ .parent = TYPE_SYS_BUS_DEVICE,
+ .instance_size = sizeof (PcSysFwDevice),
+ .class_init = pcsysfw_class_init,
+};
+
+static void pcsysfw_register (void)
+{
+ type_register_static (&pcsysfw_info);
+}
+
+type_init (pcsysfw_register);
+
diff --git a/hw/pci.c b/hw/pci.c
index 5f4f80ed10..bf046bfcad 100644
--- a/hw/pci.c
+++ b/hw/pci.c
@@ -614,7 +614,7 @@ static void pci_init_w1cmask(PCIDevice *dev)
PCI_STATUS_SIG_SYSTEM_ERROR | PCI_STATUS_DETECTED_PARITY);
}
-static void pci_init_wmask_bridge(PCIDevice *d)
+static void pci_init_mask_bridge(PCIDevice *d)
{
/* PCI_PRIMARY_BUS, PCI_SECONDARY_BUS, PCI_SUBORDINATE_BUS and
PCI_SEC_LETENCY_TIMER */
@@ -635,6 +635,14 @@ static void pci_init_wmask_bridge(PCIDevice *d)
/* PCI_PREF_BASE_UPPER32 and PCI_PREF_LIMIT_UPPER32 */
memset(d->wmask + PCI_PREF_BASE_UPPER32, 0xff, 8);
+ /* Supported memory and i/o types */
+ d->config[PCI_IO_BASE] |= PCI_IO_RANGE_TYPE_32;
+ d->config[PCI_IO_LIMIT] |= PCI_IO_RANGE_TYPE_32;
+ pci_word_test_and_set_mask(d->config + PCI_PREF_MEMORY_BASE,
+ PCI_PREF_RANGE_TYPE_64);
+ pci_word_test_and_set_mask(d->config + PCI_PREF_MEMORY_LIMIT,
+ PCI_PREF_RANGE_TYPE_64);
+
/* TODO: add this define to pci_regs.h in linux and then in qemu. */
#define PCI_BRIDGE_CTL_VGA_16BIT 0x10 /* VGA 16-bit decode */
#define PCI_BRIDGE_CTL_DISCARD 0x100 /* Primary discard timer */
@@ -657,6 +665,9 @@ static void pci_init_wmask_bridge(PCIDevice *d)
* completeness. */
pci_set_word(d->w1cmask + PCI_BRIDGE_CONTROL,
PCI_BRIDGE_CTL_DISCARD_STATUS);
+ d->cmask[PCI_IO_BASE] |= PCI_IO_RANGE_TYPE_MASK;
+ pci_word_test_and_set_mask(d->cmask + PCI_PREF_MEMORY_BASE,
+ PCI_PREF_RANGE_TYPE_MASK);
}
static int pci_init_multifunction(PCIBus *bus, PCIDevice *dev)
@@ -778,7 +789,7 @@ static PCIDevice *do_pci_register_device(PCIDevice *pci_dev, PCIBus *bus,
pci_init_wmask(pci_dev);
pci_init_w1cmask(pci_dev);
if (pc->is_bridge) {
- pci_init_wmask_bridge(pci_dev);
+ pci_init_mask_bridge(pci_dev);
}
if (pci_init_multifunction(bus, pci_dev)) {
pci_config_free(pci_dev);
@@ -2003,9 +2014,9 @@ static TypeInfo pci_device_type_info = {
.class_init = pci_device_class_init,
};
-static void pci_register_devices(void)
+static void pci_register_types(void)
{
type_register_static(&pci_device_type_info);
}
-device_init(pci_register_devices);
+type_init(pci_register_types)
diff --git a/hw/pci.h b/hw/pci.h
index 33b0b184ea..4f19fdbd89 100644
--- a/hw/pci.h
+++ b/hw/pci.h
@@ -75,6 +75,7 @@
#define PCI_DEVICE_ID_VIRTIO_BLOCK 0x1001
#define PCI_DEVICE_ID_VIRTIO_BALLOON 0x1002
#define PCI_DEVICE_ID_VIRTIO_CONSOLE 0x1003
+#define PCI_DEVICE_ID_VIRTIO_SCSI 0x1004
#define FMT_PCIBUS PRIx64
@@ -464,6 +465,67 @@ pci_quad_test_and_set_mask(uint8_t *config, uint64_t mask)
return val & mask;
}
+/* Access a register specified by a mask */
+static inline void
+pci_set_byte_by_mask(uint8_t *config, uint8_t mask, uint8_t reg)
+{
+ uint8_t val = pci_get_byte(config);
+ uint8_t rval = reg << (ffs(mask) - 1);
+ pci_set_byte(config, (~mask & val) | (mask & rval));
+}
+
+static inline uint8_t
+pci_get_byte_by_mask(uint8_t *config, uint8_t mask)
+{
+ uint8_t val = pci_get_byte(config);
+ return (val & mask) >> (ffs(mask) - 1);
+}
+
+static inline void
+pci_set_word_by_mask(uint8_t *config, uint16_t mask, uint16_t reg)
+{
+ uint16_t val = pci_get_word(config);
+ uint16_t rval = reg << (ffs(mask) - 1);
+ pci_set_word(config, (~mask & val) | (mask & rval));
+}
+
+static inline uint16_t
+pci_get_word_by_mask(uint8_t *config, uint16_t mask)
+{
+ uint16_t val = pci_get_word(config);
+ return (val & mask) >> (ffs(mask) - 1);
+}
+
+static inline void
+pci_set_long_by_mask(uint8_t *config, uint32_t mask, uint32_t reg)
+{
+ uint32_t val = pci_get_long(config);
+ uint32_t rval = reg << (ffs(mask) - 1);
+ pci_set_long(config, (~mask & val) | (mask & rval));
+}
+
+static inline uint32_t
+pci_get_long_by_mask(uint8_t *config, uint32_t mask)
+{
+ uint32_t val = pci_get_long(config);
+ return (val & mask) >> (ffs(mask) - 1);
+}
+
+static inline void
+pci_set_quad_by_mask(uint8_t *config, uint64_t mask, uint64_t reg)
+{
+ uint64_t val = pci_get_quad(config);
+ uint64_t rval = reg << (ffs(mask) - 1);
+ pci_set_quad(config, (~mask & val) | (mask & rval));
+}
+
+static inline uint64_t
+pci_get_quad_by_mask(uint8_t *config, uint64_t mask)
+{
+ uint64_t val = pci_get_quad(config);
+ return (val & mask) >> (ffs(mask) - 1);
+}
+
PCIDevice *pci_create_multifunction(PCIBus *bus, int devfn, bool multifunction,
const char *name);
PCIDevice *pci_create_simple_multifunction(PCIBus *bus, int devfn,
diff --git a/hw/pckbd.c b/hw/pckbd.c
index b4c53bed6c..69857bade9 100644
--- a/hw/pckbd.c
+++ b/hw/pckbd.c
@@ -513,8 +513,9 @@ static TypeInfo i8042_info = {
.class_init = i8042_class_initfn,
};
-static void i8042_register(void)
+static void i8042_register_types(void)
{
type_register_static(&i8042_info);
}
-device_init(i8042_register)
+
+type_init(i8042_register_types)
diff --git a/hw/pcnet-pci.c b/hw/pcnet-pci.c
index 439f32caae..34d73aaea1 100644
--- a/hw/pcnet-pci.c
+++ b/hw/pcnet-pci.c
@@ -330,14 +330,6 @@ static int pci_pcnet_init(PCIDevice *pci_dev)
s->phys_mem_write = pci_physical_memory_write;
s->dma_opaque = pci_dev;
- if (!pci_dev->qdev.hotplugged) {
- static int loaded = 0;
- if (!loaded) {
- rom_add_option("pxe-pcnet.rom", -1);
- loaded = 1;
- }
- }
-
return pcnet_common_init(&pci_dev->qdev, s, &net_pci_pcnet_info);
}
@@ -360,6 +352,7 @@ static void pcnet_class_init(ObjectClass *klass, void *data)
k->init = pci_pcnet_init;
k->exit = pci_pcnet_uninit;
+ k->romfile = "pxe-pcnet.rom",
k->vendor_id = PCI_VENDOR_ID_AMD;
k->device_id = PCI_DEVICE_ID_AMD_LANCE;
k->revision = 0x10;
@@ -376,9 +369,9 @@ static TypeInfo pcnet_info = {
.class_init = pcnet_class_init,
};
-static void pci_pcnet_register_devices(void)
+static void pci_pcnet_register_types(void)
{
type_register_static(&pcnet_info);
}
-device_init(pci_pcnet_register_devices)
+type_init(pci_pcnet_register_types)
diff --git a/hw/pcspk.c b/hw/pcspk.c
index acb016773b..e4303247d4 100644
--- a/hw/pcspk.c
+++ b/hw/pcspk.c
@@ -27,6 +27,8 @@
#include "isa.h"
#include "audio/audio.h"
#include "qemu-timer.h"
+#include "i8254.h"
+#include "pcspk.h"
#define PCSPK_BUF_LEN 1792
#define PCSPK_SAMPLE_RATE 32000
@@ -34,10 +36,13 @@
#define PCSPK_MIN_COUNT ((PIT_FREQ + PCSPK_MAX_FREQ - 1) / PCSPK_MAX_FREQ)
typedef struct {
+ ISADevice dev;
+ MemoryRegion ioport;
+ uint32_t iobase;
uint8_t sample_buf[PCSPK_BUF_LEN];
QEMUSoundCard card;
SWVoiceOut *voice;
- ISADevice *pit;
+ void *pit;
unsigned int pit_count;
unsigned int samples;
unsigned int play_pos;
@@ -46,7 +51,7 @@ typedef struct {
} PCSpkState;
static const char *s_spk = "pcspk";
-static PCSpkState pcspk_state;
+static PCSpkState *pcspk_state;
static inline void generate_samples(PCSpkState *s)
{
@@ -70,12 +75,16 @@ static inline void generate_samples(PCSpkState *s)
static void pcspk_callback(void *opaque, int free)
{
PCSpkState *s = opaque;
+ PITChannelInfo ch;
unsigned int n;
- if (pit_get_mode(s->pit, 2) != 3)
+ pit_get_channel_info(s->pit, 2, &ch);
+
+ if (ch.mode != 3) {
return;
+ }
- n = pit_get_initial_count(s->pit, 2);
+ n = ch.initial_count;
/* avoid frequencies that are not reproducible with sample rate */
if (n < PCSPK_MIN_COUNT)
n = 0;
@@ -98,7 +107,7 @@ static void pcspk_callback(void *opaque, int free)
int pcspk_audio_init(ISABus *bus)
{
- PCSpkState *s = &pcspk_state;
+ PCSpkState *s = pcspk_state;
struct audsettings as = {PCSPK_SAMPLE_RATE, 1, AUD_FMT_U8, 0};
AUD_register_card(s_spk, &s->card);
@@ -112,18 +121,22 @@ int pcspk_audio_init(ISABus *bus)
return 0;
}
-static uint32_t pcspk_ioport_read(void *opaque, uint32_t addr)
+static uint64_t pcspk_io_read(void *opaque, target_phys_addr_t addr,
+ unsigned size)
{
PCSpkState *s = opaque;
- int out;
+ PITChannelInfo ch;
+
+ pit_get_channel_info(s->pit, 2, &ch);
s->dummy_refresh_clock ^= (1 << 4);
- out = pit_get_out(s->pit, 2, qemu_get_clock_ns(vm_clock)) << 5;
- return pit_get_gate(s->pit, 2) | (s->data_on << 1) | s->dummy_refresh_clock | out;
+ return ch.gate | (s->data_on << 1) | s->dummy_refresh_clock |
+ (ch.out << 5);
}
-static void pcspk_ioport_write(void *opaque, uint32_t addr, uint32_t val)
+static void pcspk_io_write(void *opaque, target_phys_addr_t addr, uint64_t val,
+ unsigned size)
{
PCSpkState *s = opaque;
const int gate = val & 1;
@@ -137,11 +150,52 @@ static void pcspk_ioport_write(void *opaque, uint32_t addr, uint32_t val)
}
}
-void pcspk_init(ISADevice *pit)
+static const MemoryRegionOps pcspk_io_ops = {
+ .read = pcspk_io_read,
+ .write = pcspk_io_write,
+ .impl = {
+ .min_access_size = 1,
+ .max_access_size = 1,
+ },
+};
+
+static int pcspk_initfn(ISADevice *dev)
+{
+ PCSpkState *s = DO_UPCAST(PCSpkState, dev, dev);
+
+ memory_region_init_io(&s->ioport, &pcspk_io_ops, s, "elcr", 1);
+ isa_register_ioport(dev, &s->ioport, s->iobase);
+
+ pcspk_state = s;
+
+ return 0;
+}
+
+static Property pcspk_properties[] = {
+ DEFINE_PROP_HEX32("iobase", PCSpkState, iobase, -1),
+ DEFINE_PROP_PTR("pit", PCSpkState, pit),
+ DEFINE_PROP_END_OF_LIST(),
+};
+
+static void pcspk_class_initfn(ObjectClass *klass, void *data)
{
- PCSpkState *s = &pcspk_state;
+ DeviceClass *dc = DEVICE_CLASS(klass);
+ ISADeviceClass *ic = ISA_DEVICE_CLASS(klass);
- s->pit = pit;
- register_ioport_read(0x61, 1, 1, pcspk_ioport_read, s);
- register_ioport_write(0x61, 1, 1, pcspk_ioport_write, s);
+ ic->init = pcspk_initfn;
+ dc->no_user = 1;
+ dc->props = pcspk_properties;
+}
+
+static TypeInfo pcspk_info = {
+ .name = "isa-pcspk",
+ .parent = TYPE_ISA_DEVICE,
+ .instance_size = sizeof(PCSpkState),
+ .class_init = pcspk_class_initfn,
+};
+
+static void pcspk_register(void)
+{
+ type_register_static(&pcspk_info);
}
+type_init(pcspk_register)
diff --git a/hw/pcspk.h b/hw/pcspk.h
new file mode 100644
index 0000000000..7f42bac1c8
--- /dev/null
+++ b/hw/pcspk.h
@@ -0,0 +1,45 @@
+/*
+ * QEMU PC speaker emulation
+ *
+ * Copyright (c) 2006 Joachim Henke
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#ifndef HW_PCSPK_H
+#define HW_PCSPK_H
+
+#include "hw.h"
+#include "isa.h"
+
+static inline ISADevice *pcspk_init(ISABus *bus, ISADevice *pit)
+{
+ ISADevice *dev;
+
+ dev = isa_create(bus, "isa-pcspk");
+ qdev_prop_set_uint32(&dev->qdev, "iobase", 0x61);
+ qdev_prop_set_ptr(&dev->qdev, "pit", pit);
+ qdev_init_nofail(&dev->qdev);
+
+ return dev;
+}
+
+int pcspk_audio_init(ISABus *bus);
+
+#endif /* !HW_PCSPK_H */
diff --git a/hw/pflash_cfi01.c b/hw/pflash_cfi01.c
index ee0c3baab1..b03f623cb1 100644
--- a/hw/pflash_cfi01.c
+++ b/hw/pflash_cfi01.c
@@ -283,8 +283,12 @@ static void pflash_write(pflash_t *pfl, target_phys_addr_t offset,
TARGET_FMT_plx "\n",
__func__, offset, pfl->sector_len);
- memset(p + offset, 0xff, pfl->sector_len);
- pflash_update(pfl, offset, pfl->sector_len);
+ if (!pfl->ro) {
+ memset(p + offset, 0xff, pfl->sector_len);
+ pflash_update(pfl, offset, pfl->sector_len);
+ } else {
+ pfl->status |= 0x20; /* Block erase error */
+ }
pfl->status |= 0x80; /* Ready! */
break;
case 0x50: /* Clear status bits */
@@ -323,8 +327,12 @@ static void pflash_write(pflash_t *pfl, target_phys_addr_t offset,
case 0x10: /* Single Byte Program */
case 0x40: /* Single Byte Program */
DPRINTF("%s: Single Byte Program\n", __func__);
- pflash_data_write(pfl, offset, value, width, be);
- pflash_update(pfl, offset, width);
+ if (!pfl->ro) {
+ pflash_data_write(pfl, offset, value, width, be);
+ pflash_update(pfl, offset, width);
+ } else {
+ pfl->status |= 0x10; /* Programming error */
+ }
pfl->status |= 0x80; /* Ready! */
pfl->wcycle = 0;
break;
@@ -372,7 +380,11 @@ static void pflash_write(pflash_t *pfl, target_phys_addr_t offset,
case 2:
switch (pfl->cmd) {
case 0xe8: /* Block write */
- pflash_data_write(pfl, offset, value, width, be);
+ if (!pfl->ro) {
+ pflash_data_write(pfl, offset, value, width, be);
+ } else {
+ pfl->status |= 0x10; /* Programming error */
+ }
pfl->status |= 0x80;
@@ -382,8 +394,12 @@ static void pflash_write(pflash_t *pfl, target_phys_addr_t offset,
DPRINTF("%s: block write finished\n", __func__);
pfl->wcycle++;
- /* Flush the entire write buffer onto backing storage. */
- pflash_update(pfl, offset & mask, pfl->writeblock_size);
+ if (!pfl->ro) {
+ /* Flush the entire write buffer onto backing storage. */
+ pflash_update(pfl, offset & mask, pfl->writeblock_size);
+ } else {
+ pfl->status |= 0x10; /* Programming error */
+ }
}
pfl->counter--;
@@ -607,13 +623,13 @@ pflash_t *pflash_cfi01_register(target_phys_addr_t base,
}
bdrv_attach_dev_nofail(pfl->bs, pfl);
}
-#if 0 /* XXX: there should be a bit to set up read-only,
- * the same way the hardware does (with WP pin).
- */
- pfl->ro = 1;
-#else
- pfl->ro = 0;
-#endif
+
+ if (pfl->bs) {
+ pfl->ro = bdrv_is_read_only(pfl->bs);
+ } else {
+ pfl->ro = 0;
+ }
+
pfl->timer = qemu_new_timer_ns(vm_clock, pflash_timer, pfl);
pfl->base = base;
pfl->sector_len = sector_len;
diff --git a/hw/pflash_cfi02.c b/hw/pflash_cfi02.c
index 2ca0fd4560..3e2002e4b3 100644
--- a/hw/pflash_cfi02.c
+++ b/hw/pflash_cfi02.c
@@ -330,35 +330,37 @@ static void pflash_write (pflash_t *pfl, target_phys_addr_t offset,
DPRINTF("%s: write data offset " TARGET_FMT_plx " %08x %d\n",
__func__, offset, value, width);
p = pfl->storage;
- switch (width) {
- case 1:
- p[offset] &= value;
- pflash_update(pfl, offset, 1);
- break;
- case 2:
- if (be) {
- p[offset] &= value >> 8;
- p[offset + 1] &= value;
- } else {
+ if (!pfl->ro) {
+ switch (width) {
+ case 1:
p[offset] &= value;
- p[offset + 1] &= value >> 8;
+ pflash_update(pfl, offset, 1);
+ break;
+ case 2:
+ if (be) {
+ p[offset] &= value >> 8;
+ p[offset + 1] &= value;
+ } else {
+ p[offset] &= value;
+ p[offset + 1] &= value >> 8;
+ }
+ pflash_update(pfl, offset, 2);
+ break;
+ case 4:
+ if (be) {
+ p[offset] &= value >> 24;
+ p[offset + 1] &= value >> 16;
+ p[offset + 2] &= value >> 8;
+ p[offset + 3] &= value;
+ } else {
+ p[offset] &= value;
+ p[offset + 1] &= value >> 8;
+ p[offset + 2] &= value >> 16;
+ p[offset + 3] &= value >> 24;
+ }
+ pflash_update(pfl, offset, 4);
+ break;
}
- pflash_update(pfl, offset, 2);
- break;
- case 4:
- if (be) {
- p[offset] &= value >> 24;
- p[offset + 1] &= value >> 16;
- p[offset + 2] &= value >> 8;
- p[offset + 3] &= value;
- } else {
- p[offset] &= value;
- p[offset + 1] &= value >> 8;
- p[offset + 2] &= value >> 16;
- p[offset + 3] &= value >> 24;
- }
- pflash_update(pfl, offset, 4);
- break;
}
pfl->status = 0x00 | ~(value & 0x80);
/* Let's pretend write is immediate */
@@ -404,9 +406,11 @@ static void pflash_write (pflash_t *pfl, target_phys_addr_t offset,
}
/* Chip erase */
DPRINTF("%s: start chip erase\n", __func__);
- memset(pfl->storage, 0xFF, pfl->chip_len);
+ if (!pfl->ro) {
+ memset(pfl->storage, 0xFF, pfl->chip_len);
+ pflash_update(pfl, 0, pfl->chip_len);
+ }
pfl->status = 0x00;
- pflash_update(pfl, 0, pfl->chip_len);
/* Let's wait 5 seconds before chip erase is done */
qemu_mod_timer(pfl->timer,
qemu_get_clock_ns(vm_clock) + (get_ticks_per_sec() * 5));
@@ -417,8 +421,10 @@ static void pflash_write (pflash_t *pfl, target_phys_addr_t offset,
offset &= ~(pfl->sector_len - 1);
DPRINTF("%s: start sector erase at " TARGET_FMT_plx "\n", __func__,
offset);
- memset(p + offset, 0xFF, pfl->sector_len);
- pflash_update(pfl, offset, pfl->sector_len);
+ if (!pfl->ro) {
+ memset(p + offset, 0xFF, pfl->sector_len);
+ pflash_update(pfl, offset, pfl->sector_len);
+ }
pfl->status = 0x00;
/* Let's wait 1/2 second before sector erase is done */
qemu_mod_timer(pfl->timer,
@@ -645,16 +651,17 @@ pflash_t *pflash_cfi02_register(target_phys_addr_t base,
}
bdrv_attach_dev_nofail(pfl->bs, pfl);
}
+
pflash_setup_mappings(pfl);
pfl->rom_mode = 1;
memory_region_add_subregion(get_system_memory(), pfl->base, &pfl->mem);
-#if 0 /* XXX: there should be a bit to set up read-only,
- * the same way the hardware does (with WP pin).
- */
- pfl->ro = 1;
-#else
- pfl->ro = 0;
-#endif
+
+ if (pfl->bs) {
+ pfl->ro = bdrv_is_read_only(pfl->bs);
+ } else {
+ pfl->ro = 0;
+ }
+
pfl->timer = qemu_new_timer_ns(vm_clock, pflash_timer, pfl);
pfl->sector_len = sector_len;
pfl->width = width;
diff --git a/hw/piix4.c b/hw/piix4.c
index 4e7a2370cd..ce4eb0d1ae 100644
--- a/hw/piix4.c
+++ b/hw/piix4.c
@@ -124,8 +124,9 @@ static TypeInfo piix4_info = {
.class_init = piix4_class_init,
};
-static void piix4_register(void)
+static void piix4_register_types(void)
{
type_register_static(&piix4_info);
}
-device_init(piix4_register);
+
+type_init(piix4_register_types)
diff --git a/hw/piix_pci.c b/hw/piix_pci.c
index 190642733f..e0268fe053 100644
--- a/hw/piix_pci.c
+++ b/hw/piix_pci.c
@@ -589,11 +589,12 @@ static TypeInfo i440fx_pcihost_info = {
.class_init = i440fx_pcihost_class_init,
};
-static void i440fx_register(void)
+static void i440fx_register_types(void)
{
type_register_static(&i440fx_info);
type_register_static(&piix3_info);
type_register_static(&piix3_xen_info);
type_register_static(&i440fx_pcihost_info);
}
-device_init(i440fx_register);
+
+type_init(i440fx_register_types)
diff --git a/hw/pl011.c b/hw/pl011.c
index 752cbf9725..8a5a8f554a 100644
--- a/hw/pl011.c
+++ b/hw/pl011.c
@@ -316,10 +316,10 @@ static TypeInfo pl011_luminary_info = {
.class_init = pl011_luminary_class_init,
};
-static void pl011_register_devices(void)
+static void pl011_register_types(void)
{
type_register_static(&pl011_arm_info);
type_register_static(&pl011_luminary_info);
}
-device_init(pl011_register_devices)
+type_init(pl011_register_types)
diff --git a/hw/pl022.c b/hw/pl022.c
index 30bd3442bb..60e35daeb5 100644
--- a/hw/pl022.c
+++ b/hw/pl022.c
@@ -9,7 +9,6 @@
#include "sysbus.h"
#include "ssi.h"
-#include "primecell.h"
//#define DEBUG_PL022 1
@@ -299,9 +298,9 @@ static TypeInfo pl022_info = {
.class_init = pl022_class_init,
};
-static void pl022_register_devices(void)
+static void pl022_register_types(void)
{
type_register_static(&pl022_info);
}
-device_init(pl022_register_devices)
+type_init(pl022_register_types)
diff --git a/hw/pl031.c b/hw/pl031.c
index 8416a609ef..69abc4f345 100644
--- a/hw/pl031.c
+++ b/hw/pl031.c
@@ -76,7 +76,7 @@ static void pl031_interrupt(void * opaque)
{
pl031_state *s = (pl031_state *)opaque;
- s->im = 1;
+ s->is = 1;
DPRINTF("Alarm raised\n");
pl031_update(s);
}
@@ -230,9 +230,9 @@ static TypeInfo pl031_info = {
.class_init = pl031_class_init,
};
-static void pl031_register_devices(void)
+static void pl031_register_types(void)
{
type_register_static(&pl031_info);
}
-device_init(pl031_register_devices)
+type_init(pl031_register_types)
diff --git a/hw/pl041.c b/hw/pl041.c
index 6d99c9cbbc..b6723be0a9 100644
--- a/hw/pl041.c
+++ b/hw/pl041.c
@@ -638,9 +638,9 @@ static TypeInfo pl041_device_info = {
.class_init = pl041_device_class_init,
};
-static void pl041_register_device(void)
+static void pl041_register_types(void)
{
type_register_static(&pl041_device_info);
}
-device_init(pl041_register_device)
+type_init(pl041_register_types)
diff --git a/hw/pl050.c b/hw/pl050.c
index b0094ac9b3..b13924a160 100644
--- a/hw/pl050.c
+++ b/hw/pl050.c
@@ -189,10 +189,10 @@ static TypeInfo pl050_mouse_info = {
.class_init = pl050_mouse_class_init,
};
-static void pl050_register_devices(void)
+static void pl050_register_types(void)
{
type_register_static(&pl050_kbd_info);
type_register_static(&pl050_mouse_info);
}
-device_init(pl050_register_devices)
+type_init(pl050_register_types)
diff --git a/hw/pl061.c b/hw/pl061.c
index 3136c99423..2aac7e8e9e 100644
--- a/hw/pl061.c
+++ b/hw/pl061.c
@@ -325,10 +325,10 @@ static TypeInfo pl061_luminary_info = {
.class_init = pl061_luminary_class_init,
};
-static void pl061_register_devices(void)
+static void pl061_register_types(void)
{
type_register_static(&pl061_info);
type_register_static(&pl061_luminary_info);
}
-device_init(pl061_register_devices)
+type_init(pl061_register_types)
diff --git a/hw/pl080.c b/hw/pl080.c
index 4405d1823b..b3cf651ab7 100644
--- a/hw/pl080.c
+++ b/hw/pl080.c
@@ -409,10 +409,10 @@ static TypeInfo pl081_info = {
/* The PL080 and PL081 are the same except for the number of channels
they implement (8 and 2 respectively). */
-static void pl080_register_devices(void)
+static void pl080_register_types(void)
{
type_register_static(&pl080_info);
type_register_static(&pl081_info);
}
-device_init(pl080_register_devices)
+type_init(pl080_register_types)
diff --git a/hw/pl110.c b/hw/pl110.c
index 86e95a3ffd..f94608cb62 100644
--- a/hw/pl110.c
+++ b/hw/pl110.c
@@ -520,11 +520,11 @@ static TypeInfo pl111_info = {
.class_init = pl111_class_init,
};
-static void pl110_register_devices(void)
+static void pl110_register_types(void)
{
type_register_static(&pl110_info);
type_register_static(&pl110_versatile_info);
type_register_static(&pl111_info);
}
-device_init(pl110_register_devices)
+type_init(pl110_register_types)
diff --git a/hw/pl181.c b/hw/pl181.c
index ae636e220e..7d91fbba1d 100644
--- a/hw/pl181.c
+++ b/hw/pl181.c
@@ -505,9 +505,9 @@ static TypeInfo pl181_info = {
.class_init = pl181_class_init,
};
-static void pl181_register_devices(void)
+static void pl181_register_types(void)
{
type_register_static(&pl181_info);
}
-device_init(pl181_register_devices)
+type_init(pl181_register_types)
diff --git a/hw/pl190.c b/hw/pl190.c
index 956ab21c6a..cb50afb9f4 100644
--- a/hw/pl190.c
+++ b/hw/pl190.c
@@ -273,9 +273,9 @@ static TypeInfo pl190_info = {
.class_init = pl190_class_init,
};
-static void pl190_register_devices(void)
+static void pl190_register_types(void)
{
type_register_static(&pl190_info);
}
-device_init(pl190_register_devices)
+type_init(pl190_register_types)
diff --git a/hw/ppc4xx_pci.c b/hw/ppc4xx_pci.c
index d11f120d32..203c3cdc47 100644
--- a/hw/ppc4xx_pci.c
+++ b/hw/ppc4xx_pci.c
@@ -400,9 +400,10 @@ static TypeInfo ppc4xx_pcihost_info = {
.class_init = ppc4xx_pcihost_class_init,
};
-static void ppc4xx_pci_register(void)
+static void ppc4xx_pci_register_types(void)
{
type_register_static(&ppc4xx_pcihost_info);
type_register_static(&ppc4xx_host_bridge_info);
}
-device_init(ppc4xx_pci_register);
+
+type_init(ppc4xx_pci_register_types)
diff --git a/hw/ppce500_pci.c b/hw/ppce500_pci.c
index d5bce71429..0f60b24134 100644
--- a/hw/ppce500_pci.c
+++ b/hw/ppce500_pci.c
@@ -373,9 +373,10 @@ static TypeInfo e500_pcihost_info = {
.class_init = e500_pcihost_class_init,
};
-static void e500_pci_register(void)
+static void e500_pci_register_types(void)
{
type_register_static(&e500_pcihost_info);
type_register_static(&e500_host_bridge_info);
}
-device_init(e500_pci_register);
+
+type_init(e500_pci_register_types)
diff --git a/hw/ppce500_spin.c b/hw/ppce500_spin.c
index 9d648ec90a..6b8a189c0e 100644
--- a/hw/ppce500_spin.c
+++ b/hw/ppce500_spin.c
@@ -217,8 +217,9 @@ static TypeInfo ppce500_spin_info = {
.class_init = ppce500_spin_class_init,
};
-static void ppce500_spin_register(void)
+static void ppce500_spin_register_types(void)
{
type_register_static(&ppce500_spin_info);
}
-device_init(ppce500_spin_register);
+
+type_init(ppce500_spin_register_types)
diff --git a/hw/prep_pci.c b/hw/prep_pci.c
index 40b8bb0edd..8b29da9948 100644
--- a/hw/prep_pci.c
+++ b/hw/prep_pci.c
@@ -173,10 +173,10 @@ static TypeInfo raven_pcihost_info = {
.class_init = raven_pcihost_class_init,
};
-static void raven_register_devices(void)
+static void raven_register_types(void)
{
type_register_static(&raven_pcihost_info);
type_register_static(&raven_info);
}
-device_init(raven_register_devices)
+type_init(raven_register_types)
diff --git a/hw/primecell.h b/hw/primecell.h
index de7d6f2df2..7337c3b3ca 100644
--- a/hw/primecell.h
+++ b/hw/primecell.h
@@ -5,12 +5,6 @@
/* Also includes some devices that are currently only used by the
ARM boards. */
-/* pl080.c */
-void *pl080_init(uint32_t base, qemu_irq irq, int nchannels);
-
-/* arm_sysctl.c */
-void arm_sysctl_init(uint32_t base, uint32_t sys_id, uint32_t proc_id);
-
/* arm_sysctl GPIO lines */
#define ARM_SYSCTL_GPIO_MMC_WPROT 0
#define ARM_SYSCTL_GPIO_MMC_CARDIN 1
diff --git a/hw/ps2.c b/hw/ps2.c
index 1d9057bbf5..b1a67bc625 100644
--- a/hw/ps2.c
+++ b/hw/ps2.c
@@ -24,6 +24,7 @@
#include "hw.h"
#include "ps2.h"
#include "console.h"
+#include "sysemu.h"
/* debug PC keyboard */
//#define DEBUG_KBD
@@ -154,6 +155,7 @@ static void ps2_put_keycode(void *opaque, int keycode)
{
PS2KbdState *s = opaque;
+ qemu_system_wakeup_request(QEMU_WAKEUP_REASON_OTHER);
/* XXX: add support for scancode set 1 */
if (!s->translate && keycode < 0xe0 && s->scancode_set > 1) {
if (keycode & 0x80) {
@@ -368,6 +370,10 @@ static void ps2_mouse_event(void *opaque,
return;
s->mouse_buttons = buttons_state;
+ if (buttons_state) {
+ qemu_system_wakeup_request(QEMU_WAKEUP_REASON_OTHER);
+ }
+
if (!(s->mouse_status & MOUSE_STATUS_REMOTE) &&
(s->common.queue.count < (PS2_QUEUE_SIZE - 16))) {
for(;;) {
diff --git a/hw/pxa2xx.c b/hw/pxa2xx.c
index 244c614510..1ab27012c1 100644
--- a/hw/pxa2xx.c
+++ b/hw/pxa2xx.c
@@ -2330,7 +2330,7 @@ static TypeInfo pxa2xx_ssp_info = {
.class_init = pxa2xx_ssp_class_init,
};
-static void pxa2xx_register_devices(void)
+static void pxa2xx_register_types(void)
{
type_register_static(&pxa2xx_i2c_slave_info);
type_register_static(&pxa2xx_ssp_info);
@@ -2338,4 +2338,4 @@ static void pxa2xx_register_devices(void)
type_register_static(&pxa2xx_rtc_sysbus_info);
}
-device_init(pxa2xx_register_devices)
+type_init(pxa2xx_register_types)
diff --git a/hw/pxa2xx_dma.c b/hw/pxa2xx_dma.c
index 2d61565f1d..8ced0dd8ec 100644
--- a/hw/pxa2xx_dma.c
+++ b/hw/pxa2xx_dma.c
@@ -566,8 +566,9 @@ static TypeInfo pxa2xx_dma_info = {
.class_init = pxa2xx_dma_class_init,
};
-static void pxa2xx_dma_register(void)
+static void pxa2xx_dma_register_types(void)
{
type_register_static(&pxa2xx_dma_info);
}
-device_init(pxa2xx_dma_register);
+
+type_init(pxa2xx_dma_register_types)
diff --git a/hw/pxa2xx_gpio.c b/hw/pxa2xx_gpio.c
index 67fd17c4ca..d5f57162ed 100644
--- a/hw/pxa2xx_gpio.c
+++ b/hw/pxa2xx_gpio.c
@@ -340,8 +340,9 @@ static TypeInfo pxa2xx_gpio_info = {
.class_init = pxa2xx_gpio_class_init,
};
-static void pxa2xx_gpio_register(void)
+static void pxa2xx_gpio_register_types(void)
{
type_register_static(&pxa2xx_gpio_info);
}
-device_init(pxa2xx_gpio_register);
+
+type_init(pxa2xx_gpio_register_types)
diff --git a/hw/pxa2xx_lcd.c b/hw/pxa2xx_lcd.c
index 4e9f7b48ac..fcbdfb3fba 100644
--- a/hw/pxa2xx_lcd.c
+++ b/hw/pxa2xx_lcd.c
@@ -308,9 +308,12 @@ static void pxa2xx_descriptor_load(PXA2xxLCDState *s)
} else
descptr = s->dma_ch[i].descriptor;
- if (!(descptr >= PXA2XX_SDRAM_BASE && descptr +
- sizeof(desc) <= PXA2XX_SDRAM_BASE + ram_size))
+ if (!((descptr >= PXA2XX_SDRAM_BASE && descptr +
+ sizeof(desc) <= PXA2XX_SDRAM_BASE + ram_size) ||
+ (descptr >= PXA2XX_INTERNAL_BASE && descptr + sizeof(desc) <=
+ PXA2XX_INTERNAL_BASE + PXA2XX_INTERNAL_SIZE))) {
continue;
+ }
cpu_physical_memory_read(descptr, (void *)&desc, sizeof(desc));
s->dma_ch[i].descriptor = tswap32(desc.fdaddr);
@@ -830,8 +833,10 @@ static void pxa2xx_update_display(void *opaque)
continue;
}
fbptr = s->dma_ch[ch].source;
- if (!(fbptr >= PXA2XX_SDRAM_BASE &&
- fbptr <= PXA2XX_SDRAM_BASE + ram_size)) {
+ if (!((fbptr >= PXA2XX_SDRAM_BASE &&
+ fbptr <= PXA2XX_SDRAM_BASE + ram_size) ||
+ (fbptr >= PXA2XX_INTERNAL_BASE &&
+ fbptr <= PXA2XX_INTERNAL_BASE + PXA2XX_INTERNAL_SIZE))) {
pxa2xx_dma_ber_set(s, ch);
continue;
}
@@ -894,11 +899,6 @@ static void pxa2xx_invalidate_display(void *opaque)
s->invalidated = 1;
}
-static void pxa2xx_screen_dump(void *opaque, const char *filename)
-{
- /* TODO */
-}
-
static void pxa2xx_lcdc_orientation(void *opaque, int angle)
{
PXA2xxLCDState *s = (PXA2xxLCDState *) opaque;
@@ -1004,7 +1004,7 @@ PXA2xxLCDState *pxa2xx_lcdc_init(MemoryRegion *sysmem,
s->ds = graphic_console_init(pxa2xx_update_display,
pxa2xx_invalidate_display,
- pxa2xx_screen_dump, NULL, s);
+ NULL, NULL, s);
switch (ds_get_bits_per_pixel(s->ds)) {
case 0:
diff --git a/hw/pxa2xx_pic.c b/hw/pxa2xx_pic.c
index ca85743aea..6b2bdb0df1 100644
--- a/hw/pxa2xx_pic.c
+++ b/hw/pxa2xx_pic.c
@@ -313,8 +313,9 @@ static TypeInfo pxa2xx_pic_info = {
.class_init = pxa2xx_pic_class_init,
};
-static void pxa2xx_pic_register(void)
+static void pxa2xx_pic_register_types(void)
{
type_register_static(&pxa2xx_pic_info);
}
-device_init(pxa2xx_pic_register);
+
+type_init(pxa2xx_pic_register_types)
diff --git a/hw/pxa2xx_timer.c b/hw/pxa2xx_timer.c
index 90800751b1..77b033b541 100644
--- a/hw/pxa2xx_timer.c
+++ b/hw/pxa2xx_timer.c
@@ -527,9 +527,10 @@ static TypeInfo pxa27x_timer_dev_info = {
.class_init = pxa27x_timer_dev_class_init,
};
-static void pxa2xx_timer_register(void)
+static void pxa2xx_timer_register_types(void)
{
type_register_static(&pxa25x_timer_dev_info);
type_register_static(&pxa27x_timer_dev_info);
-};
-device_init(pxa2xx_timer_register);
+}
+
+type_init(pxa2xx_timer_register_types)
diff --git a/hw/qdev-monitor.c b/hw/qdev-monitor.c
index 49f13ca087..a310cc7b16 100644
--- a/hw/qdev-monitor.c
+++ b/hw/qdev-monitor.c
@@ -457,6 +457,16 @@ DeviceState *qdev_device_add(QemuOpts *opts)
id = qemu_opts_id(opts);
if (id) {
qdev->id = id;
+ }
+ if (qemu_opt_foreach(opts, set_property, qdev, 1) != 0) {
+ qdev_free(qdev);
+ return NULL;
+ }
+ if (qdev_init(qdev) < 0) {
+ qerror_report(QERR_DEVICE_INIT_FAILED, driver);
+ return NULL;
+ }
+ if (qdev->id) {
object_property_add_child(qdev_get_peripheral(), qdev->id,
OBJECT(qdev), NULL);
} else {
@@ -466,14 +476,6 @@ DeviceState *qdev_device_add(QemuOpts *opts)
OBJECT(qdev), NULL);
g_free(name);
}
- if (qemu_opt_foreach(opts, set_property, qdev, 1) != 0) {
- qdev_free(qdev);
- return NULL;
- }
- if (qdev_init(qdev) < 0) {
- qerror_report(QERR_DEVICE_INIT_FAILED, driver);
- return NULL;
- }
qdev->opts = opts;
return qdev;
}
diff --git a/hw/qdev-properties.c b/hw/qdev-properties.c
index b6d6fcff01..0423af1c31 100644
--- a/hw/qdev-properties.c
+++ b/hw/qdev-properties.c
@@ -27,16 +27,6 @@ static void bit_prop_set(DeviceState *dev, Property *props, bool val)
}
/* Bit */
-static int parse_bit(DeviceState *dev, Property *prop, const char *str)
-{
- if (!strcasecmp(str, "on"))
- bit_prop_set(dev, prop, true);
- else if (!strcasecmp(str, "off"))
- bit_prop_set(dev, prop, false);
- else
- return -EINVAL;
- return 0;
-}
static int print_bit(DeviceState *dev, Property *prop, char *dest, size_t len)
{
@@ -79,7 +69,6 @@ static void set_bit(Object *obj, Visitor *v, void *opaque,
PropertyInfo qdev_prop_bit = {
.name = "boolean",
.legacy_name = "on/off",
- .parse = parse_bit,
.print = print_bit,
.get = get_bit,
.set = set_bit,
@@ -87,26 +76,6 @@ PropertyInfo qdev_prop_bit = {
/* --- 8bit integer --- */
-static int parse_uint8(DeviceState *dev, Property *prop, const char *str)
-{
- uint8_t *ptr = qdev_get_prop_ptr(dev, prop);
- char *end;
-
- /* accept both hex and decimal */
- *ptr = strtoul(str, &end, 0);
- if ((*end != '\0') || (end == str)) {
- return -EINVAL;
- }
-
- return 0;
-}
-
-static int print_uint8(DeviceState *dev, Property *prop, char *dest, size_t len)
-{
- uint8_t *ptr = qdev_get_prop_ptr(dev, prop);
- return snprintf(dest, len, "%" PRIu8, *ptr);
-}
-
static void get_int8(Object *obj, Visitor *v, void *opaque,
const char *name, Error **errp)
{
@@ -149,8 +118,6 @@ static void set_int8(Object *obj, Visitor *v, void *opaque,
PropertyInfo qdev_prop_uint8 = {
.name = "uint8",
- .parse = parse_uint8,
- .print = print_uint8,
.get = get_int8,
.set = set_int8,
.min = 0,
@@ -164,6 +131,10 @@ static int parse_hex8(DeviceState *dev, Property *prop, const char *str)
uint8_t *ptr = qdev_get_prop_ptr(dev, prop);
char *end;
+ if (str[0] != '0' || str[1] != 'x') {
+ return -EINVAL;
+ }
+
*ptr = strtoul(str, &end, 16);
if ((*end != '\0') || (end == str)) {
return -EINVAL;
@@ -191,26 +162,6 @@ PropertyInfo qdev_prop_hex8 = {
/* --- 16bit integer --- */
-static int parse_uint16(DeviceState *dev, Property *prop, const char *str)
-{
- uint16_t *ptr = qdev_get_prop_ptr(dev, prop);
- char *end;
-
- /* accept both hex and decimal */
- *ptr = strtoul(str, &end, 0);
- if ((*end != '\0') || (end == str)) {
- return -EINVAL;
- }
-
- return 0;
-}
-
-static int print_uint16(DeviceState *dev, Property *prop, char *dest, size_t len)
-{
- uint16_t *ptr = qdev_get_prop_ptr(dev, prop);
- return snprintf(dest, len, "%" PRIu16, *ptr);
-}
-
static void get_int16(Object *obj, Visitor *v, void *opaque,
const char *name, Error **errp)
{
@@ -253,8 +204,6 @@ static void set_int16(Object *obj, Visitor *v, void *opaque,
PropertyInfo qdev_prop_uint16 = {
.name = "uint16",
- .parse = parse_uint16,
- .print = print_uint16,
.get = get_int16,
.set = set_int16,
.min = 0,
@@ -263,26 +212,6 @@ PropertyInfo qdev_prop_uint16 = {
/* --- 32bit integer --- */
-static int parse_uint32(DeviceState *dev, Property *prop, const char *str)
-{
- uint32_t *ptr = qdev_get_prop_ptr(dev, prop);
- char *end;
-
- /* accept both hex and decimal */
- *ptr = strtoul(str, &end, 0);
- if ((*end != '\0') || (end == str)) {
- return -EINVAL;
- }
-
- return 0;
-}
-
-static int print_uint32(DeviceState *dev, Property *prop, char *dest, size_t len)
-{
- uint32_t *ptr = qdev_get_prop_ptr(dev, prop);
- return snprintf(dest, len, "%" PRIu32, *ptr);
-}
-
static void get_int32(Object *obj, Visitor *v, void *opaque,
const char *name, Error **errp)
{
@@ -325,37 +254,14 @@ static void set_int32(Object *obj, Visitor *v, void *opaque,
PropertyInfo qdev_prop_uint32 = {
.name = "uint32",
- .parse = parse_uint32,
- .print = print_uint32,
.get = get_int32,
.set = set_int32,
.min = 0,
.max = 0xFFFFFFFFULL,
};
-static int parse_int32(DeviceState *dev, Property *prop, const char *str)
-{
- int32_t *ptr = qdev_get_prop_ptr(dev, prop);
- char *end;
-
- *ptr = strtol(str, &end, 10);
- if ((*end != '\0') || (end == str)) {
- return -EINVAL;
- }
-
- return 0;
-}
-
-static int print_int32(DeviceState *dev, Property *prop, char *dest, size_t len)
-{
- int32_t *ptr = qdev_get_prop_ptr(dev, prop);
- return snprintf(dest, len, "%" PRId32, *ptr);
-}
-
PropertyInfo qdev_prop_int32 = {
.name = "int32",
- .parse = parse_int32,
- .print = print_int32,
.get = get_int32,
.set = set_int32,
.min = -0x80000000LL,
@@ -369,6 +275,10 @@ static int parse_hex32(DeviceState *dev, Property *prop, const char *str)
uint32_t *ptr = qdev_get_prop_ptr(dev, prop);
char *end;
+ if (str[0] != '0' || str[1] != 'x') {
+ return -EINVAL;
+ }
+
*ptr = strtoul(str, &end, 16);
if ((*end != '\0') || (end == str)) {
return -EINVAL;
@@ -396,26 +306,6 @@ PropertyInfo qdev_prop_hex32 = {
/* --- 64bit integer --- */
-static int parse_uint64(DeviceState *dev, Property *prop, const char *str)
-{
- uint64_t *ptr = qdev_get_prop_ptr(dev, prop);
- char *end;
-
- /* accept both hex and decimal */
- *ptr = strtoull(str, &end, 0);
- if ((*end != '\0') || (end == str)) {
- return -EINVAL;
- }
-
- return 0;
-}
-
-static int print_uint64(DeviceState *dev, Property *prop, char *dest, size_t len)
-{
- uint64_t *ptr = qdev_get_prop_ptr(dev, prop);
- return snprintf(dest, len, "%" PRIu64, *ptr);
-}
-
static void get_int64(Object *obj, Visitor *v, void *opaque,
const char *name, Error **errp)
{
@@ -443,8 +333,6 @@ static void set_int64(Object *obj, Visitor *v, void *opaque,
PropertyInfo qdev_prop_uint64 = {
.name = "uint64",
- .parse = parse_uint64,
- .print = print_uint64,
.get = get_int64,
.set = set_int64,
};
@@ -456,6 +344,10 @@ static int parse_hex64(DeviceState *dev, Property *prop, const char *str)
uint64_t *ptr = qdev_get_prop_ptr(dev, prop);
char *end;
+ if (str[0] != '0' || str[1] != 'x') {
+ return -EINVAL;
+ }
+
*ptr = strtoull(str, &end, 16);
if ((*end != '\0') || (end == str)) {
return -EINVAL;
@@ -613,7 +505,7 @@ static void set_pointer(Object *obj, Visitor *v, Property *prop,
}
if (!*str) {
g_free(str);
- error_set_from_qdev_prop_error(errp, EINVAL, dev, prop, str);
+ *ptr = NULL;
return;
}
ret = parse(dev, str, ptr);
@@ -737,19 +629,6 @@ PropertyInfo qdev_prop_netdev = {
/* --- vlan --- */
-static int parse_vlan(DeviceState *dev, Property *prop, const char *str)
-{
- VLANState **ptr = qdev_get_prop_ptr(dev, prop);
- int id;
-
- if (sscanf(str, "%d", &id) != 1)
- return -EINVAL;
- *ptr = qemu_find_vlan(id, 1);
- if (*ptr == NULL)
- return -ENOENT;
- return 0;
-}
-
static int print_vlan(DeviceState *dev, Property *prop, char *dest, size_t len)
{
VLANState **ptr = qdev_get_prop_ptr(dev, prop);
@@ -808,7 +687,6 @@ static void set_vlan(Object *obj, Visitor *v, void *opaque,
PropertyInfo qdev_prop_vlan = {
.name = "vlan",
- .parse = parse_vlan,
.print = print_vlan,
.get = get_vlan,
.set = set_vlan,
@@ -943,25 +821,40 @@ PropertyInfo qdev_prop_losttickpolicy = {
/*
* bus-local address, i.e. "$slot" or "$slot.$fn"
*/
-static int parse_pci_devfn(DeviceState *dev, Property *prop, const char *str)
+static void set_pci_devfn(Object *obj, Visitor *v, void *opaque,
+ const char *name, Error **errp)
{
+ DeviceState *dev = DEVICE(obj);
+ Property *prop = opaque;
uint32_t *ptr = qdev_get_prop_ptr(dev, prop);
unsigned int slot, fn, n;
+ Error *local_err = NULL;
+ char *str = (char *)"";
+
+ if (dev->state != DEV_STATE_CREATED) {
+ error_set(errp, QERR_PERMISSION_DENIED);
+ return;
+ }
+
+ visit_type_str(v, &str, name, &local_err);
+ if (local_err) {
+ return set_int32(obj, v, opaque, name, errp);
+ }
if (sscanf(str, "%x.%x%n", &slot, &fn, &n) != 2) {
fn = 0;
if (sscanf(str, "%x%n", &slot, &n) != 1) {
- return -EINVAL;
+ goto invalid;
}
}
- if (str[n] != '\0')
- return -EINVAL;
- if (fn > 7)
- return -EINVAL;
- if (slot > 31)
- return -EINVAL;
+ if (str[n] != '\0' || fn > 7 || slot > 31) {
+ goto invalid;
+ }
*ptr = slot << 3 | fn;
- return 0;
+ return;
+
+invalid:
+ error_set_from_qdev_prop_error(errp, EINVAL, dev, prop, str);
}
static int print_pci_devfn(DeviceState *dev, Property *prop, char *dest, size_t len)
@@ -978,10 +871,9 @@ static int print_pci_devfn(DeviceState *dev, Property *prop, char *dest, size_t
PropertyInfo qdev_prop_pci_devfn = {
.name = "int32",
.legacy_name = "pci-devfn",
- .parse = parse_pci_devfn,
.print = print_pci_devfn,
.get = get_int32,
- .set = set_int32,
+ .set = set_pci_devfn,
/* FIXME: this should be -1...255, but the address is stored
* into an uint32_t rather than int32_t.
*/
@@ -1054,9 +946,9 @@ int qdev_prop_parse(DeviceState *dev, const char *name, const char *value)
legacy_name = g_strdup_printf("legacy-%s", name);
if (object_property_get_type(OBJECT(dev), legacy_name, NULL)) {
- object_property_set_str(OBJECT(dev), value, legacy_name, &err);
+ object_property_parse(OBJECT(dev), value, legacy_name, &err);
} else {
- object_property_set_str(OBJECT(dev), value, name, &err);
+ object_property_parse(OBJECT(dev), value, name, &err);
}
g_free(legacy_name);
@@ -1072,55 +964,56 @@ void qdev_prop_set_bit(DeviceState *dev, const char *name, bool value)
{
Error *errp = NULL;
object_property_set_bool(OBJECT(dev), value, name, &errp);
- assert(!errp);
+ assert_no_error(errp);
}
void qdev_prop_set_uint8(DeviceState *dev, const char *name, uint8_t value)
{
Error *errp = NULL;
object_property_set_int(OBJECT(dev), value, name, &errp);
- assert(!errp);
+ assert_no_error(errp);
}
void qdev_prop_set_uint16(DeviceState *dev, const char *name, uint16_t value)
{
Error *errp = NULL;
object_property_set_int(OBJECT(dev), value, name, &errp);
- assert(!errp);
+ assert_no_error(errp);
}
void qdev_prop_set_uint32(DeviceState *dev, const char *name, uint32_t value)
{
Error *errp = NULL;
object_property_set_int(OBJECT(dev), value, name, &errp);
- assert(!errp);
+ assert_no_error(errp);
}
void qdev_prop_set_int32(DeviceState *dev, const char *name, int32_t value)
{
Error *errp = NULL;
object_property_set_int(OBJECT(dev), value, name, &errp);
- assert(!errp);
+ assert_no_error(errp);
}
void qdev_prop_set_uint64(DeviceState *dev, const char *name, uint64_t value)
{
Error *errp = NULL;
object_property_set_int(OBJECT(dev), value, name, &errp);
- assert(!errp);
+ assert_no_error(errp);
}
void qdev_prop_set_string(DeviceState *dev, const char *name, char *value)
{
Error *errp = NULL;
object_property_set_str(OBJECT(dev), value, name, &errp);
- assert(!errp);
+ assert_no_error(errp);
}
int qdev_prop_set_drive(DeviceState *dev, const char *name, BlockDriverState *value)
{
Error *errp = NULL;
- object_property_set_str(OBJECT(dev), bdrv_get_device_name(value),
+ const char *bdrv_name = value ? bdrv_get_device_name(value) : "";
+ object_property_set_str(OBJECT(dev), bdrv_name,
name, &errp);
if (errp) {
qerror_report_err(errp);
@@ -1139,24 +1032,26 @@ void qdev_prop_set_drive_nofail(DeviceState *dev, const char *name, BlockDriverS
void qdev_prop_set_chr(DeviceState *dev, const char *name, CharDriverState *value)
{
Error *errp = NULL;
- assert(value->label);
- object_property_set_str(OBJECT(dev), value->label, name, &errp);
- assert(!errp);
+ assert(!value || value->label);
+ object_property_set_str(OBJECT(dev),
+ value ? value->label : "", name, &errp);
+ assert_no_error(errp);
}
void qdev_prop_set_netdev(DeviceState *dev, const char *name, VLANClientState *value)
{
Error *errp = NULL;
- assert(value->name);
- object_property_set_str(OBJECT(dev), value->name, name, &errp);
- assert(!errp);
+ assert(!value || value->name);
+ object_property_set_str(OBJECT(dev),
+ value ? value->name : "", name, &errp);
+ assert_no_error(errp);
}
void qdev_prop_set_vlan(DeviceState *dev, const char *name, VLANState *value)
{
Error *errp = NULL;
object_property_set_int(OBJECT(dev), value ? value->id : -1, name, &errp);
- assert(!errp);
+ assert_no_error(errp);
}
void qdev_prop_set_macaddr(DeviceState *dev, const char *name, uint8_t *value)
@@ -1167,7 +1062,7 @@ void qdev_prop_set_macaddr(DeviceState *dev, const char *name, uint8_t *value)
value[0], value[1], value[2], value[3], value[4], value[5]);
object_property_set_str(OBJECT(dev), str, name, &errp);
- assert(!errp);
+ assert_no_error(errp);
}
void qdev_prop_set_enum(DeviceState *dev, const char *name, int value)
@@ -1178,7 +1073,7 @@ void qdev_prop_set_enum(DeviceState *dev, const char *name, int value)
prop = qdev_prop_find(dev, name);
object_property_set_str(OBJECT(dev), prop->info->enum_table[value],
name, &errp);
- assert(!errp);
+ assert_no_error(errp);
}
void qdev_prop_set_ptr(DeviceState *dev, const char *name, void *value)
@@ -1210,7 +1105,7 @@ void qdev_prop_set_defaults(DeviceState *dev, Property *props)
} else if (props->qtype == QTYPE_QINT) {
object_property_set_int(obj, props->defval, props->name, &errp);
}
- assert(!errp);
+ assert_no_error(errp);
}
}
diff --git a/hw/qdev.c b/hw/qdev.c
index 8a413ef0ed..ee21d90e80 100644
--- a/hw/qdev.c
+++ b/hw/qdev.c
@@ -117,6 +117,9 @@ DeviceState *qdev_try_create(BusState *bus, const char *name)
{
DeviceState *dev;
+ if (object_class_by_name(name) == NULL) {
+ return NULL;
+ }
dev = DEVICE(object_new(name));
if (!dev) {
return NULL;
@@ -665,9 +668,9 @@ static TypeInfo device_type_info = {
.class_size = sizeof(DeviceClass),
};
-static void init_qdev(void)
+static void qdev_register_types(void)
{
type_register_static(&device_type_info);
}
-device_init(init_qdev);
+type_init(qdev_register_types)
diff --git a/hw/qxl-render.c b/hw/qxl-render.c
index 133d09324c..25857f6a20 100644
--- a/hw/qxl-render.c
+++ b/hw/qxl-render.c
@@ -21,14 +21,31 @@
#include "qxl.h"
-static void qxl_flip(PCIQXLDevice *qxl, QXLRect *rect)
+static void qxl_blit(PCIQXLDevice *qxl, QXLRect *rect)
{
- uint8_t *src = qxl->guest_primary.data;
- uint8_t *dst = qxl->guest_primary.flipped;
+ uint8_t *src;
+ uint8_t *dst = qxl->vga.ds->surface->data;
int len, i;
- src += (qxl->guest_primary.surface.height - rect->top - 1) *
- qxl->guest_primary.abs_stride;
+ if (is_buffer_shared(qxl->vga.ds->surface)) {
+ return;
+ }
+ if (!qxl->guest_primary.data) {
+ dprint(qxl, 1, "%s: initializing guest_primary.data\n", __func__);
+ qxl->guest_primary.data = memory_region_get_ram_ptr(&qxl->vga.vram);
+ }
+ dprint(qxl, 2, "%s: stride %d, [%d, %d, %d, %d]\n", __func__,
+ qxl->guest_primary.qxl_stride,
+ rect->left, rect->right, rect->top, rect->bottom);
+ src = qxl->guest_primary.data;
+ if (qxl->guest_primary.qxl_stride < 0) {
+ /* qxl surface is upside down, walk src scanlines
+ * in reverse order to flip it */
+ src += (qxl->guest_primary.surface.height - rect->top - 1) *
+ qxl->guest_primary.abs_stride;
+ } else {
+ src += rect->top * qxl->guest_primary.abs_stride;
+ }
dst += rect->top * qxl->guest_primary.abs_stride;
src += rect->left * qxl->guest_primary.bytes_pp;
dst += rect->left * qxl->guest_primary.bytes_pp;
@@ -37,7 +54,7 @@ static void qxl_flip(PCIQXLDevice *qxl, QXLRect *rect)
for (i = rect->top; i < rect->bottom; i++) {
memcpy(dst, src, len);
dst += qxl->guest_primary.abs_stride;
- src -= qxl->guest_primary.abs_stride;
+ src += qxl->guest_primary.qxl_stride;
}
}
@@ -71,86 +88,109 @@ void qxl_render_resize(PCIQXLDevice *qxl)
}
}
-void qxl_render_update(PCIQXLDevice *qxl)
+static void qxl_set_rect_to_surface(PCIQXLDevice *qxl, QXLRect *area)
+{
+ area->left = 0;
+ area->right = qxl->guest_primary.surface.width;
+ area->top = 0;
+ area->bottom = qxl->guest_primary.surface.height;
+}
+
+static void qxl_render_update_area_unlocked(PCIQXLDevice *qxl)
{
VGACommonState *vga = &qxl->vga;
- QXLRect dirty[32], update;
- void *ptr;
- int i, redraw = 0;
-
- if (!is_buffer_shared(vga->ds->surface)) {
- dprint(qxl, 1, "%s: restoring shared displaysurface\n", __func__);
- qxl->guest_primary.resized++;
- qxl->guest_primary.commands++;
- redraw = 1;
- }
+ int i;
+ DisplaySurface *surface = vga->ds->surface;
if (qxl->guest_primary.resized) {
qxl->guest_primary.resized = 0;
-
- if (qxl->guest_primary.flipped) {
- g_free(qxl->guest_primary.flipped);
- qxl->guest_primary.flipped = NULL;
- }
- qemu_free_displaysurface(vga->ds);
-
qxl->guest_primary.data = memory_region_get_ram_ptr(&qxl->vga.vram);
- if (qxl->guest_primary.qxl_stride < 0) {
- /* spice surface is upside down -> need extra buffer to flip */
- qxl->guest_primary.flipped =
- g_malloc(qxl->guest_primary.surface.width *
- qxl->guest_primary.abs_stride);
- ptr = qxl->guest_primary.flipped;
- } else {
- ptr = qxl->guest_primary.data;
- }
- dprint(qxl, 1, "%s: %dx%d, stride %d, bpp %d, depth %d, flip %s\n",
+ qxl_set_rect_to_surface(qxl, &qxl->dirty[0]);
+ qxl->num_dirty_rects = 1;
+ dprint(qxl, 1, "%s: %dx%d, stride %d, bpp %d, depth %d\n",
__FUNCTION__,
qxl->guest_primary.surface.width,
qxl->guest_primary.surface.height,
qxl->guest_primary.qxl_stride,
qxl->guest_primary.bytes_pp,
- qxl->guest_primary.bits_pp,
- qxl->guest_primary.flipped ? "yes" : "no");
- vga->ds->surface =
+ qxl->guest_primary.bits_pp);
+ }
+ if (surface->width != qxl->guest_primary.surface.width ||
+ surface->height != qxl->guest_primary.surface.height) {
+ if (qxl->guest_primary.qxl_stride > 0) {
+ dprint(qxl, 1, "%s: using guest_primary for displaysurface\n",
+ __func__);
+ qemu_free_displaysurface(vga->ds);
qemu_create_displaysurface_from(qxl->guest_primary.surface.width,
qxl->guest_primary.surface.height,
qxl->guest_primary.bits_pp,
qxl->guest_primary.abs_stride,
- ptr);
- dpy_resize(vga->ds);
+ qxl->guest_primary.data);
+ } else {
+ dprint(qxl, 1, "%s: resizing displaysurface to guest_primary\n",
+ __func__);
+ qemu_resize_displaysurface(vga->ds,
+ qxl->guest_primary.surface.width,
+ qxl->guest_primary.surface.height);
+ }
+ }
+ for (i = 0; i < qxl->num_dirty_rects; i++) {
+ if (qemu_spice_rect_is_empty(qxl->dirty+i)) {
+ break;
+ }
+ qxl_blit(qxl, qxl->dirty+i);
+ dpy_update(vga->ds,
+ qxl->dirty[i].left, qxl->dirty[i].top,
+ qxl->dirty[i].right - qxl->dirty[i].left,
+ qxl->dirty[i].bottom - qxl->dirty[i].top);
}
+ qxl->num_dirty_rects = 0;
+}
- if (!qxl->guest_primary.commands) {
+/*
+ * use ssd.lock to protect render_update_cookie_num.
+ * qxl_render_update is called by io thread or vcpu thread, and the completion
+ * callbacks are called by spice_server thread, defering to bh called from the
+ * io thread.
+ */
+void qxl_render_update(PCIQXLDevice *qxl)
+{
+ QXLCookie *cookie;
+
+ qemu_mutex_lock(&qxl->ssd.lock);
+
+ if (!runstate_is_running() || !qxl->guest_primary.commands) {
+ qxl_render_update_area_unlocked(qxl);
+ qemu_mutex_unlock(&qxl->ssd.lock);
return;
}
+
qxl->guest_primary.commands = 0;
+ qxl->render_update_cookie_num++;
+ qemu_mutex_unlock(&qxl->ssd.lock);
+ cookie = qxl_cookie_new(QXL_COOKIE_TYPE_RENDER_UPDATE_AREA,
+ 0);
+ qxl_set_rect_to_surface(qxl, &cookie->u.render.area);
+ qxl_spice_update_area(qxl, 0, &cookie->u.render.area, NULL,
+ 0, 1 /* clear_dirty_region */, QXL_ASYNC, cookie);
+}
- update.left = 0;
- update.right = qxl->guest_primary.surface.width;
- update.top = 0;
- update.bottom = qxl->guest_primary.surface.height;
-
- memset(dirty, 0, sizeof(dirty));
- qxl_spice_update_area(qxl, 0, &update,
- dirty, ARRAY_SIZE(dirty), 1, QXL_SYNC);
- if (redraw) {
- memset(dirty, 0, sizeof(dirty));
- dirty[0] = update;
- }
+void qxl_render_update_area_bh(void *opaque)
+{
+ PCIQXLDevice *qxl = opaque;
- for (i = 0; i < ARRAY_SIZE(dirty); i++) {
- if (qemu_spice_rect_is_empty(dirty+i)) {
- break;
- }
- if (qxl->guest_primary.flipped) {
- qxl_flip(qxl, dirty+i);
- }
- dpy_update(vga->ds,
- dirty[i].left, dirty[i].top,
- dirty[i].right - dirty[i].left,
- dirty[i].bottom - dirty[i].top);
- }
+ qemu_mutex_lock(&qxl->ssd.lock);
+ qxl_render_update_area_unlocked(qxl);
+ qemu_mutex_unlock(&qxl->ssd.lock);
+}
+
+void qxl_render_update_area_done(PCIQXLDevice *qxl, QXLCookie *cookie)
+{
+ qemu_mutex_lock(&qxl->ssd.lock);
+ qemu_bh_schedule(qxl->update_area_bh);
+ qxl->render_update_cookie_num--;
+ qemu_mutex_unlock(&qxl->ssd.lock);
+ g_free(cookie);
}
static QEMUCursor *qxl_cursor(PCIQXLDevice *qxl, QXLCursor *cursor)
diff --git a/hw/qxl.c b/hw/qxl.c
index bc03c1dc77..e17b0e31af 100644
--- a/hw/qxl.c
+++ b/hw/qxl.c
@@ -125,9 +125,7 @@ static void qxl_ring_set_dirty(PCIQXLDevice *qxl);
void qxl_guest_bug(PCIQXLDevice *qxl, const char *msg, ...)
{
-#if SPICE_INTERFACE_QXL_MINOR >= 1
qxl_send_events(qxl, QXL_INTERRUPT_ERROR);
-#endif
if (qxl->guestdebug) {
va_list ap;
va_start(ap, msg);
@@ -143,18 +141,15 @@ void qxl_spice_update_area(PCIQXLDevice *qxl, uint32_t surface_id,
struct QXLRect *area, struct QXLRect *dirty_rects,
uint32_t num_dirty_rects,
uint32_t clear_dirty_region,
- qxl_async_io async)
+ qxl_async_io async, struct QXLCookie *cookie)
{
if (async == QXL_SYNC) {
qxl->ssd.worker->update_area(qxl->ssd.worker, surface_id, area,
dirty_rects, num_dirty_rects, clear_dirty_region);
} else {
-#if SPICE_INTERFACE_QXL_MINOR >= 1
+ assert(cookie != NULL);
spice_qxl_update_area_async(&qxl->ssd.qxl, surface_id, area,
- clear_dirty_region, 0);
-#else
- abort();
-#endif
+ clear_dirty_region, (uint64_t)cookie);
}
}
@@ -170,25 +165,25 @@ static void qxl_spice_destroy_surface_wait_complete(PCIQXLDevice *qxl,
static void qxl_spice_destroy_surface_wait(PCIQXLDevice *qxl, uint32_t id,
qxl_async_io async)
{
+ QXLCookie *cookie;
+
if (async) {
-#if SPICE_INTERFACE_QXL_MINOR < 1
- abort();
-#else
- spice_qxl_destroy_surface_async(&qxl->ssd.qxl, id,
- (uint64_t)id);
-#endif
+ cookie = qxl_cookie_new(QXL_COOKIE_TYPE_IO,
+ QXL_IO_DESTROY_SURFACE_ASYNC);
+ cookie->u.surface_id = id;
+ spice_qxl_destroy_surface_async(&qxl->ssd.qxl, id, (uint64_t)cookie);
} else {
qxl->ssd.worker->destroy_surface_wait(qxl->ssd.worker, id);
qxl_spice_destroy_surface_wait_complete(qxl, id);
}
}
-#if SPICE_INTERFACE_QXL_MINOR >= 1
static void qxl_spice_flush_surfaces_async(PCIQXLDevice *qxl)
{
- spice_qxl_flush_surfaces_async(&qxl->ssd.qxl, 0);
+ spice_qxl_flush_surfaces_async(&qxl->ssd.qxl,
+ (uint64_t)qxl_cookie_new(QXL_COOKIE_TYPE_IO,
+ QXL_IO_FLUSH_SURFACES_ASYNC));
}
-#endif
void qxl_spice_loadvm_commands(PCIQXLDevice *qxl, struct QXLCommandExt *ext,
uint32_t count)
@@ -217,11 +212,9 @@ static void qxl_spice_destroy_surfaces_complete(PCIQXLDevice *qxl)
static void qxl_spice_destroy_surfaces(PCIQXLDevice *qxl, qxl_async_io async)
{
if (async) {
-#if SPICE_INTERFACE_QXL_MINOR < 1
- abort();
-#else
- spice_qxl_destroy_surfaces_async(&qxl->ssd.qxl, 0);
-#endif
+ spice_qxl_destroy_surfaces_async(&qxl->ssd.qxl,
+ (uint64_t)qxl_cookie_new(QXL_COOKIE_TYPE_IO,
+ QXL_IO_DESTROY_ALL_SURFACES_ASYNC));
} else {
qxl->ssd.worker->destroy_surfaces(qxl->ssd.worker);
qxl_spice_destroy_surfaces_complete(qxl);
@@ -490,7 +483,6 @@ static const char *io_port_to_string(uint32_t io_port)
[QXL_IO_DESTROY_PRIMARY] = "QXL_IO_DESTROY_PRIMARY",
[QXL_IO_DESTROY_SURFACE_WAIT] = "QXL_IO_DESTROY_SURFACE_WAIT",
[QXL_IO_DESTROY_ALL_SURFACES] = "QXL_IO_DESTROY_ALL_SURFACES",
-#if SPICE_INTERFACE_QXL_MINOR >= 1
[QXL_IO_UPDATE_AREA_ASYNC] = "QXL_IO_UPDATE_AREA_ASYNC",
[QXL_IO_MEMSLOT_ADD_ASYNC] = "QXL_IO_MEMSLOT_ADD_ASYNC",
[QXL_IO_CREATE_PRIMARY_ASYNC] = "QXL_IO_CREATE_PRIMARY_ASYNC",
@@ -500,7 +492,6 @@ static const char *io_port_to_string(uint32_t io_port)
= "QXL_IO_DESTROY_ALL_SURFACES_ASYNC",
[QXL_IO_FLUSH_SURFACES_ASYNC] = "QXL_IO_FLUSH_SURFACES_ASYNC",
[QXL_IO_FLUSH_RELEASE] = "QXL_IO_FLUSH_RELEASE",
-#endif
};
return io_port_to_string[io_port];
}
@@ -625,7 +616,7 @@ static void interface_release_resource(QXLInstance *sin,
if (ext.group_id == MEMSLOT_GROUP_HOST) {
/* host group -> vga mode update request */
- qemu_spice_destroy_update(&qxl->ssd, (void*)ext.info->id);
+ qemu_spice_destroy_update(&qxl->ssd, (void *)(intptr_t)ext.info->id);
return;
}
@@ -735,12 +726,9 @@ static int interface_flush_resources(QXLInstance *sin)
static void qxl_create_guest_primary_complete(PCIQXLDevice *d);
-#if SPICE_INTERFACE_QXL_MINOR >= 1
-
/* called from spice server thread context only */
-static void interface_async_complete(QXLInstance *sin, uint64_t cookie)
+static void interface_async_complete_io(PCIQXLDevice *qxl, QXLCookie *cookie)
{
- PCIQXLDevice *qxl = container_of(sin, PCIQXLDevice, ssd.qxl);
uint32_t current_async;
qemu_mutex_lock(&qxl->async_lock);
@@ -748,8 +736,22 @@ static void interface_async_complete(QXLInstance *sin, uint64_t cookie)
qxl->current_async = QXL_UNDEFINED_IO;
qemu_mutex_unlock(&qxl->async_lock);
- dprint(qxl, 2, "async_complete: %d (%ld) done\n", current_async, cookie);
+ dprint(qxl, 2, "async_complete: %d (%p) done\n", current_async, cookie);
+ if (!cookie) {
+ fprintf(stderr, "qxl: %s: error, cookie is NULL\n", __func__);
+ return;
+ }
+ if (cookie && current_async != cookie->io) {
+ fprintf(stderr,
+ "qxl: %s: error: current_async = %d != %ld = cookie->io\n",
+ __func__, current_async, cookie->io);
+ }
switch (current_async) {
+ case QXL_IO_MEMSLOT_ADD_ASYNC:
+ case QXL_IO_DESTROY_PRIMARY_ASYNC:
+ case QXL_IO_UPDATE_AREA_ASYNC:
+ case QXL_IO_FLUSH_SURFACES_ASYNC:
+ break;
case QXL_IO_CREATE_PRIMARY_ASYNC:
qxl_create_guest_primary_complete(qxl);
break;
@@ -757,13 +759,75 @@ static void interface_async_complete(QXLInstance *sin, uint64_t cookie)
qxl_spice_destroy_surfaces_complete(qxl);
break;
case QXL_IO_DESTROY_SURFACE_ASYNC:
- qxl_spice_destroy_surface_wait_complete(qxl, (uint32_t)cookie);
+ qxl_spice_destroy_surface_wait_complete(qxl, cookie->u.surface_id);
break;
+ default:
+ fprintf(stderr, "qxl: %s: unexpected current_async %d\n", __func__,
+ current_async);
}
qxl_send_events(qxl, QXL_INTERRUPT_IO_CMD);
}
-#endif
+/* called from spice server thread context only */
+static void interface_update_area_complete(QXLInstance *sin,
+ uint32_t surface_id,
+ QXLRect *dirty, uint32_t num_updated_rects)
+{
+ PCIQXLDevice *qxl = container_of(sin, PCIQXLDevice, ssd.qxl);
+ int i;
+ int qxl_i;
+
+ qemu_mutex_lock(&qxl->ssd.lock);
+ if (surface_id != 0 || !qxl->render_update_cookie_num) {
+ qemu_mutex_unlock(&qxl->ssd.lock);
+ return;
+ }
+ if (qxl->num_dirty_rects + num_updated_rects > QXL_NUM_DIRTY_RECTS) {
+ /*
+ * overflow - treat this as a full update. Not expected to be common.
+ */
+ dprint(qxl, 1, "%s: overflow of dirty rects\n", __func__);
+ qxl->guest_primary.resized = 1;
+ }
+ if (qxl->guest_primary.resized) {
+ /*
+ * Don't bother copying or scheduling the bh since we will flip
+ * the whole area anyway on completion of the update_area async call
+ */
+ qemu_mutex_unlock(&qxl->ssd.lock);
+ return;
+ }
+ qxl_i = qxl->num_dirty_rects;
+ for (i = 0; i < num_updated_rects; i++) {
+ qxl->dirty[qxl_i++] = dirty[i];
+ }
+ qxl->num_dirty_rects += num_updated_rects;
+ dprint(qxl, 1, "%s: scheduling update_area_bh, #dirty %d\n",
+ __func__, qxl->num_dirty_rects);
+ qemu_bh_schedule(qxl->update_area_bh);
+ qemu_mutex_unlock(&qxl->ssd.lock);
+}
+
+/* called from spice server thread context only */
+static void interface_async_complete(QXLInstance *sin, uint64_t cookie_token)
+{
+ PCIQXLDevice *qxl = container_of(sin, PCIQXLDevice, ssd.qxl);
+ QXLCookie *cookie = (QXLCookie *)cookie_token;
+
+ switch (cookie->type) {
+ case QXL_COOKIE_TYPE_IO:
+ interface_async_complete_io(qxl, cookie);
+ g_free(cookie);
+ break;
+ case QXL_COOKIE_TYPE_RENDER_UPDATE_AREA:
+ qxl_render_update_area_done(qxl, cookie);
+ break;
+ default:
+ fprintf(stderr, "qxl: %s: unexpected cookie type %d\n",
+ __func__, cookie->type);
+ g_free(cookie);
+ }
+}
static const QXLInterface qxl_interface = {
.base.type = SPICE_INTERFACE_QXL,
@@ -784,9 +848,8 @@ static const QXLInterface qxl_interface = {
.req_cursor_notification = interface_req_cursor_notification,
.notify_update = interface_notify_update,
.flush_resources = interface_flush_resources,
-#if SPICE_INTERFACE_QXL_MINOR >= 1
.async_complete = interface_async_complete,
-#endif
+ .update_area_complete = interface_update_area_complete,
};
static void qxl_enter_vga_mode(PCIQXLDevice *d)
@@ -913,6 +976,7 @@ static void qxl_add_memslot(PCIQXLDevice *d, uint32_t slot_id, uint64_t delta,
static const int regions[] = {
QXL_RAM_RANGE_INDEX,
QXL_VRAM_RANGE_INDEX,
+ QXL_VRAM64_RANGE_INDEX,
};
uint64_t guest_start;
uint64_t guest_end;
@@ -959,6 +1023,7 @@ static void qxl_add_memslot(PCIQXLDevice *d, uint32_t slot_id, uint64_t delta,
virt_start = (intptr_t)memory_region_get_ram_ptr(&d->vga.vram);
break;
case QXL_VRAM_RANGE_INDEX:
+ case 4 /* vram 64bit */:
virt_start = (intptr_t)memory_region_get_ram_ptr(&d->vram_bar);
break;
default:
@@ -1006,7 +1071,7 @@ static void qxl_reset_surfaces(PCIQXLDevice *d)
qxl_spice_destroy_surfaces(d, QXL_SYNC);
}
-/* called from spice server thread context only */
+/* can be also called from spice server thread context */
void *qxl_phys2virt(PCIQXLDevice *qxl, QXLPHYSICAL pqxl, int group_id)
{
uint64_t phys = le64_to_cpu(pqxl);
@@ -1015,7 +1080,7 @@ void *qxl_phys2virt(PCIQXLDevice *qxl, QXLPHYSICAL pqxl, int group_id)
switch (group_id) {
case MEMSLOT_GROUP_HOST:
- return (void*)offset;
+ return (void *)(intptr_t)offset;
case MEMSLOT_GROUP_GUEST:
PANIC_ON(slot >= NUM_MEMSLOTS);
PANIC_ON(!qxl->guest_slots[slot].active);
@@ -1077,9 +1142,7 @@ static int qxl_destroy_primary(PCIQXLDevice *d, qxl_async_io async)
if (d->mode == QXL_MODE_UNDEFINED) {
return 0;
}
-
dprint(d, 1, "%s\n", __FUNCTION__);
-
d->mode = QXL_MODE_UNDEFINED;
qemu_spice_destroy_primary_surface(&d->ssd, 0, async);
qxl_spice_reset_cursor(d);
@@ -1136,9 +1199,7 @@ static void ioport_write(void *opaque, target_phys_addr_t addr,
PCIQXLDevice *d = opaque;
uint32_t io_port = addr;
qxl_async_io async = QXL_SYNC;
-#if SPICE_INTERFACE_QXL_MINOR >= 1
uint32_t orig_io_port = io_port;
-#endif
switch (io_port) {
case QXL_IO_RESET:
@@ -1148,10 +1209,8 @@ static void ioport_write(void *opaque, target_phys_addr_t addr,
case QXL_IO_CREATE_PRIMARY:
case QXL_IO_UPDATE_IRQ:
case QXL_IO_LOG:
-#if SPICE_INTERFACE_QXL_MINOR >= 1
case QXL_IO_MEMSLOT_ADD_ASYNC:
case QXL_IO_CREATE_PRIMARY_ASYNC:
-#endif
break;
default:
if (d->mode != QXL_MODE_VGA) {
@@ -1159,17 +1218,14 @@ static void ioport_write(void *opaque, target_phys_addr_t addr,
}
dprint(d, 1, "%s: unexpected port 0x%x (%s) in vga mode\n",
__func__, io_port, io_port_to_string(io_port));
-#if SPICE_INTERFACE_QXL_MINOR >= 1
/* be nice to buggy guest drivers */
if (io_port >= QXL_IO_UPDATE_AREA_ASYNC &&
io_port <= QXL_IO_DESTROY_ALL_SURFACES_ASYNC) {
qxl_send_events(d, QXL_INTERRUPT_IO_CMD);
}
-#endif
return;
}
-#if SPICE_INTERFACE_QXL_MINOR >= 1
/* we change the io_port to avoid ifdeffery in the main switch */
orig_io_port = io_port;
switch (io_port) {
@@ -1208,14 +1264,21 @@ async_common:
default:
break;
}
-#endif
switch (io_port) {
case QXL_IO_UPDATE_AREA:
{
+ QXLCookie *cookie = NULL;
QXLRect update = d->ram->update_area;
+
+ if (async == QXL_ASYNC) {
+ cookie = qxl_cookie_new(QXL_COOKIE_TYPE_IO,
+ QXL_IO_UPDATE_AREA_ASYNC);
+ cookie->u.area = update;
+ }
qxl_spice_update_area(d, d->ram->update_surface,
- &update, NULL, 0, 0, async);
+ cookie ? &cookie->u.area : &update,
+ NULL, 0, 0, async, cookie);
break;
}
case QXL_IO_NOTIFY_CMD:
@@ -1300,7 +1363,6 @@ async_common:
}
qxl_spice_destroy_surface_wait(d, val, async);
break;
-#if SPICE_INTERFACE_QXL_MINOR >= 1
case QXL_IO_FLUSH_RELEASE: {
QXLReleaseRing *ring = &d->ram->release_ring;
if (ring->prod - ring->cons + 1 == ring->num_items) {
@@ -1321,7 +1383,6 @@ async_common:
d->num_free_res);
qxl_spice_flush_surfaces_async(d);
break;
-#endif
case QXL_IO_DESTROY_ALL_SURFACES:
d->mode = QXL_MODE_UNDEFINED;
qxl_spice_destroy_surfaces(d, async);
@@ -1332,16 +1393,12 @@ async_common:
}
return;
cancel_async:
-#if SPICE_INTERFACE_QXL_MINOR >= 1
if (async) {
qxl_send_events(d, QXL_INTERRUPT_IO_CMD);
qemu_mutex_lock(&d->async_lock);
d->current_async = QXL_UNDEFINED_IO;
qemu_mutex_unlock(&d->async_lock);
}
-#else
- return;
-#endif
}
static uint64_t ioport_read(void *opaque, target_phys_addr_t addr,
@@ -1435,7 +1492,7 @@ static void qxl_hw_invalidate(void *opaque)
vga->invalidate(vga);
}
-static void qxl_hw_screen_dump(void *opaque, const char *filename)
+static void qxl_hw_screen_dump(void *opaque, const char *filename, bool cswitch)
{
PCIQXLDevice *qxl = opaque;
VGACommonState *vga = &qxl->vga;
@@ -1447,7 +1504,7 @@ static void qxl_hw_screen_dump(void *opaque, const char *filename)
ppm_save(filename, qxl->ssd.ds->surface);
break;
case QXL_MODE_VGA:
- vga->screen_dump(vga, filename);
+ vga->screen_dump(vga, filename, cswitch);
break;
default:
break;
@@ -1465,6 +1522,46 @@ static void qxl_hw_text_update(void *opaque, console_ch_t *chardata)
}
}
+static void qxl_dirty_surfaces(PCIQXLDevice *qxl)
+{
+ intptr_t vram_start;
+ int i;
+
+ if (qxl->mode != QXL_MODE_NATIVE && qxl->mode != QXL_MODE_COMPAT) {
+ return;
+ }
+
+ /* dirty the primary surface */
+ qxl_set_dirty(&qxl->vga.vram, qxl->shadow_rom.draw_area_offset,
+ qxl->shadow_rom.surface0_area_size);
+
+ vram_start = (intptr_t)memory_region_get_ram_ptr(&qxl->vram_bar);
+
+ /* dirty the off-screen surfaces */
+ for (i = 0; i < NUM_SURFACES; i++) {
+ QXLSurfaceCmd *cmd;
+ intptr_t surface_offset;
+ int surface_size;
+
+ if (qxl->guest_surfaces.cmds[i] == 0) {
+ continue;
+ }
+
+ cmd = qxl_phys2virt(qxl, qxl->guest_surfaces.cmds[i],
+ MEMSLOT_GROUP_GUEST);
+ assert(cmd->type == QXL_SURFACE_CMD_CREATE);
+ surface_offset = (intptr_t)qxl_phys2virt(qxl,
+ cmd->u.surface_create.data,
+ MEMSLOT_GROUP_GUEST);
+ surface_offset -= vram_start;
+ surface_size = cmd->u.surface_create.height *
+ abs(cmd->u.surface_create.stride);
+ dprint(qxl, 3, "%s: dirty surface %d, offset %d, size %d\n", __func__,
+ i, (int)surface_offset, surface_size);
+ qxl_set_dirty(&qxl->vram_bar, surface_offset, surface_size);
+ }
+}
+
static void qxl_vm_change_state_handler(void *opaque, int running,
RunState state)
{
@@ -1478,14 +1575,9 @@ static void qxl_vm_change_state_handler(void *opaque, int running,
* called
*/
qxl_update_irq(qxl);
- } else if (qxl->mode == QXL_MODE_NATIVE) {
- /* dirty all vram (which holds surfaces) and devram (primary surface)
- * to make sure they are saved */
- /* FIXME #1: should go out during "live" stage */
- /* FIXME #2: we only need to save the areas which are actually used */
- qxl_set_dirty(&qxl->vram_bar, 0, qxl->vram_size);
- qxl_set_dirty(&qxl->vga.vram, qxl->shadow_rom.draw_area_offset,
- qxl->shadow_rom.surface0_area_size);
+ } else {
+ /* make sure surfaces are saved before migration */
+ qxl_dirty_surfaces(qxl);
}
}
@@ -1509,6 +1601,10 @@ static void display_refresh(struct DisplayState *ds)
{
if (qxl0->mode == QXL_MODE_VGA) {
qemu_spice_display_refresh(&qxl0->ssd);
+ } else {
+ qemu_mutex_lock(&qxl0->ssd.lock);
+ qemu_spice_cursor_refresh_unlocked(&qxl0->ssd);
+ qemu_mutex_unlock(&qxl0->ssd.lock);
}
}
@@ -1518,6 +1614,41 @@ static DisplayChangeListener display_listener = {
.dpy_refresh = display_refresh,
};
+static void qxl_init_ramsize(PCIQXLDevice *qxl, uint32_t ram_min_mb)
+{
+ /* vga ram (bar 0) */
+ if (qxl->ram_size_mb != -1) {
+ qxl->vga.vram_size = qxl->ram_size_mb * 1024 * 1024;
+ }
+ if (qxl->vga.vram_size < ram_min_mb * 1024 * 1024) {
+ qxl->vga.vram_size = ram_min_mb * 1024 * 1024;
+ }
+
+ /* vram32 (surfaces, 32bit, bar 1) */
+ if (qxl->vram32_size_mb != -1) {
+ qxl->vram32_size = qxl->vram32_size_mb * 1024 * 1024;
+ }
+ if (qxl->vram32_size < 4096) {
+ qxl->vram32_size = 4096;
+ }
+
+ /* vram (surfaces, 64bit, bar 4+5) */
+ if (qxl->vram_size_mb != -1) {
+ qxl->vram_size = qxl->vram_size_mb * 1024 * 1024;
+ }
+ if (qxl->vram_size < qxl->vram32_size) {
+ qxl->vram_size = qxl->vram32_size;
+ }
+
+ if (qxl->revision == 1) {
+ qxl->vram32_size = 4096;
+ qxl->vram_size = 4096;
+ }
+ qxl->vga.vram_size = msb_mask(qxl->vga.vram_size * 2 - 1);
+ qxl->vram32_size = msb_mask(qxl->vram32_size * 2 - 1);
+ qxl->vram_size = msb_mask(qxl->vram_size * 2 - 1);
+}
+
static int qxl_init_common(PCIQXLDevice *qxl)
{
uint8_t* config = qxl->pci.config;
@@ -1539,9 +1670,7 @@ static int qxl_init_common(PCIQXLDevice *qxl)
case 2: /* spice 0.6 -- qxl-2 */
pci_device_rev = QXL_REVISION_STABLE_V06;
break;
-#if SPICE_INTERFACE_QXL_MINOR >= 1
case 3: /* qxl-3 */
-#endif
default:
pci_device_rev = QXL_DEFAULT_REVISION;
break;
@@ -1556,15 +1685,10 @@ static int qxl_init_common(PCIQXLDevice *qxl)
init_qxl_rom(qxl);
init_qxl_ram(qxl);
- if (qxl->vram_size < 16 * 1024 * 1024) {
- qxl->vram_size = 16 * 1024 * 1024;
- }
- if (qxl->revision == 1) {
- qxl->vram_size = 4096;
- }
- qxl->vram_size = msb_mask(qxl->vram_size * 2 - 1);
memory_region_init_ram(&qxl->vram_bar, "qxl.vram", qxl->vram_size);
vmstate_register_ram(&qxl->vram_bar, &qxl->pci.qdev);
+ memory_region_init_alias(&qxl->vram32_bar, "qxl.vram32", &qxl->vram_bar,
+ 0, qxl->vram32_size);
io_size = msb_mask(QXL_IO_RANGE_SIZE * 2 - 1);
if (qxl->revision == 1) {
@@ -1588,7 +1712,29 @@ static int qxl_init_common(PCIQXLDevice *qxl)
PCI_BASE_ADDRESS_SPACE_MEMORY, &qxl->vga.vram);
pci_register_bar(&qxl->pci, QXL_VRAM_RANGE_INDEX,
- PCI_BASE_ADDRESS_SPACE_MEMORY, &qxl->vram_bar);
+ PCI_BASE_ADDRESS_SPACE_MEMORY, &qxl->vram32_bar);
+
+ if (qxl->vram32_size < qxl->vram_size) {
+ /*
+ * Make the 64bit vram bar show up only in case it is
+ * configured to be larger than the 32bit vram bar.
+ */
+ pci_register_bar(&qxl->pci, QXL_VRAM64_RANGE_INDEX,
+ PCI_BASE_ADDRESS_SPACE_MEMORY |
+ PCI_BASE_ADDRESS_MEM_TYPE_64 |
+ PCI_BASE_ADDRESS_MEM_PREFETCH,
+ &qxl->vram_bar);
+ }
+
+ /* print pci bar details */
+ dprint(qxl, 1, "ram/%s: %d MB [region 0]\n",
+ qxl->id == 0 ? "pri" : "sec",
+ qxl->vga.vram_size / (1024*1024));
+ dprint(qxl, 1, "vram/32: %d MB [region 1]\n",
+ qxl->vram32_size / (1024*1024));
+ dprint(qxl, 1, "vram/64: %d MB %s\n",
+ qxl->vram_size / (1024*1024),
+ qxl->vram32_size < qxl->vram_size ? "[region 4]" : "[unmapped]");
qxl->ssd.qxl.base.sif = &qxl_interface.base;
qxl->ssd.qxl.id = qxl->id;
@@ -1598,6 +1744,8 @@ static int qxl_init_common(PCIQXLDevice *qxl)
init_pipe_signaling(qxl);
qxl_reset_state(qxl);
+ qxl->update_area_bh = qemu_bh_new(qxl_render_update_area_bh, qxl);
+
return 0;
}
@@ -1605,15 +1753,11 @@ static int qxl_init_primary(PCIDevice *dev)
{
PCIQXLDevice *qxl = DO_UPCAST(PCIQXLDevice, pci, dev);
VGACommonState *vga = &qxl->vga;
- ram_addr_t ram_size = msb_mask(qxl->vga.vram_size * 2 - 1);
PortioList *qxl_vga_port_list = g_new(PortioList, 1);
qxl->id = 0;
-
- if (ram_size < 32 * 1024 * 1024) {
- ram_size = 32 * 1024 * 1024;
- }
- vga_common_init(vga, ram_size);
+ qxl_init_ramsize(qxl, 32);
+ vga_common_init(vga, qxl->vga.vram_size);
vga_init(vga, pci_address_space(dev), pci_address_space_io(dev), false);
portio_list_init(qxl_vga_port_list, qxl_vga_portio_list, vga, "vga");
portio_list_add(qxl_vga_port_list, pci_address_space_io(dev), 0x3b0);
@@ -1632,14 +1776,9 @@ static int qxl_init_secondary(PCIDevice *dev)
{
static int device_id = 1;
PCIQXLDevice *qxl = DO_UPCAST(PCIQXLDevice, pci, dev);
- ram_addr_t ram_size = msb_mask(qxl->vga.vram_size * 2 - 1);
qxl->id = device_id++;
-
- if (ram_size < 16 * 1024 * 1024) {
- ram_size = 16 * 1024 * 1024;
- }
- qxl->vga.vram_size = ram_size;
+ qxl_init_ramsize(qxl, 16);
memory_region_init_ram(&qxl->vga.vram, "qxl.vgavram", qxl->vga.vram_size);
vmstate_register_ram(&qxl->vga.vram, &qxl->pci.qdev);
qxl->vga.vram_ptr = memory_region_get_ram_ptr(&qxl->vga.vram);
@@ -1814,13 +1953,16 @@ static VMStateDescription qxl_vmstate = {
static Property qxl_properties[] = {
DEFINE_PROP_UINT32("ram_size", PCIQXLDevice, vga.vram_size,
64 * 1024 * 1024),
- DEFINE_PROP_UINT32("vram_size", PCIQXLDevice, vram_size,
+ DEFINE_PROP_UINT32("vram_size", PCIQXLDevice, vram32_size,
64 * 1024 * 1024),
DEFINE_PROP_UINT32("revision", PCIQXLDevice, revision,
QXL_DEFAULT_REVISION),
DEFINE_PROP_UINT32("debug", PCIQXLDevice, debug, 0),
DEFINE_PROP_UINT32("guestdebug", PCIQXLDevice, guestdebug, 0),
DEFINE_PROP_UINT32("cmdlog", PCIQXLDevice, cmdlog, 0),
+ DEFINE_PROP_UINT32("ram_size_mb", PCIQXLDevice, ram_size_mb, -1),
+ DEFINE_PROP_UINT32("vram_size_mb", PCIQXLDevice, vram32_size_mb, 0),
+ DEFINE_PROP_UINT32("vram64_size_mb", PCIQXLDevice, vram_size_mb, 0),
DEFINE_PROP_END_OF_LIST(),
};
@@ -1870,10 +2012,10 @@ static TypeInfo qxl_secondary_info = {
.class_init = qxl_secondary_class_init,
};
-static void qxl_register(void)
+static void qxl_register_types(void)
{
type_register_static(&qxl_primary_info);
type_register_static(&qxl_secondary_info);
}
-device_init(qxl_register);
+type_init(qxl_register_types)
diff --git a/hw/qxl.h b/hw/qxl.h
index 766aa6d68e..11a0db3f7d 100644
--- a/hw/qxl.h
+++ b/hw/qxl.h
@@ -16,8 +16,14 @@ enum qxl_mode {
QXL_MODE_NATIVE,
};
+#ifndef QXL_VRAM64_RANGE_INDEX
+#define QXL_VRAM64_RANGE_INDEX 4
+#endif
+
#define QXL_UNDEFINED_IO UINT32_MAX
+#define QXL_NUM_DIRTY_RECTS 64
+
typedef struct PCIQXLDevice {
PCIDevice pci;
SimpleSpiceDisplay ssd;
@@ -52,7 +58,7 @@ typedef struct PCIQXLDevice {
uint32_t abs_stride;
uint32_t bits_pp;
uint32_t bytes_pp;
- uint8_t *data, *flipped;
+ uint8_t *data;
} guest_primary;
struct surfaces {
@@ -86,9 +92,22 @@ typedef struct PCIQXLDevice {
/* vram pci bar */
uint32_t vram_size;
MemoryRegion vram_bar;
+ uint32_t vram32_size;
+ MemoryRegion vram32_bar;
/* io bar */
MemoryRegion io_bar;
+
+ /* user-friendly properties (in megabytes) */
+ uint32_t ram_size_mb;
+ uint32_t vram_size_mb;
+ uint32_t vram32_size_mb;
+
+ /* qxl_render_update state */
+ int render_update_cookie_num;
+ int num_dirty_rects;
+ QXLRect dirty[QXL_NUM_DIRTY_RECTS];
+ QEMUBH *update_area_bh;
} PCIQXLDevice;
#define PANIC_ON(x) if ((x)) { \
@@ -104,11 +123,7 @@ typedef struct PCIQXLDevice {
} \
} while (0)
-#if SPICE_INTERFACE_QXL_MINOR >= 1
#define QXL_DEFAULT_REVISION QXL_REVISION_STABLE_V10
-#else
-#define QXL_DEFAULT_REVISION QXL_REVISION_STABLE_V06
-#endif
/* qxl.c */
void *qxl_phys2virt(PCIQXLDevice *qxl, QXLPHYSICAL phys, int group_id);
@@ -118,7 +133,7 @@ void qxl_spice_update_area(PCIQXLDevice *qxl, uint32_t surface_id,
struct QXLRect *area, struct QXLRect *dirty_rects,
uint32_t num_dirty_rects,
uint32_t clear_dirty_region,
- qxl_async_io async);
+ qxl_async_io async, QXLCookie *cookie);
void qxl_spice_loadvm_commands(PCIQXLDevice *qxl, struct QXLCommandExt *ext,
uint32_t count);
void qxl_spice_oom(PCIQXLDevice *qxl);
@@ -134,9 +149,5 @@ void qxl_log_command(PCIQXLDevice *qxl, const char *ring, QXLCommandExt *ext);
void qxl_render_resize(PCIQXLDevice *qxl);
void qxl_render_update(PCIQXLDevice *qxl);
void qxl_render_cursor(PCIQXLDevice *qxl, QXLCommandExt *ext);
-#if SPICE_INTERFACE_QXL_MINOR >= 1
-void qxl_spice_update_area_async(PCIQXLDevice *qxl, uint32_t surface_id,
- struct QXLRect *area,
- uint32_t clear_dirty_region,
- int is_vga);
-#endif
+void qxl_render_update_area_done(PCIQXLDevice *qxl, QXLCookie *cookie);
+void qxl_render_update_area_bh(void *opaque);
diff --git a/hw/realview.c b/hw/realview.c
index 8b0b03d2d3..ae1bbcdac3 100644
--- a/hw/realview.c
+++ b/hw/realview.c
@@ -96,7 +96,7 @@ static TypeInfo realview_i2c_info = {
.class_init = realview_i2c_class_init,
};
-static void realview_register_devices(void)
+static void realview_register_types(void)
{
type_register_static(&realview_i2c_info);
}
@@ -222,21 +222,23 @@ static void realview_init(ram_addr_t ram_size,
sysbus_mmio_map(sysbus_from_qdev(sysctl), 0, 0x10000000);
if (is_mpcore) {
+ target_phys_addr_t periphbase;
dev = qdev_create(NULL, is_pb ? "a9mpcore_priv": "realview_mpcore");
qdev_prop_set_uint32(dev, "num-cpu", smp_cpus);
qdev_init_nofail(dev);
busdev = sysbus_from_qdev(dev);
if (is_pb) {
- realview_binfo.smp_priv_base = 0x1f000000;
+ periphbase = 0x1f000000;
} else {
- realview_binfo.smp_priv_base = 0x10100000;
+ periphbase = 0x10100000;
}
- sysbus_mmio_map(busdev, 0, realview_binfo.smp_priv_base);
+ sysbus_mmio_map(busdev, 0, periphbase);
for (n = 0; n < smp_cpus; n++) {
sysbus_connect_irq(busdev, n, cpu_irq[n]);
}
- sysbus_create_varargs("l2x0", realview_binfo.smp_priv_base + 0x2000,
- NULL);
+ sysbus_create_varargs("l2x0", periphbase + 0x2000, NULL);
+ /* Both A9 and 11MPCore put the GIC CPU i/f at base + 0x100 */
+ realview_binfo.gic_cpu_if_addr = periphbase + 0x100;
} else {
uint32_t gic_addr = is_pb ? 0x1e000000 : 0x10040000;
/* For now just create the nIRQ GIC, and ignore the others. */
@@ -491,4 +493,4 @@ static void realview_machine_init(void)
}
machine_init(realview_machine_init);
-device_init(realview_register_devices)
+type_init(realview_register_types)
diff --git a/hw/realview_gic.c b/hw/realview_gic.c
index 4121502c7b..071ef13c9e 100644
--- a/hw/realview_gic.c
+++ b/hw/realview_gic.c
@@ -60,9 +60,9 @@ static TypeInfo realview_gic_info = {
.class_init = realview_gic_class_init,
};
-static void realview_gic_register_devices(void)
+static void realview_gic_register_types(void)
{
type_register_static(&realview_gic_info);
}
-device_init(realview_gic_register_devices)
+type_init(realview_gic_register_types)
diff --git a/hw/rtl8139.c b/hw/rtl8139.c
index 1668390e1f..05b8e1e3d7 100644
--- a/hw/rtl8139.c
+++ b/hw/rtl8139.c
@@ -3523,9 +3523,9 @@ static TypeInfo rtl8139_info = {
.class_init = rtl8139_class_init,
};
-static void rtl8139_register_devices(void)
+static void rtl8139_register_types(void)
{
type_register_static(&rtl8139_info);
}
-device_init(rtl8139_register_devices)
+type_init(rtl8139_register_types)
diff --git a/hw/s390-virtio-bus.c b/hw/s390-virtio-bus.c
index 49140f8a9e..c450e4bb5b 100644
--- a/hw/s390-virtio-bus.c
+++ b/hw/s390-virtio-bus.c
@@ -169,6 +169,18 @@ static int s390_virtio_serial_init(VirtIOS390Device *dev)
return r;
}
+static int s390_virtio_scsi_init(VirtIOS390Device *dev)
+{
+ VirtIODevice *vdev;
+
+ vdev = virtio_scsi_init((DeviceState *)dev, &dev->scsi);
+ if (!vdev) {
+ return -1;
+ }
+
+ return s390_virtio_device_init(dev, vdev);
+}
+
static uint64_t s390_virtio_device_vq_token(VirtIOS390Device *dev, int vq)
{
ram_addr_t token_off;
@@ -433,15 +445,26 @@ static TypeInfo virtio_s390_device_info = {
.abstract = true,
};
-static void s390_virtio_register(void)
+static Property s390_virtio_scsi_properties[] = {
+ DEFINE_VIRTIO_SCSI_PROPERTIES(VirtIOS390Device, host_features, scsi),
+ DEFINE_PROP_END_OF_LIST(),
+};
+
+static void s390_virtio_scsi_class_init(ObjectClass *klass, void *data)
{
- type_register_static(&virtio_s390_device_info);
- type_register_static(&s390_virtio_serial);
- type_register_static(&s390_virtio_blk);
- type_register_static(&s390_virtio_net);
+ DeviceClass *dc = DEVICE_CLASS(klass);
+ VirtIOS390DeviceClass *k = VIRTIO_S390_DEVICE_CLASS(klass);
+
+ k->init = s390_virtio_scsi_init;
+ dc->props = s390_virtio_scsi_properties;
}
-device_init(s390_virtio_register);
+static TypeInfo s390_virtio_scsi = {
+ .name = "virtio-scsi-s390",
+ .parent = TYPE_VIRTIO_S390_DEVICE,
+ .instance_size = sizeof(VirtIOS390Device),
+ .class_init = s390_virtio_scsi_class_init,
+};
/***************** S390 Virtio Bus Bridge Device *******************/
/* Only required to have the virtio bus as child in the system bus */
@@ -468,9 +491,14 @@ static TypeInfo s390_virtio_bridge_info = {
.class_init = s390_virtio_bridge_class_init,
};
-static void s390_virtio_register_devices(void)
+static void s390_virtio_register_types(void)
{
+ type_register_static(&virtio_s390_device_info);
+ type_register_static(&s390_virtio_serial);
+ type_register_static(&s390_virtio_blk);
+ type_register_static(&s390_virtio_net);
+ type_register_static(&s390_virtio_scsi);
type_register_static(&s390_virtio_bridge_info);
}
-device_init(s390_virtio_register_devices)
+type_init(s390_virtio_register_types)
diff --git a/hw/s390-virtio-bus.h b/hw/s390-virtio-bus.h
index b5e59b7d4b..0e60bc0fa2 100644
--- a/hw/s390-virtio-bus.h
+++ b/hw/s390-virtio-bus.h
@@ -19,6 +19,7 @@
#include "virtio-net.h"
#include "virtio-serial.h"
+#include "virtio-scsi.h"
#define VIRTIO_DEV_OFFS_TYPE 0 /* 8 bits */
#define VIRTIO_DEV_OFFS_NUM_VQ 1 /* 8 bits */
@@ -67,6 +68,7 @@ struct VirtIOS390Device {
uint32_t host_features;
virtio_serial_conf serial;
virtio_net_conf net;
+ VirtIOSCSIConf scsi;
};
typedef struct VirtIOS390Bus {
diff --git a/hw/sb16.c b/hw/sb16.c
index db8929b98e..c81455d7f3 100644
--- a/hw/sb16.c
+++ b/hw/sb16.c
@@ -1417,8 +1417,9 @@ static TypeInfo sb16_info = {
.class_init = sb16_class_initfn,
};
-static void sb16_register (void)
+static void sb16_register_types (void)
{
type_register_static (&sb16_info);
}
-device_init (sb16_register)
+
+type_init (sb16_register_types)
diff --git a/hw/sbi.c b/hw/sbi.c
index 847a4dde84..52982a9a3e 100644
--- a/hw/sbi.c
+++ b/hw/sbi.c
@@ -148,9 +148,9 @@ static TypeInfo sbi_info = {
.class_init = sbi_class_init,
};
-static void sbi_register_devices(void)
+static void sbi_register_types(void)
{
type_register_static(&sbi_info);
}
-device_init(sbi_register_devices)
+type_init(sbi_register_types)
diff --git a/hw/scsi-bus.c b/hw/scsi-bus.c
index 0ee50a8360..2cb5a18da2 100644
--- a/hw/scsi-bus.c
+++ b/hw/scsi-bus.c
@@ -5,6 +5,7 @@
#include "qdev.h"
#include "blockdev.h"
#include "trace.h"
+#include "dma.h"
static char *scsibus_get_fw_dev_path(DeviceState *dev);
static int scsi_req_parse(SCSICommand *cmd, SCSIDevice *dev, uint8_t *buf);
@@ -86,6 +87,7 @@ static void scsi_dma_restart_bh(void *opaque)
scsi_req_continue(req);
break;
case SCSI_XFER_NONE:
+ assert(!req->sg);
scsi_req_dequeue(req);
scsi_req_enqueue(req);
break;
@@ -130,6 +132,10 @@ static int scsi_qdev_init(DeviceState *qdev)
error_report("bad scsi device id: %d", dev->id);
goto err;
}
+ if (dev->lun != -1 && dev->lun > bus->info->max_lun) {
+ error_report("bad scsi device lun: %d", dev->lun);
+ goto err;
+ }
if (dev->id == -1) {
int id = -1;
@@ -138,8 +144,8 @@ static int scsi_qdev_init(DeviceState *qdev)
}
do {
d = scsi_device_find(bus, dev->channel, ++id, dev->lun);
- } while (d && d->lun == dev->lun && id <= bus->info->max_target);
- if (id > bus->info->max_target) {
+ } while (d && d->lun == dev->lun && id < bus->info->max_target);
+ if (d && d->lun == dev->lun) {
error_report("no free target");
goto err;
}
@@ -149,14 +155,15 @@ static int scsi_qdev_init(DeviceState *qdev)
do {
d = scsi_device_find(bus, dev->channel, dev->id, ++lun);
} while (d && d->lun == lun && lun < bus->info->max_lun);
- if (lun > bus->info->max_lun) {
+ if (d && d->lun == lun) {
error_report("no free lun");
goto err;
}
dev->lun = lun;
} else {
d = scsi_device_find(bus, dev->channel, dev->id, dev->lun);
- if (dev->lun == d->lun && dev != d) {
+ assert(d);
+ if (d->lun == dev->lun && dev != d) {
qdev_free(&d->qdev);
}
}
@@ -215,7 +222,7 @@ int scsi_bus_legacy_handle_cmdline(SCSIBus *bus)
int res = 0, unit;
loc_push_none(&loc);
- for (unit = 0; unit < bus->info->max_target; unit++) {
+ for (unit = 0; unit <= bus->info->max_target; unit++) {
dinfo = drive_get(IF_SCSI, bus->busnr, unit);
if (dinfo == NULL) {
continue;
@@ -378,7 +385,7 @@ static bool scsi_target_emulate_inquiry(SCSITargetReq *r)
/* PAGE CODE == 0 */
if (r->req.cmd.xfer < 5) {
- return -1;
+ return false;
}
r->len = MIN(r->req.cmd.xfer, 36);
@@ -533,6 +540,8 @@ SCSIRequest *scsi_req_new(SCSIDevice *d, uint32_t tag, uint32_t lun,
}
req->cmd = cmd;
+ req->resid = req->cmd.xfer;
+
switch (buf[0]) {
case INQUIRY:
trace_scsi_inquiry(d->id, lun, tag, cmd.buf[1], cmd.buf[2]);
@@ -643,15 +652,25 @@ void scsi_req_build_sense(SCSIRequest *req, SCSISense sense)
req->sense_len = 18;
}
-int32_t scsi_req_enqueue(SCSIRequest *req)
+static void scsi_req_enqueue_internal(SCSIRequest *req)
{
- int32_t rc;
-
assert(!req->enqueued);
scsi_req_ref(req);
+ if (req->bus->info->get_sg_list) {
+ req->sg = req->bus->info->get_sg_list(req);
+ } else {
+ req->sg = NULL;
+ }
req->enqueued = true;
QTAILQ_INSERT_TAIL(&req->dev->requests, req, next);
+}
+int32_t scsi_req_enqueue(SCSIRequest *req)
+{
+ int32_t rc;
+
+ assert(!req->retry);
+ scsi_req_enqueue_internal(req);
scsi_req_ref(req);
rc = req->ops->send_command(req, req->cmd.buf);
scsi_req_unref(req);
@@ -1273,12 +1292,32 @@ void scsi_req_continue(SCSIRequest *req)
Once it completes, calling scsi_req_continue will restart I/O. */
void scsi_req_data(SCSIRequest *req, int len)
{
+ uint8_t *buf;
if (req->io_canceled) {
trace_scsi_req_data_canceled(req->dev->id, req->lun, req->tag, len);
- } else {
- trace_scsi_req_data(req->dev->id, req->lun, req->tag, len);
+ return;
+ }
+ trace_scsi_req_data(req->dev->id, req->lun, req->tag, len);
+ assert(req->cmd.mode != SCSI_XFER_NONE);
+ if (!req->sg) {
+ req->resid -= len;
req->bus->info->transfer_data(req, len);
+ return;
}
+
+ /* If the device calls scsi_req_data and the HBA specified a
+ * scatter/gather list, the transfer has to happen in a single
+ * step. */
+ assert(!req->dma_started);
+ req->dma_started = true;
+
+ buf = scsi_req_get_buf(req);
+ if (req->cmd.mode == SCSI_XFER_FROM_DEV) {
+ req->resid = dma_buf_read(buf, len, req->sg);
+ } else {
+ req->resid = dma_buf_write(buf, len, req->sg);
+ }
+ scsi_req_continue(req);
}
void scsi_req_print(SCSIRequest *req)
@@ -1337,7 +1376,7 @@ void scsi_req_complete(SCSIRequest *req, int status)
scsi_req_ref(req);
scsi_req_dequeue(req);
- req->bus->info->complete(req, req->status);
+ req->bus->info->complete(req, req->status, req->resid);
scsi_req_unref(req);
}
@@ -1413,6 +1452,102 @@ SCSIDevice *scsi_device_find(SCSIBus *bus, int channel, int id, int lun)
return target_dev;
}
+/* SCSI request list. For simplicity, pv points to the whole device */
+
+static void put_scsi_requests(QEMUFile *f, void *pv, size_t size)
+{
+ SCSIDevice *s = pv;
+ SCSIBus *bus = DO_UPCAST(SCSIBus, qbus, s->qdev.parent_bus);
+ SCSIRequest *req;
+
+ QTAILQ_FOREACH(req, &s->requests, next) {
+ assert(!req->io_canceled);
+ assert(req->status == -1);
+ assert(req->retry);
+ assert(req->enqueued);
+
+ qemu_put_sbyte(f, 1);
+ qemu_put_buffer(f, req->cmd.buf, sizeof(req->cmd.buf));
+ qemu_put_be32s(f, &req->tag);
+ qemu_put_be32s(f, &req->lun);
+ if (bus->info->save_request) {
+ bus->info->save_request(f, req);
+ }
+ if (req->ops->save_request) {
+ req->ops->save_request(f, req);
+ }
+ }
+ qemu_put_sbyte(f, 0);
+}
+
+static int get_scsi_requests(QEMUFile *f, void *pv, size_t size)
+{
+ SCSIDevice *s = pv;
+ SCSIBus *bus = DO_UPCAST(SCSIBus, qbus, s->qdev.parent_bus);
+
+ while (qemu_get_sbyte(f)) {
+ uint8_t buf[SCSI_CMD_BUF_SIZE];
+ uint32_t tag;
+ uint32_t lun;
+ SCSIRequest *req;
+
+ qemu_get_buffer(f, buf, sizeof(buf));
+ qemu_get_be32s(f, &tag);
+ qemu_get_be32s(f, &lun);
+ req = scsi_req_new(s, tag, lun, buf, NULL);
+ if (bus->info->load_request) {
+ req->hba_private = bus->info->load_request(f, req);
+ }
+ if (req->ops->load_request) {
+ req->ops->load_request(f, req);
+ }
+
+ /* Just restart it later. */
+ req->retry = true;
+ scsi_req_enqueue_internal(req);
+
+ /* At this point, the request will be kept alive by the reference
+ * added by scsi_req_enqueue_internal, so we can release our reference.
+ * The HBA of course will add its own reference in the load_request
+ * callback if it needs to hold on the SCSIRequest.
+ */
+ scsi_req_unref(req);
+ }
+
+ return 0;
+}
+
+const VMStateInfo vmstate_info_scsi_requests = {
+ .name = "scsi-requests",
+ .get = get_scsi_requests,
+ .put = put_scsi_requests,
+};
+
+const VMStateDescription vmstate_scsi_device = {
+ .name = "SCSIDevice",
+ .version_id = 1,
+ .minimum_version_id = 1,
+ .minimum_version_id_old = 1,
+ .fields = (VMStateField[]) {
+ VMSTATE_UINT8(unit_attention.key, SCSIDevice),
+ VMSTATE_UINT8(unit_attention.asc, SCSIDevice),
+ VMSTATE_UINT8(unit_attention.ascq, SCSIDevice),
+ VMSTATE_BOOL(sense_is_ua, SCSIDevice),
+ VMSTATE_UINT8_ARRAY(sense, SCSIDevice, SCSI_SENSE_BUF_SIZE),
+ VMSTATE_UINT32(sense_len, SCSIDevice),
+ {
+ .name = "requests",
+ .version_id = 0,
+ .field_exists = NULL,
+ .size = 0, /* ouch */
+ .info = &vmstate_info_scsi_requests,
+ .flags = VMS_SINGLE,
+ .offset = 0,
+ },
+ VMSTATE_END_OF_LIST()
+ }
+};
+
static void scsi_device_class_init(ObjectClass *klass, void *data)
{
DeviceClass *k = DEVICE_CLASS(klass);
@@ -1431,9 +1566,9 @@ static TypeInfo scsi_device_type_info = {
.class_init = scsi_device_class_init,
};
-static void scsi_register_devices(void)
+static void scsi_register_types(void)
{
type_register_static(&scsi_device_type_info);
}
-device_init(scsi_register_devices);
+type_init(scsi_register_types)
diff --git a/hw/scsi-disk.c b/hw/scsi-disk.c
index 399e51e494..add399e97b 100644
--- a/hw/scsi-disk.c
+++ b/hw/scsi-disk.c
@@ -38,6 +38,7 @@ do { fprintf(stderr, "scsi-disk: " fmt , ## __VA_ARGS__); } while (0)
#include "sysemu.h"
#include "blockdev.h"
#include "block_int.h"
+#include "dma.h"
#ifdef __linux
#include <scsi/sg.h>
@@ -110,12 +111,12 @@ static void scsi_cancel_io(SCSIRequest *req)
r->req.aiocb = NULL;
}
-static uint32_t scsi_init_iovec(SCSIDiskReq *r)
+static uint32_t scsi_init_iovec(SCSIDiskReq *r, size_t size)
{
SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, r->req.dev);
if (!r->iov.iov_base) {
- r->buflen = SCSI_DMA_BUF_SIZE;
+ r->buflen = size;
r->iov.iov_base = qemu_blockalign(s->qdev.conf.bs, r->buflen);
}
r->iov.iov_len = MIN(r->sector_count * 512, r->buflen);
@@ -123,6 +124,56 @@ static uint32_t scsi_init_iovec(SCSIDiskReq *r)
return r->qiov.size / 512;
}
+static void scsi_disk_save_request(QEMUFile *f, SCSIRequest *req)
+{
+ SCSIDiskReq *r = DO_UPCAST(SCSIDiskReq, req, req);
+
+ qemu_put_be64s(f, &r->sector);
+ qemu_put_be32s(f, &r->sector_count);
+ qemu_put_be32s(f, &r->buflen);
+ if (r->buflen && r->req.cmd.mode == SCSI_XFER_TO_DEV) {
+ qemu_put_buffer(f, r->iov.iov_base, r->iov.iov_len);
+ }
+}
+
+static void scsi_disk_load_request(QEMUFile *f, SCSIRequest *req)
+{
+ SCSIDiskReq *r = DO_UPCAST(SCSIDiskReq, req, req);
+
+ qemu_get_be64s(f, &r->sector);
+ qemu_get_be32s(f, &r->sector_count);
+ qemu_get_be32s(f, &r->buflen);
+ if (r->buflen) {
+ scsi_init_iovec(r, r->buflen);
+ if (r->req.cmd.mode == SCSI_XFER_TO_DEV) {
+ qemu_get_buffer(f, r->iov.iov_base, r->iov.iov_len);
+ }
+ }
+
+ qemu_iovec_init_external(&r->qiov, &r->iov, 1);
+}
+
+static void scsi_dma_complete(void *opaque, int ret)
+{
+ SCSIDiskReq *r = (SCSIDiskReq *)opaque;
+ SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, r->req.dev);
+
+ bdrv_acct_done(s->qdev.conf.bs, &r->acct);
+
+ if (ret) {
+ if (scsi_handle_rw_error(r, -ret)) {
+ goto done;
+ }
+ }
+
+ r->sector += r->sector_count;
+ r->sector_count = 0;
+ scsi_req_complete(&r->req, GOOD);
+
+done:
+ scsi_req_unref(&r->req);
+}
+
static void scsi_read_complete(void * opaque, int ret)
{
SCSIDiskReq *r = (SCSIDiskReq *)opaque;
@@ -213,10 +264,17 @@ static void scsi_read_data(SCSIRequest *req)
return;
}
- n = scsi_init_iovec(r);
- bdrv_acct_start(s->qdev.conf.bs, &r->acct, n * BDRV_SECTOR_SIZE, BDRV_ACCT_READ);
- r->req.aiocb = bdrv_aio_readv(s->qdev.conf.bs, r->sector, &r->qiov, n,
- scsi_read_complete, r);
+ if (r->req.sg) {
+ dma_acct_start(s->qdev.conf.bs, &r->acct, r->req.sg, BDRV_ACCT_READ);
+ r->req.resid -= r->req.sg->size;
+ r->req.aiocb = dma_bdrv_read(s->qdev.conf.bs, r->req.sg, r->sector,
+ scsi_dma_complete, r);
+ } else {
+ n = scsi_init_iovec(r, SCSI_DMA_BUF_SIZE);
+ bdrv_acct_start(s->qdev.conf.bs, &r->acct, n * BDRV_SECTOR_SIZE, BDRV_ACCT_READ);
+ r->req.aiocb = bdrv_aio_readv(s->qdev.conf.bs, r->sector, &r->qiov, n,
+ scsi_read_complete, r);
+ }
}
/*
@@ -233,14 +291,14 @@ static int scsi_handle_rw_error(SCSIDiskReq *r, int error)
BlockErrorAction action = bdrv_get_on_error(s->qdev.conf.bs, is_read);
if (action == BLOCK_ERR_IGNORE) {
- bdrv_mon_event(s->qdev.conf.bs, BDRV_ACTION_IGNORE, is_read);
+ bdrv_emit_qmp_error_event(s->qdev.conf.bs, BDRV_ACTION_IGNORE, is_read);
return 0;
}
if ((error == ENOSPC && action == BLOCK_ERR_STOP_ENOSPC)
|| action == BLOCK_ERR_STOP_ANY) {
- bdrv_mon_event(s->qdev.conf.bs, BDRV_ACTION_STOP, is_read);
+ bdrv_emit_qmp_error_event(s->qdev.conf.bs, BDRV_ACTION_STOP, is_read);
vm_stop(RUN_STATE_IO_ERROR);
bdrv_iostatus_set_err(s->qdev.conf.bs, error);
scsi_req_retry(&r->req);
@@ -259,7 +317,7 @@ static int scsi_handle_rw_error(SCSIDiskReq *r, int error)
scsi_check_condition(r, SENSE_CODE(IO_ERROR));
break;
}
- bdrv_mon_event(s->qdev.conf.bs, BDRV_ACTION_REPORT, is_read);
+ bdrv_emit_qmp_error_event(s->qdev.conf.bs, BDRV_ACTION_REPORT, is_read);
}
return 1;
}
@@ -287,7 +345,7 @@ static void scsi_write_complete(void * opaque, int ret)
if (r->sector_count == 0) {
scsi_req_complete(&r->req, GOOD);
} else {
- scsi_init_iovec(r);
+ scsi_init_iovec(r, SCSI_DMA_BUF_SIZE);
DPRINTF("Write complete tag=0x%x more=%d\n", r->req.tag, r->qiov.size);
scsi_req_data(&r->req, r->qiov.size);
}
@@ -315,18 +373,26 @@ static void scsi_write_data(SCSIRequest *req)
return;
}
- n = r->qiov.size / 512;
- if (n) {
- if (s->tray_open) {
- scsi_write_complete(r, -ENOMEDIUM);
- return;
- }
+ if (!r->req.sg && !r->qiov.size) {
+ /* Called for the first time. Ask the driver to send us more data. */
+ scsi_write_complete(r, 0);
+ return;
+ }
+ if (s->tray_open) {
+ scsi_write_complete(r, -ENOMEDIUM);
+ return;
+ }
+
+ if (r->req.sg) {
+ dma_acct_start(s->qdev.conf.bs, &r->acct, r->req.sg, BDRV_ACCT_WRITE);
+ r->req.resid -= r->req.sg->size;
+ r->req.aiocb = dma_bdrv_write(s->qdev.conf.bs, r->req.sg, r->sector,
+ scsi_dma_complete, r);
+ } else {
+ n = r->qiov.size / 512;
bdrv_acct_start(s->qdev.conf.bs, &r->acct, n * BDRV_SECTOR_SIZE, BDRV_ACCT_WRITE);
r->req.aiocb = bdrv_aio_writev(s->qdev.conf.bs, r->sector, &r->qiov, n,
scsi_write_complete, r);
- } else {
- /* Called for the first time. Ask the driver to send us more data. */
- scsi_write_complete(r, 0);
}
}
@@ -1050,8 +1116,11 @@ static int scsi_disk_emulate_start_stop(SCSIDiskReq *r)
: SENSE_CODE(NOT_READY_REMOVAL_PREVENTED));
return -1;
}
- bdrv_eject(s->qdev.conf.bs, !start);
- s->tray_open = !start;
+
+ if (s->tray_open != !start) {
+ bdrv_eject(s->qdev.conf.bs, !start);
+ s->tray_open = !start;
+ }
}
return 0;
}
@@ -1584,6 +1653,8 @@ static const SCSIReqOps scsi_disk_reqops = {
.write_data = scsi_write_data,
.cancel_io = scsi_cancel_io,
.get_buf = scsi_get_buf,
+ .load_request = scsi_disk_load_request,
+ .save_request = scsi_disk_save_request,
};
static SCSIRequest *scsi_new_request(SCSIDevice *d, uint32_t tag, uint32_t lun,
@@ -1686,6 +1757,15 @@ static SCSIRequest *scsi_block_new_request(SCSIDevice *d, uint32_t tag,
case WRITE_VERIFY_10:
case WRITE_VERIFY_12:
case WRITE_VERIFY_16:
+ /* If we are not using O_DIRECT, we might read stale data from the
+ * host cache if writes were made using other commands than these
+ * ones (such as WRITE SAME or EXTENDED COPY, etc.). So, without
+ * O_DIRECT everything must go through SG_IO.
+ */
+ if (!(s->qdev.conf.bs->open_flags & BDRV_O_NOCACHE)) {
+ break;
+ }
+
/* MMC writing cannot be done via pread/pwrite, because it sometimes
* involves writing beyond the maximum LBA or to negative LBA (lead-in).
* And once you do these writes, reading from the block device is
@@ -1696,10 +1776,11 @@ static SCSIRequest *scsi_block_new_request(SCSIDevice *d, uint32_t tag,
* seen, but performance usually isn't paramount on optical media. So,
* just make scsi-block operate the same as scsi-generic for them.
*/
- if (s->qdev.type != TYPE_ROM) {
- return scsi_req_alloc(&scsi_disk_reqops, &s->qdev, tag, lun,
- hba_private);
- }
+ if (s->qdev.type == TYPE_ROM) {
+ break;
+ }
+ return scsi_req_alloc(&scsi_disk_reqops, &s->qdev, tag, lun,
+ hba_private);
}
return scsi_req_alloc(&scsi_generic_req_ops, &s->qdev, tag, lun,
@@ -1718,6 +1799,22 @@ static Property scsi_hd_properties[] = {
DEFINE_PROP_END_OF_LIST(),
};
+static const VMStateDescription vmstate_scsi_disk_state = {
+ .name = "scsi-disk",
+ .version_id = 1,
+ .minimum_version_id = 1,
+ .minimum_version_id_old = 1,
+ .fields = (VMStateField[]) {
+ VMSTATE_SCSI_DEVICE(qdev, SCSIDiskState),
+ VMSTATE_BOOL(media_changed, SCSIDiskState),
+ VMSTATE_BOOL(media_event, SCSIDiskState),
+ VMSTATE_BOOL(eject_request, SCSIDiskState),
+ VMSTATE_BOOL(tray_open, SCSIDiskState),
+ VMSTATE_BOOL(tray_locked, SCSIDiskState),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
static void scsi_hd_class_initfn(ObjectClass *klass, void *data)
{
DeviceClass *dc = DEVICE_CLASS(klass);
@@ -1731,6 +1828,7 @@ static void scsi_hd_class_initfn(ObjectClass *klass, void *data)
dc->desc = "virtual SCSI disk";
dc->reset = scsi_disk_reset;
dc->props = scsi_hd_properties;
+ dc->vmsd = &vmstate_scsi_disk_state;
}
static TypeInfo scsi_hd_info = {
@@ -1758,6 +1856,7 @@ static void scsi_cd_class_initfn(ObjectClass *klass, void *data)
dc->desc = "virtual SCSI CD-ROM";
dc->reset = scsi_disk_reset;
dc->props = scsi_cd_properties;
+ dc->vmsd = &vmstate_scsi_disk_state;
}
static TypeInfo scsi_cd_info = {
@@ -1785,6 +1884,7 @@ static void scsi_block_class_initfn(ObjectClass *klass, void *data)
dc->desc = "SCSI block device passthrough";
dc->reset = scsi_disk_reset;
dc->props = scsi_block_properties;
+ dc->vmsd = &vmstate_scsi_disk_state;
}
static TypeInfo scsi_block_info = {
@@ -1814,6 +1914,7 @@ static void scsi_disk_class_initfn(ObjectClass *klass, void *data)
dc->desc = "virtual SCSI disk or CD-ROM (legacy)";
dc->reset = scsi_disk_reset;
dc->props = scsi_disk_properties;
+ dc->vmsd = &vmstate_scsi_disk_state;
}
static TypeInfo scsi_disk_info = {
@@ -1823,7 +1924,7 @@ static TypeInfo scsi_disk_info = {
.class_init = scsi_disk_class_initfn,
};
-static void scsi_disk_register_devices(void)
+static void scsi_disk_register_types(void)
{
type_register_static(&scsi_hd_info);
type_register_static(&scsi_cd_info);
@@ -1832,4 +1933,5 @@ static void scsi_disk_register_devices(void)
#endif
type_register_static(&scsi_disk_info);
}
-device_init(scsi_disk_register_devices)
+
+type_init(scsi_disk_register_types)
diff --git a/hw/scsi-generic.c b/hw/scsi-generic.c
index 4859212734..d856d23b3b 100644
--- a/hw/scsi-generic.c
+++ b/hw/scsi-generic.c
@@ -59,6 +59,28 @@ typedef struct SCSIGenericReq {
sg_io_hdr_t io_header;
} SCSIGenericReq;
+static void scsi_generic_save_request(QEMUFile *f, SCSIRequest *req)
+{
+ SCSIGenericReq *r = DO_UPCAST(SCSIGenericReq, req, req);
+
+ qemu_put_sbe32s(f, &r->buflen);
+ if (r->buflen && r->req.cmd.mode == SCSI_XFER_TO_DEV) {
+ assert(!r->req.sg);
+ qemu_put_buffer(f, r->buf, r->req.cmd.xfer);
+ }
+}
+
+static void scsi_generic_load_request(QEMUFile *f, SCSIRequest *req)
+{
+ SCSIGenericReq *r = DO_UPCAST(SCSIGenericReq, req, req);
+
+ qemu_get_sbe32s(f, &r->buflen);
+ if (r->buflen && r->req.cmd.mode == SCSI_XFER_TO_DEV) {
+ assert(!r->req.sg);
+ qemu_get_buffer(f, r->buf, r->req.cmd.xfer);
+ }
+}
+
static void scsi_free_request(SCSIRequest *req)
{
SCSIGenericReq *r = DO_UPCAST(SCSIGenericReq, req, req);
@@ -446,6 +468,8 @@ const SCSIReqOps scsi_generic_req_ops = {
.write_data = scsi_write_data,
.cancel_io = scsi_cancel_io,
.get_buf = scsi_get_buf,
+ .load_request = scsi_generic_load_request,
+ .save_request = scsi_generic_save_request,
};
static SCSIRequest *scsi_new_request(SCSIDevice *d, uint32_t tag, uint32_t lun,
@@ -474,6 +498,7 @@ static void scsi_generic_class_initfn(ObjectClass *klass, void *data)
dc->desc = "pass through generic scsi device (/dev/sg*)";
dc->reset = scsi_generic_reset;
dc->props = scsi_generic_properties;
+ dc->vmsd = &vmstate_scsi_device;
}
static TypeInfo scsi_generic_info = {
@@ -483,10 +508,11 @@ static TypeInfo scsi_generic_info = {
.class_init = scsi_generic_class_initfn,
};
-static void scsi_generic_register_devices(void)
+static void scsi_generic_register_types(void)
{
type_register_static(&scsi_generic_info);
}
-device_init(scsi_generic_register_devices)
+
+type_init(scsi_generic_register_types)
#endif /* __linux__ */
diff --git a/hw/scsi.h b/hw/scsi.h
index dc72b6fc1e..2eb66f7393 100644
--- a/hw/scsi.h
+++ b/hw/scsi.h
@@ -46,8 +46,11 @@ struct SCSIRequest {
uint32_t tag;
uint32_t lun;
uint32_t status;
+ size_t resid;
SCSICommand cmd;
BlockDriverAIOCB *aiocb;
+ QEMUSGList *sg;
+ bool dma_started;
uint8_t sense[SCSI_SENSE_BUF_SIZE];
uint32_t sense_len;
bool enqueued;
@@ -93,6 +96,16 @@ struct SCSIDevice
uint64_t max_lba;
};
+extern const VMStateDescription vmstate_scsi_device;
+
+#define VMSTATE_SCSI_DEVICE(_field, _state) { \
+ .name = (stringify(_field)), \
+ .size = sizeof(SCSIDevice), \
+ .vmsd = &vmstate_scsi_device, \
+ .flags = VMS_STRUCT, \
+ .offset = vmstate_offset_value(_state, _field, SCSIDevice), \
+}
+
/* cdrom.c */
int cdrom_read_toc(int nb_sectors, uint8_t *buf, int msf, int start_track);
int cdrom_read_toc_raw(int nb_sectors, uint8_t *buf, int msf, int session_num);
@@ -106,14 +119,21 @@ struct SCSIReqOps {
void (*write_data)(SCSIRequest *req);
void (*cancel_io)(SCSIRequest *req);
uint8_t *(*get_buf)(SCSIRequest *req);
+
+ void (*save_request)(QEMUFile *f, SCSIRequest *req);
+ void (*load_request)(QEMUFile *f, SCSIRequest *req);
};
struct SCSIBusInfo {
int tcq;
int max_channel, max_target, max_lun;
void (*transfer_data)(SCSIRequest *req, uint32_t arg);
- void (*complete)(SCSIRequest *req, uint32_t arg);
+ void (*complete)(SCSIRequest *req, uint32_t arg, size_t resid);
void (*cancel)(SCSIRequest *req);
+ QEMUSGList *(*get_sg_list)(SCSIRequest *req);
+
+ void (*save_request)(QEMUFile *f, SCSIRequest *req);
+ void *(*load_request)(QEMUFile *f, SCSIRequest *req);
};
struct SCSIBus {
diff --git a/hw/serial.c b/hw/serial.c
index 82917e24d7..c0ee55d20c 100644
--- a/hw/serial.c
+++ b/hw/serial.c
@@ -139,6 +139,7 @@ struct SerialState {
int it_shift;
int baudbase;
int tsr_retry;
+ uint32_t wakeup;
uint64_t last_xmit_ts; /* Time when the last byte was successfully sent out of the tsr */
SerialFIFO recv_fifo;
@@ -635,6 +636,10 @@ static int serial_can_receive1(void *opaque)
static void serial_receive1(void *opaque, const uint8_t *buf, int size)
{
SerialState *s = opaque;
+
+ if (s->wakeup) {
+ qemu_system_wakeup_request(QEMU_WAKEUP_REASON_OTHER);
+ }
if(s->fcr & UART_FCR_FE) {
int i;
for (i = 0; i < size; i++) {
@@ -884,6 +889,7 @@ static Property serial_isa_properties[] = {
DEFINE_PROP_HEX32("iobase", ISASerialState, iobase, -1),
DEFINE_PROP_UINT32("irq", ISASerialState, isairq, -1),
DEFINE_PROP_CHR("chardev", ISASerialState, state.chr),
+ DEFINE_PROP_UINT32("wakeup", ISASerialState, state.wakeup, 0),
DEFINE_PROP_END_OF_LIST(),
};
@@ -903,9 +909,9 @@ static TypeInfo serial_isa_info = {
.class_init = serial_isa_class_initfn,
};
-static void serial_register_devices(void)
+static void serial_register_types(void)
{
type_register_static(&serial_isa_info);
}
-device_init(serial_register_devices)
+type_init(serial_register_types)
diff --git a/hw/sga.c b/hw/sga.c
index b08e3c5aea..a666349f82 100644
--- a/hw/sga.c
+++ b/hw/sga.c
@@ -55,9 +55,9 @@ static TypeInfo sga_info = {
.class_init = sga_class_initfn,
};
-static void sga_register(void)
+static void sga_register_types(void)
{
type_register_static(&sga_info);
}
-device_init(sga_register);
+type_init(sga_register_types)
diff --git a/hw/sh_pci.c b/hw/sh_pci.c
index 4234d935e7..0cfac46f7f 100644
--- a/hw/sh_pci.c
+++ b/hw/sh_pci.c
@@ -177,10 +177,10 @@ static TypeInfo sh_pci_device_info = {
.class_init = sh_pci_device_class_init,
};
-static void sh_pci_register_devices(void)
+static void sh_pci_register_types(void)
{
type_register_static(&sh_pci_device_info);
type_register_static(&sh_pci_host_info);
}
-device_init(sh_pci_register_devices)
+type_init(sh_pci_register_types)
diff --git a/hw/slavio_intctl.c b/hw/slavio_intctl.c
index e3701c75d2..7fdc3be086 100644
--- a/hw/slavio_intctl.c
+++ b/hw/slavio_intctl.c
@@ -463,9 +463,9 @@ static TypeInfo slavio_intctl_info = {
.class_init = slavio_intctl_class_init,
};
-static void slavio_intctl_register_devices(void)
+static void slavio_intctl_register_types(void)
{
type_register_static(&slavio_intctl_info);
}
-device_init(slavio_intctl_register_devices)
+type_init(slavio_intctl_register_types)
diff --git a/hw/slavio_misc.c b/hw/slavio_misc.c
index 5a025186af..944835e880 100644
--- a/hw/slavio_misc.c
+++ b/hw/slavio_misc.c
@@ -499,10 +499,10 @@ static TypeInfo apc_info = {
.class_init = apc_class_init,
};
-static void slavio_misc_register_devices(void)
+static void slavio_misc_register_types(void)
{
type_register_static(&slavio_misc_info);
type_register_static(&apc_info);
}
-device_init(slavio_misc_register_devices)
+type_init(slavio_misc_register_types)
diff --git a/hw/slavio_timer.c b/hw/slavio_timer.c
index 3878f6fa5e..97edebb3ba 100644
--- a/hw/slavio_timer.c
+++ b/hw/slavio_timer.c
@@ -427,9 +427,9 @@ static TypeInfo slavio_timer_info = {
.class_init = slavio_timer_class_init,
};
-static void slavio_timer_register_devices(void)
+static void slavio_timer_register_types(void)
{
type_register_static(&slavio_timer_info);
}
-device_init(slavio_timer_register_devices)
+type_init(slavio_timer_register_types)
diff --git a/hw/smbus.c b/hw/smbus.c
index 77626f3645..e3cf6a2cc8 100644
--- a/hw/smbus.c
+++ b/hw/smbus.c
@@ -327,9 +327,9 @@ static TypeInfo smbus_device_type_info = {
.class_init = smbus_device_class_init,
};
-static void smbus_device_register_devices(void)
+static void smbus_device_register_types(void)
{
type_register_static(&smbus_device_type_info);
}
-device_init(smbus_device_register_devices);
+type_init(smbus_device_register_types)
diff --git a/hw/smbus_eeprom.c b/hw/smbus_eeprom.c
index 9d96cbe41a..11adab01b8 100644
--- a/hw/smbus_eeprom.c
+++ b/hw/smbus_eeprom.c
@@ -130,12 +130,12 @@ static TypeInfo smbus_eeprom_info = {
.class_init = smbus_eeprom_class_initfn,
};
-static void smbus_eeprom_register_devices(void)
+static void smbus_eeprom_register_types(void)
{
type_register_static(&smbus_eeprom_info);
}
-device_init(smbus_eeprom_register_devices)
+type_init(smbus_eeprom_register_types)
void smbus_eeprom_init(i2c_bus *smbus, int nb_eeprom,
const uint8_t *eeprom_spd, int eeprom_spd_size)
diff --git a/hw/smc91c111.c b/hw/smc91c111.c
index 1bf2901d1a..1a5213fa56 100644
--- a/hw/smc91c111.c
+++ b/hw/smc91c111.c
@@ -781,7 +781,7 @@ static TypeInfo smc91c111_info = {
.class_init = smc91c111_class_init,
};
-static void smc91c111_register_devices(void)
+static void smc91c111_register_types(void)
{
type_register_static(&smc91c111_info);
}
@@ -802,4 +802,4 @@ void smc91c111_init(NICInfo *nd, uint32_t base, qemu_irq irq)
sysbus_connect_irq(s, 0, irq);
}
-device_init(smc91c111_register_devices)
+type_init(smc91c111_register_types)
diff --git a/hw/spapr_hcall.c b/hw/spapr_hcall.c
index 84281be9e2..6ac7384013 100644
--- a/hw/spapr_hcall.c
+++ b/hw/spapr_hcall.c
@@ -672,7 +672,7 @@ target_ulong spapr_hypercall(CPUState *env, target_ulong opcode,
return H_FUNCTION;
}
-static void hypercall_init(void)
+static void hypercall_register_types(void)
{
/* hcall-pft */
spapr_register_hypercall(H_ENTER, h_enter);
@@ -704,4 +704,5 @@ static void hypercall_init(void)
/* qemu/KVM-PPC specific hcalls */
spapr_register_hypercall(KVMPPC_H_RTAS, h_rtas);
}
-device_init(hypercall_init);
+
+type_init(hypercall_register_types)
diff --git a/hw/spapr_llan.c b/hw/spapr_llan.c
index 79b394132b..77d40479c8 100644
--- a/hw/spapr_llan.c
+++ b/hw/spapr_llan.c
@@ -501,7 +501,7 @@ static TypeInfo spapr_vlan_info = {
.class_init = spapr_vlan_class_init,
};
-static void spapr_vlan_register(void)
+static void spapr_vlan_register_types(void)
{
spapr_register_hypercall(H_REGISTER_LOGICAL_LAN, h_register_logical_lan);
spapr_register_hypercall(H_FREE_LOGICAL_LAN, h_free_logical_lan);
@@ -511,4 +511,5 @@ static void spapr_vlan_register(void)
spapr_register_hypercall(H_MULTICAST_CTRL, h_multicast_ctrl);
type_register_static(&spapr_vlan_info);
}
-device_init(spapr_vlan_register);
+
+type_init(spapr_vlan_register_types)
diff --git a/hw/spapr_pci.c b/hw/spapr_pci.c
index ed2e4b33e7..cfdd9ddd41 100644
--- a/hw/spapr_pci.c
+++ b/hw/spapr_pci.c
@@ -242,13 +242,13 @@ static TypeInfo spapr_phb_info = {
.class_init = spapr_phb_class_init,
};
-static void spapr_register_devices(void)
+static void spapr_register_types(void)
{
type_register_static(&spapr_phb_info);
type_register_static(&spapr_main_pci_host_info);
}
-device_init(spapr_register_devices)
+type_init(spapr_register_types)
static uint64_t spapr_io_read(void *opaque, target_phys_addr_t addr,
unsigned size)
diff --git a/hw/spapr_rtas.c b/hw/spapr_rtas.c
index d1ac74cfbd..c0723b3039 100644
--- a/hw/spapr_rtas.c
+++ b/hw/spapr_rtas.c
@@ -288,7 +288,7 @@ int spapr_rtas_device_tree_setup(void *fdt, target_phys_addr_t rtas_addr,
return 0;
}
-static void register_core_rtas(void)
+static void core_rtas_register_types(void)
{
spapr_rtas_register("display-character", rtas_display_character);
spapr_rtas_register("get-time-of-day", rtas_get_time_of_day);
@@ -298,4 +298,5 @@ static void register_core_rtas(void)
rtas_query_cpu_stopped_state);
spapr_rtas_register("start-cpu", rtas_start_cpu);
}
-device_init(register_core_rtas);
+
+type_init(core_rtas_register_types)
diff --git a/hw/spapr_vio.c b/hw/spapr_vio.c
index 64f0009814..ea317efbe4 100644
--- a/hw/spapr_vio.c
+++ b/hw/spapr_vio.c
@@ -778,13 +778,13 @@ static TypeInfo spapr_vio_type_info = {
.class_init = vio_spapr_device_class_init,
};
-static void spapr_vio_register_devices(void)
+static void spapr_vio_register_types(void)
{
type_register_static(&spapr_vio_bridge_info);
type_register_static(&spapr_vio_type_info);
}
-device_init(spapr_vio_register_devices)
+type_init(spapr_vio_register_types)
#ifdef CONFIG_FDT
static int compare_reg(const void *p1, const void *p2)
diff --git a/hw/spapr_vscsi.c b/hw/spapr_vscsi.c
index 9cfce19a73..21670170e8 100644
--- a/hw/spapr_vscsi.c
+++ b/hw/spapr_vscsi.c
@@ -494,7 +494,7 @@ static void vscsi_transfer_data(SCSIRequest *sreq, uint32_t len)
}
/* Callback to indicate that the SCSI layer has completed a transfer. */
-static void vscsi_command_complete(SCSIRequest *sreq, uint32_t status)
+static void vscsi_command_complete(SCSIRequest *sreq, uint32_t status, size_t resid)
{
VSCSIState *s = DO_UPCAST(VSCSIState, vdev.qdev, sreq->bus->qbus.parent);
vscsi_req *req = sreq->hba_private;
@@ -973,8 +973,9 @@ static TypeInfo spapr_vscsi_info = {
.class_init = spapr_vscsi_class_init,
};
-static void spapr_vscsi_register(void)
+static void spapr_vscsi_register_types(void)
{
type_register_static(&spapr_vscsi_info);
}
-device_init(spapr_vscsi_register);
+
+type_init(spapr_vscsi_register_types)
diff --git a/hw/spapr_vty.c b/hw/spapr_vty.c
index a954e7d29b..3efe24211e 100644
--- a/hw/spapr_vty.c
+++ b/hw/spapr_vty.c
@@ -212,10 +212,11 @@ static VIOsPAPRDevice *vty_lookup(sPAPREnvironment *spapr, target_ulong reg)
return sdev;
}
-static void spapr_vty_register(void)
+static void spapr_vty_register_types(void)
{
spapr_register_hypercall(H_PUT_TERM_CHAR, h_put_term_char);
spapr_register_hypercall(H_GET_TERM_CHAR, h_get_term_char);
type_register_static(&spapr_vty_info);
}
-device_init(spapr_vty_register);
+
+type_init(spapr_vty_register_types)
diff --git a/hw/sparc32_dma.c b/hw/sparc32_dma.c
index f07cc6fdf0..1dbf69e808 100644
--- a/hw/sparc32_dma.c
+++ b/hw/sparc32_dma.c
@@ -307,9 +307,9 @@ static TypeInfo sparc32_dma_info = {
.class_init = sparc32_dma_class_init,
};
-static void sparc32_dma_register_devices(void)
+static void sparc32_dma_register_types(void)
{
type_register_static(&sparc32_dma_info);
}
-device_init(sparc32_dma_register_devices)
+type_init(sparc32_dma_register_types)
diff --git a/hw/spitz.c b/hw/spitz.c
index 4e6540d976..1d6d2b0e1b 100644
--- a/hw/spitz.c
+++ b/hw/spitz.c
@@ -1138,7 +1138,7 @@ static TypeInfo spitz_lcdtg_info = {
.class_init = spitz_lcdtg_class_init,
};
-static void spitz_register_devices(void)
+static void spitz_register_types(void)
{
type_register_static(&corgi_ssp_info);
type_register_static(&spitz_lcdtg_info);
@@ -1146,4 +1146,4 @@ static void spitz_register_devices(void)
type_register_static(&sl_nand_info);
}
-device_init(spitz_register_devices)
+type_init(spitz_register_types)
diff --git a/hw/ssd0303.c b/hw/ssd0303.c
index 685602a584..4e1ee6e12b 100644
--- a/hw/ssd0303.c
+++ b/hw/ssd0303.c
@@ -313,9 +313,9 @@ static TypeInfo ssd0303_info = {
.class_init = ssd0303_class_init,
};
-static void ssd0303_register_devices(void)
+static void ssd0303_register_types(void)
{
type_register_static(&ssd0303_info);
}
-device_init(ssd0303_register_devices)
+type_init(ssd0303_register_types)
diff --git a/hw/ssd0323.c b/hw/ssd0323.c
index 3c43738ae1..b0b2e94a81 100644
--- a/hw/ssd0323.c
+++ b/hw/ssd0323.c
@@ -355,9 +355,9 @@ static TypeInfo ssd0323_info = {
.class_init = ssd0323_class_init,
};
-static void ssd03232_register_devices(void)
+static void ssd03232_register_types(void)
{
type_register_static(&ssd0323_info);
}
-device_init(ssd03232_register_devices)
+type_init(ssd03232_register_types)
diff --git a/hw/ssi-sd.c b/hw/ssi-sd.c
index f2e6cecb71..b519bdb29a 100644
--- a/hw/ssi-sd.c
+++ b/hw/ssi-sd.c
@@ -259,9 +259,9 @@ static TypeInfo ssi_sd_info = {
.class_init = ssi_sd_class_init,
};
-static void ssi_sd_register_devices(void)
+static void ssi_sd_register_types(void)
{
type_register_static(&ssi_sd_info);
}
-device_init(ssi_sd_register_devices)
+type_init(ssi_sd_register_types)
diff --git a/hw/ssi.c b/hw/ssi.c
index ead446c49a..8f2d9bc034 100644
--- a/hw/ssi.c
+++ b/hw/ssi.c
@@ -80,9 +80,9 @@ uint32_t ssi_transfer(SSIBus *bus, uint32_t val)
return ssc->transfer(slave, val);
}
-static void register_ssi_slave(void)
+static void ssi_slave_register_types(void)
{
type_register_static(&ssi_slave_info);
}
-device_init(register_ssi_slave);
+type_init(ssi_slave_register_types)
diff --git a/hw/stellaris.c b/hw/stellaris.c
index 31a65cfb77..562fbbf494 100644
--- a/hw/stellaris.c
+++ b/hw/stellaris.c
@@ -1451,7 +1451,7 @@ static TypeInfo stellaris_adc_info = {
.class_init = stellaris_adc_class_init,
};
-static void stellaris_register_devices(void)
+static void stellaris_register_types(void)
{
type_register_static(&stellaris_i2c_info);
type_register_static(&stellaris_gptm_info);
@@ -1459,4 +1459,4 @@ static void stellaris_register_devices(void)
type_register_static(&stellaris_ssi_bus_info);
}
-device_init(stellaris_register_devices)
+type_init(stellaris_register_types)
diff --git a/hw/stellaris_enet.c b/hw/stellaris_enet.c
index 9b1be8dadc..fbe99cb4a9 100644
--- a/hw/stellaris_enet.c
+++ b/hw/stellaris_enet.c
@@ -441,9 +441,9 @@ static TypeInfo stellaris_enet_info = {
.class_init = stellaris_enet_class_init,
};
-static void stellaris_enet_register_devices(void)
+static void stellaris_enet_register_types(void)
{
type_register_static(&stellaris_enet_info);
}
-device_init(stellaris_enet_register_devices)
+type_init(stellaris_enet_register_types)
diff --git a/hw/strongarm.c b/hw/strongarm.c
index 8d2e7eb6fe..4d5b60fd1f 100644
--- a/hw/strongarm.c
+++ b/hw/strongarm.c
@@ -1609,7 +1609,7 @@ StrongARMState *sa1110_init(MemoryRegion *sysmem,
return s;
}
-static void strongarm_register_devices(void)
+static void strongarm_register_types(void)
{
type_register_static(&strongarm_pic_info);
type_register_static(&strongarm_rtc_sysbus_info);
@@ -1618,4 +1618,5 @@ static void strongarm_register_devices(void)
type_register_static(&strongarm_uart_info);
type_register_static(&strongarm_ssp_info);
}
-device_init(strongarm_register_devices)
+
+type_init(strongarm_register_types)
diff --git a/hw/sun4c_intctl.c b/hw/sun4c_intctl.c
index 081d6cc8bd..8dfa5ecab6 100644
--- a/hw/sun4c_intctl.c
+++ b/hw/sun4c_intctl.c
@@ -223,9 +223,9 @@ static TypeInfo sun4c_intctl_info = {
.class_init = sun4c_intctl_class_init,
};
-static void sun4c_intctl_register_devices(void)
+static void sun4c_intctl_register_types(void)
{
type_register_static(&sun4c_intctl_info);
}
-device_init(sun4c_intctl_register_devices)
+type_init(sun4c_intctl_register_types)
diff --git a/hw/sun4m.c b/hw/sun4m.c
index b79d14c35a..99fb219b3a 100644
--- a/hw/sun4m.c
+++ b/hw/sun4m.c
@@ -623,13 +623,6 @@ static TypeInfo idreg_info = {
.class_init = idreg_class_init,
};
-static void idreg_register_devices(void)
-{
- type_register_static(&idreg_info);
-}
-
-device_init(idreg_register_devices);
-
typedef struct AFXState {
SysBusDevice busdev;
MemoryRegion mem;
@@ -672,13 +665,6 @@ static TypeInfo afx_info = {
.class_init = afx_class_init,
};
-static void afx_register_devices(void)
-{
- type_register_static(&afx_info);
-}
-
-device_init(afx_register_devices);
-
typedef struct PROMState {
SysBusDevice busdev;
MemoryRegion prom;
@@ -756,13 +742,6 @@ static TypeInfo prom_info = {
.class_init = prom_class_init,
};
-static void prom_register_devices(void)
-{
- type_register_static(&prom_info);
-}
-
-device_init(prom_register_devices);
-
typedef struct RamDevice
{
SysBusDevice busdev;
@@ -827,13 +806,6 @@ static TypeInfo ram_info = {
.class_init = ram_class_init,
};
-static void ram_register_devices(void)
-{
- type_register_static(&ram_info);
-}
-
-device_init(ram_register_devices);
-
static void cpu_devinit(const char *cpu_model, unsigned int id,
uint64_t prom_addr, qemu_irq **cpu_irqs)
{
@@ -1865,6 +1837,14 @@ static QEMUMachine ss2_machine = {
.use_scsi = 1,
};
+static void sun4m_register_types(void)
+{
+ type_register_static(&idreg_info);
+ type_register_static(&afx_info);
+ type_register_static(&prom_info);
+ type_register_static(&ram_info);
+}
+
static void ss2_machine_init(void)
{
qemu_register_machine(&ss5_machine);
@@ -1881,4 +1861,5 @@ static void ss2_machine_init(void)
qemu_register_machine(&ss2_machine);
}
+type_init(sun4m_register_types)
machine_init(ss2_machine_init);
diff --git a/hw/sun4m_iommu.c b/hw/sun4m_iommu.c
index 727532cb09..ebefa91b7a 100644
--- a/hw/sun4m_iommu.c
+++ b/hw/sun4m_iommu.c
@@ -380,9 +380,9 @@ static TypeInfo iommu_info = {
.class_init = iommu_class_init,
};
-static void iommu_register_devices(void)
+static void iommu_register_types(void)
{
type_register_static(&iommu_info);
}
-device_init(iommu_register_devices)
+type_init(iommu_register_types)
diff --git a/hw/sun4u.c b/hw/sun4u.c
index 79bbd495eb..423108f236 100644
--- a/hw/sun4u.c
+++ b/hw/sun4u.c
@@ -580,13 +580,6 @@ static TypeInfo ebus_info = {
.class_init = ebus_class_init,
};
-static void pci_ebus_register(void)
-{
- type_register_static(&ebus_info);
-}
-
-device_init(pci_ebus_register);
-
typedef struct PROMState {
SysBusDevice busdev;
MemoryRegion prom;
@@ -664,13 +657,6 @@ static TypeInfo prom_info = {
.class_init = prom_class_init,
};
-static void prom_register_devices(void)
-{
- type_register_static(&prom_info);
-}
-
-device_init(prom_register_devices);
-
typedef struct RamDevice
{
@@ -728,13 +714,6 @@ static TypeInfo ram_info = {
.class_init = ram_class_init,
};
-static void ram_register_devices(void)
-{
- type_register_static(&ram_info);
-}
-
-device_init(ram_register_devices);
-
static CPUState *cpu_devinit(const char *cpu_model, const struct hwdef *hwdef)
{
CPUState *env;
@@ -957,6 +936,13 @@ static QEMUMachine niagara_machine = {
.max_cpus = 1, // XXX for now
};
+static void sun4u_register_types(void)
+{
+ type_register_static(&ebus_info);
+ type_register_static(&prom_info);
+ type_register_static(&ram_info);
+}
+
static void sun4u_machine_init(void)
{
qemu_register_machine(&sun4u_machine);
@@ -964,4 +950,5 @@ static void sun4u_machine_init(void)
qemu_register_machine(&niagara_machine);
}
+type_init(sun4u_register_types)
machine_init(sun4u_machine_init);
diff --git a/hw/sysbus.c b/hw/sysbus.c
index 282060abb6..db4efccef4 100644
--- a/hw/sysbus.c
+++ b/hw/sysbus.c
@@ -256,9 +256,9 @@ static TypeInfo sysbus_device_type_info = {
.class_init = sysbus_device_class_init,
};
-static void sysbus_register(void)
+static void sysbus_register_types(void)
{
type_register_static(&sysbus_device_type_info);
}
-device_init(sysbus_register);
+type_init(sysbus_register_types)
diff --git a/hw/tcx.c b/hw/tcx.c
index ceb94c74a7..ac7dcb428c 100644
--- a/hw/tcx.c
+++ b/hw/tcx.c
@@ -56,8 +56,8 @@ typedef struct TCXState {
uint8_t dac_index, dac_state;
} TCXState;
-static void tcx_screen_dump(void *opaque, const char *filename);
-static void tcx24_screen_dump(void *opaque, const char *filename);
+static void tcx_screen_dump(void *opaque, const char *filename, bool cswitch);
+static void tcx24_screen_dump(void *opaque, const char *filename, bool cswitch);
static void tcx_set_dirty(TCXState *s)
{
@@ -574,7 +574,7 @@ static int tcx_init1(SysBusDevice *dev)
return 0;
}
-static void tcx_screen_dump(void *opaque, const char *filename)
+static void tcx_screen_dump(void *opaque, const char *filename, bool cswitch)
{
TCXState *s = opaque;
FILE *f;
@@ -601,7 +601,7 @@ static void tcx_screen_dump(void *opaque, const char *filename)
return;
}
-static void tcx24_screen_dump(void *opaque, const char *filename)
+static void tcx24_screen_dump(void *opaque, const char *filename, bool cswitch)
{
TCXState *s = opaque;
FILE *f;
@@ -664,9 +664,9 @@ static TypeInfo tcx_info = {
.class_init = tcx_class_init,
};
-static void tcx_register_devices(void)
+static void tcx_register_types(void)
{
type_register_static(&tcx_info);
}
-device_init(tcx_register_devices)
+type_init(tcx_register_types)
diff --git a/hw/tmp105.c b/hw/tmp105.c
index a3bdd91d40..8e8dbd94eb 100644
--- a/hw/tmp105.c
+++ b/hw/tmp105.c
@@ -245,9 +245,9 @@ static TypeInfo tmp105_info = {
.class_init = tmp105_class_init,
};
-static void tmp105_register_devices(void)
+static void tmp105_register_types(void)
{
type_register_static(&tmp105_info);
}
-device_init(tmp105_register_devices)
+type_init(tmp105_register_types)
diff --git a/hw/tosa.c b/hw/tosa.c
index c0d401759e..6baa17d6b5 100644
--- a/hw/tosa.c
+++ b/hw/tosa.c
@@ -291,10 +291,10 @@ static TypeInfo tosa_ssp_info = {
.class_init = tosa_ssp_class_init,
};
-static void tosa_register_devices(void)
+static void tosa_register_types(void)
{
type_register_static(&tosa_dac_info);
type_register_static(&tosa_ssp_info);
}
-device_init(tosa_register_devices)
+type_init(tosa_register_types)
diff --git a/hw/tusb6010.c b/hw/tusb6010.c
index 0ade670906..5ba8da6d6a 100644
--- a/hw/tusb6010.c
+++ b/hw/tusb6010.c
@@ -805,9 +805,9 @@ static TypeInfo tusb6010_info = {
.class_init = tusb6010_class_init,
};
-static void tusb6010_register_device(void)
+static void tusb6010_register_types(void)
{
type_register_static(&tusb6010_info);
}
-device_init(tusb6010_register_device)
+type_init(tusb6010_register_types)
diff --git a/hw/twl92230.c b/hw/twl92230.c
index 03fdccc8ce..22da6f8001 100644
--- a/hw/twl92230.c
+++ b/hw/twl92230.c
@@ -61,9 +61,7 @@ typedef struct {
} rtc;
uint16_t rtc_next_vmstate;
qemu_irq out[4];
- qemu_irq *in;
uint8_t pwrbtn_state;
- qemu_irq pwrbtn;
} MenelausState;
static inline void menelaus_update(MenelausState *s)
@@ -186,14 +184,12 @@ static void menelaus_gpio_set(void *opaque, int line, int level)
{
MenelausState *s = (MenelausState *) opaque;
- /* No interrupt generated */
- s->inputs &= ~(1 << line);
- s->inputs |= level << line;
-}
-
-static void menelaus_pwrbtn_set(void *opaque, int line, int level)
-{
- MenelausState *s = (MenelausState *) opaque;
+ if (line < 3) {
+ /* No interrupt generated */
+ s->inputs &= ~(1 << line);
+ s->inputs |= level << line;
+ return;
+ }
if (!s->pwrbtn_state && level) {
s->status |= 1 << 11; /* PSHBTN */
@@ -849,8 +845,9 @@ static int twl92230_init(I2CSlave *i2c)
s->rtc.hz_tm = qemu_new_timer_ms(rt_clock, menelaus_rtc_hz, s);
/* Three output pins plus one interrupt pin. */
qdev_init_gpio_out(&i2c->qdev, s->out, 4);
- qdev_init_gpio_in(&i2c->qdev, menelaus_gpio_set, 3);
- s->pwrbtn = qemu_allocate_irqs(menelaus_pwrbtn_set, s, 1)[0];
+
+ /* Three input pins plus one power-button pin. */
+ qdev_init_gpio_in(&i2c->qdev, menelaus_gpio_set, 4);
menelaus_reset(&s->i2c);
@@ -876,9 +873,9 @@ static TypeInfo twl92230_info = {
.class_init = twl92230_class_init,
};
-static void twl92230_register_devices(void)
+static void twl92230_register_types(void)
{
type_register_static(&twl92230_info);
}
-device_init(twl92230_register_devices)
+type_init(twl92230_register_types)
diff --git a/hw/unin_pci.c b/hw/unin_pci.c
index 17d86aac68..409bcd4cc6 100644
--- a/hw/unin_pci.c
+++ b/hw/unin_pci.c
@@ -467,7 +467,7 @@ static TypeInfo pci_unin_internal_info = {
.class_init = pci_unin_internal_class_init,
};
-static void unin_register_devices(void)
+static void unin_register_types(void)
{
type_register_static(&unin_main_pci_host_info);
type_register_static(&u3_agp_pci_host_info);
@@ -480,4 +480,4 @@ static void unin_register_devices(void)
type_register_static(&pci_unin_internal_info);
}
-device_init(unin_register_devices)
+type_init(unin_register_types)
diff --git a/hw/usb-audio.c b/hw/usb-audio.c
index cd589b718a..fed136117b 100644
--- a/hw/usb-audio.c
+++ b/hw/usb-audio.c
@@ -607,7 +607,7 @@ static int usb_audio_handle_data(USBDevice *dev, USBPacket *p)
switch (p->pid) {
case USB_TOKEN_OUT:
- switch (p->devep) {
+ switch (p->ep->nr) {
case 1:
ret = usb_audio_handle_dataout(s, p);
break;
@@ -624,7 +624,7 @@ fail:
if (ret == USB_RET_STALL && s->debug) {
fprintf(stderr, "usb-audio: failed data transaction: "
"pid 0x%x ep 0x%x len 0x%zx\n",
- p->pid, p->devep, p->iov.size);
+ p->pid, p->ep->nr, p->iov.size);
}
return ret;
}
@@ -691,7 +691,6 @@ static void usb_audio_class_init(ObjectClass *klass, void *data)
k->product_desc = "QEMU USB Audio Interface";
k->usb_desc = &desc_audio;
k->init = usb_audio_initfn;
- k->handle_packet = usb_generic_handle_packet;
k->handle_reset = usb_audio_handle_reset;
k->handle_control = usb_audio_handle_control;
k->handle_data = usb_audio_handle_data;
@@ -706,10 +705,10 @@ static TypeInfo usb_audio_info = {
.class_init = usb_audio_class_init,
};
-static void usb_audio_register_devices(void)
+static void usb_audio_register_types(void)
{
type_register_static(&usb_audio_info);
usb_legacy_register("usb-audio", "audio", NULL);
}
-device_init(usb_audio_register_devices)
+type_init(usb_audio_register_types)
diff --git a/hw/usb-bt.c b/hw/usb-bt.c
index 90c3b0e0eb..23c39ecc23 100644
--- a/hw/usb-bt.c
+++ b/hw/usb-bt.c
@@ -423,7 +423,7 @@ static int usb_bt_handle_data(USBDevice *dev, USBPacket *p)
switch (p->pid) {
case USB_TOKEN_IN:
- switch (p->devep & 0xf) {
+ switch (p->ep->nr) {
case USB_EVT_EP:
ret = usb_bt_fifo_dequeue(&s->evt, p);
break;
@@ -442,7 +442,7 @@ static int usb_bt_handle_data(USBDevice *dev, USBPacket *p)
break;
case USB_TOKEN_OUT:
- switch (p->devep & 0xf) {
+ switch (p->ep->nr) {
case USB_ACL_EP:
usb_bt_fifo_out_enqueue(s, &s->outacl, s->hci->acl_send,
usb_bt_hci_acl_complete, p);
@@ -498,14 +498,14 @@ static int usb_bt_initfn(USBDevice *dev)
return 0;
}
-USBDevice *usb_bt_init(HCIInfo *hci)
+USBDevice *usb_bt_init(USBBus *bus, HCIInfo *hci)
{
USBDevice *dev;
struct USBBtState *s;
if (!hci)
return NULL;
- dev = usb_create_simple(NULL /* FIXME */, "usb-bt-dongle");
+ dev = usb_create_simple(bus, "usb-bt-dongle");
if (!dev) {
return NULL;
}
@@ -535,7 +535,6 @@ static void usb_bt_class_initfn(ObjectClass *klass, void *data)
uc->init = usb_bt_initfn;
uc->product_desc = "QEMU BT dongle";
uc->usb_desc = &desc_bluetooth;
- uc->handle_packet = usb_generic_handle_packet;
uc->handle_reset = usb_bt_handle_reset;
uc->handle_control = usb_bt_handle_control;
uc->handle_data = usb_bt_handle_data;
@@ -550,8 +549,9 @@ static TypeInfo bt_info = {
.class_init = usb_bt_class_initfn,
};
-static void usb_bt_register_devices(void)
+static void usb_bt_register_types(void)
{
type_register_static(&bt_info);
}
-device_init(usb_bt_register_devices)
+
+type_init(usb_bt_register_types)
diff --git a/hw/usb-bus.c b/hw/usb-bus.c
index b753834584..70b7ebc086 100644
--- a/hw/usb-bus.c
+++ b/hw/usb-bus.c
@@ -74,21 +74,21 @@ static int usb_device_init(USBDevice *dev)
return 0;
}
-static void usb_device_handle_destroy(USBDevice *dev)
+USBDevice *usb_device_find_device(USBDevice *dev, uint8_t addr)
{
USBDeviceClass *klass = USB_DEVICE_GET_CLASS(dev);
- if (klass->handle_destroy) {
- klass->handle_destroy(dev);
+ if (klass->find_device) {
+ return klass->find_device(dev, addr);
}
+ return NULL;
}
-int usb_device_handle_packet(USBDevice *dev, USBPacket *p)
+static void usb_device_handle_destroy(USBDevice *dev)
{
USBDeviceClass *klass = USB_DEVICE_GET_CLASS(dev);
- if (klass->handle_packet) {
- return klass->handle_packet(dev, p);
+ if (klass->handle_destroy) {
+ klass->handle_destroy(dev);
}
- return -ENOSYS;
}
void usb_device_cancel_packet(USBDevice *dev, USBPacket *p)
@@ -203,13 +203,14 @@ typedef struct LegacyUSBFactory
{
const char *name;
const char *usbdevice_name;
- USBDevice *(*usbdevice_init)(const char *params);
+ USBDevice *(*usbdevice_init)(USBBus *bus, const char *params);
} LegacyUSBFactory;
static GSList *legacy_usb_factory;
void usb_legacy_register(const char *typename, const char *usbdevice_name,
- USBDevice *(*usbdevice_init)(const char *params))
+ USBDevice *(*usbdevice_init)(USBBus *bus,
+ const char *params))
{
if (usbdevice_name) {
LegacyUSBFactory *f = g_malloc0(sizeof(*f));
@@ -224,17 +225,6 @@ USBDevice *usb_create(USBBus *bus, const char *name)
{
DeviceState *dev;
-#if 1
- /* temporary stopgap until all usb is properly qdev-ified */
- if (!bus) {
- bus = usb_bus_find(-1);
- if (!bus)
- return NULL;
- error_report("%s: no bus specified, using \"%s\" for \"%s\"",
- __FUNCTION__, bus->qbus.name, name);
- }
-#endif
-
dev = qdev_create(&bus->qbus, name);
return USB_DEVICE(dev);
}
@@ -565,7 +555,7 @@ USBDevice *usbdevice_create(const char *cmdline)
}
return usb_create_simple(bus, f->name);
}
- return f->usbdevice_init(params);
+ return f->usbdevice_init(bus, params);
}
static void usb_device_class_init(ObjectClass *klass, void *data)
@@ -586,9 +576,9 @@ static TypeInfo usb_device_type_info = {
.class_init = usb_device_class_init,
};
-static void usb_register_devices(void)
+static void usb_register_types(void)
{
type_register_static(&usb_device_type_info);
}
-device_init(usb_register_devices);
+type_init(usb_register_types)
diff --git a/hw/usb-ccid.c b/hw/usb-ccid.c
index 881da3002e..ce01e343c6 100644
--- a/hw/usb-ccid.c
+++ b/hw/usb-ccid.c
@@ -267,6 +267,7 @@ typedef struct CCIDBus {
*/
typedef struct USBCCIDState {
USBDevice dev;
+ USBEndpoint *intr;
CCIDBus bus;
CCIDCardState *card;
BulkIn bulk_in_pending[BULK_IN_PENDING_NUM]; /* circular */
@@ -446,7 +447,7 @@ static const USBDescDevice desc_device = {
{
.bNumInterfaces = 1,
.bConfigurationValue = 1,
- .bmAttributes = 0xa0,
+ .bmAttributes = 0xe0,
.bMaxPower = 50,
.nif = 1,
.ifs = &desc_iface0,
@@ -839,7 +840,7 @@ static void ccid_on_slot_change(USBCCIDState *s, bool full)
s->bmSlotICCState |= SLOT_0_CHANGED_MASK;
}
s->notify_slot_change = true;
- usb_wakeup(&s->dev);
+ usb_wakeup(s->intr);
}
static void ccid_write_data_block_error(
@@ -995,7 +996,7 @@ static int ccid_handle_data(USBDevice *dev, USBPacket *p)
break;
case USB_TOKEN_IN:
- switch (p->devep & 0xf) {
+ switch (p->ep->nr) {
case CCID_BULK_IN_EP:
if (!p->iov.size) {
ret = USB_RET_NAK;
@@ -1190,6 +1191,7 @@ static int ccid_initfn(USBDevice *dev)
usb_desc_init(dev);
qbus_create_inplace(&s->bus.qbus, &ccid_bus_info, &dev->qdev, NULL);
+ s->intr = usb_ep_get(dev, USB_TOKEN_IN, CCID_INT_IN_EP);
s->bus.qbus.allow_hotplug = 1;
s->card = NULL;
s->migration_state = MIGRATION_NONE;
@@ -1320,7 +1322,6 @@ static void ccid_class_initfn(ObjectClass *klass, void *data)
uc->init = ccid_initfn;
uc->product_desc = "QEMU USB CCID";
uc->usb_desc = &desc_ccid;
- uc->handle_packet = usb_generic_handle_packet;
uc->handle_reset = ccid_handle_reset;
uc->handle_control = ccid_handle_control;
uc->handle_data = ccid_handle_data;
@@ -1354,10 +1355,11 @@ static TypeInfo ccid_card_type_info = {
.class_init = ccid_card_class_init,
};
-static void ccid_register_devices(void)
+static void ccid_register_types(void)
{
type_register_static(&ccid_card_type_info);
type_register_static(&ccid_info);
usb_legacy_register(CCID_DEV_NAME, "ccid", NULL);
}
-device_init(ccid_register_devices)
+
+type_init(ccid_register_types)
diff --git a/hw/usb-desc.c b/hw/usb-desc.c
index 3c3ed6a802..ccf85ade9e 100644
--- a/hw/usb-desc.c
+++ b/hw/usb-desc.c
@@ -536,7 +536,11 @@ int usb_desc_handle_control(USBDevice *dev, USBPacket *p,
break;
case DeviceRequest | USB_REQ_GET_CONFIGURATION:
- data[0] = dev->config->bConfigurationValue;
+ /*
+ * 9.4.2: 0 should be returned if the device is unconfigured, otherwise
+ * the non zero value of bConfigurationValue.
+ */
+ data[0] = dev->config ? dev->config->bConfigurationValue : 0;
ret = 1;
break;
case DeviceOutRequest | USB_REQ_SET_CONFIGURATION:
@@ -544,9 +548,18 @@ int usb_desc_handle_control(USBDevice *dev, USBPacket *p,
trace_usb_set_config(dev->addr, value, ret);
break;
- case DeviceRequest | USB_REQ_GET_STATUS:
+ case DeviceRequest | USB_REQ_GET_STATUS: {
+ const USBDescConfig *config = dev->config ?
+ dev->config : &dev->device->confs[0];
+
data[0] = 0;
- if (dev->config->bmAttributes & 0x40) {
+ /*
+ * Default state: Device behavior when this request is received while
+ * the device is in the Default state is not specified.
+ * We return the same value that a configured device would return if
+ * it used the first configuration.
+ */
+ if (config->bmAttributes & 0x40) {
data[0] |= 1 << USB_DEVICE_SELF_POWERED;
}
if (dev->remote_wakeup) {
@@ -555,6 +568,7 @@ int usb_desc_handle_control(USBDevice *dev, USBPacket *p,
data[1] = 0x00;
ret = 2;
break;
+ }
case DeviceOutRequest | USB_REQ_CLEAR_FEATURE:
if (value == USB_DEVICE_REMOTE_WAKEUP) {
dev->remote_wakeup = 0;
diff --git a/hw/usb-ehci.c b/hw/usb-ehci.c
index 75ef71e69e..afc8ccf458 100644
--- a/hw/usb-ehci.c
+++ b/hw/usb-ehci.c
@@ -715,8 +715,8 @@ static void ehci_queues_rip_device(EHCIState *ehci, USBDevice *dev)
EHCIQueue *q, *tmp;
QTAILQ_FOREACH_SAFE(q, &ehci->queues, next, tmp) {
- if (q->packet.owner == NULL ||
- q->packet.owner->dev != dev) {
+ if (!usb_packet_is_inflight(&q->packet) ||
+ q->packet.ep->dev != dev) {
continue;
}
ehci_free_queue(q);
@@ -765,6 +765,11 @@ static void ehci_detach(USBPort *port)
USBPort *companion = s->companion_ports[port->index];
companion->ops->detach(companion);
companion->dev = NULL;
+ /*
+ * EHCI spec 4.2.2: "When a disconnect occurs... On the event,
+ * the port ownership is returned immediately to the EHCI controller."
+ */
+ *portsc &= ~PORTSC_POWNER;
return;
}
@@ -845,6 +850,26 @@ static int ehci_register_companion(USBBus *bus, USBPort *ports[],
return 0;
}
+static USBDevice *ehci_find_device(EHCIState *ehci, uint8_t addr)
+{
+ USBDevice *dev;
+ USBPort *port;
+ int i;
+
+ for (i = 0; i < NB_PORTS; i++) {
+ port = &ehci->ports[i];
+ if (!(ehci->portsc[i] & PORTSC_PED)) {
+ DPRINTF("Port %d not enabled\n", i);
+ continue;
+ }
+ dev = usb_find_device(port, addr);
+ if (dev != NULL) {
+ return dev;
+ }
+ }
+ return NULL;
+}
+
/* 4.1 host controller initialization */
static void ehci_reset(void *opaque)
{
@@ -883,10 +908,11 @@ static void ehci_reset(void *opaque)
}
if (devs[i] && devs[i]->attached) {
usb_attach(&s->ports[i]);
- usb_send_msg(devs[i], USB_MSG_RESET);
+ usb_device_reset(devs[i]);
}
}
ehci_queues_rip_all(s);
+ qemu_del_timer(s->frame_timer);
}
static uint32_t ehci_mem_readb(void *ptr, target_phys_addr_t addr)
@@ -982,7 +1008,7 @@ static void handle_port_status_write(EHCIState *s, int port, uint32_t val)
if (!(val & PORTSC_PRESET) &&(*portsc & PORTSC_PRESET)) {
trace_usb_ehci_port_reset(port, 0);
if (dev && dev->attached) {
- usb_reset(&s->ports[port]);
+ usb_port_reset(&s->ports[port]);
*portsc &= ~PORTSC_CSC;
}
@@ -1045,7 +1071,7 @@ static void ehci_mem_writel(void *ptr, target_phys_addr_t addr, uint32_t val)
if (val & USBCMD_HCRESET) {
ehci_reset(s);
- val &= ~USBCMD_HCRESET;
+ val = s->usbcmd;
}
/* not supporting dynamic frame list size at the moment */
@@ -1331,10 +1357,9 @@ err:
static int ehci_execute(EHCIQueue *q)
{
- USBPort *port;
USBDevice *dev;
+ USBEndpoint *ep;
int ret;
- int i;
int endp;
int devadr;
@@ -1364,33 +1389,18 @@ static int ehci_execute(EHCIQueue *q)
endp = get_field(q->qh.epchar, QH_EPCHAR_EP);
devadr = get_field(q->qh.epchar, QH_EPCHAR_DEVADDR);
- ret = USB_RET_NODEV;
+ /* TODO: associating device with ehci port */
+ dev = ehci_find_device(q->ehci, devadr);
+ ep = usb_ep_get(dev, q->pid, endp);
- usb_packet_setup(&q->packet, q->pid, devadr, endp);
+ usb_packet_setup(&q->packet, q->pid, ep);
usb_packet_map(&q->packet, &q->sgl);
- // TO-DO: associating device with ehci port
- for(i = 0; i < NB_PORTS; i++) {
- port = &q->ehci->ports[i];
- dev = port->dev;
-
- if (!(q->ehci->portsc[i] &(PORTSC_CONNECT))) {
- DPRINTF("Port %d, no exec, not connected(%08X)\n",
- i, q->ehci->portsc[i]);
- continue;
- }
-
- ret = usb_handle_packet(dev, &q->packet);
-
- DPRINTF("submit: qh %x next %x qtd %x pid %x len %zd "
- "(total %d) endp %x ret %d\n",
- q->qhaddr, q->qh.next, q->qtdaddr, q->pid,
- q->packet.iov.size, q->tbytes, endp, ret);
-
- if (ret != USB_RET_NODEV) {
- break;
- }
- }
+ ret = usb_handle_packet(dev, &q->packet);
+ DPRINTF("submit: qh %x next %x qtd %x pid %x len %zd "
+ "(total %d) endp %x ret %d\n",
+ q->qhaddr, q->qh.next, q->qtdaddr, q->pid,
+ q->packet.iov.size, q->tbytes, endp, ret);
if (ret > BUFF_SIZE) {
fprintf(stderr, "ret from usb_handle_packet > BUFF_SIZE\n");
@@ -1406,10 +1416,10 @@ static int ehci_execute(EHCIQueue *q)
static int ehci_process_itd(EHCIState *ehci,
EHCIitd *itd)
{
- USBPort *port;
USBDevice *dev;
+ USBEndpoint *ep;
int ret;
- uint32_t i, j, len, pid, dir, devaddr, endp;
+ uint32_t i, len, pid, dir, devaddr, endp;
uint32_t pg, off, ptr1, ptr2, max, mult;
dir =(itd->bufptr[1] & ITD_BUFPTR_DIRECTION);
@@ -1447,58 +1457,24 @@ static int ehci_process_itd(EHCIState *ehci,
pid = dir ? USB_TOKEN_IN : USB_TOKEN_OUT;
- usb_packet_setup(&ehci->ipacket, pid, devaddr, endp);
- usb_packet_map(&ehci->ipacket, &ehci->isgl);
-
- ret = USB_RET_NODEV;
- for (j = 0; j < NB_PORTS; j++) {
- port = &ehci->ports[j];
- dev = port->dev;
-
- if (!(ehci->portsc[j] &(PORTSC_CONNECT))) {
- continue;
- }
-
+ dev = ehci_find_device(ehci, devaddr);
+ ep = usb_ep_get(dev, pid, endp);
+ if (ep->type == USB_ENDPOINT_XFER_ISOC) {
+ usb_packet_setup(&ehci->ipacket, pid, ep);
+ usb_packet_map(&ehci->ipacket, &ehci->isgl);
ret = usb_handle_packet(dev, &ehci->ipacket);
-
- if (ret != USB_RET_NODEV) {
- break;
- }
+ assert(ret != USB_RET_ASYNC);
+ usb_packet_unmap(&ehci->ipacket);
+ } else {
+ DPRINTF("ISOCH: attempt to addess non-iso endpoint\n");
+ ret = USB_RET_NAK;
}
-
- usb_packet_unmap(&ehci->ipacket);
qemu_sglist_destroy(&ehci->isgl);
-#if 0
- /* In isoch, there is no facility to indicate a NAK so let's
- * instead just complete a zero-byte transaction. Setting
- * DBERR seems too draconian.
- */
-
- if (ret == USB_RET_NAK) {
- if (ehci->isoch_pause > 0) {
- DPRINTF("ISOCH: received a NAK but paused so returning\n");
- ehci->isoch_pause--;
- return 0;
- } else if (ehci->isoch_pause == -1) {
- DPRINTF("ISOCH: recv NAK & isoch pause inactive, setting\n");
- // Pause frindex for up to 50 msec waiting for data from
- // remote
- ehci->isoch_pause = 50;
- return 0;
- } else {
- DPRINTF("ISOCH: isoch pause timeout! return 0\n");
- ret = 0;
- }
- } else {
- DPRINTF("ISOCH: received ACK, clearing pause\n");
- ehci->isoch_pause = -1;
- }
-#else
if (ret == USB_RET_NAK) {
+ /* no data for us, so do a zero-length transfer */
ret = 0;
}
-#endif
if (ret >= 0) {
if (!dir) {
@@ -1508,11 +1484,27 @@ static int ehci_process_itd(EHCIState *ehci,
/* IN */
set_field(&itd->transact[i], ret, ITD_XACT_LENGTH);
}
-
- if (itd->transact[i] & ITD_XACT_IOC) {
- ehci_record_interrupt(ehci, USBSTS_INT);
+ } else {
+ switch (ret) {
+ default:
+ fprintf(stderr, "Unexpected iso usb result: %d\n", ret);
+ /* Fall through */
+ case USB_RET_NODEV:
+ /* 3.3.2: XACTERR is only allowed on IN transactions */
+ if (dir) {
+ itd->transact[i] |= ITD_XACT_XACTERR;
+ ehci_record_interrupt(ehci, USBSTS_ERRINT);
+ }
+ break;
+ case USB_RET_BABBLE:
+ itd->transact[i] |= ITD_XACT_BABBLE;
+ ehci_record_interrupt(ehci, USBSTS_ERRINT);
+ break;
}
}
+ if (itd->transact[i] & ITD_XACT_IOC) {
+ ehci_record_interrupt(ehci, USBSTS_INT);
+ }
itd->transact[i] &= ~ITD_XACT_ACTIVE;
}
}
@@ -2371,17 +2363,16 @@ static int usb_ehci_initfn(PCIDevice *dev)
memory_region_init_io(&s->mem, &ehci_mem_ops, s, "ehci", MMIO_SIZE);
pci_register_bar(&s->dev, 0, PCI_BASE_ADDRESS_SPACE_MEMORY, &s->mem);
- fprintf(stderr, "*** EHCI support is under development ***\n");
-
return 0;
}
-static void ehci_register(void)
+static void ehci_register_types(void)
{
type_register_static(&ehci_info);
type_register_static(&ich9_ehci_info);
}
-device_init(ehci_register);
+
+type_init(ehci_register_types)
/*
* vim: expandtab ts=4
diff --git a/hw/usb-hid.c b/hw/usb-hid.c
index 3c4e45da70..37bca78eca 100644
--- a/hw/usb-hid.c
+++ b/hw/usb-hid.c
@@ -44,6 +44,7 @@
typedef struct USBHIDState {
USBDevice dev;
+ USBEndpoint *intr;
HIDState hid;
} USBHIDState;
@@ -360,7 +361,7 @@ static void usb_hid_changed(HIDState *hs)
{
USBHIDState *us = container_of(hs, USBHIDState, hid);
- usb_wakeup(&us->dev);
+ usb_wakeup(us->intr);
}
static void usb_hid_handle_reset(USBDevice *dev)
@@ -463,8 +464,11 @@ static int usb_hid_handle_data(USBDevice *dev, USBPacket *p)
switch (p->pid) {
case USB_TOKEN_IN:
- if (p->devep == 1) {
+ if (p->ep->nr == 1) {
int64_t curtime = qemu_get_clock_ns(vm_clock);
+ if (hs->kind == HID_MOUSE || hs->kind == HID_TABLET) {
+ hid_pointer_activate(hs);
+ }
if (!hid_has_events(hs) &&
(!hs->idle || hs->next_idle_clock - curtime > 0)) {
return USB_RET_NAK;
@@ -501,6 +505,7 @@ static int usb_hid_initfn(USBDevice *dev, int kind)
USBHIDState *us = DO_UPCAST(USBHIDState, dev, dev);
usb_desc_init(dev);
+ us->intr = usb_ep_get(dev, USB_TOKEN_IN, 1);
hid_init(&us->hid, kind, usb_hid_changed);
return 0;
}
@@ -557,7 +562,6 @@ static void usb_hid_class_initfn(ObjectClass *klass, void *data)
{
USBDeviceClass *uc = USB_DEVICE_CLASS(klass);
- uc->handle_packet = usb_generic_handle_packet;
uc->handle_reset = usb_hid_handle_reset;
uc->handle_control = usb_hid_handle_control;
uc->handle_data = usb_hid_handle_data;
@@ -621,7 +625,7 @@ static TypeInfo usb_keyboard_info = {
.class_init = usb_keyboard_class_initfn,
};
-static void usb_hid_register_devices(void)
+static void usb_hid_register_types(void)
{
type_register_static(&usb_tablet_info);
usb_legacy_register("usb-tablet", "tablet", NULL);
@@ -630,4 +634,5 @@ static void usb_hid_register_devices(void)
type_register_static(&usb_keyboard_info);
usb_legacy_register("usb-kbd", "keyboard", NULL);
}
-device_init(usb_hid_register_devices)
+
+type_init(usb_hid_register_types)
diff --git a/hw/usb-hub.c b/hw/usb-hub.c
index 956b020eed..a12856e2e9 100644
--- a/hw/usb-hub.c
+++ b/hw/usb-hub.c
@@ -37,6 +37,7 @@ typedef struct USBHubPort {
typedef struct USBHubState {
USBDevice dev;
+ USBEndpoint *intr;
USBHubPort ports[NUM_PORTS];
} USBHubState;
@@ -163,7 +164,7 @@ static void usb_hub_attach(USBPort *port1)
} else {
port->wPortStatus &= ~PORT_STAT_LOW_SPEED;
}
- usb_wakeup(&s->dev);
+ usb_wakeup(s->intr);
}
static void usb_hub_detach(USBPort *port1)
@@ -171,7 +172,7 @@ static void usb_hub_detach(USBPort *port1)
USBHubState *s = port1->opaque;
USBHubPort *port = &s->ports[port1->index];
- usb_wakeup(&s->dev);
+ usb_wakeup(s->intr);
/* Let upstream know the device on this port is gone */
s->dev.port->ops->child_detach(s->dev.port, port1->dev);
@@ -199,7 +200,7 @@ static void usb_hub_wakeup(USBPort *port1)
if (port->wPortStatus & PORT_STAT_SUSPEND) {
port->wPortChange |= PORT_STAT_C_SUSPEND;
- usb_wakeup(&s->dev);
+ usb_wakeup(s->intr);
}
}
@@ -220,6 +221,26 @@ static void usb_hub_complete(USBPort *port, USBPacket *packet)
s->dev.port->ops->complete(s->dev.port, packet);
}
+static USBDevice *usb_hub_find_device(USBDevice *dev, uint8_t addr)
+{
+ USBHubState *s = DO_UPCAST(USBHubState, dev, dev);
+ USBHubPort *port;
+ USBDevice *downstream;
+ int i;
+
+ for (i = 0; i < NUM_PORTS; i++) {
+ port = &s->ports[i];
+ if (!(port->wPortStatus & PORT_STAT_ENABLE)) {
+ continue;
+ }
+ downstream = usb_find_device(&port->port, addr);
+ if (downstream != NULL) {
+ return downstream;
+ }
+ }
+ return NULL;
+}
+
static void usb_hub_handle_reset(USBDevice *dev)
{
USBHubState *s = DO_UPCAST(USBHubState, dev, dev);
@@ -305,7 +326,7 @@ static int usb_hub_handle_control(USBDevice *dev, USBPacket *p,
break;
case PORT_RESET:
if (dev && dev->attached) {
- usb_send_msg(dev, USB_MSG_RESET);
+ usb_device_reset(dev);
port->wPortChange |= PORT_STAT_C_RESET;
/* set enable bit */
port->wPortStatus |= PORT_STAT_ENABLE;
@@ -396,7 +417,7 @@ static int usb_hub_handle_data(USBDevice *dev, USBPacket *p)
switch(p->pid) {
case USB_TOKEN_IN:
- if (p->devep == 1) {
+ if (p->ep->nr == 1) {
USBHubPort *port;
unsigned int status;
uint8_t buf[4];
@@ -435,44 +456,6 @@ static int usb_hub_handle_data(USBDevice *dev, USBPacket *p)
return ret;
}
-static int usb_hub_broadcast_packet(USBHubState *s, USBPacket *p)
-{
- USBHubPort *port;
- USBDevice *dev;
- int i, ret;
-
- for(i = 0; i < NUM_PORTS; i++) {
- port = &s->ports[i];
- dev = port->port.dev;
- if (dev && dev->attached && (port->wPortStatus & PORT_STAT_ENABLE)) {
- ret = usb_handle_packet(dev, p);
- if (ret != USB_RET_NODEV) {
- return ret;
- }
- }
- }
- return USB_RET_NODEV;
-}
-
-static int usb_hub_handle_packet(USBDevice *dev, USBPacket *p)
-{
- USBHubState *s = (USBHubState *)dev;
-
-#if defined(DEBUG) && 0
- printf("usb_hub: pid=0x%x\n", pid);
-#endif
- if (dev->state == USB_STATE_DEFAULT &&
- dev->addr != 0 &&
- p->devaddr != dev->addr &&
- (p->pid == USB_TOKEN_SETUP ||
- p->pid == USB_TOKEN_OUT ||
- p->pid == USB_TOKEN_IN)) {
- /* broadcast the packet to the devices */
- return usb_hub_broadcast_packet(s, p);
- }
- return usb_generic_handle_packet(dev, p);
-}
-
static void usb_hub_handle_destroy(USBDevice *dev)
{
USBHubState *s = (USBHubState *)dev;
@@ -499,6 +482,7 @@ static int usb_hub_initfn(USBDevice *dev)
int i;
usb_desc_init(dev);
+ s->intr = usb_ep_get(dev, USB_TOKEN_IN, 1);
for (i = 0; i < NUM_PORTS; i++) {
port = &s->ports[i];
usb_register_port(usb_bus_from_device(dev),
@@ -541,7 +525,7 @@ static void usb_hub_class_initfn(ObjectClass *klass, void *data)
uc->init = usb_hub_initfn;
uc->product_desc = "QEMU USB Hub";
uc->usb_desc = &desc_hub;
- uc->handle_packet = usb_hub_handle_packet;
+ uc->find_device = usb_hub_find_device;
uc->handle_reset = usb_hub_handle_reset;
uc->handle_control = usb_hub_handle_control;
uc->handle_data = usb_hub_handle_data;
@@ -557,8 +541,9 @@ static TypeInfo hub_info = {
.class_init = usb_hub_class_initfn,
};
-static void usb_hub_register_devices(void)
+static void usb_hub_register_types(void)
{
type_register_static(&hub_info);
}
-device_init(usb_hub_register_devices)
+
+type_init(usb_hub_register_types)
diff --git a/hw/usb-msd.c b/hw/usb-msd.c
index 6153376f3f..c6f08a0313 100644
--- a/hw/usb-msd.c
+++ b/hw/usb-msd.c
@@ -223,7 +223,7 @@ static void usb_msd_transfer_data(SCSIRequest *req, uint32_t len)
}
}
-static void usb_msd_command_complete(SCSIRequest *req, uint32_t status)
+static void usb_msd_command_complete(SCSIRequest *req, uint32_t status, size_t resid)
{
MSDState *s = DO_UPCAST(MSDState, dev.qdev, req->bus->qbus.parent);
USBPacket *p = s->packet;
@@ -341,7 +341,7 @@ static int usb_msd_handle_data(USBDevice *dev, USBPacket *p)
uint32_t tag;
int ret = 0;
struct usb_msd_cbw cbw;
- uint8_t devep = p->devep;
+ uint8_t devep = p->ep->nr;
switch (p->pid) {
case USB_TOKEN_OUT:
@@ -568,7 +568,7 @@ static int usb_msd_initfn(USBDevice *dev)
return 0;
}
-static USBDevice *usb_msd_init(const char *filename)
+static USBDevice *usb_msd_init(USBBus *bus, const char *filename)
{
static int nr=0;
char id[8];
@@ -611,7 +611,7 @@ static USBDevice *usb_msd_init(const char *filename)
}
/* create guest device */
- dev = usb_create(NULL /* FIXME */, "usb-storage");
+ dev = usb_create(bus, "usb-storage");
if (!dev) {
return NULL;
}
@@ -651,7 +651,6 @@ static void usb_msd_class_initfn(ObjectClass *klass, void *data)
uc->init = usb_msd_initfn;
uc->product_desc = "QEMU USB MSD";
uc->usb_desc = &desc;
- uc->handle_packet = usb_generic_handle_packet;
uc->cancel_packet = usb_msd_cancel_io;
uc->handle_attach = usb_desc_attach;
uc->handle_reset = usb_msd_handle_reset;
@@ -669,9 +668,10 @@ static TypeInfo msd_info = {
.class_init = usb_msd_class_initfn,
};
-static void usb_msd_register_devices(void)
+static void usb_msd_register_types(void)
{
type_register_static(&msd_info);
usb_legacy_register("usb-storage", "disk", usb_msd_init);
}
-device_init(usb_msd_register_devices)
+
+type_init(usb_msd_register_types)
diff --git a/hw/usb-musb.c b/hw/usb-musb.c
index 4f528d25e5..820907a9a9 100644
--- a/hw/usb-musb.c
+++ b/hw/usb-musb.c
@@ -605,6 +605,8 @@ static int musb_timeout(int ttype, int speed, int val)
static void musb_packet(MUSBState *s, MUSBEndPoint *ep,
int epnum, int pid, int len, USBCallback cb, int dir)
{
+ USBDevice *dev;
+ USBEndpoint *uep;
int ret;
int idx = epnum && dir;
int ttype;
@@ -622,16 +624,14 @@ static void musb_packet(MUSBState *s, MUSBEndPoint *ep,
ep->delayed_cb[dir] = cb;
/* A wild guess on the FADDR semantics... */
- usb_packet_setup(&ep->packey[dir].p, pid, ep->faddr[idx],
- ep->type[idx] & 0xf);
+ dev = usb_find_device(&s->port, ep->faddr[idx]);
+ uep = usb_ep_get(dev, pid, ep->type[idx] & 0xf);
+ usb_packet_setup(&ep->packey[dir].p, pid, uep);
usb_packet_addbuf(&ep->packey[dir].p, ep->buf[idx], len);
ep->packey[dir].ep = ep;
ep->packey[dir].dir = dir;
- if (s->port.dev)
- ret = usb_handle_packet(s->port.dev, &ep->packey[dir].p);
- else
- ret = USB_RET_NODEV;
+ ret = usb_handle_packet(dev, &ep->packey[dir].p);
if (ret == USB_RET_ASYNC) {
ep->status[dir] = len;
@@ -812,8 +812,8 @@ static void musb_async_cancel_device(MUSBState *s, USBDevice *dev)
for (ep = 0; ep < 16; ep++) {
for (dir = 0; dir < 2; dir++) {
- if (s->ep[ep].packey[dir].p.owner == NULL ||
- s->ep[ep].packey[dir].p.owner->dev != dev) {
+ if (!usb_packet_is_inflight(&s->ep[ep].packey[dir].p) ||
+ s->ep[ep].packey[dir].p.ep->dev != dev) {
continue;
}
usb_cancel_packet(&s->ep[ep].packey[dir].p);
@@ -1310,7 +1310,7 @@ static void musb_writeb(void *opaque, target_phys_addr_t addr, uint32_t value)
s->power = (value & 0xef) | (s->power & 0x10);
/* MGC_M_POWER_RESET is also read-only in Peripheral Mode */
if ((value & MGC_M_POWER_RESET) && s->port.dev) {
- usb_send_msg(s->port.dev, USB_MSG_RESET);
+ usb_device_reset(s->port.dev);
/* Negotiate high-speed operation if MGC_M_POWER_HSENAB is set. */
if ((value & MGC_M_POWER_HSENAB) &&
s->port.dev->speed == USB_SPEED_HIGH)
diff --git a/hw/usb-net.c b/hw/usb-net.c
index e2111413b4..22b82017e3 100644
--- a/hw/usb-net.c
+++ b/hw/usb-net.c
@@ -1210,7 +1210,7 @@ static int usb_net_handle_data(USBDevice *dev, USBPacket *p)
switch(p->pid) {
case USB_TOKEN_IN:
- switch (p->devep) {
+ switch (p->ep->nr) {
case 1:
ret = usb_net_handle_statusin(s, p);
break;
@@ -1225,7 +1225,7 @@ static int usb_net_handle_data(USBDevice *dev, USBPacket *p)
break;
case USB_TOKEN_OUT:
- switch (p->devep) {
+ switch (p->ep->nr) {
case 2:
ret = usb_net_handle_dataout(s, p);
break;
@@ -1243,7 +1243,7 @@ static int usb_net_handle_data(USBDevice *dev, USBPacket *p)
if (ret == USB_RET_STALL)
fprintf(stderr, "usbnet: failed data transaction: "
"pid 0x%x ep 0x%x len 0x%zx\n",
- p->pid, p->devep, p->iov.size);
+ p->pid, p->ep->nr, p->iov.size);
return ret;
}
@@ -1353,7 +1353,7 @@ static int usb_net_initfn(USBDevice *dev)
return 0;
}
-static USBDevice *usb_net_init(const char *cmdline)
+static USBDevice *usb_net_init(USBBus *bus, const char *cmdline)
{
USBDevice *dev;
QemuOpts *opts;
@@ -1371,7 +1371,7 @@ static USBDevice *usb_net_init(const char *cmdline)
return NULL;
}
- dev = usb_create(NULL /* FIXME */, "usb-net");
+ dev = usb_create(bus, "usb-net");
if (!dev) {
return NULL;
}
@@ -1398,7 +1398,6 @@ static void usb_net_class_initfn(ObjectClass *klass, void *data)
uc->init = usb_net_initfn;
uc->product_desc = "QEMU USB Network Interface";
uc->usb_desc = &desc_net;
- uc->handle_packet = usb_generic_handle_packet;
uc->handle_reset = usb_net_handle_reset;
uc->handle_control = usb_net_handle_control;
uc->handle_data = usb_net_handle_data;
@@ -1415,9 +1414,10 @@ static TypeInfo net_info = {
.class_init = usb_net_class_initfn,
};
-static void usb_net_register_devices(void)
+static void usb_net_register_types(void)
{
type_register_static(&net_info);
usb_legacy_register("usb-net", "net", usb_net_init);
}
-device_init(usb_net_register_devices)
+
+type_init(usb_net_register_types)
diff --git a/hw/usb-ohci.c b/hw/usb-ohci.c
index 425030f141..7aa19fe781 100644
--- a/hw/usb-ohci.c
+++ b/hw/usb-ohci.c
@@ -408,6 +408,23 @@ static void ohci_child_detach(USBPort *port1, USBDevice *child)
ohci_async_cancel_device(s, child);
}
+static USBDevice *ohci_find_device(OHCIState *ohci, uint8_t addr)
+{
+ USBDevice *dev;
+ int i;
+
+ for (i = 0; i < ohci->num_ports; i++) {
+ if ((ohci->rhport[i].ctrl & OHCI_PORT_PES) == 0) {
+ continue;
+ }
+ dev = usb_find_device(&ohci->rhport[i].port, addr);
+ if (dev != NULL) {
+ return dev;
+ }
+ }
+ return NULL;
+}
+
/* Reset the controller */
static void ohci_reset(void *opaque)
{
@@ -449,7 +466,7 @@ static void ohci_reset(void *opaque)
port = &ohci->rhport[i];
port->ctrl = 0;
if (port->port.dev && port->port.dev->attached) {
- usb_reset(&port->port);
+ usb_port_reset(&port->port);
}
}
if (ohci->async_td) {
@@ -640,6 +657,7 @@ static int ohci_service_iso_td(OHCIState *ohci, struct ohci_ed *ed,
int ret;
int i;
USBDevice *dev;
+ USBEndpoint *ep;
struct ohci_iso_td iso_td;
uint32_t addr;
uint16_t starting_frame;
@@ -779,20 +797,11 @@ static int ohci_service_iso_td(OHCIState *ohci, struct ohci_ed *ed,
if (completion) {
ret = ohci->usb_packet.result;
} else {
- ret = USB_RET_NODEV;
- for (i = 0; i < ohci->num_ports; i++) {
- dev = ohci->rhport[i].port.dev;
- if ((ohci->rhport[i].ctrl & OHCI_PORT_PES) == 0)
- continue;
- usb_packet_setup(&ohci->usb_packet, pid,
- OHCI_BM(ed->flags, ED_FA),
- OHCI_BM(ed->flags, ED_EN));
- usb_packet_addbuf(&ohci->usb_packet, ohci->usb_buf, len);
- ret = usb_handle_packet(dev, &ohci->usb_packet);
- if (ret != USB_RET_NODEV)
- break;
- }
-
+ dev = ohci_find_device(ohci, OHCI_BM(ed->flags, ED_FA));
+ ep = usb_ep_get(dev, pid, OHCI_BM(ed->flags, ED_EN));
+ usb_packet_setup(&ohci->usb_packet, pid, ep);
+ usb_packet_addbuf(&ohci->usb_packet, ohci->usb_buf, len);
+ ret = usb_handle_packet(dev, &ohci->usb_packet);
if (ret == USB_RET_ASYNC) {
return 1;
}
@@ -880,6 +889,7 @@ static int ohci_service_td(OHCIState *ohci, struct ohci_ed *ed)
int ret;
int i;
USBDevice *dev;
+ USBEndpoint *ep;
struct ohci_td td;
uint32_t addr;
int flag_r;
@@ -972,31 +982,22 @@ static int ohci_service_td(OHCIState *ohci, struct ohci_ed *ed)
ohci->async_td = 0;
ohci->async_complete = 0;
} else {
- ret = USB_RET_NODEV;
- for (i = 0; i < ohci->num_ports; i++) {
- dev = ohci->rhport[i].port.dev;
- if ((ohci->rhport[i].ctrl & OHCI_PORT_PES) == 0)
- continue;
-
- if (ohci->async_td) {
- /* ??? The hardware should allow one active packet per
- endpoint. We only allow one active packet per controller.
- This should be sufficient as long as devices respond in a
- timely manner.
- */
+ if (ohci->async_td) {
+ /* ??? The hardware should allow one active packet per
+ endpoint. We only allow one active packet per controller.
+ This should be sufficient as long as devices respond in a
+ timely manner.
+ */
#ifdef DEBUG_PACKET
- DPRINTF("Too many pending packets\n");
+ DPRINTF("Too many pending packets\n");
#endif
- return 1;
- }
- usb_packet_setup(&ohci->usb_packet, pid,
- OHCI_BM(ed->flags, ED_FA),
- OHCI_BM(ed->flags, ED_EN));
- usb_packet_addbuf(&ohci->usb_packet, ohci->usb_buf, pktlen);
- ret = usb_handle_packet(dev, &ohci->usb_packet);
- if (ret != USB_RET_NODEV)
- break;
+ return 1;
}
+ dev = ohci_find_device(ohci, OHCI_BM(ed->flags, ED_FA));
+ ep = usb_ep_get(dev, pid, OHCI_BM(ed->flags, ED_EN));
+ usb_packet_setup(&ohci->usb_packet, pid, ep);
+ usb_packet_addbuf(&ohci->usb_packet, ohci->usb_buf, pktlen);
+ ret = usb_handle_packet(dev, &ohci->usb_packet);
#ifdef DEBUG_PACKET
DPRINTF("ret=%d\n", ret);
#endif
@@ -1435,7 +1436,7 @@ static void ohci_port_set_status(OHCIState *ohci, int portnum, uint32_t val)
if (ohci_port_set_if_connected(ohci, portnum, val & OHCI_PORT_PRS)) {
DPRINTF("usb-ohci: port %d: RESET\n", portnum);
- usb_send_msg(port->port.dev, USB_MSG_RESET);
+ usb_device_reset(port->port.dev);
port->ctrl &= ~OHCI_PORT_PRS;
/* ??? Should this also set OHCI_PORT_PESC. */
port->ctrl |= OHCI_PORT_PES | OHCI_PORT_PRSC;
@@ -1708,8 +1709,8 @@ static void ohci_mem_write(void *opaque,
static void ohci_async_cancel_device(OHCIState *ohci, USBDevice *dev)
{
if (ohci->async_td &&
- ohci->usb_packet.owner != NULL &&
- ohci->usb_packet.owner->dev == dev) {
+ usb_packet_is_inflight(&ohci->usb_packet) &&
+ ohci->usb_packet.ep->dev == dev) {
usb_cancel_packet(&ohci->usb_packet);
ohci->async_td = 0;
}
@@ -1886,9 +1887,10 @@ static TypeInfo ohci_sysbus_info = {
.class_init = ohci_sysbus_class_init,
};
-static void ohci_register(void)
+static void ohci_register_types(void)
{
type_register_static(&ohci_pci_info);
type_register_static(&ohci_sysbus_info);
}
-device_init(ohci_register);
+
+type_init(ohci_register_types)
diff --git a/hw/usb-serial.c b/hw/usb-serial.c
index c2cb6d24e8..0aae379b20 100644
--- a/hw/usb-serial.c
+++ b/hw/usb-serial.c
@@ -353,7 +353,7 @@ static int usb_serial_handle_data(USBDevice *dev, USBPacket *p)
{
USBSerialState *s = (USBSerialState *)dev;
int i, ret = 0;
- uint8_t devep = p->devep;
+ uint8_t devep = p->ep->nr;
struct iovec *iov;
uint8_t header[2];
int first_len, len;
@@ -492,7 +492,7 @@ static int usb_serial_initfn(USBDevice *dev)
return 0;
}
-static USBDevice *usb_serial_init(const char *filename)
+static USBDevice *usb_serial_init(USBBus *bus, const char *filename)
{
USBDevice *dev;
CharDriverState *cdrv;
@@ -535,7 +535,7 @@ static USBDevice *usb_serial_init(const char *filename)
if (!cdrv)
return NULL;
- dev = usb_create(NULL /* FIXME */, "usb-serial");
+ dev = usb_create(bus, "usb-serial");
if (!dev) {
return NULL;
}
@@ -549,7 +549,7 @@ static USBDevice *usb_serial_init(const char *filename)
return dev;
}
-static USBDevice *usb_braille_init(const char *unused)
+static USBDevice *usb_braille_init(USBBus *bus, const char *unused)
{
USBDevice *dev;
CharDriverState *cdrv;
@@ -558,7 +558,7 @@ static USBDevice *usb_braille_init(const char *unused)
if (!cdrv)
return NULL;
- dev = usb_create(NULL /* FIXME */, "usb-braille");
+ dev = usb_create(bus, "usb-braille");
qdev_prop_set_chr(&dev->qdev, "chardev", cdrv);
qdev_init_nofail(&dev->qdev);
@@ -583,7 +583,6 @@ static void usb_serial_class_initfn(ObjectClass *klass, void *data)
uc->init = usb_serial_initfn;
uc->product_desc = "QEMU USB Serial";
uc->usb_desc = &desc_serial;
- uc->handle_packet = usb_generic_handle_packet;
uc->handle_reset = usb_serial_handle_reset;
uc->handle_control = usb_serial_handle_control;
uc->handle_data = usb_serial_handle_data;
@@ -612,7 +611,6 @@ static void usb_braille_class_initfn(ObjectClass *klass, void *data)
uc->init = usb_serial_initfn;
uc->product_desc = "QEMU USB Braille";
uc->usb_desc = &desc_braille;
- uc->handle_packet = usb_generic_handle_packet;
uc->handle_reset = usb_serial_handle_reset;
uc->handle_control = usb_serial_handle_control;
uc->handle_data = usb_serial_handle_data;
@@ -628,11 +626,12 @@ static TypeInfo braille_info = {
.class_init = usb_braille_class_initfn,
};
-static void usb_serial_register_devices(void)
+static void usb_serial_register_types(void)
{
type_register_static(&serial_info);
usb_legacy_register("usb-serial", "serial", usb_serial_init);
type_register_static(&braille_info);
usb_legacy_register("usb-braille", "braille", usb_braille_init);
}
-device_init(usb_serial_register_devices)
+
+type_init(usb_serial_register_types)
diff --git a/hw/usb-uhci.c b/hw/usb-uhci.c
index cddcc8927a..70e3881321 100644
--- a/hw/usb-uhci.c
+++ b/hw/usb-uhci.c
@@ -73,7 +73,7 @@
#define FRAME_TIMER_FREQ 1000
-#define FRAME_MAX_LOOPS 100
+#define FRAME_MAX_LOOPS 256
#define NB_PORTS 2
@@ -94,33 +94,33 @@ static const char *pid2str(int pid)
#define DPRINTF(...)
#endif
-#ifdef DEBUG_DUMP_DATA
-static void dump_data(USBPacket *p, int ret)
-{
- iov_hexdump(p->iov.iov, p->iov.niov, stderr, "uhci", ret);
-}
-#else
-static void dump_data(USBPacket *p, int ret) {}
-#endif
-
typedef struct UHCIState UHCIState;
+typedef struct UHCIAsync UHCIAsync;
+typedef struct UHCIQueue UHCIQueue;
/*
* Pending async transaction.
* 'packet' must be the first field because completion
* handler does "(UHCIAsync *) pkt" cast.
*/
-typedef struct UHCIAsync {
+
+struct UHCIAsync {
USBPacket packet;
QEMUSGList sgl;
- UHCIState *uhci;
+ UHCIQueue *queue;
QTAILQ_ENTRY(UHCIAsync) next;
uint32_t td;
- uint32_t token;
- int8_t valid;
uint8_t isoc;
uint8_t done;
-} UHCIAsync;
+};
+
+struct UHCIQueue {
+ uint32_t token;
+ UHCIState *uhci;
+ QTAILQ_ENTRY(UHCIQueue) next;
+ QTAILQ_HEAD(, UHCIAsync) asyncs;
+ int8_t valid;
+};
typedef struct UHCIPort {
USBPort port;
@@ -146,7 +146,7 @@ struct UHCIState {
uint32_t pending_int_mask;
/* Active packets */
- QTAILQ_HEAD(,UHCIAsync) async_pending;
+ QTAILQ_HEAD(, UHCIQueue) queues;
uint8_t num_ports_vmstate;
/* Properties */
@@ -166,62 +166,90 @@ typedef struct UHCI_QH {
uint32_t el_link;
} UHCI_QH;
-static UHCIAsync *uhci_async_alloc(UHCIState *s)
+static inline int32_t uhci_queue_token(UHCI_TD *td)
{
- UHCIAsync *async = g_malloc(sizeof(UHCIAsync));
-
- memset(&async->packet, 0, sizeof(async->packet));
- async->uhci = s;
- async->valid = 0;
- async->td = 0;
- async->token = 0;
- async->done = 0;
- async->isoc = 0;
+ /* covers ep, dev, pid -> identifies the endpoint */
+ return td->token & 0x7ffff;
+}
+
+static UHCIQueue *uhci_queue_get(UHCIState *s, UHCI_TD *td)
+{
+ uint32_t token = uhci_queue_token(td);
+ UHCIQueue *queue;
+
+ QTAILQ_FOREACH(queue, &s->queues, next) {
+ if (queue->token == token) {
+ return queue;
+ }
+ }
+
+ queue = g_new0(UHCIQueue, 1);
+ queue->uhci = s;
+ queue->token = token;
+ QTAILQ_INIT(&queue->asyncs);
+ QTAILQ_INSERT_HEAD(&s->queues, queue, next);
+ return queue;
+}
+
+static void uhci_queue_free(UHCIQueue *queue)
+{
+ UHCIState *s = queue->uhci;
+
+ QTAILQ_REMOVE(&s->queues, queue, next);
+ g_free(queue);
+}
+
+static UHCIAsync *uhci_async_alloc(UHCIQueue *queue)
+{
+ UHCIAsync *async = g_new0(UHCIAsync, 1);
+
+ async->queue = queue;
usb_packet_init(&async->packet);
- pci_dma_sglist_init(&async->sgl, &s->dev, 1);
+ pci_dma_sglist_init(&async->sgl, &queue->uhci->dev, 1);
return async;
}
-static void uhci_async_free(UHCIState *s, UHCIAsync *async)
+static void uhci_async_free(UHCIAsync *async)
{
usb_packet_cleanup(&async->packet);
qemu_sglist_destroy(&async->sgl);
g_free(async);
}
-static void uhci_async_link(UHCIState *s, UHCIAsync *async)
+static void uhci_async_link(UHCIAsync *async)
{
- QTAILQ_INSERT_HEAD(&s->async_pending, async, next);
+ UHCIQueue *queue = async->queue;
+ QTAILQ_INSERT_TAIL(&queue->asyncs, async, next);
}
-static void uhci_async_unlink(UHCIState *s, UHCIAsync *async)
+static void uhci_async_unlink(UHCIAsync *async)
{
- QTAILQ_REMOVE(&s->async_pending, async, next);
+ UHCIQueue *queue = async->queue;
+ QTAILQ_REMOVE(&queue->asyncs, async, next);
}
-static void uhci_async_cancel(UHCIState *s, UHCIAsync *async)
+static void uhci_async_cancel(UHCIAsync *async)
{
DPRINTF("uhci: cancel td 0x%x token 0x%x done %u\n",
async->td, async->token, async->done);
if (!async->done)
usb_cancel_packet(&async->packet);
- uhci_async_free(s, async);
+ uhci_async_free(async);
}
/*
* Mark all outstanding async packets as invalid.
* This is used for canceling them when TDs are removed by the HCD.
*/
-static UHCIAsync *uhci_async_validate_begin(UHCIState *s)
+static void uhci_async_validate_begin(UHCIState *s)
{
- UHCIAsync *async;
+ UHCIQueue *queue;
- QTAILQ_FOREACH(async, &s->async_pending, next) {
- async->valid--;
+ QTAILQ_FOREACH(queue, &s->queues, next) {
+ queue->valid--;
}
- return NULL;
}
/*
@@ -229,77 +257,74 @@ static UHCIAsync *uhci_async_validate_begin(UHCIState *s)
*/
static void uhci_async_validate_end(UHCIState *s)
{
- UHCIAsync *curr, *n;
+ UHCIQueue *queue, *n;
+ UHCIAsync *async;
- QTAILQ_FOREACH_SAFE(curr, &s->async_pending, next, n) {
- if (curr->valid > 0) {
+ QTAILQ_FOREACH_SAFE(queue, &s->queues, next, n) {
+ if (queue->valid > 0) {
continue;
}
- uhci_async_unlink(s, curr);
- uhci_async_cancel(s, curr);
+ while (!QTAILQ_EMPTY(&queue->asyncs)) {
+ async = QTAILQ_FIRST(&queue->asyncs);
+ uhci_async_unlink(async);
+ uhci_async_cancel(async);
+ }
+ uhci_queue_free(queue);
}
}
static void uhci_async_cancel_device(UHCIState *s, USBDevice *dev)
{
+ UHCIQueue *queue;
UHCIAsync *curr, *n;
- QTAILQ_FOREACH_SAFE(curr, &s->async_pending, next, n) {
- if (curr->packet.owner == NULL ||
- curr->packet.owner->dev != dev) {
- continue;
+ QTAILQ_FOREACH(queue, &s->queues, next) {
+ QTAILQ_FOREACH_SAFE(curr, &queue->asyncs, next, n) {
+ if (!usb_packet_is_inflight(&curr->packet) ||
+ curr->packet.ep->dev != dev) {
+ continue;
+ }
+ uhci_async_unlink(curr);
+ uhci_async_cancel(curr);
}
- uhci_async_unlink(s, curr);
- uhci_async_cancel(s, curr);
}
}
static void uhci_async_cancel_all(UHCIState *s)
{
+ UHCIQueue *queue;
UHCIAsync *curr, *n;
- QTAILQ_FOREACH_SAFE(curr, &s->async_pending, next, n) {
- uhci_async_unlink(s, curr);
- uhci_async_cancel(s, curr);
+ QTAILQ_FOREACH(queue, &s->queues, next) {
+ QTAILQ_FOREACH_SAFE(curr, &queue->asyncs, next, n) {
+ uhci_async_unlink(curr);
+ uhci_async_cancel(curr);
+ }
}
}
-static UHCIAsync *uhci_async_find_td(UHCIState *s, uint32_t addr, uint32_t token)
+static UHCIAsync *uhci_async_find_td(UHCIState *s, uint32_t addr, UHCI_TD *td)
{
+ uint32_t token = uhci_queue_token(td);
+ UHCIQueue *queue;
UHCIAsync *async;
- UHCIAsync *match = NULL;
- int count = 0;
-
- /*
- * We're looking for the best match here. ie both td addr and token.
- * Otherwise we return last good match. ie just token.
- * It's ok to match just token because it identifies the transaction
- * rather well, token includes: device addr, endpoint, size, etc.
- *
- * Also since we queue async transactions in reverse order by returning
- * last good match we restores the order.
- *
- * It's expected that we wont have a ton of outstanding transactions.
- * If we ever do we'd want to optimize this algorithm.
- */
-
- QTAILQ_FOREACH(async, &s->async_pending, next) {
- if (async->token == token) {
- /* Good match */
- match = async;
- if (async->td == addr) {
- /* Best match */
- break;
- }
+ QTAILQ_FOREACH(queue, &s->queues, next) {
+ if (queue->token == token) {
+ break;
}
- count++;
+ }
+ if (queue == NULL) {
+ return NULL;
}
- if (count > 64)
- fprintf(stderr, "uhci: warning lots of async transactions\n");
+ QTAILQ_FOREACH(async, &queue->asyncs, next) {
+ if (async->td == addr) {
+ return async;
+ }
+ }
- return match;
+ return NULL;
}
static void uhci_update_irq(UHCIState *s)
@@ -342,7 +367,7 @@ static void uhci_reset(void *opaque)
port = &s->ports[i];
port->ctrl = 0x0080;
if (port->port.dev && port->port.dev->attached) {
- usb_reset(&port->port);
+ usb_port_reset(&port->port);
}
}
@@ -440,16 +465,12 @@ static void uhci_ioport_writew(void *opaque, uint32_t addr, uint32_t val)
}
if (val & UHCI_CMD_GRESET) {
UHCIPort *port;
- USBDevice *dev;
int i;
/* send reset on the USB bus */
for(i = 0; i < NB_PORTS; i++) {
port = &s->ports[i];
- dev = port->port.dev;
- if (dev && dev->attached) {
- usb_send_msg(dev, USB_MSG_RESET);
- }
+ usb_device_reset(port->port.dev);
}
uhci_reset(s);
return;
@@ -491,7 +512,7 @@ static void uhci_ioport_writew(void *opaque, uint32_t addr, uint32_t val)
/* port reset */
if ( (val & UHCI_PORT_RESET) &&
!(port->ctrl & UHCI_PORT_RESET) ) {
- usb_send_msg(dev, USB_MSG_RESET);
+ usb_device_reset(dev);
}
}
port->ctrl &= UHCI_PORT_READ_ONLY;
@@ -647,30 +668,22 @@ static void uhci_wakeup(USBPort *port1)
}
}
-static int uhci_broadcast_packet(UHCIState *s, USBPacket *p)
+static USBDevice *uhci_find_device(UHCIState *s, uint8_t addr)
{
- int i, ret;
-
- DPRINTF("uhci: packet enter. pid %s addr 0x%02x ep %d len %zd\n",
- pid2str(p->pid), p->devaddr, p->devep, p->iov.size);
- if (p->pid == USB_TOKEN_OUT || p->pid == USB_TOKEN_SETUP)
- dump_data(p, 0);
+ USBDevice *dev;
+ int i;
- ret = USB_RET_NODEV;
- for (i = 0; i < NB_PORTS && ret == USB_RET_NODEV; i++) {
+ for (i = 0; i < NB_PORTS; i++) {
UHCIPort *port = &s->ports[i];
- USBDevice *dev = port->port.dev;
-
- if (dev && dev->attached && (port->ctrl & UHCI_PORT_EN)) {
- ret = usb_handle_packet(dev, p);
+ if (!(port->ctrl & UHCI_PORT_EN)) {
+ continue;
+ }
+ dev = usb_find_device(&port->port, addr);
+ if (dev != NULL) {
+ return dev;
}
}
-
- DPRINTF("uhci: packet exit. ret %d len %zd\n", ret, p->iov.size);
- if (p->pid == USB_TOKEN_IN && ret > 0)
- dump_data(p, ret);
-
- return ret;
+ return NULL;
}
static void uhci_async_complete(USBPort *port, USBPacket *packet);
@@ -780,79 +793,69 @@ static int uhci_handle_td(UHCIState *s, uint32_t addr, UHCI_TD *td, uint32_t *in
{
UHCIAsync *async;
int len = 0, max_len;
- uint8_t pid, isoc;
- uint32_t token;
+ uint8_t pid;
+ USBDevice *dev;
+ USBEndpoint *ep;
/* Is active ? */
if (!(td->ctrl & TD_CTRL_ACTIVE))
return 1;
- /* token field is not unique for isochronous requests,
- * so use the destination buffer
- */
- if (td->ctrl & TD_CTRL_IOS) {
- token = td->buffer;
- isoc = 1;
- } else {
- token = td->token;
- isoc = 0;
- }
-
- async = uhci_async_find_td(s, addr, token);
+ async = uhci_async_find_td(s, addr, td);
if (async) {
/* Already submitted */
- async->valid = 32;
+ async->queue->valid = 32;
if (!async->done)
return 1;
- uhci_async_unlink(s, async);
+ uhci_async_unlink(async);
goto done;
}
/* Allocate new packet */
- async = uhci_async_alloc(s);
+ async = uhci_async_alloc(uhci_queue_get(s, td));
if (!async)
return 1;
/* valid needs to be large enough to handle 10 frame delay
* for initial isochronous requests
*/
- async->valid = 32;
+ async->queue->valid = 32;
async->td = addr;
- async->token = token;
- async->isoc = isoc;
+ async->isoc = td->ctrl & TD_CTRL_IOS;
max_len = ((td->token >> 21) + 1) & 0x7ff;
pid = td->token & 0xff;
- usb_packet_setup(&async->packet, pid, (td->token >> 8) & 0x7f,
- (td->token >> 15) & 0xf);
+ dev = uhci_find_device(s, (td->token >> 8) & 0x7f);
+ ep = usb_ep_get(dev, pid, (td->token >> 15) & 0xf);
+ usb_packet_setup(&async->packet, pid, ep);
qemu_sglist_add(&async->sgl, td->buffer, max_len);
usb_packet_map(&async->packet, &async->sgl);
switch(pid) {
case USB_TOKEN_OUT:
case USB_TOKEN_SETUP:
- len = uhci_broadcast_packet(s, &async->packet);
+ len = usb_handle_packet(dev, &async->packet);
if (len >= 0)
len = max_len;
break;
case USB_TOKEN_IN:
- len = uhci_broadcast_packet(s, &async->packet);
+ len = usb_handle_packet(dev, &async->packet);
break;
default:
/* invalid pid : frame interrupted */
- uhci_async_free(s, async);
+ uhci_async_free(async);
s->status |= UHCI_STS_HCPERR;
uhci_update_irq(s);
return -1;
}
if (len == USB_RET_ASYNC) {
- uhci_async_link(s, async);
+ uhci_async_link(async);
return 2;
}
@@ -861,14 +864,14 @@ static int uhci_handle_td(UHCIState *s, uint32_t addr, UHCI_TD *td, uint32_t *in
done:
len = uhci_complete_td(s, td, async, int_mask);
usb_packet_unmap(&async->packet);
- uhci_async_free(s, async);
+ uhci_async_free(async);
return len;
}
static void uhci_async_complete(USBPort *port, USBPacket *packet)
{
UHCIAsync *async = container_of(packet, UHCIAsync, packet);
- UHCIState *s = async->uhci;
+ UHCIState *s = async->queue->uhci;
DPRINTF("uhci: async complete. td 0x%x token 0x%x\n", async->td, async->token);
@@ -883,14 +886,14 @@ static void uhci_async_complete(USBPort *port, USBPacket *packet)
le32_to_cpus(&td.token);
le32_to_cpus(&td.buffer);
- uhci_async_unlink(s, async);
+ uhci_async_unlink(async);
uhci_complete_td(s, &td, async, &int_mask);
s->pending_int_mask |= int_mask;
/* update the status bits of the TD */
val = cpu_to_le32(td.ctrl);
pci_dma_write(&s->dev, (link & ~0xf) + 4, &val, sizeof(val));
- uhci_async_free(s, async);
+ uhci_async_free(async);
} else {
async->done = 1;
uhci_process_frame(s);
@@ -939,10 +942,38 @@ static int qhdb_insert(QhDb *db, uint32_t addr)
return 0;
}
+static void uhci_fill_queue(UHCIState *s, UHCI_TD *td)
+{
+ uint32_t int_mask = 0;
+ uint32_t plink = td->link;
+ uint32_t token = uhci_queue_token(td);
+ UHCI_TD ptd;
+ int ret;
+
+ fprintf(stderr, "%s: -- %x\n", __func__, token);
+ while (is_valid(plink)) {
+ pci_dma_read(&s->dev, plink & ~0xf, &ptd, sizeof(ptd));
+ le32_to_cpus(&ptd.link);
+ le32_to_cpus(&ptd.ctrl);
+ le32_to_cpus(&ptd.token);
+ le32_to_cpus(&ptd.buffer);
+ if (!(ptd.ctrl & TD_CTRL_ACTIVE)) {
+ break;
+ }
+ if (uhci_queue_token(&ptd) != token) {
+ break;
+ }
+ ret = uhci_handle_td(s, plink, &ptd, &int_mask);
+ assert(ret == 2); /* got USB_RET_ASYNC */
+ assert(int_mask == 0);
+ plink = ptd.link;
+ }
+}
+
static void uhci_process_frame(UHCIState *s)
{
uint32_t frame_addr, link, old_td_ctrl, val, int_mask;
- uint32_t curr_qh;
+ uint32_t curr_qh, td_count = 0, bytes_count = 0;
int cnt, ret;
UHCI_TD td;
UHCI_QH qh;
@@ -967,13 +998,26 @@ static void uhci_process_frame(UHCIState *s)
if (qhdb_insert(&qhdb, link)) {
/*
* We're going in circles. Which is not a bug because
- * HCD is allowed to do that as part of the BW management.
- * In our case though it makes no sense to spin here. Sync transations
- * are already done, and async completion handler will re-process
- * the frame when something is ready.
+ * HCD is allowed to do that as part of the BW management.
+ *
+ * Stop processing here if
+ * (a) no transaction has been done since we've been
+ * here last time, or
+ * (b) we've reached the usb 1.1 bandwidth, which is
+ * 1280 bytes/frame.
*/
DPRINTF("uhci: detected loop. qh 0x%x\n", link);
- break;
+ if (td_count == 0) {
+ DPRINTF("uhci: no transaction last round, stop\n");
+ break;
+ } else if (bytes_count >= 1280) {
+ DPRINTF("uhci: bandwidth limit reached, stop\n");
+ break;
+ } else {
+ td_count = 0;
+ qhdb_reset(&qhdb);
+ qhdb_insert(&qhdb, link);
+ }
}
pci_dma_read(&s->dev, link & ~0xf, &qh, sizeof(qh));
@@ -1013,47 +1057,62 @@ static void uhci_process_frame(UHCIState *s)
pci_dma_write(&s->dev, (link & ~0xf) + 4, &val, sizeof(val));
}
- if (ret < 0) {
- /* interrupted frame */
- break;
- }
-
- if (ret == 2 || ret == 1) {
- DPRINTF("uhci: TD 0x%x %s. link 0x%x ctrl 0x%x token 0x%x qh 0x%x\n",
- link, ret == 2 ? "pend" : "skip",
- td.link, td.ctrl, td.token, curr_qh);
+ switch (ret) {
+ case -1: /* interrupted frame */
+ goto out;
+ case 1: /* goto next queue */
+ DPRINTF("uhci: TD 0x%x skip. "
+ "link 0x%x ctrl 0x%x token 0x%x qh 0x%x\n",
+ link, td.link, td.ctrl, td.token, curr_qh);
link = curr_qh ? qh.link : td.link;
continue;
- }
- /* completed TD */
+ case 2: /* got USB_RET_ASYNC */
+ DPRINTF("uhci: TD 0x%x async. "
+ "link 0x%x ctrl 0x%x token 0x%x qh 0x%x\n",
+ link, td.link, td.ctrl, td.token, curr_qh);
+ if (is_valid(td.link)) {
+ uhci_fill_queue(s, &td);
+ }
+ link = curr_qh ? qh.link : td.link;
+ continue;
- DPRINTF("uhci: TD 0x%x done. link 0x%x ctrl 0x%x token 0x%x qh 0x%x\n",
- link, td.link, td.ctrl, td.token, curr_qh);
+ case 0: /* completed TD */
+ DPRINTF("uhci: TD 0x%x done. "
+ "link 0x%x ctrl 0x%x token 0x%x qh 0x%x\n",
+ link, td.link, td.ctrl, td.token, curr_qh);
- link = td.link;
+ link = td.link;
+ td_count++;
+ bytes_count += (td.ctrl & 0x7ff) + 1;
- if (curr_qh) {
- /* update QH element link */
- qh.el_link = link;
- val = cpu_to_le32(qh.el_link);
- pci_dma_write(&s->dev, (curr_qh & ~0xf) + 4, &val, sizeof(val));
+ if (curr_qh) {
+ /* update QH element link */
+ qh.el_link = link;
+ val = cpu_to_le32(qh.el_link);
+ pci_dma_write(&s->dev, (curr_qh & ~0xf) + 4, &val, sizeof(val));
- if (!depth_first(link)) {
- /* done with this QH */
+ if (!depth_first(link)) {
+ /* done with this QH */
- DPRINTF("uhci: QH 0x%x done. link 0x%x elink 0x%x\n",
- curr_qh, qh.link, qh.el_link);
+ DPRINTF("uhci: QH 0x%x done. link 0x%x elink 0x%x\n",
+ curr_qh, qh.link, qh.el_link);
- curr_qh = 0;
- link = qh.link;
+ curr_qh = 0;
+ link = qh.link;
+ }
}
+ break;
+
+ default:
+ assert(!"unknown return code");
}
/* go to the next entry */
}
+out:
s->pending_int_mask |= int_mask;
}
@@ -1151,7 +1210,7 @@ static int usb_uhci_common_initfn(PCIDevice *dev)
}
s->frame_timer = qemu_new_timer_ns(vm_clock, uhci_frame_timer, s);
s->num_ports_vmstate = NB_PORTS;
- QTAILQ_INIT(&s->async_pending);
+ QTAILQ_INIT(&s->queues);
qemu_register_reset(uhci_reset, s);
@@ -1321,7 +1380,7 @@ static TypeInfo ich9_uhci3_info = {
.class_init = ich9_uhci3_class_init,
};
-static void uhci_register(void)
+static void uhci_register_types(void)
{
type_register_static(&piix3_uhci_info);
type_register_static(&piix4_uhci_info);
@@ -1330,7 +1389,8 @@ static void uhci_register(void)
type_register_static(&ich9_uhci2_info);
type_register_static(&ich9_uhci3_info);
}
-device_init(uhci_register);
+
+type_init(uhci_register_types)
void usb_uhci_piix3_init(PCIBus *bus, int devfn)
{
diff --git a/hw/usb-wacom.c b/hw/usb-wacom.c
index 14de14d5f7..197e2dced5 100644
--- a/hw/usb-wacom.c
+++ b/hw/usb-wacom.c
@@ -306,7 +306,7 @@ static int usb_wacom_handle_data(USBDevice *dev, USBPacket *p)
switch (p->pid) {
case USB_TOKEN_IN:
- if (p->devep == 1) {
+ if (p->ep->nr == 1) {
if (!(s->changed || s->idle))
return USB_RET_NAK;
s->changed = 0;
@@ -357,7 +357,6 @@ static void usb_wacom_class_init(ObjectClass *klass, void *data)
uc->product_desc = "QEMU PenPartner Tablet";
uc->usb_desc = &desc_wacom;
uc->init = usb_wacom_initfn;
- uc->handle_packet = usb_generic_handle_packet;
uc->handle_reset = usb_wacom_handle_reset;
uc->handle_control = usb_wacom_handle_control;
uc->handle_data = usb_wacom_handle_data;
@@ -373,9 +372,10 @@ static TypeInfo wacom_info = {
.class_init = usb_wacom_class_init,
};
-static void usb_wacom_register_devices(void)
+static void usb_wacom_register_types(void)
{
type_register_static(&wacom_info);
usb_legacy_register("usb-wacom-tablet", "wacom-tablet", NULL);
}
-device_init(usb_wacom_register_devices)
+
+type_init(usb_wacom_register_types)
diff --git a/hw/usb-xhci.c b/hw/usb-xhci.c
index 37e887c431..fc5b542d99 100644
--- a/hw/usb-xhci.c
+++ b/hw/usb-xhci.c
@@ -307,7 +307,8 @@ typedef struct XHCIState XHCIState;
typedef struct XHCITransfer {
XHCIState *xhci;
USBPacket packet;
- bool running;
+ bool running_async;
+ bool running_retry;
bool cancelled;
bool complete;
bool backgrounded;
@@ -338,6 +339,7 @@ typedef struct XHCIEPContext {
unsigned int next_xfer;
unsigned int comp_xfer;
XHCITransfer transfers[TD_QUEUE];
+ XHCITransfer *retry;
bool bg_running;
bool bg_updating;
unsigned int next_bg;
@@ -420,6 +422,60 @@ typedef struct XHCIEvRingSeg {
uint32_t rsvd;
} XHCIEvRingSeg;
+#ifdef DEBUG_XHCI
+static const char *TRBType_names[] = {
+ [TRB_RESERVED] = "TRB_RESERVED",
+ [TR_NORMAL] = "TR_NORMAL",
+ [TR_SETUP] = "TR_SETUP",
+ [TR_DATA] = "TR_DATA",
+ [TR_STATUS] = "TR_STATUS",
+ [TR_ISOCH] = "TR_ISOCH",
+ [TR_LINK] = "TR_LINK",
+ [TR_EVDATA] = "TR_EVDATA",
+ [TR_NOOP] = "TR_NOOP",
+ [CR_ENABLE_SLOT] = "CR_ENABLE_SLOT",
+ [CR_DISABLE_SLOT] = "CR_DISABLE_SLOT",
+ [CR_ADDRESS_DEVICE] = "CR_ADDRESS_DEVICE",
+ [CR_CONFIGURE_ENDPOINT] = "CR_CONFIGURE_ENDPOINT",
+ [CR_EVALUATE_CONTEXT] = "CR_EVALUATE_CONTEXT",
+ [CR_RESET_ENDPOINT] = "CR_RESET_ENDPOINT",
+ [CR_STOP_ENDPOINT] = "CR_STOP_ENDPOINT",
+ [CR_SET_TR_DEQUEUE] = "CR_SET_TR_DEQUEUE",
+ [CR_RESET_DEVICE] = "CR_RESET_DEVICE",
+ [CR_FORCE_EVENT] = "CR_FORCE_EVENT",
+ [CR_NEGOTIATE_BW] = "CR_NEGOTIATE_BW",
+ [CR_SET_LATENCY_TOLERANCE] = "CR_SET_LATENCY_TOLERANCE",
+ [CR_GET_PORT_BANDWIDTH] = "CR_GET_PORT_BANDWIDTH",
+ [CR_FORCE_HEADER] = "CR_FORCE_HEADER",
+ [CR_NOOP] = "CR_NOOP",
+ [ER_TRANSFER] = "ER_TRANSFER",
+ [ER_COMMAND_COMPLETE] = "ER_COMMAND_COMPLETE",
+ [ER_PORT_STATUS_CHANGE] = "ER_PORT_STATUS_CHANGE",
+ [ER_BANDWIDTH_REQUEST] = "ER_BANDWIDTH_REQUEST",
+ [ER_DOORBELL] = "ER_DOORBELL",
+ [ER_HOST_CONTROLLER] = "ER_HOST_CONTROLLER",
+ [ER_DEVICE_NOTIFICATION] = "ER_DEVICE_NOTIFICATION",
+ [ER_MFINDEX_WRAP] = "ER_MFINDEX_WRAP",
+ [CR_VENDOR_VIA_CHALLENGE_RESPONSE] = "CR_VENDOR_VIA_CHALLENGE_RESPONSE",
+ [CR_VENDOR_NEC_FIRMWARE_REVISION] = "CR_VENDOR_NEC_FIRMWARE_REVISION",
+ [CR_VENDOR_NEC_CHALLENGE_RESPONSE] = "CR_VENDOR_NEC_CHALLENGE_RESPONSE",
+};
+
+static const char *lookup_name(uint32_t index, const char **list, uint32_t llen)
+{
+ if (index >= llen || list[index] == NULL) {
+ return "???";
+ }
+ return list[index];
+}
+
+static const char *trb_name(XHCITRB *trb)
+{
+ return lookup_name(TRB_TYPE(*trb), TRBType_names,
+ ARRAY_SIZE(TRBType_names));
+}
+#endif
+
static void xhci_kick_ep(XHCIState *xhci, unsigned int slotid,
unsigned int epid);
@@ -487,8 +543,9 @@ static void xhci_write_event(XHCIState *xhci, XHCIEvent *event)
}
ev_trb.control = cpu_to_le32(ev_trb.control);
- DPRINTF("xhci_write_event(): [%d] %016"PRIx64" %08x %08x\n",
- xhci->er_ep_idx, ev_trb.parameter, ev_trb.status, ev_trb.control);
+ DPRINTF("xhci_write_event(): [%d] %016"PRIx64" %08x %08x %s\n",
+ xhci->er_ep_idx, ev_trb.parameter, ev_trb.status, ev_trb.control,
+ trb_name(&ev_trb));
addr = xhci->er_start + TRB_SIZE*xhci->er_ep_idx;
cpu_physical_memory_write(addr, (uint8_t *) &ev_trb, TRB_SIZE);
@@ -649,8 +706,9 @@ static TRBType xhci_ring_fetch(XHCIState *xhci, XHCIRing *ring, XHCITRB *trb,
le32_to_cpus(&trb->control);
DPRINTF("xhci: TRB fetched [" TARGET_FMT_plx "]: "
- "%016" PRIx64 " %08x %08x\n",
- ring->dequeue, trb->parameter, trb->status, trb->control);
+ "%016" PRIx64 " %08x %08x %s\n",
+ ring->dequeue, trb->parameter, trb->status, trb->control,
+ trb_name(trb));
if ((trb->control & TRB_C) != ring->ccs) {
return 0;
@@ -859,12 +917,17 @@ static int xhci_ep_nuke_xfers(XHCIState *xhci, unsigned int slotid,
xferi = epctx->next_xfer;
for (i = 0; i < TD_QUEUE; i++) {
XHCITransfer *t = &epctx->transfers[xferi];
- if (t->running) {
+ if (t->running_async) {
+ usb_cancel_packet(&t->packet);
+ t->running_async = 0;
t->cancelled = 1;
- /* libusb_cancel_transfer(t->usbxfer) */
DPRINTF("xhci: cancelling transfer %d, waiting for it to complete...\n", i);
killed++;
}
+ if (t->running_retry) {
+ t->running_retry = 0;
+ epctx->retry = NULL;
+ }
if (t->backgrounded) {
t->backgrounded = 0;
}
@@ -885,9 +948,10 @@ static int xhci_ep_nuke_xfers(XHCIState *xhci, unsigned int slotid,
xferi = epctx->next_bg;
for (i = 0; i < BG_XFERS; i++) {
XHCITransfer *t = &epctx->bg_transfers[xferi];
- if (t->running) {
+ if (t->running_async) {
+ usb_cancel_packet(&t->packet);
+ t->running_async = 0;
t->cancelled = 1;
- /* libusb_cancel_transfer(t->usbxfer); */
DPRINTF("xhci: cancelling bg transfer %d, waiting for it to complete...\n", i);
killed++;
}
@@ -1336,27 +1400,37 @@ static int xhci_hle_control(XHCIState *xhci, XHCITransfer *xfer,
}
#endif
-static int xhci_setup_packet(XHCITransfer *xfer, XHCIPort *port, int ep)
+static int xhci_setup_packet(XHCITransfer *xfer, USBDevice *dev)
{
- usb_packet_setup(&xfer->packet,
- xfer->in_xfer ? USB_TOKEN_IN : USB_TOKEN_OUT,
- xfer->xhci->slots[xfer->slotid-1].devaddr,
- ep & 0x7f);
+ USBEndpoint *ep;
+ int dir;
+
+ dir = xfer->in_xfer ? USB_TOKEN_IN : USB_TOKEN_OUT;
+ ep = usb_ep_get(dev, dir, xfer->epid >> 1);
+ usb_packet_setup(&xfer->packet, dir, ep);
usb_packet_addbuf(&xfer->packet, xfer->data, xfer->data_length);
DPRINTF("xhci: setup packet pid 0x%x addr %d ep %d\n",
- xfer->packet.pid, xfer->packet.devaddr, xfer->packet.devep);
+ xfer->packet.pid, dev->addr, ep->nr);
return 0;
}
static int xhci_complete_packet(XHCITransfer *xfer, int ret)
{
if (ret == USB_RET_ASYNC) {
- xfer->running = 1;
+ xfer->running_async = 1;
+ xfer->running_retry = 0;
+ xfer->complete = 0;
+ xfer->cancelled = 0;
+ return 0;
+ } else if (ret == USB_RET_NAK) {
+ xfer->running_async = 0;
+ xfer->running_retry = 1;
xfer->complete = 0;
xfer->cancelled = 0;
return 0;
} else {
- xfer->running = 0;
+ xfer->running_async = 0;
+ xfer->running_retry = 0;
xfer->complete = 1;
}
@@ -1385,6 +1459,14 @@ static int xhci_complete_packet(XHCITransfer *xfer, int ret)
return 0;
}
+static USBDevice *xhci_find_device(XHCIPort *port, uint8_t addr)
+{
+ if (!(port->portsc & PORTSC_PED)) {
+ return NULL;
+ }
+ return usb_find_device(&port->port, addr);
+}
+
static int xhci_fire_ctl_transfer(XHCIState *xhci, XHCITransfer *xfer)
{
XHCITRB *trb_setup, *trb_status;
@@ -1444,7 +1526,7 @@ static int xhci_fire_ctl_transfer(XHCIState *xhci, XHCITransfer *xfer)
xfer->data_length = wLength;
port = &xhci->ports[xhci->slots[xfer->slotid-1].port-1];
- dev = port->port.dev;
+ dev = xhci_find_device(port, xhci->slots[xfer->slotid-1].devaddr);
if (!dev) {
fprintf(stderr, "xhci: slot %d port %d has no device\n", xfer->slotid,
xhci->slots[xfer->slotid-1].port);
@@ -1454,7 +1536,7 @@ static int xhci_fire_ctl_transfer(XHCIState *xhci, XHCITransfer *xfer)
xfer->in_xfer = bmRequestType & USB_DIR_IN;
xfer->iso_xfer = false;
- xhci_setup_packet(xfer, port, 0);
+ xhci_setup_packet(xfer, dev);
if (!xfer->in_xfer) {
xhci_xfer_data(xfer, xfer->data, wLength, 0, 1, 0);
}
@@ -1463,7 +1545,7 @@ static int xhci_fire_ctl_transfer(XHCIState *xhci, XHCITransfer *xfer)
wValue, wIndex, wLength, xfer->data);
xhci_complete_packet(xfer, ret);
- if (!xfer->running) {
+ if (!xfer->running_async && !xfer->running_retry) {
xhci_kick_ep(xhci, xfer->slotid, xfer->epid);
}
return 0;
@@ -1476,12 +1558,8 @@ static int xhci_submit(XHCIState *xhci, XHCITransfer *xfer, XHCIEPContext *epctx
int ret;
DPRINTF("xhci_submit(slotid=%d,epid=%d)\n", xfer->slotid, xfer->epid);
- uint8_t ep = xfer->epid>>1;
xfer->in_xfer = epctx->type>>2;
- if (xfer->in_xfer) {
- ep |= 0x80;
- }
if (xfer->data && xfer->data_alloced < xfer->data_length) {
xfer->data_alloced = 0;
@@ -1502,14 +1580,14 @@ static int xhci_submit(XHCIState *xhci, XHCITransfer *xfer, XHCIEPContext *epctx
}
port = &xhci->ports[xhci->slots[xfer->slotid-1].port-1];
- dev = port->port.dev;
+ dev = xhci_find_device(port, xhci->slots[xfer->slotid-1].devaddr);
if (!dev) {
fprintf(stderr, "xhci: slot %d port %d has no device\n", xfer->slotid,
xhci->slots[xfer->slotid-1].port);
return -1;
}
- xhci_setup_packet(xfer, port, ep);
+ xhci_setup_packet(xfer, dev);
switch(epctx->type) {
case ET_INTR_OUT:
@@ -1522,8 +1600,9 @@ static int xhci_submit(XHCIState *xhci, XHCITransfer *xfer, XHCIEPContext *epctx
FIXME();
break;
default:
- fprintf(stderr, "xhci: unknown or unhandled EP type %d (ep %02x)\n",
- epctx->type, ep);
+ fprintf(stderr, "xhci: unknown or unhandled EP "
+ "(type %d, in %d, ep %02x)\n",
+ epctx->type, xfer->in_xfer, xfer->epid);
return -1;
}
@@ -1533,7 +1612,7 @@ static int xhci_submit(XHCIState *xhci, XHCITransfer *xfer, XHCIEPContext *epctx
ret = usb_handle_packet(dev, &xfer->packet);
xhci_complete_packet(xfer, ret);
- if (!xfer->running) {
+ if (!xfer->running_async && !xfer->running_retry) {
xhci_kick_ep(xhci, xfer->slotid, xfer->epid);
}
return 0;
@@ -1604,6 +1683,25 @@ static void xhci_kick_ep(XHCIState *xhci, unsigned int slotid, unsigned int epid
return;
}
+ if (epctx->retry) {
+ /* retry nak'ed transfer */
+ XHCITransfer *xfer = epctx->retry;
+ int result;
+
+ DPRINTF("xhci: retry nack'ed transfer ...\n");
+ assert(xfer->running_retry);
+ xhci_setup_packet(xfer, xfer->packet.ep->dev);
+ result = usb_handle_packet(xfer->packet.ep->dev, &xfer->packet);
+ if (result == USB_RET_NAK) {
+ DPRINTF("xhci: ... xfer still nacked\n");
+ return;
+ }
+ DPRINTF("xhci: ... result %d\n", result);
+ xhci_complete_packet(xfer, result);
+ assert(!xfer->running_retry);
+ epctx->retry = NULL;
+ }
+
if (epctx->state == EP_HALTED) {
DPRINTF("xhci: ep halted, not running schedule\n");
return;
@@ -1613,9 +1711,13 @@ static void xhci_kick_ep(XHCIState *xhci, unsigned int slotid, unsigned int epid
while (1) {
XHCITransfer *xfer = &epctx->transfers[epctx->next_xfer];
- if (xfer->running || xfer->backgrounded) {
- DPRINTF("xhci: ep is busy\n");
+ if (xfer->running_async || xfer->running_retry || xfer->backgrounded) {
+ DPRINTF("xhci: ep is busy (#%d,%d,%d,%d)\n",
+ epctx->next_xfer, xfer->running_async,
+ xfer->running_retry, xfer->backgrounded);
break;
+ } else {
+ DPRINTF("xhci: ep: using #%d\n", epctx->next_xfer);
}
length = xhci_ring_chain_length(xhci, &epctx->ring);
if (length < 0) {
@@ -1658,12 +1760,15 @@ static void xhci_kick_ep(XHCIState *xhci, unsigned int slotid, unsigned int epid
}
}
- /*
- * Qemu usb can't handle multiple in-flight xfers.
- * Also xfers might be finished here already,
- * possibly with an error. Stop here for now.
- */
- break;
+ if (epctx->state == EP_HALTED) {
+ DPRINTF("xhci: ep halted, stopping schedule\n");
+ break;
+ }
+ if (xfer->running_retry) {
+ DPRINTF("xhci: xfer nacked, stopping schedule\n");
+ epctx->retry = xfer;
+ break;
+ }
}
}
@@ -2346,9 +2451,7 @@ static void xhci_port_write(XHCIState *xhci, uint32_t reg, uint32_t val)
/* write-1-to-start bits */
if (val & PORTSC_PR) {
DPRINTF("xhci: port %d reset\n", port);
- if (xhci->ports[port].port.dev) {
- usb_send_msg(xhci->ports[port].port.dev, USB_MSG_RESET);
- }
+ usb_device_reset(xhci->ports[port].port.dev);
portsc |= PORTSC_PRC | PORTSC_PED;
}
xhci->ports[port].portsc = portsc;
@@ -2633,6 +2736,26 @@ static void xhci_detach(USBPort *usbport)
xhci_update_port(xhci, port, 1);
}
+static void xhci_wakeup(USBPort *usbport)
+{
+ XHCIState *xhci = usbport->opaque;
+ XHCIPort *port = &xhci->ports[usbport->index];
+ int nr = port->port.index + 1;
+ XHCIEvent ev = { ER_PORT_STATUS_CHANGE, CC_SUCCESS, nr << 24};
+ uint32_t pls;
+
+ pls = (port->portsc >> PORTSC_PLS_SHIFT) & PORTSC_PLS_MASK;
+ if (pls != 3) {
+ return;
+ }
+ port->portsc |= 0xf << PORTSC_PLS_SHIFT;
+ if (port->portsc & PORTSC_PLC) {
+ return;
+ }
+ port->portsc |= PORTSC_PLC;
+ xhci_event(xhci, &ev);
+}
+
static void xhci_complete(USBPort *port, USBPacket *packet)
{
XHCITransfer *xfer = container_of(packet, XHCITransfer, packet);
@@ -2649,11 +2772,53 @@ static void xhci_child_detach(USBPort *port, USBDevice *child)
static USBPortOps xhci_port_ops = {
.attach = xhci_attach,
.detach = xhci_detach,
+ .wakeup = xhci_wakeup,
.complete = xhci_complete,
.child_detach = xhci_child_detach,
};
+static int xhci_find_slotid(XHCIState *xhci, USBDevice *dev)
+{
+ XHCISlot *slot;
+ int slotid;
+
+ for (slotid = 1; slotid <= MAXSLOTS; slotid++) {
+ slot = &xhci->slots[slotid-1];
+ if (slot->devaddr == dev->addr) {
+ return slotid;
+ }
+ }
+ return 0;
+}
+
+static int xhci_find_epid(USBEndpoint *ep)
+{
+ if (ep->nr == 0) {
+ return 1;
+ }
+ if (ep->pid == USB_TOKEN_IN) {
+ return ep->nr * 2 + 1;
+ } else {
+ return ep->nr * 2;
+ }
+}
+
+static void xhci_wakeup_endpoint(USBBus *bus, USBEndpoint *ep)
+{
+ XHCIState *xhci = container_of(bus, XHCIState, bus);
+ int slotid;
+
+ DPRINTF("%s\n", __func__);
+ slotid = xhci_find_slotid(xhci, ep->dev);
+ if (slotid == 0 || !xhci->slots[slotid-1].enabled) {
+ DPRINTF("%s: oops, no slot for dev %d\n", __func__, ep->dev->addr);
+ return;
+ }
+ xhci_kick_ep(xhci, slotid, xhci_find_epid(ep));
+}
+
static USBBusOps xhci_bus_ops = {
+ .wakeup_endpoint = xhci_wakeup_endpoint,
};
static void usb_xhci_init(XHCIState *xhci, DeviceState *dev)
@@ -2667,7 +2832,10 @@ static void usb_xhci_init(XHCIState *xhci, DeviceState *dev)
for (i = 0; i < MAXPORTS; i++) {
memset(&xhci->ports[i], 0, sizeof(xhci->ports[i]));
usb_register_port(&xhci->bus, &xhci->ports[i].port, xhci, i,
- &xhci_port_ops, USB_SPEED_MASK_HIGH);
+ &xhci_port_ops,
+ USB_SPEED_MASK_LOW |
+ USB_SPEED_MASK_FULL |
+ USB_SPEED_MASK_HIGH);
}
for (i = 0; i < MAXSLOTS; i++) {
xhci->slots[i].enabled = 0;
@@ -2752,8 +2920,9 @@ static TypeInfo xhci_info = {
.class_init = xhci_class_init,
};
-static void xhci_register(void)
+static void xhci_register_types(void)
{
type_register_static(&xhci_info);
}
-device_init(xhci_register);
+
+type_init(xhci_register_types)
diff --git a/hw/usb.c b/hw/usb.c
index c3ff5b7093..57fc5e3cfd 100644
--- a/hw/usb.c
+++ b/hw/usb.c
@@ -26,6 +26,7 @@
#include "qemu-common.h"
#include "usb.h"
#include "iov.h"
+#include "trace.h"
void usb_attach(USBPort *port)
{
@@ -35,7 +36,8 @@ void usb_attach(USBPort *port)
assert(dev->attached);
assert(dev->state == USB_STATE_NOTATTACHED);
port->ops->attach(port);
- usb_send_msg(dev, USB_MSG_ATTACH);
+ dev->state = USB_STATE_ATTACHED;
+ usb_device_handle_attach(dev);
}
void usb_detach(USBPort *port)
@@ -45,24 +47,41 @@ void usb_detach(USBPort *port)
assert(dev != NULL);
assert(dev->state != USB_STATE_NOTATTACHED);
port->ops->detach(port);
- usb_send_msg(dev, USB_MSG_DETACH);
+ dev->state = USB_STATE_NOTATTACHED;
}
-void usb_reset(USBPort *port)
+void usb_port_reset(USBPort *port)
{
USBDevice *dev = port->dev;
assert(dev != NULL);
usb_detach(port);
usb_attach(port);
- usb_send_msg(dev, USB_MSG_RESET);
+ usb_device_reset(dev);
}
-void usb_wakeup(USBDevice *dev)
+void usb_device_reset(USBDevice *dev)
{
+ if (dev == NULL || !dev->attached) {
+ return;
+ }
+ dev->remote_wakeup = 0;
+ dev->addr = 0;
+ dev->state = USB_STATE_DEFAULT;
+ usb_device_handle_reset(dev);
+}
+
+void usb_wakeup(USBEndpoint *ep)
+{
+ USBDevice *dev = ep->dev;
+ USBBus *bus = usb_bus_from_device(dev);
+
if (dev->remote_wakeup && dev->port && dev->port->ops->wakeup) {
dev->port->ops->wakeup(dev->port);
}
+ if (bus->ops->wakeup_endpoint) {
+ bus->ops->wakeup_endpoint(bus, ep);
+ }
}
/**********************/
@@ -128,8 +147,7 @@ static int do_token_in(USBDevice *s, USBPacket *p)
int request, value, index;
int ret = 0;
- if (p->devep != 0)
- return usb_device_handle_data(s, p);
+ assert(p->ep->nr == 0);
request = (s->setup_buf[0] << 8) | s->setup_buf[1];
value = (s->setup_buf[3] << 8) | s->setup_buf[2];
@@ -175,8 +193,7 @@ static int do_token_in(USBDevice *s, USBPacket *p)
static int do_token_out(USBDevice *s, USBPacket *p)
{
- if (p->devep != 0)
- return usb_device_handle_data(s, p);
+ assert(p->ep->nr == 0);
switch(s->setup_state) {
case SETUP_STATE_ACK:
@@ -209,51 +226,6 @@ static int do_token_out(USBDevice *s, USBPacket *p)
}
}
-/*
- * Generic packet handler.
- * Called by the HC (host controller).
- *
- * Returns length of the transaction or one of the USB_RET_XXX codes.
- */
-int usb_generic_handle_packet(USBDevice *s, USBPacket *p)
-{
- switch(p->pid) {
- case USB_MSG_ATTACH:
- s->state = USB_STATE_ATTACHED;
- usb_device_handle_attach(s);
- return 0;
-
- case USB_MSG_DETACH:
- s->state = USB_STATE_NOTATTACHED;
- return 0;
-
- case USB_MSG_RESET:
- s->remote_wakeup = 0;
- s->addr = 0;
- s->state = USB_STATE_DEFAULT;
- usb_device_handle_reset(s);
- return 0;
- }
-
- /* Rest of the PIDs must match our address */
- if (s->state < USB_STATE_DEFAULT || p->devaddr != s->addr)
- return USB_RET_NODEV;
-
- switch (p->pid) {
- case USB_TOKEN_SETUP:
- return do_token_setup(s, p);
-
- case USB_TOKEN_IN:
- return do_token_in(s, p);
-
- case USB_TOKEN_OUT:
- return do_token_out(s, p);
-
- default:
- return USB_RET_STALL;
- }
-}
-
/* ctrl complete function for devices which use usb_generic_handle_packet and
may return USB_RET_ASYNC from their handle_control callback. Device code
which does this *must* call this function instead of the normal
@@ -301,17 +273,39 @@ int set_usb_string(uint8_t *buf, const char *str)
return q - buf;
}
-/* Send an internal message to a USB device. */
-void usb_send_msg(USBDevice *dev, int msg)
+USBDevice *usb_find_device(USBPort *port, uint8_t addr)
{
- USBPacket p;
- int ret;
+ USBDevice *dev = port->dev;
- memset(&p, 0, sizeof(p));
- p.pid = msg;
- ret = usb_handle_packet(dev, &p);
- /* This _must_ be synchronous */
- assert(ret != USB_RET_ASYNC);
+ if (dev == NULL || !dev->attached || dev->state != USB_STATE_DEFAULT) {
+ return NULL;
+ }
+ if (dev->addr == addr) {
+ return dev;
+ }
+ return usb_device_find_device(dev, addr);
+}
+
+static int usb_process_one(USBPacket *p)
+{
+ USBDevice *dev = p->ep->dev;
+
+ if (p->ep->nr == 0) {
+ /* control pipe */
+ switch (p->pid) {
+ case USB_TOKEN_SETUP:
+ return do_token_setup(dev, p);
+ case USB_TOKEN_IN:
+ return do_token_in(dev, p);
+ case USB_TOKEN_OUT:
+ return do_token_out(dev, p);
+ default:
+ return USB_RET_STALL;
+ }
+ } else {
+ /* data pipe */
+ return usb_device_handle_data(dev, p);
+ }
}
/* Hand over a packet to a device for processing. Return value
@@ -321,17 +315,27 @@ int usb_handle_packet(USBDevice *dev, USBPacket *p)
{
int ret;
- assert(p->owner == NULL);
- ret = usb_device_handle_packet(dev, p);
- if (ret == USB_RET_ASYNC) {
- if (p->owner == NULL) {
- p->owner = usb_ep_get(dev, p->pid, p->devep);
+ if (dev == NULL) {
+ return USB_RET_NODEV;
+ }
+ assert(dev == p->ep->dev);
+ assert(dev->state == USB_STATE_DEFAULT);
+ assert(p->state == USB_PACKET_SETUP);
+ assert(p->ep != NULL);
+
+ if (QTAILQ_EMPTY(&p->ep->queue)) {
+ ret = usb_process_one(p);
+ if (ret == USB_RET_ASYNC) {
+ usb_packet_set_state(p, USB_PACKET_ASYNC);
+ QTAILQ_INSERT_TAIL(&p->ep->queue, p, queue);
} else {
- /* We'll end up here when usb_handle_packet is called
- * recursively due to a hub being in the chain. Nothing
- * to do. Leave p->owner pointing to the device, not the
- * hub. */;
+ p->result = ret;
+ usb_packet_set_state(p, USB_PACKET_COMPLETE);
}
+ } else {
+ ret = USB_RET_ASYNC;
+ usb_packet_set_state(p, USB_PACKET_QUEUED);
+ QTAILQ_INSERT_TAIL(&p->ep->queue, p, queue);
}
return ret;
}
@@ -341,10 +345,28 @@ int usb_handle_packet(USBDevice *dev, USBPacket *p)
handle_packet. */
void usb_packet_complete(USBDevice *dev, USBPacket *p)
{
- /* Note: p->owner != dev is possible in case dev is a hub */
- assert(p->owner != NULL);
- p->owner = NULL;
+ USBEndpoint *ep = p->ep;
+ int ret;
+
+ assert(p->state == USB_PACKET_ASYNC);
+ assert(QTAILQ_FIRST(&ep->queue) == p);
+ usb_packet_set_state(p, USB_PACKET_COMPLETE);
+ QTAILQ_REMOVE(&ep->queue, p, queue);
dev->port->ops->complete(dev->port, p);
+
+ while (!QTAILQ_EMPTY(&ep->queue)) {
+ p = QTAILQ_FIRST(&ep->queue);
+ assert(p->state == USB_PACKET_QUEUED);
+ ret = usb_process_one(p);
+ if (ret == USB_RET_ASYNC) {
+ usb_packet_set_state(p, USB_PACKET_ASYNC);
+ break;
+ }
+ p->result = ret;
+ usb_packet_set_state(p, USB_PACKET_COMPLETE);
+ QTAILQ_REMOVE(&ep->queue, p, queue);
+ dev->port->ops->complete(dev->port, p);
+ }
}
/* Cancel an active packet. The packed must have been deferred by
@@ -352,9 +374,13 @@ void usb_packet_complete(USBDevice *dev, USBPacket *p)
completed. */
void usb_cancel_packet(USBPacket * p)
{
- assert(p->owner != NULL);
- usb_device_cancel_packet(p->owner->dev, p);
- p->owner = NULL;
+ bool callback = (p->state == USB_PACKET_ASYNC);
+ assert(usb_packet_is_inflight(p));
+ usb_packet_set_state(p, USB_PACKET_CANCELED);
+ QTAILQ_REMOVE(&p->ep->queue, p, queue);
+ if (callback) {
+ usb_device_cancel_packet(p->ep->dev, p);
+ }
}
@@ -363,13 +389,32 @@ void usb_packet_init(USBPacket *p)
qemu_iovec_init(&p->iov, 1);
}
-void usb_packet_setup(USBPacket *p, int pid, uint8_t addr, uint8_t ep)
+void usb_packet_set_state(USBPacket *p, USBPacketState state)
{
+ static const char *name[] = {
+ [USB_PACKET_UNDEFINED] = "undef",
+ [USB_PACKET_SETUP] = "setup",
+ [USB_PACKET_QUEUED] = "queued",
+ [USB_PACKET_ASYNC] = "async",
+ [USB_PACKET_COMPLETE] = "complete",
+ [USB_PACKET_CANCELED] = "canceled",
+ };
+ USBDevice *dev = p->ep->dev;
+ USBBus *bus = usb_bus_from_device(dev);
+
+ trace_usb_packet_state_change(bus->busnr, dev->port->path, p->ep->nr,
+ p, name[p->state], name[state]);
+ p->state = state;
+}
+
+void usb_packet_setup(USBPacket *p, int pid, USBEndpoint *ep)
+{
+ assert(!usb_packet_is_inflight(p));
p->pid = pid;
- p->devaddr = addr;
- p->devep = ep;
+ p->ep = ep;
p->result = 0;
qemu_iovec_reset(&p->iov);
+ usb_packet_set_state(p, USB_PACKET_SETUP);
}
void usb_packet_addbuf(USBPacket *p, void *ptr, size_t len)
@@ -408,6 +453,7 @@ void usb_packet_skip(USBPacket *p, size_t bytes)
void usb_packet_cleanup(USBPacket *p)
{
+ assert(!usb_packet_is_inflight(p));
qemu_iovec_destroy(&p->iov);
}
@@ -415,16 +461,24 @@ void usb_ep_init(USBDevice *dev)
{
int ep;
+ dev->ep_ctl.nr = 0;
dev->ep_ctl.type = USB_ENDPOINT_XFER_CONTROL;
dev->ep_ctl.ifnum = 0;
dev->ep_ctl.dev = dev;
+ QTAILQ_INIT(&dev->ep_ctl.queue);
for (ep = 0; ep < USB_MAX_ENDPOINTS; ep++) {
+ dev->ep_in[ep].nr = ep + 1;
+ dev->ep_out[ep].nr = ep + 1;
+ dev->ep_in[ep].pid = USB_TOKEN_IN;
+ dev->ep_out[ep].pid = USB_TOKEN_OUT;
dev->ep_in[ep].type = USB_ENDPOINT_XFER_INVALID;
dev->ep_out[ep].type = USB_ENDPOINT_XFER_INVALID;
dev->ep_in[ep].ifnum = 0;
dev->ep_out[ep].ifnum = 0;
dev->ep_in[ep].dev = dev;
dev->ep_out[ep].dev = dev;
+ QTAILQ_INIT(&dev->ep_in[ep].queue);
+ QTAILQ_INIT(&dev->ep_out[ep].queue);
}
}
@@ -472,7 +526,12 @@ void usb_ep_dump(USBDevice *dev)
struct USBEndpoint *usb_ep_get(USBDevice *dev, int pid, int ep)
{
- struct USBEndpoint *eps = pid == USB_TOKEN_IN ? dev->ep_in : dev->ep_out;
+ struct USBEndpoint *eps;
+
+ if (dev == NULL) {
+ return NULL;
+ }
+ eps = (pid == USB_TOKEN_IN) ? dev->ep_in : dev->ep_out;
if (ep == 0) {
return &dev->ep_ctl;
}
diff --git a/hw/usb.h b/hw/usb.h
index 13e7c8ea7f..8e83697fb7 100644
--- a/hw/usb.h
+++ b/hw/usb.h
@@ -39,11 +39,6 @@
#define USB_TOKEN_IN 0x69 /* device -> host */
#define USB_TOKEN_OUT 0xe1 /* host -> device */
-/* specific usb messages, also sent in the 'pid' parameter */
-#define USB_MSG_ATTACH 0x100
-#define USB_MSG_DETACH 0x101
-#define USB_MSG_RESET 0x102
-
#define USB_RET_NODEV (-1)
#define USB_RET_NAK (-2)
#define USB_RET_STALL (-3)
@@ -176,10 +171,13 @@ struct USBDescString {
#define USB_MAX_INTERFACES 16
struct USBEndpoint {
+ uint8_t nr;
+ uint8_t pid;
uint8_t type;
uint8_t ifnum;
int max_packet_size;
USBDevice *dev;
+ QTAILQ_HEAD(, USBPacket) queue;
};
/* definition of a USB device */
@@ -234,13 +232,10 @@ typedef struct USBDeviceClass {
int (*init)(USBDevice *dev);
/*
- * Process USB packet.
- * Called by the HC (Host Controller).
- *
- * Returns length of the transaction
- * or one of the USB_RET_XXX codes.
+ * Walk (enabled) downstream ports, check for a matching device.
+ * Only hubs implement this.
*/
- int (*handle_packet)(USBDevice *dev, USBPacket *p);
+ USBDevice *(*find_device)(USBDevice *dev, uint8_t addr);
/*
* Called when a packet is canceled.
@@ -297,8 +292,7 @@ typedef struct USBPortOps {
void (*wakeup)(USBPort *port);
/*
* Note that port->dev will be different then the device from which
- * the packet originated when a hub is involved, if you want the orginating
- * device use p->owner
+ * the packet originated when a hub is involved.
*/
void (*complete)(USBPort *port, USBPacket *p);
} USBPortOps;
@@ -316,20 +310,30 @@ struct USBPort {
typedef void USBCallback(USBPacket * packet, void *opaque);
+typedef enum USBPacketState {
+ USB_PACKET_UNDEFINED = 0,
+ USB_PACKET_SETUP,
+ USB_PACKET_QUEUED,
+ USB_PACKET_ASYNC,
+ USB_PACKET_COMPLETE,
+ USB_PACKET_CANCELED,
+} USBPacketState;
+
/* Structure used to hold information about an active USB packet. */
struct USBPacket {
/* Data fields for use by the driver. */
int pid;
- uint8_t devaddr;
- uint8_t devep;
+ USBEndpoint *ep;
QEMUIOVector iov;
int result; /* transfer length or USB_RET_* status code */
/* Internal use by the USB layer. */
- USBEndpoint *owner;
+ USBPacketState state;
+ QTAILQ_ENTRY(USBPacket) queue;
};
void usb_packet_init(USBPacket *p);
-void usb_packet_setup(USBPacket *p, int pid, uint8_t addr, uint8_t ep);
+void usb_packet_set_state(USBPacket *p, USBPacketState state);
+void usb_packet_setup(USBPacket *p, int pid, USBEndpoint *ep);
void usb_packet_addbuf(USBPacket *p, void *ptr, size_t len);
int usb_packet_map(USBPacket *p, QEMUSGList *sgl);
void usb_packet_unmap(USBPacket *p);
@@ -337,6 +341,14 @@ void usb_packet_copy(USBPacket *p, void *ptr, size_t bytes);
void usb_packet_skip(USBPacket *p, size_t bytes);
void usb_packet_cleanup(USBPacket *p);
+static inline bool usb_packet_is_inflight(USBPacket *p)
+{
+ return (p->state == USB_PACKET_QUEUED ||
+ p->state == USB_PACKET_ASYNC);
+}
+
+USBDevice *usb_find_device(USBPort *port, uint8_t addr);
+
int usb_handle_packet(USBDevice *dev, USBPacket *p);
void usb_packet_complete(USBDevice *dev, USBPacket *p);
void usb_cancel_packet(USBPacket * p);
@@ -354,20 +366,19 @@ int usb_ep_get_max_packet_size(USBDevice *dev, int pid, int ep);
void usb_attach(USBPort *port);
void usb_detach(USBPort *port);
-void usb_reset(USBPort *port);
-void usb_wakeup(USBDevice *dev);
-int usb_generic_handle_packet(USBDevice *s, USBPacket *p);
+void usb_port_reset(USBPort *port);
+void usb_device_reset(USBDevice *dev);
+void usb_wakeup(USBEndpoint *ep);
void usb_generic_async_ctrl_complete(USBDevice *s, USBPacket *p);
int set_usb_string(uint8_t *buf, const char *str);
-void usb_send_msg(USBDevice *dev, int msg);
/* usb-linux.c */
-USBDevice *usb_host_device_open(const char *devname);
+USBDevice *usb_host_device_open(USBBus *bus, const char *devname);
int usb_host_device_close(const char *devname);
void usb_host_info(Monitor *mon);
/* usb-bt.c */
-USBDevice *usb_bt_init(HCIInfo *hci);
+USBDevice *usb_bt_init(USBBus *bus, HCIInfo *hci);
/* usb ports of the VM */
@@ -414,12 +425,14 @@ struct USBBus {
struct USBBusOps {
int (*register_companion)(USBBus *bus, USBPort *ports[],
uint32_t portcount, uint32_t firstport);
+ void (*wakeup_endpoint)(USBBus *bus, USBEndpoint *ep);
};
void usb_bus_new(USBBus *bus, USBBusOps *ops, DeviceState *host);
USBBus *usb_bus_find(int busnr);
void usb_legacy_register(const char *typename, const char *usbdevice_name,
- USBDevice *(*usbdevice_init)(const char *params));
+ USBDevice *(*usbdevice_init)(USBBus *bus,
+ const char *params));
USBDevice *usb_create(USBBus *bus, const char *name);
USBDevice *usb_create_simple(USBBus *bus, const char *name);
USBDevice *usbdevice_create(const char *cmdline);
@@ -451,7 +464,7 @@ extern const VMStateDescription vmstate_usb_device;
.offset = vmstate_offset_value(_state, _field, USBDevice), \
}
-int usb_device_handle_packet(USBDevice *dev, USBPacket *p);
+USBDevice *usb_device_find_device(USBDevice *dev, uint8_t addr);
void usb_device_cancel_packet(USBDevice *dev, USBPacket *p);
diff --git a/hw/versatile_pci.c b/hw/versatile_pci.c
index c4105e977b..ae53a8b37b 100644
--- a/hw/versatile_pci.c
+++ b/hw/versatile_pci.c
@@ -154,11 +154,11 @@ static TypeInfo pci_realview_info = {
.class_init = pci_realview_class_init,
};
-static void versatile_pci_register_devices(void)
+static void versatile_pci_register_types(void)
{
type_register_static(&pci_vpb_info);
type_register_static(&pci_realview_info);
type_register_static(&versatile_pci_host_info);
}
-device_init(versatile_pci_register_devices)
+type_init(versatile_pci_register_types)
diff --git a/hw/versatilepb.c b/hw/versatilepb.c
index 6ea0ce5fb3..b9102f4a54 100644
--- a/hw/versatilepb.c
+++ b/hw/versatilepb.c
@@ -9,7 +9,6 @@
#include "sysbus.h"
#include "arm-misc.h"
-#include "primecell.h"
#include "devices.h"
#include "net.h"
#include "sysemu.h"
@@ -382,9 +381,9 @@ static TypeInfo vpb_sic_info = {
.class_init = vpb_sic_class_init,
};
-static void versatilepb_register_devices(void)
+static void versatilepb_register_types(void)
{
type_register_static(&vpb_sic_info);
}
-device_init(versatilepb_register_devices)
+type_init(versatilepb_register_types)
diff --git a/hw/vexpress.c b/hw/vexpress.c
index 43f47a65ce..b9aafec4cc 100644
--- a/hw/vexpress.c
+++ b/hw/vexpress.c
@@ -30,42 +30,152 @@
#include "boards.h"
#include "exec-memory.h"
-#define SMP_BOOT_ADDR 0xe0000000
-#define SMP_BOOTREG_ADDR 0x10000030
-
#define VEXPRESS_BOARD_ID 0x8e0
-static struct arm_boot_info vexpress_binfo = {
- .smp_loader_start = SMP_BOOT_ADDR,
- .smp_bootreg_addr = SMP_BOOTREG_ADDR,
+static struct arm_boot_info vexpress_binfo;
+
+/* Address maps for peripherals:
+ * the Versatile Express motherboard has two possible maps,
+ * the "legacy" one (used for A9) and the "Cortex-A Series"
+ * map (used for newer cores).
+ * Individual daughterboards can also have different maps for
+ * their peripherals.
+ */
+
+enum {
+ VE_SYSREGS,
+ VE_SP810,
+ VE_SERIALPCI,
+ VE_PL041,
+ VE_MMCI,
+ VE_KMI0,
+ VE_KMI1,
+ VE_UART0,
+ VE_UART1,
+ VE_UART2,
+ VE_UART3,
+ VE_WDT,
+ VE_TIMER01,
+ VE_TIMER23,
+ VE_SERIALDVI,
+ VE_RTC,
+ VE_COMPACTFLASH,
+ VE_CLCD,
+ VE_NORFLASH0,
+ VE_NORFLASH0ALIAS,
+ VE_NORFLASH1,
+ VE_SRAM,
+ VE_VIDEORAM,
+ VE_ETHERNET,
+ VE_USB,
+ VE_DAPROM,
};
-static void vexpress_a9_init(ram_addr_t ram_size,
- const char *boot_device,
- const char *kernel_filename, const char *kernel_cmdline,
- const char *initrd_filename, const char *cpu_model)
+static target_phys_addr_t motherboard_legacy_map[] = {
+ /* CS7: 0x10000000 .. 0x10020000 */
+ [VE_SYSREGS] = 0x10000000,
+ [VE_SP810] = 0x10001000,
+ [VE_SERIALPCI] = 0x10002000,
+ [VE_PL041] = 0x10004000,
+ [VE_MMCI] = 0x10005000,
+ [VE_KMI0] = 0x10006000,
+ [VE_KMI1] = 0x10007000,
+ [VE_UART0] = 0x10009000,
+ [VE_UART1] = 0x1000a000,
+ [VE_UART2] = 0x1000b000,
+ [VE_UART3] = 0x1000c000,
+ [VE_WDT] = 0x1000f000,
+ [VE_TIMER01] = 0x10011000,
+ [VE_TIMER23] = 0x10012000,
+ [VE_SERIALDVI] = 0x10016000,
+ [VE_RTC] = 0x10017000,
+ [VE_COMPACTFLASH] = 0x1001a000,
+ [VE_CLCD] = 0x1001f000,
+ /* CS0: 0x40000000 .. 0x44000000 */
+ [VE_NORFLASH0] = 0x40000000,
+ /* CS1: 0x44000000 .. 0x48000000 */
+ [VE_NORFLASH1] = 0x44000000,
+ /* CS2: 0x48000000 .. 0x4a000000 */
+ [VE_SRAM] = 0x48000000,
+ /* CS3: 0x4c000000 .. 0x50000000 */
+ [VE_VIDEORAM] = 0x4c000000,
+ [VE_ETHERNET] = 0x4e000000,
+ [VE_USB] = 0x4f000000,
+};
+
+static target_phys_addr_t motherboard_aseries_map[] = {
+ /* CS0: 0x00000000 .. 0x0c000000 */
+ [VE_NORFLASH0] = 0x00000000,
+ [VE_NORFLASH0ALIAS] = 0x08000000,
+ /* CS4: 0x0c000000 .. 0x10000000 */
+ [VE_NORFLASH1] = 0x0c000000,
+ /* CS5: 0x10000000 .. 0x14000000 */
+ /* CS1: 0x14000000 .. 0x18000000 */
+ [VE_SRAM] = 0x14000000,
+ /* CS2: 0x18000000 .. 0x1c000000 */
+ [VE_VIDEORAM] = 0x18000000,
+ [VE_ETHERNET] = 0x1a000000,
+ [VE_USB] = 0x1b000000,
+ /* CS3: 0x1c000000 .. 0x20000000 */
+ [VE_DAPROM] = 0x1c000000,
+ [VE_SYSREGS] = 0x1c010000,
+ [VE_SP810] = 0x1c020000,
+ [VE_SERIALPCI] = 0x1c030000,
+ [VE_PL041] = 0x1c040000,
+ [VE_MMCI] = 0x1c050000,
+ [VE_KMI0] = 0x1c060000,
+ [VE_KMI1] = 0x1c070000,
+ [VE_UART0] = 0x1c090000,
+ [VE_UART1] = 0x1c0a0000,
+ [VE_UART2] = 0x1c0b0000,
+ [VE_UART3] = 0x1c0c0000,
+ [VE_WDT] = 0x1c0f0000,
+ [VE_TIMER01] = 0x1c110000,
+ [VE_TIMER23] = 0x1c120000,
+ [VE_SERIALDVI] = 0x1c160000,
+ [VE_RTC] = 0x1c170000,
+ [VE_COMPACTFLASH] = 0x1c1a0000,
+ [VE_CLCD] = 0x1c1f0000,
+};
+
+/* Structure defining the peculiarities of a specific daughterboard */
+
+typedef struct VEDBoardInfo VEDBoardInfo;
+
+typedef void DBoardInitFn(const VEDBoardInfo *daughterboard,
+ ram_addr_t ram_size,
+ const char *cpu_model,
+ qemu_irq *pic, uint32_t *proc_id);
+
+struct VEDBoardInfo {
+ const target_phys_addr_t *motherboard_map;
+ target_phys_addr_t loader_start;
+ const target_phys_addr_t gic_cpu_if_addr;
+ DBoardInitFn *init;
+};
+
+static void a9_daughterboard_init(const VEDBoardInfo *daughterboard,
+ ram_addr_t ram_size,
+ const char *cpu_model,
+ qemu_irq *pic, uint32_t *proc_id)
{
CPUState *env = NULL;
MemoryRegion *sysmem = get_system_memory();
MemoryRegion *ram = g_new(MemoryRegion, 1);
MemoryRegion *lowram = g_new(MemoryRegion, 1);
- MemoryRegion *vram = g_new(MemoryRegion, 1);
- MemoryRegion *sram = g_new(MemoryRegion, 1);
- MemoryRegion *hackram = g_new(MemoryRegion, 1);
- DeviceState *dev, *sysctl, *pl041;
+ DeviceState *dev;
SysBusDevice *busdev;
qemu_irq *irqp;
- qemu_irq pic[64];
int n;
qemu_irq cpu_irq[4];
- uint32_t proc_id;
- uint32_t sys_id;
- ram_addr_t low_ram_size, vram_size, sram_size;
+ ram_addr_t low_ram_size;
if (!cpu_model) {
cpu_model = "cortex-a9";
}
+ *proc_id = 0x0c000191;
+
for (n = 0; n < smp_cpus; n++) {
env = cpu_init(cpu_model);
if (!env) {
@@ -78,7 +188,7 @@ static void vexpress_a9_init(ram_addr_t ram_size,
if (ram_size > 0x40000000) {
/* 1GB is the maximum the address space permits */
- fprintf(stderr, "vexpress: cannot model more than 1GB RAM\n");
+ fprintf(stderr, "vexpress-a9: cannot model more than 1GB RAM\n");
exit(1);
}
@@ -101,8 +211,7 @@ static void vexpress_a9_init(ram_addr_t ram_size,
qdev_prop_set_uint32(dev, "num-cpu", smp_cpus);
qdev_init_nofail(dev);
busdev = sysbus_from_qdev(dev);
- vexpress_binfo.smp_priv_base = 0x1e000000;
- sysbus_mmio_map(busdev, 0, vexpress_binfo.smp_priv_base);
+ sysbus_mmio_map(busdev, 0, 0x1e000000);
for (n = 0; n < smp_cpus; n++) {
sysbus_connect_irq(busdev, n, cpu_irq[n]);
}
@@ -116,104 +225,208 @@ static void vexpress_a9_init(ram_addr_t ram_size,
pic[n] = qdev_get_gpio_in(dev, n);
}
- /* Motherboard peripherals CS7 : 0x10000000 .. 0x10020000 */
+ /* Daughterboard peripherals : 0x10020000 .. 0x20000000 */
+
+ /* 0x10020000 PL111 CLCD (daughterboard) */
+ sysbus_create_simple("pl111", 0x10020000, pic[44]);
+
+ /* 0x10060000 AXI RAM */
+ /* 0x100e0000 PL341 Dynamic Memory Controller */
+ /* 0x100e1000 PL354 Static Memory Controller */
+ /* 0x100e2000 System Configuration Controller */
+
+ sysbus_create_simple("sp804", 0x100e4000, pic[48]);
+ /* 0x100e5000 SP805 Watchdog module */
+ /* 0x100e6000 BP147 TrustZone Protection Controller */
+ /* 0x100e9000 PL301 'Fast' AXI matrix */
+ /* 0x100ea000 PL301 'Slow' AXI matrix */
+ /* 0x100ec000 TrustZone Address Space Controller */
+ /* 0x10200000 CoreSight debug APB */
+ /* 0x1e00a000 PL310 L2 Cache Controller */
+ sysbus_create_varargs("l2x0", 0x1e00a000, NULL);
+}
+
+static const VEDBoardInfo a9_daughterboard = {
+ .motherboard_map = motherboard_legacy_map,
+ .loader_start = 0x60000000,
+ .gic_cpu_if_addr = 0x1e000100,
+ .init = a9_daughterboard_init,
+};
+
+static void a15_daughterboard_init(const VEDBoardInfo *daughterboard,
+ ram_addr_t ram_size,
+ const char *cpu_model,
+ qemu_irq *pic, uint32_t *proc_id)
+{
+ int n;
+ CPUState *env = NULL;
+ MemoryRegion *sysmem = get_system_memory();
+ MemoryRegion *ram = g_new(MemoryRegion, 1);
+ MemoryRegion *sram = g_new(MemoryRegion, 1);
+ qemu_irq cpu_irq[4];
+ DeviceState *dev;
+ SysBusDevice *busdev;
+
+ if (!cpu_model) {
+ cpu_model = "cortex-a15";
+ }
+
+ *proc_id = 0x14000217;
+
+ for (n = 0; n < smp_cpus; n++) {
+ qemu_irq *irqp;
+ env = cpu_init(cpu_model);
+ if (!env) {
+ fprintf(stderr, "Unable to find CPU definition\n");
+ exit(1);
+ }
+ irqp = arm_pic_init_cpu(env);
+ cpu_irq[n] = irqp[ARM_PIC_CPU_IRQ];
+ }
+
+ if (ram_size > 0x80000000) {
+ fprintf(stderr, "vexpress-a15: cannot model more than 2GB RAM\n");
+ exit(1);
+ }
+
+ memory_region_init_ram(ram, "vexpress.highmem", ram_size);
+ vmstate_register_ram_global(ram);
+ /* RAM is from 0x80000000 upwards; there is no low-memory alias for it. */
+ memory_region_add_subregion(sysmem, 0x80000000, ram);
+
+ /* 0x2c000000 A15MPCore private memory region (GIC) */
+ dev = qdev_create(NULL, "a15mpcore_priv");
+ qdev_prop_set_uint32(dev, "num-cpu", smp_cpus);
+ qdev_init_nofail(dev);
+ busdev = sysbus_from_qdev(dev);
+ sysbus_mmio_map(busdev, 0, 0x2c000000);
+ for (n = 0; n < smp_cpus; n++) {
+ sysbus_connect_irq(busdev, n, cpu_irq[n]);
+ }
+ /* Interrupts [42:0] are from the motherboard;
+ * [47:43] are reserved; [63:48] are daughterboard
+ * peripherals. Note that some documentation numbers
+ * external interrupts starting from 32 (because there
+ * are internal interrupts 0..31).
+ */
+ for (n = 0; n < 64; n++) {
+ pic[n] = qdev_get_gpio_in(dev, n);
+ }
+
+ /* A15 daughterboard peripherals: */
+
+ /* 0x20000000: CoreSight interfaces: not modelled */
+ /* 0x2a000000: PL301 AXI interconnect: not modelled */
+ /* 0x2a420000: SCC: not modelled */
+ /* 0x2a430000: system counter: not modelled */
+ /* 0x2b000000: HDLCD controller: not modelled */
+ /* 0x2b060000: SP805 watchdog: not modelled */
+ /* 0x2b0a0000: PL341 dynamic memory controller: not modelled */
+ /* 0x2e000000: system SRAM */
+ memory_region_init_ram(sram, "vexpress.a15sram", 0x10000);
+ vmstate_register_ram_global(sram);
+ memory_region_add_subregion(sysmem, 0x2e000000, sram);
+
+ /* 0x7ffb0000: DMA330 DMA controller: not modelled */
+ /* 0x7ffd0000: PL354 static memory controller: not modelled */
+}
+
+static const VEDBoardInfo a15_daughterboard = {
+ .motherboard_map = motherboard_aseries_map,
+ .loader_start = 0x80000000,
+ .gic_cpu_if_addr = 0x2c002000,
+ .init = a15_daughterboard_init,
+};
+
+static void vexpress_common_init(const VEDBoardInfo *daughterboard,
+ ram_addr_t ram_size,
+ const char *boot_device,
+ const char *kernel_filename,
+ const char *kernel_cmdline,
+ const char *initrd_filename,
+ const char *cpu_model)
+{
+ DeviceState *dev, *sysctl, *pl041;
+ qemu_irq pic[64];
+ uint32_t proc_id;
+ uint32_t sys_id;
+ ram_addr_t vram_size, sram_size;
+ MemoryRegion *sysmem = get_system_memory();
+ MemoryRegion *vram = g_new(MemoryRegion, 1);
+ MemoryRegion *sram = g_new(MemoryRegion, 1);
+ const target_phys_addr_t *map = daughterboard->motherboard_map;
+
+ daughterboard->init(daughterboard, ram_size, cpu_model, pic, &proc_id);
+
+ /* Motherboard peripherals: the wiring is the same but the
+ * addresses vary between the legacy and A-Series memory maps.
+ */
+
sys_id = 0x1190f500;
- proc_id = 0x0c000191;
- /* 0x10000000 System registers */
sysctl = qdev_create(NULL, "realview_sysctl");
qdev_prop_set_uint32(sysctl, "sys_id", sys_id);
qdev_prop_set_uint32(sysctl, "proc_id", proc_id);
qdev_init_nofail(sysctl);
- sysbus_mmio_map(sysbus_from_qdev(sysctl), 0, 0x10000000);
+ sysbus_mmio_map(sysbus_from_qdev(sysctl), 0, map[VE_SYSREGS]);
+
+ /* VE_SP810: not modelled */
+ /* VE_SERIALPCI: not modelled */
- /* 0x10001000 SP810 system control */
- /* 0x10002000 serial bus PCI */
- /* 0x10004000 PL041 audio */
pl041 = qdev_create(NULL, "pl041");
qdev_prop_set_uint32(pl041, "nc_fifo_depth", 512);
qdev_init_nofail(pl041);
- sysbus_mmio_map(sysbus_from_qdev(pl041), 0, 0x10004000);
+ sysbus_mmio_map(sysbus_from_qdev(pl041), 0, map[VE_PL041]);
sysbus_connect_irq(sysbus_from_qdev(pl041), 0, pic[11]);
- dev = sysbus_create_varargs("pl181", 0x10005000, pic[9], pic[10], NULL);
+ dev = sysbus_create_varargs("pl181", map[VE_MMCI], pic[9], pic[10], NULL);
/* Wire up MMC card detect and read-only signals */
qdev_connect_gpio_out(dev, 0,
qdev_get_gpio_in(sysctl, ARM_SYSCTL_GPIO_MMC_WPROT));
qdev_connect_gpio_out(dev, 1,
qdev_get_gpio_in(sysctl, ARM_SYSCTL_GPIO_MMC_CARDIN));
- sysbus_create_simple("pl050_keyboard", 0x10006000, pic[12]);
- sysbus_create_simple("pl050_mouse", 0x10007000, pic[13]);
-
- sysbus_create_simple("pl011", 0x10009000, pic[5]);
- sysbus_create_simple("pl011", 0x1000a000, pic[6]);
- sysbus_create_simple("pl011", 0x1000b000, pic[7]);
- sysbus_create_simple("pl011", 0x1000c000, pic[8]);
-
- /* 0x1000f000 SP805 WDT */
+ sysbus_create_simple("pl050_keyboard", map[VE_KMI0], pic[12]);
+ sysbus_create_simple("pl050_mouse", map[VE_KMI1], pic[13]);
- sysbus_create_simple("sp804", 0x10011000, pic[2]);
- sysbus_create_simple("sp804", 0x10012000, pic[3]);
+ sysbus_create_simple("pl011", map[VE_UART0], pic[5]);
+ sysbus_create_simple("pl011", map[VE_UART1], pic[6]);
+ sysbus_create_simple("pl011", map[VE_UART2], pic[7]);
+ sysbus_create_simple("pl011", map[VE_UART3], pic[8]);
- /* 0x10016000 Serial Bus DVI */
+ sysbus_create_simple("sp804", map[VE_TIMER01], pic[2]);
+ sysbus_create_simple("sp804", map[VE_TIMER23], pic[3]);
- sysbus_create_simple("pl031", 0x10017000, pic[4]); /* RTC */
+ /* VE_SERIALDVI: not modelled */
- /* 0x1001a000 Compact Flash */
+ sysbus_create_simple("pl031", map[VE_RTC], pic[4]); /* RTC */
- /* 0x1001f000 PL111 CLCD (motherboard) */
+ /* VE_COMPACTFLASH: not modelled */
- /* Daughterboard peripherals : 0x10020000 .. 0x20000000 */
-
- /* 0x10020000 PL111 CLCD (daughterboard) */
- sysbus_create_simple("pl111", 0x10020000, pic[44]);
+ sysbus_create_simple("pl111", map[VE_CLCD], pic[14]);
- /* 0x10060000 AXI RAM */
- /* 0x100e0000 PL341 Dynamic Memory Controller */
- /* 0x100e1000 PL354 Static Memory Controller */
- /* 0x100e2000 System Configuration Controller */
-
- sysbus_create_simple("sp804", 0x100e4000, pic[48]);
- /* 0x100e5000 SP805 Watchdog module */
- /* 0x100e6000 BP147 TrustZone Protection Controller */
- /* 0x100e9000 PL301 'Fast' AXI matrix */
- /* 0x100ea000 PL301 'Slow' AXI matrix */
- /* 0x100ec000 TrustZone Address Space Controller */
- /* 0x10200000 CoreSight debug APB */
- /* 0x1e00a000 PL310 L2 Cache Controller */
- sysbus_create_varargs("l2x0", 0x1e00a000, NULL);
+ /* VE_NORFLASH0: not modelled */
+ /* VE_NORFLASH0ALIAS: not modelled */
+ /* VE_NORFLASH1: not modelled */
- /* CS0: NOR0 flash : 0x40000000 .. 0x44000000 */
- /* CS4: NOR1 flash : 0x44000000 .. 0x48000000 */
- /* CS2: SRAM : 0x48000000 .. 0x4a000000 */
sram_size = 0x2000000;
memory_region_init_ram(sram, "vexpress.sram", sram_size);
vmstate_register_ram_global(sram);
- memory_region_add_subregion(sysmem, 0x48000000, sram);
-
- /* CS3: USB, ethernet, VRAM : 0x4c000000 .. 0x50000000 */
+ memory_region_add_subregion(sysmem, map[VE_SRAM], sram);
- /* 0x4c000000 Video RAM */
vram_size = 0x800000;
memory_region_init_ram(vram, "vexpress.vram", vram_size);
vmstate_register_ram_global(vram);
- memory_region_add_subregion(sysmem, 0x4c000000, vram);
+ memory_region_add_subregion(sysmem, map[VE_VIDEORAM], vram);
/* 0x4e000000 LAN9118 Ethernet */
if (nd_table[0].vlan) {
- lan9118_init(&nd_table[0], 0x4e000000, pic[15]);
+ lan9118_init(&nd_table[0], map[VE_ETHERNET], pic[15]);
}
- /* 0x4f000000 ISP1761 USB */
+ /* VE_USB: not modelled */
- /* ??? Hack to map an additional page of ram for the secondary CPU
- startup code. I guess this works on real hardware because the
- BootROM happens to be in ROM/flash or in memory that isn't clobbered
- until after Linux boots the secondary CPUs. */
- memory_region_init_ram(hackram, "vexpress.hack", 0x1000);
- vmstate_register_ram_global(hackram);
- memory_region_add_subregion(sysmem, SMP_BOOT_ADDR, hackram);
+ /* VE_DAPROM: not modelled */
vexpress_binfo.ram_size = ram_size;
vexpress_binfo.kernel_filename = kernel_filename;
@@ -221,10 +434,36 @@ static void vexpress_a9_init(ram_addr_t ram_size,
vexpress_binfo.initrd_filename = initrd_filename;
vexpress_binfo.nb_cpus = smp_cpus;
vexpress_binfo.board_id = VEXPRESS_BOARD_ID;
- vexpress_binfo.loader_start = 0x60000000;
+ vexpress_binfo.loader_start = daughterboard->loader_start;
+ vexpress_binfo.smp_loader_start = map[VE_SRAM];
+ vexpress_binfo.smp_bootreg_addr = map[VE_SYSREGS] + 0x30;
+ vexpress_binfo.gic_cpu_if_addr = daughterboard->gic_cpu_if_addr;
arm_load_kernel(first_cpu, &vexpress_binfo);
}
+static void vexpress_a9_init(ram_addr_t ram_size,
+ const char *boot_device,
+ const char *kernel_filename,
+ const char *kernel_cmdline,
+ const char *initrd_filename,
+ const char *cpu_model)
+{
+ vexpress_common_init(&a9_daughterboard,
+ ram_size, boot_device, kernel_filename,
+ kernel_cmdline, initrd_filename, cpu_model);
+}
+
+static void vexpress_a15_init(ram_addr_t ram_size,
+ const char *boot_device,
+ const char *kernel_filename,
+ const char *kernel_cmdline,
+ const char *initrd_filename,
+ const char *cpu_model)
+{
+ vexpress_common_init(&a15_daughterboard,
+ ram_size, boot_device, kernel_filename,
+ kernel_cmdline, initrd_filename, cpu_model);
+}
static QEMUMachine vexpress_a9_machine = {
.name = "vexpress-a9",
@@ -234,9 +473,18 @@ static QEMUMachine vexpress_a9_machine = {
.max_cpus = 4,
};
+static QEMUMachine vexpress_a15_machine = {
+ .name = "vexpress-a15",
+ .desc = "ARM Versatile Express for Cortex-A15",
+ .init = vexpress_a15_init,
+ .use_scsi = 1,
+ .max_cpus = 4,
+};
+
static void vexpress_machine_init(void)
{
qemu_register_machine(&vexpress_a9_machine);
+ qemu_register_machine(&vexpress_a15_machine);
}
machine_init(vexpress_machine_init);
diff --git a/hw/vga-isa.c b/hw/vga-isa.c
index 8d3ff0d47b..4bcc4db62f 100644
--- a/hw/vga-isa.c
+++ b/hw/vga-isa.c
@@ -85,8 +85,9 @@ static TypeInfo vga_info = {
.class_init = vga_class_initfn,
};
-static void vga_register(void)
+static void vga_register_types(void)
{
type_register_static(&vga_info);
}
-device_init(vga_register)
+
+type_init(vga_register_types)
diff --git a/hw/vga-pci.c b/hw/vga-pci.c
index 974a7a9d6f..465b643d21 100644
--- a/hw/vga-pci.c
+++ b/hw/vga-pci.c
@@ -96,8 +96,9 @@ static TypeInfo vga_info = {
.class_init = vga_class_init,
};
-static void vga_register(void)
+static void vga_register_types(void)
{
type_register_static(&vga_info);
}
-device_init(vga_register);
+
+type_init(vga_register_types)
diff --git a/hw/vga.c b/hw/vga.c
index c1029dbd9b..5994f43b75 100644
--- a/hw/vga.c
+++ b/hw/vga.c
@@ -162,9 +162,7 @@ static uint32_t expand4[256];
static uint16_t expand2[256];
static uint8_t expand4to8[16];
-static void vga_screen_dump(void *opaque, const char *filename);
-static const char *screen_dump_filename;
-static DisplayChangeListener *screen_dump_dcl;
+static void vga_screen_dump(void *opaque, const char *filename, bool cswitch);
static void vga_update_memory_access(VGACommonState *s)
{
@@ -2364,22 +2362,6 @@ void vga_init_vbe(VGACommonState *s, MemoryRegion *system_memory)
/********************************************************/
/* vga screen dump */
-static void vga_save_dpy_update(DisplayState *ds,
- int x, int y, int w, int h)
-{
- if (screen_dump_filename) {
- ppm_save(screen_dump_filename, ds->surface);
- }
-}
-
-static void vga_save_dpy_resize(DisplayState *s)
-{
-}
-
-static void vga_save_dpy_refresh(DisplayState *s)
-{
-}
-
int ppm_save(const char *filename, struct DisplaySurface *ds)
{
FILE *f;
@@ -2423,29 +2405,15 @@ int ppm_save(const char *filename, struct DisplaySurface *ds)
return 0;
}
-static DisplayChangeListener* vga_screen_dump_init(DisplayState *ds)
-{
- DisplayChangeListener *dcl;
-
- dcl = g_malloc0(sizeof(DisplayChangeListener));
- dcl->dpy_update = vga_save_dpy_update;
- dcl->dpy_resize = vga_save_dpy_resize;
- dcl->dpy_refresh = vga_save_dpy_refresh;
- register_displaychangelistener(ds, dcl);
- return dcl;
-}
-
/* save the vga display in a PPM image even if no display is
available */
-static void vga_screen_dump(void *opaque, const char *filename)
+static void vga_screen_dump(void *opaque, const char *filename, bool cswitch)
{
VGACommonState *s = opaque;
- if (!screen_dump_dcl)
- screen_dump_dcl = vga_screen_dump_init(s->ds);
-
- screen_dump_filename = filename;
- vga_invalidate_display(s);
- vga_hw_update();
- screen_dump_filename = NULL;
+ if (cswitch) {
+ vga_invalidate_display(s);
+ vga_hw_update();
+ }
+ ppm_save(filename, s->ds->surface);
}
diff --git a/hw/virtio-blk.c b/hw/virtio-blk.c
index a5a439668b..49990f8efe 100644
--- a/hw/virtio-blk.c
+++ b/hw/virtio-blk.c
@@ -69,7 +69,7 @@ static int virtio_blk_handle_rw_error(VirtIOBlockReq *req, int error,
VirtIOBlock *s = req->dev;
if (action == BLOCK_ERR_IGNORE) {
- bdrv_mon_event(s->bs, BDRV_ACTION_IGNORE, is_read);
+ bdrv_emit_qmp_error_event(s->bs, BDRV_ACTION_IGNORE, is_read);
return 0;
}
@@ -77,14 +77,14 @@ static int virtio_blk_handle_rw_error(VirtIOBlockReq *req, int error,
|| action == BLOCK_ERR_STOP_ANY) {
req->next = s->rq;
s->rq = req;
- bdrv_mon_event(s->bs, BDRV_ACTION_STOP, is_read);
+ bdrv_emit_qmp_error_event(s->bs, BDRV_ACTION_STOP, is_read);
vm_stop(RUN_STATE_IO_ERROR);
bdrv_iostatus_set_err(s->bs, error);
} else {
virtio_blk_req_complete(req, VIRTIO_BLK_S_IOERR);
bdrv_acct_done(s->bs, &req->acct);
g_free(req);
- bdrv_mon_event(s->bs, BDRV_ACTION_REPORT, is_read);
+ bdrv_emit_qmp_error_event(s->bs, BDRV_ACTION_REPORT, is_read);
}
return 1;
diff --git a/hw/virtio-console.c b/hw/virtio-console.c
index 4f2c3e4379..cffee3d470 100644
--- a/hw/virtio-console.c
+++ b/hw/virtio-console.c
@@ -149,12 +149,6 @@ static TypeInfo virtconsole_info = {
.class_init = virtconsole_class_init,
};
-static void virtconsole_register(void)
-{
- type_register_static(&virtconsole_info);
-}
-device_init(virtconsole_register)
-
static Property virtserialport_properties[] = {
DEFINE_PROP_CHR("chardev", VirtConsole, chr),
DEFINE_PROP_END_OF_LIST(),
@@ -179,8 +173,10 @@ static TypeInfo virtserialport_info = {
.class_init = virtserialport_class_init,
};
-static void virtserialport_register(void)
+static void virtconsole_register_types(void)
{
+ type_register_static(&virtconsole_info);
type_register_static(&virtserialport_info);
}
-device_init(virtserialport_register)
+
+type_init(virtconsole_register_types)
diff --git a/hw/virtio-pci.c b/hw/virtio-pci.c
index 93fff54782..a0fb7c1b9c 100644
--- a/hw/virtio-pci.c
+++ b/hw/virtio-pci.c
@@ -21,6 +21,7 @@
#include "virtio-blk.h"
#include "virtio-net.h"
#include "virtio-serial.h"
+#include "virtio-scsi.h"
#include "pci.h"
#include "qemu-error.h"
#include "msix.h"
@@ -930,12 +931,67 @@ static TypeInfo virtio_balloon_info = {
.class_init = virtio_balloon_class_init,
};
-static void virtio_pci_register_devices(void)
+static int virtio_scsi_init_pci(PCIDevice *pci_dev)
+{
+ VirtIOPCIProxy *proxy = DO_UPCAST(VirtIOPCIProxy, pci_dev, pci_dev);
+ VirtIODevice *vdev;
+
+ vdev = virtio_scsi_init(&pci_dev->qdev, &proxy->scsi);
+ if (!vdev) {
+ return -EINVAL;
+ }
+
+ vdev->nvectors = proxy->nvectors;
+ virtio_init_pci(proxy, vdev);
+
+ /* make the actual value visible */
+ proxy->nvectors = vdev->nvectors;
+ return 0;
+}
+
+static int virtio_scsi_exit_pci(PCIDevice *pci_dev)
+{
+ VirtIOPCIProxy *proxy = DO_UPCAST(VirtIOPCIProxy, pci_dev, pci_dev);
+
+ virtio_scsi_exit(proxy->vdev);
+ return virtio_exit_pci(pci_dev);
+}
+
+static Property virtio_scsi_properties[] = {
+ DEFINE_PROP_UINT32("vectors", VirtIOPCIProxy, nvectors, 2),
+ DEFINE_VIRTIO_SCSI_PROPERTIES(VirtIOPCIProxy, host_features, scsi),
+ DEFINE_PROP_END_OF_LIST(),
+};
+
+static void virtio_scsi_class_init(ObjectClass *klass, void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(klass);
+ PCIDeviceClass *k = PCI_DEVICE_CLASS(klass);
+
+ k->init = virtio_scsi_init_pci;
+ k->exit = virtio_scsi_exit_pci;
+ k->vendor_id = PCI_VENDOR_ID_REDHAT_QUMRANET;
+ k->device_id = PCI_DEVICE_ID_VIRTIO_SCSI;
+ k->revision = 0x00;
+ k->class_id = PCI_CLASS_STORAGE_SCSI;
+ dc->reset = virtio_pci_reset;
+ dc->props = virtio_scsi_properties;
+}
+
+static TypeInfo virtio_scsi_info = {
+ .name = "virtio-scsi-pci",
+ .parent = TYPE_PCI_DEVICE,
+ .instance_size = sizeof(VirtIOPCIProxy),
+ .class_init = virtio_scsi_class_init,
+};
+
+static void virtio_pci_register_types(void)
{
type_register_static(&virtio_blk_info);
type_register_static(&virtio_net_info);
type_register_static(&virtio_serial_info);
type_register_static(&virtio_balloon_info);
+ type_register_static(&virtio_scsi_info);
}
-device_init(virtio_pci_register_devices)
+type_init(virtio_pci_register_types)
diff --git a/hw/virtio-pci.h b/hw/virtio-pci.h
index 344c22b68f..e5604282e5 100644
--- a/hw/virtio-pci.h
+++ b/hw/virtio-pci.h
@@ -17,6 +17,7 @@
#include "virtio-net.h"
#include "virtio-serial.h"
+#include "virtio-scsi.h"
/* Performance improves when virtqueue kick processing is decoupled from the
* vcpu thread using ioeventfd for some devices. */
@@ -40,6 +41,7 @@ typedef struct {
#endif
virtio_serial_conf serial;
virtio_net_conf net;
+ VirtIOSCSIConf scsi;
bool ioeventfd_disabled;
bool ioeventfd_started;
} VirtIOPCIProxy;
diff --git a/hw/virtio-scsi.c b/hw/virtio-scsi.c
new file mode 100644
index 0000000000..e607edc915
--- /dev/null
+++ b/hw/virtio-scsi.c
@@ -0,0 +1,617 @@
+/*
+ * Virtio SCSI HBA
+ *
+ * Copyright IBM, Corp. 2010
+ * Copyright Red Hat, Inc. 2011
+ *
+ * Authors:
+ * Stefan Hajnoczi <stefanha@linux.vnet.ibm.com>
+ * Paolo Bonzini <pbonzini@redhat.com>
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2 or later.
+ * See the COPYING file in the top-level directory.
+ *
+ */
+
+#include "virtio-scsi.h"
+#include <hw/scsi.h>
+#include <hw/scsi-defs.h>
+
+#define VIRTIO_SCSI_VQ_SIZE 128
+#define VIRTIO_SCSI_CDB_SIZE 32
+#define VIRTIO_SCSI_SENSE_SIZE 96
+#define VIRTIO_SCSI_MAX_CHANNEL 0
+#define VIRTIO_SCSI_MAX_TARGET 255
+#define VIRTIO_SCSI_MAX_LUN 16383
+
+/* Response codes */
+#define VIRTIO_SCSI_S_OK 0
+#define VIRTIO_SCSI_S_OVERRUN 1
+#define VIRTIO_SCSI_S_ABORTED 2
+#define VIRTIO_SCSI_S_BAD_TARGET 3
+#define VIRTIO_SCSI_S_RESET 4
+#define VIRTIO_SCSI_S_BUSY 5
+#define VIRTIO_SCSI_S_TRANSPORT_FAILURE 6
+#define VIRTIO_SCSI_S_TARGET_FAILURE 7
+#define VIRTIO_SCSI_S_NEXUS_FAILURE 8
+#define VIRTIO_SCSI_S_FAILURE 9
+#define VIRTIO_SCSI_S_FUNCTION_SUCCEEDED 10
+#define VIRTIO_SCSI_S_FUNCTION_REJECTED 11
+#define VIRTIO_SCSI_S_INCORRECT_LUN 12
+
+/* Controlq type codes. */
+#define VIRTIO_SCSI_T_TMF 0
+#define VIRTIO_SCSI_T_AN_QUERY 1
+#define VIRTIO_SCSI_T_AN_SUBSCRIBE 2
+
+/* Valid TMF subtypes. */
+#define VIRTIO_SCSI_T_TMF_ABORT_TASK 0
+#define VIRTIO_SCSI_T_TMF_ABORT_TASK_SET 1
+#define VIRTIO_SCSI_T_TMF_CLEAR_ACA 2
+#define VIRTIO_SCSI_T_TMF_CLEAR_TASK_SET 3
+#define VIRTIO_SCSI_T_TMF_I_T_NEXUS_RESET 4
+#define VIRTIO_SCSI_T_TMF_LOGICAL_UNIT_RESET 5
+#define VIRTIO_SCSI_T_TMF_QUERY_TASK 6
+#define VIRTIO_SCSI_T_TMF_QUERY_TASK_SET 7
+
+/* Events. */
+#define VIRTIO_SCSI_T_EVENTS_MISSED 0x80000000
+#define VIRTIO_SCSI_T_NO_EVENT 0
+#define VIRTIO_SCSI_T_TRANSPORT_RESET 1
+#define VIRTIO_SCSI_T_ASYNC_NOTIFY 2
+
+/* SCSI command request, followed by data-out */
+typedef struct {
+ uint8_t lun[8]; /* Logical Unit Number */
+ uint64_t tag; /* Command identifier */
+ uint8_t task_attr; /* Task attribute */
+ uint8_t prio;
+ uint8_t crn;
+ uint8_t cdb[];
+} QEMU_PACKED VirtIOSCSICmdReq;
+
+/* Response, followed by sense data and data-in */
+typedef struct {
+ uint32_t sense_len; /* Sense data length */
+ uint32_t resid; /* Residual bytes in data buffer */
+ uint16_t status_qualifier; /* Status qualifier */
+ uint8_t status; /* Command completion status */
+ uint8_t response; /* Response values */
+ uint8_t sense[];
+} QEMU_PACKED VirtIOSCSICmdResp;
+
+/* Task Management Request */
+typedef struct {
+ uint32_t type;
+ uint32_t subtype;
+ uint8_t lun[8];
+ uint64_t tag;
+} QEMU_PACKED VirtIOSCSICtrlTMFReq;
+
+typedef struct {
+ uint8_t response;
+} QEMU_PACKED VirtIOSCSICtrlTMFResp;
+
+/* Asynchronous notification query/subscription */
+typedef struct {
+ uint32_t type;
+ uint8_t lun[8];
+ uint32_t event_requested;
+} QEMU_PACKED VirtIOSCSICtrlANReq;
+
+typedef struct {
+ uint32_t event_actual;
+ uint8_t response;
+} QEMU_PACKED VirtIOSCSICtrlANResp;
+
+typedef struct {
+ uint32_t event;
+ uint8_t lun[8];
+ uint32_t reason;
+} QEMU_PACKED VirtIOSCSIEvent;
+
+typedef struct {
+ uint32_t num_queues;
+ uint32_t seg_max;
+ uint32_t max_sectors;
+ uint32_t cmd_per_lun;
+ uint32_t event_info_size;
+ uint32_t sense_size;
+ uint32_t cdb_size;
+ uint16_t max_channel;
+ uint16_t max_target;
+ uint32_t max_lun;
+} QEMU_PACKED VirtIOSCSIConfig;
+
+typedef struct {
+ VirtIODevice vdev;
+ DeviceState *qdev;
+ VirtIOSCSIConf *conf;
+
+ SCSIBus bus;
+ VirtQueue *ctrl_vq;
+ VirtQueue *event_vq;
+ VirtQueue *cmd_vq;
+ uint32_t sense_size;
+ uint32_t cdb_size;
+ int resetting;
+} VirtIOSCSI;
+
+typedef struct VirtIOSCSIReq {
+ VirtIOSCSI *dev;
+ VirtQueue *vq;
+ VirtQueueElement elem;
+ QEMUSGList qsgl;
+ SCSIRequest *sreq;
+ union {
+ char *buf;
+ VirtIOSCSICmdReq *cmd;
+ VirtIOSCSICtrlTMFReq *tmf;
+ VirtIOSCSICtrlANReq *an;
+ } req;
+ union {
+ char *buf;
+ VirtIOSCSICmdResp *cmd;
+ VirtIOSCSICtrlTMFResp *tmf;
+ VirtIOSCSICtrlANResp *an;
+ VirtIOSCSIEvent *event;
+ } resp;
+} VirtIOSCSIReq;
+
+static inline int virtio_scsi_get_lun(uint8_t *lun)
+{
+ return ((lun[2] << 8) | lun[3]) & 0x3FFF;
+}
+
+static inline SCSIDevice *virtio_scsi_device_find(VirtIOSCSI *s, uint8_t *lun)
+{
+ if (lun[0] != 1) {
+ return NULL;
+ }
+ if (lun[2] != 0 && !(lun[2] >= 0x40 && lun[2] < 0x80)) {
+ return NULL;
+ }
+ return scsi_device_find(&s->bus, 0, lun[1], virtio_scsi_get_lun(lun));
+}
+
+static void virtio_scsi_complete_req(VirtIOSCSIReq *req)
+{
+ VirtIOSCSI *s = req->dev;
+ VirtQueue *vq = req->vq;
+ virtqueue_push(vq, &req->elem, req->qsgl.size + req->elem.in_sg[0].iov_len);
+ qemu_sglist_destroy(&req->qsgl);
+ if (req->sreq) {
+ req->sreq->hba_private = NULL;
+ scsi_req_unref(req->sreq);
+ }
+ g_free(req);
+ virtio_notify(&s->vdev, vq);
+}
+
+static void virtio_scsi_bad_req(void)
+{
+ error_report("wrong size for virtio-scsi headers");
+ exit(1);
+}
+
+static void qemu_sgl_init_external(QEMUSGList *qsgl, struct iovec *sg,
+ target_phys_addr_t *addr, int num)
+{
+ memset(qsgl, 0, sizeof(*qsgl));
+ while (num--) {
+ qemu_sglist_add(qsgl, *(addr++), (sg++)->iov_len);
+ }
+}
+
+static void virtio_scsi_parse_req(VirtIOSCSI *s, VirtQueue *vq,
+ VirtIOSCSIReq *req)
+{
+ assert(req->elem.out_num && req->elem.in_num);
+ req->vq = vq;
+ req->dev = s;
+ req->sreq = NULL;
+ req->req.buf = req->elem.out_sg[0].iov_base;
+ req->resp.buf = req->elem.in_sg[0].iov_base;
+
+ if (req->elem.out_num > 1) {
+ qemu_sgl_init_external(&req->qsgl, &req->elem.out_sg[1],
+ &req->elem.out_addr[1],
+ req->elem.out_num - 1);
+ } else {
+ qemu_sgl_init_external(&req->qsgl, &req->elem.in_sg[1],
+ &req->elem.in_addr[1],
+ req->elem.in_num - 1);
+ }
+}
+
+static VirtIOSCSIReq *virtio_scsi_pop_req(VirtIOSCSI *s, VirtQueue *vq)
+{
+ VirtIOSCSIReq *req;
+ req = g_malloc(sizeof(*req));
+ if (!virtqueue_pop(vq, &req->elem)) {
+ g_free(req);
+ return NULL;
+ }
+
+ virtio_scsi_parse_req(s, vq, req);
+ return req;
+}
+
+static void virtio_scsi_save_request(QEMUFile *f, SCSIRequest *sreq)
+{
+ VirtIOSCSIReq *req = sreq->hba_private;
+
+ qemu_put_buffer(f, (unsigned char *)&req->elem, sizeof(req->elem));
+}
+
+static void *virtio_scsi_load_request(QEMUFile *f, SCSIRequest *sreq)
+{
+ SCSIBus *bus = sreq->bus;
+ VirtIOSCSI *s = container_of(bus, VirtIOSCSI, bus);
+ VirtIOSCSIReq *req;
+
+ req = g_malloc(sizeof(*req));
+ qemu_get_buffer(f, (unsigned char *)&req->elem, sizeof(req->elem));
+ virtio_scsi_parse_req(s, s->cmd_vq, req);
+
+ scsi_req_ref(sreq);
+ req->sreq = sreq;
+ if (req->sreq->cmd.mode != SCSI_XFER_NONE) {
+ int req_mode =
+ (req->elem.in_num > 1 ? SCSI_XFER_FROM_DEV : SCSI_XFER_TO_DEV);
+
+ assert(req->sreq->cmd.mode == req_mode);
+ }
+ return req;
+}
+
+static void virtio_scsi_do_tmf(VirtIOSCSI *s, VirtIOSCSIReq *req)
+{
+ SCSIDevice *d = virtio_scsi_device_find(s, req->req.tmf->lun);
+ SCSIRequest *r, *next;
+ DeviceState *qdev;
+ int target;
+
+ /* Here VIRTIO_SCSI_S_OK means "FUNCTION COMPLETE". */
+ req->resp.tmf->response = VIRTIO_SCSI_S_OK;
+
+ switch (req->req.tmf->subtype) {
+ case VIRTIO_SCSI_T_TMF_ABORT_TASK:
+ case VIRTIO_SCSI_T_TMF_QUERY_TASK:
+ if (!d) {
+ goto fail;
+ }
+ if (d->lun != virtio_scsi_get_lun(req->req.tmf->lun)) {
+ goto incorrect_lun;
+ }
+ QTAILQ_FOREACH_SAFE(r, &d->requests, next, next) {
+ if (r->tag == req->req.tmf->tag) {
+ break;
+ }
+ }
+ if (r && r->hba_private) {
+ if (req->req.tmf->subtype == VIRTIO_SCSI_T_TMF_QUERY_TASK) {
+ /* "If the specified command is present in the task set, then
+ * return a service response set to FUNCTION SUCCEEDED".
+ */
+ req->resp.tmf->response = VIRTIO_SCSI_S_FUNCTION_SUCCEEDED;
+ } else {
+ scsi_req_cancel(r);
+ }
+ }
+ break;
+
+ case VIRTIO_SCSI_T_TMF_LOGICAL_UNIT_RESET:
+ if (!d) {
+ goto fail;
+ }
+ if (d->lun != virtio_scsi_get_lun(req->req.tmf->lun)) {
+ goto incorrect_lun;
+ }
+ s->resetting++;
+ qdev_reset_all(&d->qdev);
+ s->resetting--;
+ break;
+
+ case VIRTIO_SCSI_T_TMF_ABORT_TASK_SET:
+ case VIRTIO_SCSI_T_TMF_CLEAR_TASK_SET:
+ case VIRTIO_SCSI_T_TMF_QUERY_TASK_SET:
+ if (!d) {
+ goto fail;
+ }
+ if (d->lun != virtio_scsi_get_lun(req->req.tmf->lun)) {
+ goto incorrect_lun;
+ }
+ QTAILQ_FOREACH_SAFE(r, &d->requests, next, next) {
+ if (r->hba_private) {
+ if (req->req.tmf->subtype == VIRTIO_SCSI_T_TMF_QUERY_TASK_SET) {
+ /* "If there is any command present in the task set, then
+ * return a service response set to FUNCTION SUCCEEDED".
+ */
+ req->resp.tmf->response = VIRTIO_SCSI_S_FUNCTION_SUCCEEDED;
+ break;
+ } else {
+ scsi_req_cancel(r);
+ }
+ }
+ }
+ break;
+
+ case VIRTIO_SCSI_T_TMF_I_T_NEXUS_RESET:
+ target = req->req.tmf->lun[1];
+ s->resetting++;
+ QTAILQ_FOREACH(qdev, &s->bus.qbus.children, sibling) {
+ d = DO_UPCAST(SCSIDevice, qdev, qdev);
+ if (d->channel == 0 && d->id == target) {
+ qdev_reset_all(&d->qdev);
+ }
+ }
+ s->resetting--;
+ break;
+
+ case VIRTIO_SCSI_T_TMF_CLEAR_ACA:
+ default:
+ req->resp.tmf->response = VIRTIO_SCSI_S_FUNCTION_REJECTED;
+ break;
+ }
+
+ return;
+
+incorrect_lun:
+ req->resp.tmf->response = VIRTIO_SCSI_S_INCORRECT_LUN;
+ return;
+
+fail:
+ req->resp.tmf->response = VIRTIO_SCSI_S_BAD_TARGET;
+}
+
+static void virtio_scsi_handle_ctrl(VirtIODevice *vdev, VirtQueue *vq)
+{
+ VirtIOSCSI *s = (VirtIOSCSI *)vdev;
+ VirtIOSCSIReq *req;
+
+ while ((req = virtio_scsi_pop_req(s, vq))) {
+ int out_size, in_size;
+ if (req->elem.out_num < 1 || req->elem.in_num < 1) {
+ virtio_scsi_bad_req();
+ continue;
+ }
+
+ out_size = req->elem.out_sg[0].iov_len;
+ in_size = req->elem.in_sg[0].iov_len;
+ if (req->req.tmf->type == VIRTIO_SCSI_T_TMF) {
+ if (out_size < sizeof(VirtIOSCSICtrlTMFReq) ||
+ in_size < sizeof(VirtIOSCSICtrlTMFResp)) {
+ virtio_scsi_bad_req();
+ }
+ virtio_scsi_do_tmf(s, req);
+
+ } else if (req->req.tmf->type == VIRTIO_SCSI_T_AN_QUERY ||
+ req->req.tmf->type == VIRTIO_SCSI_T_AN_SUBSCRIBE) {
+ if (out_size < sizeof(VirtIOSCSICtrlANReq) ||
+ in_size < sizeof(VirtIOSCSICtrlANResp)) {
+ virtio_scsi_bad_req();
+ }
+ req->resp.an->event_actual = 0;
+ req->resp.an->response = VIRTIO_SCSI_S_OK;
+ }
+ virtio_scsi_complete_req(req);
+ }
+}
+
+static void virtio_scsi_command_complete(SCSIRequest *r, uint32_t status,
+ size_t resid)
+{
+ VirtIOSCSIReq *req = r->hba_private;
+
+ req->resp.cmd->response = VIRTIO_SCSI_S_OK;
+ req->resp.cmd->status = status;
+ if (req->resp.cmd->status == GOOD) {
+ req->resp.cmd->resid = resid;
+ } else {
+ req->resp.cmd->resid = 0;
+ req->resp.cmd->sense_len =
+ scsi_req_get_sense(r, req->resp.cmd->sense, VIRTIO_SCSI_SENSE_SIZE);
+ }
+ virtio_scsi_complete_req(req);
+}
+
+static QEMUSGList *virtio_scsi_get_sg_list(SCSIRequest *r)
+{
+ VirtIOSCSIReq *req = r->hba_private;
+
+ return &req->qsgl;
+}
+
+static void virtio_scsi_request_cancelled(SCSIRequest *r)
+{
+ VirtIOSCSIReq *req = r->hba_private;
+
+ if (!req) {
+ return;
+ }
+ if (req->dev->resetting) {
+ req->resp.cmd->response = VIRTIO_SCSI_S_RESET;
+ } else {
+ req->resp.cmd->response = VIRTIO_SCSI_S_ABORTED;
+ }
+ virtio_scsi_complete_req(req);
+}
+
+static void virtio_scsi_fail_cmd_req(VirtIOSCSIReq *req)
+{
+ req->resp.cmd->response = VIRTIO_SCSI_S_FAILURE;
+ virtio_scsi_complete_req(req);
+}
+
+static void virtio_scsi_handle_cmd(VirtIODevice *vdev, VirtQueue *vq)
+{
+ VirtIOSCSI *s = (VirtIOSCSI *)vdev;
+ VirtIOSCSIReq *req;
+ int n;
+
+ while ((req = virtio_scsi_pop_req(s, vq))) {
+ SCSIDevice *d;
+ int out_size, in_size;
+ if (req->elem.out_num < 1 || req->elem.in_num < 1) {
+ virtio_scsi_bad_req();
+ }
+
+ out_size = req->elem.out_sg[0].iov_len;
+ in_size = req->elem.in_sg[0].iov_len;
+ if (out_size < sizeof(VirtIOSCSICmdReq) + s->cdb_size ||
+ in_size < sizeof(VirtIOSCSICmdResp) + s->sense_size) {
+ virtio_scsi_bad_req();
+ }
+
+ if (req->elem.out_num > 1 && req->elem.in_num > 1) {
+ virtio_scsi_fail_cmd_req(req);
+ continue;
+ }
+
+ d = virtio_scsi_device_find(s, req->req.cmd->lun);
+ if (!d) {
+ req->resp.cmd->response = VIRTIO_SCSI_S_BAD_TARGET;
+ virtio_scsi_complete_req(req);
+ continue;
+ }
+ req->sreq = scsi_req_new(d, req->req.cmd->tag,
+ virtio_scsi_get_lun(req->req.cmd->lun),
+ req->req.cmd->cdb, req);
+
+ if (req->sreq->cmd.mode != SCSI_XFER_NONE) {
+ int req_mode =
+ (req->elem.in_num > 1 ? SCSI_XFER_FROM_DEV : SCSI_XFER_TO_DEV);
+
+ if (req->sreq->cmd.mode != req_mode ||
+ req->sreq->cmd.xfer > req->qsgl.size) {
+ req->resp.cmd->response = VIRTIO_SCSI_S_OVERRUN;
+ virtio_scsi_complete_req(req);
+ continue;
+ }
+ }
+
+ n = scsi_req_enqueue(req->sreq);
+ if (n) {
+ scsi_req_continue(req->sreq);
+ }
+ }
+}
+
+static void virtio_scsi_get_config(VirtIODevice *vdev,
+ uint8_t *config)
+{
+ VirtIOSCSIConfig *scsiconf = (VirtIOSCSIConfig *)config;
+ VirtIOSCSI *s = (VirtIOSCSI *)vdev;
+
+ stl_raw(&scsiconf->num_queues, s->conf->num_queues);
+ stl_raw(&scsiconf->seg_max, 128 - 2);
+ stl_raw(&scsiconf->max_sectors, s->conf->max_sectors);
+ stl_raw(&scsiconf->cmd_per_lun, s->conf->cmd_per_lun);
+ stl_raw(&scsiconf->event_info_size, sizeof(VirtIOSCSIEvent));
+ stl_raw(&scsiconf->sense_size, s->sense_size);
+ stl_raw(&scsiconf->cdb_size, s->cdb_size);
+ stl_raw(&scsiconf->max_channel, VIRTIO_SCSI_MAX_CHANNEL);
+ stl_raw(&scsiconf->max_target, VIRTIO_SCSI_MAX_TARGET);
+ stl_raw(&scsiconf->max_lun, VIRTIO_SCSI_MAX_LUN);
+}
+
+static void virtio_scsi_set_config(VirtIODevice *vdev,
+ const uint8_t *config)
+{
+ VirtIOSCSIConfig *scsiconf = (VirtIOSCSIConfig *)config;
+ VirtIOSCSI *s = (VirtIOSCSI *)vdev;
+
+ if ((uint32_t) ldl_raw(&scsiconf->sense_size) >= 65536 ||
+ (uint32_t) ldl_raw(&scsiconf->cdb_size) >= 256) {
+ error_report("bad data written to virtio-scsi configuration space");
+ exit(1);
+ }
+
+ s->sense_size = ldl_raw(&scsiconf->sense_size);
+ s->cdb_size = ldl_raw(&scsiconf->cdb_size);
+}
+
+static uint32_t virtio_scsi_get_features(VirtIODevice *vdev,
+ uint32_t requested_features)
+{
+ return requested_features;
+}
+
+static void virtio_scsi_reset(VirtIODevice *vdev)
+{
+ VirtIOSCSI *s = (VirtIOSCSI *)vdev;
+
+ s->sense_size = VIRTIO_SCSI_SENSE_SIZE;
+ s->cdb_size = VIRTIO_SCSI_CDB_SIZE;
+}
+
+/* The device does not have anything to save beyond the virtio data.
+ * Request data is saved with callbacks from SCSI devices.
+ */
+static void virtio_scsi_save(QEMUFile *f, void *opaque)
+{
+ VirtIOSCSI *s = opaque;
+ virtio_save(&s->vdev, f);
+}
+
+static int virtio_scsi_load(QEMUFile *f, void *opaque, int version_id)
+{
+ VirtIOSCSI *s = opaque;
+ virtio_load(&s->vdev, f);
+ return 0;
+}
+
+static struct SCSIBusInfo virtio_scsi_scsi_info = {
+ .tcq = true,
+ .max_channel = VIRTIO_SCSI_MAX_CHANNEL,
+ .max_target = VIRTIO_SCSI_MAX_TARGET,
+ .max_lun = VIRTIO_SCSI_MAX_LUN,
+
+ .complete = virtio_scsi_command_complete,
+ .cancel = virtio_scsi_request_cancelled,
+ .get_sg_list = virtio_scsi_get_sg_list,
+ .save_request = virtio_scsi_save_request,
+ .load_request = virtio_scsi_load_request,
+};
+
+VirtIODevice *virtio_scsi_init(DeviceState *dev, VirtIOSCSIConf *proxyconf)
+{
+ VirtIOSCSI *s;
+ static int virtio_scsi_id;
+
+ s = (VirtIOSCSI *)virtio_common_init("virtio-scsi", VIRTIO_ID_SCSI,
+ sizeof(VirtIOSCSIConfig),
+ sizeof(VirtIOSCSI));
+
+ s->qdev = dev;
+ s->conf = proxyconf;
+
+ /* TODO set up vdev function pointers */
+ s->vdev.get_config = virtio_scsi_get_config;
+ s->vdev.set_config = virtio_scsi_set_config;
+ s->vdev.get_features = virtio_scsi_get_features;
+ s->vdev.reset = virtio_scsi_reset;
+
+ s->ctrl_vq = virtio_add_queue(&s->vdev, VIRTIO_SCSI_VQ_SIZE,
+ virtio_scsi_handle_ctrl);
+ s->event_vq = virtio_add_queue(&s->vdev, VIRTIO_SCSI_VQ_SIZE,
+ NULL);
+ s->cmd_vq = virtio_add_queue(&s->vdev, VIRTIO_SCSI_VQ_SIZE,
+ virtio_scsi_handle_cmd);
+
+ scsi_bus_new(&s->bus, dev, &virtio_scsi_scsi_info);
+ if (!dev->hotplugged) {
+ scsi_bus_legacy_handle_cmdline(&s->bus);
+ }
+
+ register_savevm(dev, "virtio-scsi", virtio_scsi_id++, 1,
+ virtio_scsi_save, virtio_scsi_load, s);
+
+ return &s->vdev;
+}
+
+void virtio_scsi_exit(VirtIODevice *vdev)
+{
+ virtio_cleanup(vdev);
+}
diff --git a/hw/virtio-scsi.h b/hw/virtio-scsi.h
new file mode 100644
index 0000000000..4bc889de02
--- /dev/null
+++ b/hw/virtio-scsi.h
@@ -0,0 +1,36 @@
+/*
+ * Virtio SCSI HBA
+ *
+ * Copyright IBM, Corp. 2010
+ *
+ * Authors:
+ * Stefan Hajnoczi <stefanha@linux.vnet.ibm.com>
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2. See
+ * the COPYING file in the top-level directory.
+ *
+ */
+
+#ifndef _QEMU_VIRTIO_SCSI_H
+#define _QEMU_VIRTIO_SCSI_H
+
+#include "virtio.h"
+#include "net.h"
+#include "pci.h"
+
+/* The ID for virtio_scsi */
+#define VIRTIO_ID_SCSI 8
+
+struct VirtIOSCSIConf {
+ uint32_t num_queues;
+ uint32_t max_sectors;
+ uint32_t cmd_per_lun;
+};
+
+#define DEFINE_VIRTIO_SCSI_PROPERTIES(_state, _features_field, _conf_field) \
+ DEFINE_VIRTIO_COMMON_FEATURES(_state, _features_field), \
+ DEFINE_PROP_UINT32("num_queues", _state, _conf_field.num_queues, 1), \
+ DEFINE_PROP_UINT32("max_sectors", _state, _conf_field.max_sectors, 0xFFFF), \
+ DEFINE_PROP_UINT32("cmd_per_lun", _state, _conf_field.cmd_per_lun, 128)
+
+#endif /* _QEMU_VIRTIO_SCSI_H */
diff --git a/hw/virtio-serial-bus.c b/hw/virtio-serial-bus.c
index 61176299a3..e22940ecb2 100644
--- a/hw/virtio-serial-bus.c
+++ b/hw/virtio-serial-bus.c
@@ -949,9 +949,9 @@ static TypeInfo virtio_serial_port_type_info = {
.class_init = virtio_serial_port_class_init,
};
-static void virtio_serial_register_devices(void)
+static void virtio_serial_register_types(void)
{
type_register_static(&virtio_serial_port_type_info);
}
-device_init(virtio_serial_register_devices);
+type_init(virtio_serial_register_types)
diff --git a/hw/virtio.c b/hw/virtio.c
index 74cc038af9..064aecf553 100644
--- a/hw/virtio.c
+++ b/hw/virtio.c
@@ -845,8 +845,7 @@ int virtio_load(VirtIODevice *vdev, QEMUFile *f)
void virtio_cleanup(VirtIODevice *vdev)
{
qemu_del_vm_change_state_handler(vdev->vmstate);
- if (vdev->config)
- g_free(vdev->config);
+ g_free(vdev->config);
g_free(vdev->vq);
g_free(vdev);
}
diff --git a/hw/virtio.h b/hw/virtio.h
index 25f55647b4..400c092c95 100644
--- a/hw/virtio.h
+++ b/hw/virtio.h
@@ -199,6 +199,8 @@ VirtIODevice *virtio_net_init(DeviceState *dev, NICConf *conf,
typedef struct virtio_serial_conf virtio_serial_conf;
VirtIODevice *virtio_serial_init(DeviceState *dev, virtio_serial_conf *serial);
VirtIODevice *virtio_balloon_init(DeviceState *dev);
+typedef struct VirtIOSCSIConf VirtIOSCSIConf;
+VirtIODevice *virtio_scsi_init(DeviceState *dev, VirtIOSCSIConf *conf);
#ifdef CONFIG_LINUX
VirtIODevice *virtio_9p_init(DeviceState *dev, V9fsConf *conf);
#endif
@@ -208,6 +210,7 @@ void virtio_net_exit(VirtIODevice *vdev);
void virtio_blk_exit(VirtIODevice *vdev);
void virtio_serial_exit(VirtIODevice *vdev);
void virtio_balloon_exit(VirtIODevice *vdev);
+void virtio_scsi_exit(VirtIODevice *vdev);
#define DEFINE_VIRTIO_COMMON_FEATURES(_state, _field) \
DEFINE_PROP_BIT("indirect_desc", _state, _field, \
diff --git a/hw/vmmouse.c b/hw/vmmouse.c
index fda4f89a76..6338efa1c3 100644
--- a/hw/vmmouse.c
+++ b/hw/vmmouse.c
@@ -294,8 +294,9 @@ static TypeInfo vmmouse_info = {
.class_init = vmmouse_class_initfn,
};
-static void vmmouse_dev_register(void)
+static void vmmouse_register_types(void)
{
type_register_static(&vmmouse_info);
}
-device_init(vmmouse_dev_register)
+
+type_init(vmmouse_register_types)
diff --git a/hw/vmport.c b/hw/vmport.c
index a2c45e1950..9373be9775 100644
--- a/hw/vmport.c
+++ b/hw/vmport.c
@@ -159,8 +159,9 @@ static TypeInfo vmport_info = {
.class_init = vmport_class_initfn,
};
-static void vmport_dev_register(void)
+static void vmport_register_types(void)
{
type_register_static(&vmport_info);
}
-device_init(vmport_dev_register)
+
+type_init(vmport_register_types)
diff --git a/hw/vmware_vga.c b/hw/vmware_vga.c
index 3f3eb21d02..142d9f4ea0 100644
--- a/hw/vmware_vga.c
+++ b/hw/vmware_vga.c
@@ -1003,11 +1003,11 @@ static void vmsvga_invalidate_display(void *opaque)
/* save the vga display in a PPM image even if no display is
available */
-static void vmsvga_screen_dump(void *opaque, const char *filename)
+static void vmsvga_screen_dump(void *opaque, const char *filename, bool cswitch)
{
struct vmsvga_state_s *s = opaque;
if (!s->enable) {
- s->vga.screen_dump(&s->vga, filename);
+ s->vga.screen_dump(&s->vga, filename, cswitch);
return;
}
@@ -1223,8 +1223,9 @@ static TypeInfo vmsvga_info = {
.class_init = vmsvga_class_init,
};
-static void vmsvga_register(void)
+static void vmsvga_register_types(void)
{
type_register_static(&vmsvga_info);
}
-device_init(vmsvga_register);
+
+type_init(vmsvga_register_types)
diff --git a/hw/vt82c686.c b/hw/vt82c686.c
index aa0954f487..6fb7950fa6 100644
--- a/hw/vt82c686.c
+++ b/hw/vt82c686.c
@@ -159,10 +159,8 @@ static void vt82c686b_write_config(PCIDevice * d, uint32_t address,
typedef struct VT686PMState {
PCIDevice dev;
- ACPIPM1EVT pm1a;
- ACPIPM1CNT pm1_cnt;
+ ACPIREGS ar;
APMState apm;
- ACPIPMTimer tmr;
PMSMBus smb;
uint32_t smb_io_base;
} VT686PMState;
@@ -179,21 +177,21 @@ static void pm_update_sci(VT686PMState *s)
{
int sci_level, pmsts;
- pmsts = acpi_pm1_evt_get_sts(&s->pm1a, s->tmr.overflow_time);
- sci_level = (((pmsts & s->pm1a.en) &
+ pmsts = acpi_pm1_evt_get_sts(&s->ar);
+ sci_level = (((pmsts & s->ar.pm1.evt.en) &
(ACPI_BITMASK_RT_CLOCK_ENABLE |
ACPI_BITMASK_POWER_BUTTON_ENABLE |
ACPI_BITMASK_GLOBAL_LOCK_ENABLE |
ACPI_BITMASK_TIMER_ENABLE)) != 0);
qemu_set_irq(s->dev.irq[0], sci_level);
/* schedule a timer interruption if needed */
- acpi_pm_tmr_update(&s->tmr, (s->pm1a.en & ACPI_BITMASK_TIMER_ENABLE) &&
+ acpi_pm_tmr_update(&s->ar, (s->ar.pm1.evt.en & ACPI_BITMASK_TIMER_ENABLE) &&
!(pmsts & ACPI_BITMASK_TIMER_STATUS));
}
-static void pm_tmr_timer(ACPIPMTimer *tmr)
+static void pm_tmr_timer(ACPIREGS *ar)
{
- VT686PMState *s = container_of(tmr, VT686PMState, tmr);
+ VT686PMState *s = container_of(ar, VT686PMState, ar);
pm_update_sci(s);
}
@@ -204,15 +202,15 @@ static void pm_ioport_writew(void *opaque, uint32_t addr, uint32_t val)
addr &= 0x0f;
switch (addr) {
case 0x00:
- acpi_pm1_evt_write_sts(&s->pm1a, &s->tmr, val);
+ acpi_pm1_evt_write_sts(&s->ar, val);
pm_update_sci(s);
break;
case 0x02:
- s->pm1a.en = val;
+ acpi_pm1_evt_write_en(&s->ar, val);
pm_update_sci(s);
break;
case 0x04:
- acpi_pm1_cnt_write(&s->pm1a, &s->pm1_cnt, val);
+ acpi_pm1_cnt_write(&s->ar, val);
break;
default:
break;
@@ -228,13 +226,13 @@ static uint32_t pm_ioport_readw(void *opaque, uint32_t addr)
addr &= 0x0f;
switch (addr) {
case 0x00:
- val = acpi_pm1_evt_get_sts(&s->pm1a, s->tmr.overflow_time);
+ val = acpi_pm1_evt_get_sts(&s->ar);
break;
case 0x02:
- val = s->pm1a.en;
+ val = s->ar.pm1.evt.en;
break;
case 0x04:
- val = s->pm1_cnt.cnt;
+ val = s->ar.pm1.cnt.cnt;
break;
default:
val = 0;
@@ -258,7 +256,7 @@ static uint32_t pm_ioport_readl(void *opaque, uint32_t addr)
addr &= 0x0f;
switch (addr) {
case 0x08:
- val = acpi_pm_tmr_get(&s->tmr);
+ val = acpi_pm_tmr_get(&s->ar);
break;
default:
val = 0;
@@ -309,12 +307,12 @@ static const VMStateDescription vmstate_acpi = {
.post_load = vmstate_acpi_post_load,
.fields = (VMStateField []) {
VMSTATE_PCI_DEVICE(dev, VT686PMState),
- VMSTATE_UINT16(pm1a.sts, VT686PMState),
- VMSTATE_UINT16(pm1a.en, VT686PMState),
- VMSTATE_UINT16(pm1_cnt.cnt, VT686PMState),
+ VMSTATE_UINT16(ar.pm1.evt.sts, VT686PMState),
+ VMSTATE_UINT16(ar.pm1.evt.en, VT686PMState),
+ VMSTATE_UINT16(ar.pm1.cnt.cnt, VT686PMState),
VMSTATE_STRUCT(apm, VT686PMState, 0, vmstate_apm, APMState),
- VMSTATE_TIMER(tmr.timer, VT686PMState),
- VMSTATE_INT64(tmr.overflow_time, VT686PMState),
+ VMSTATE_TIMER(ar.tmr.timer, VT686PMState),
+ VMSTATE_INT64(ar.tmr.overflow_time, VT686PMState),
VMSTATE_END_OF_LIST()
}
};
@@ -366,13 +364,6 @@ static TypeInfo via_ac97_info = {
.class_init = via_ac97_class_init,
};
-static void vt82c686b_ac97_register(void)
-{
- type_register_static(&via_ac97_info);
-}
-
-device_init(vt82c686b_ac97_register);
-
static int vt82c686b_mc97_initfn(PCIDevice *dev)
{
VT686MC97State *s = DO_UPCAST(VT686MC97State, dev, dev);
@@ -414,13 +405,6 @@ static TypeInfo via_mc97_info = {
.class_init = via_mc97_class_init,
};
-static void vt82c686b_mc97_register(void)
-{
- type_register_static(&via_mc97_info);
-}
-
-device_init(vt82c686b_mc97_register);
-
/* vt82c686 pm init */
static int vt82c686b_pm_initfn(PCIDevice *dev)
{
@@ -445,8 +429,8 @@ static int vt82c686b_pm_initfn(PCIDevice *dev)
apm_init(&s->apm, NULL, s);
- acpi_pm_tmr_init(&s->tmr, pm_tmr_timer);
- acpi_pm1_cnt_init(&s->pm1_cnt, NULL);
+ acpi_pm_tmr_init(&s->ar, pm_tmr_timer);
+ acpi_pm1_cnt_init(&s->ar);
pm_smbus_init(&s->dev.qdev, &s->smb);
@@ -497,13 +481,6 @@ static TypeInfo via_pm_info = {
.class_init = via_pm_class_init,
};
-static void vt82c686b_pm_register(void)
-{
- type_register_static(&via_pm_info);
-}
-
-device_init(vt82c686b_pm_register);
-
static const VMStateDescription vmstate_via = {
.name = "vt82c686b",
.version_id = 1,
@@ -571,8 +548,12 @@ static TypeInfo via_info = {
.class_init = via_class_init,
};
-static void vt82c686b_register(void)
+static void vt82c686b_register_types(void)
{
+ type_register_static(&via_ac97_info);
+ type_register_static(&via_mc97_info);
+ type_register_static(&via_pm_info);
type_register_static(&via_info);
}
-device_init(vt82c686b_register);
+
+type_init(vt82c686b_register_types)
diff --git a/hw/wdt_i6300esb.c b/hw/wdt_i6300esb.c
index 41325f0955..15c69db932 100644
--- a/hw/wdt_i6300esb.c
+++ b/hw/wdt_i6300esb.c
@@ -448,10 +448,10 @@ static TypeInfo i6300esb_info = {
.class_init = i6300esb_class_init,
};
-static void i6300esb_register_devices(void)
+static void i6300esb_register_types(void)
{
watchdog_add_model(&model);
type_register_static(&i6300esb_info);
}
-device_init(i6300esb_register_devices);
+type_init(i6300esb_register_types)
diff --git a/hw/wdt_ib700.c b/hw/wdt_ib700.c
index 8faa2316c9..7f6c21d809 100644
--- a/hw/wdt_ib700.c
+++ b/hw/wdt_ib700.c
@@ -136,10 +136,10 @@ static TypeInfo wdt_ib700_info = {
.class_init = wdt_ib700_class_init,
};
-static void wdt_ib700_register_devices(void)
+static void wdt_ib700_register_types(void)
{
watchdog_add_model(&model);
type_register_static(&wdt_ib700_info);
}
-device_init(wdt_ib700_register_devices);
+type_init(wdt_ib700_register_types)
diff --git a/hw/wm8750.c b/hw/wm8750.c
index 18afa4c1ac..11bcec3410 100644
--- a/hw/wm8750.c
+++ b/hw/wm8750.c
@@ -708,9 +708,9 @@ static TypeInfo wm8750_info = {
.class_init = wm8750_class_init,
};
-static void wm8750_register_devices(void)
+static void wm8750_register_types(void)
{
type_register_static(&wm8750_info);
}
-device_init(wm8750_register_devices)
+type_init(wm8750_register_types)
diff --git a/hw/xen_platform.c b/hw/xen_platform.c
index e7571022e4..5a7c4cc97b 100644
--- a/hw/xen_platform.c
+++ b/hw/xen_platform.c
@@ -396,9 +396,9 @@ static TypeInfo xen_platform_info = {
.class_init = xen_platform_class_init,
};
-static void xen_platform_register(void)
+static void xen_platform_register_types(void)
{
type_register_static(&xen_platform_info);
}
-device_init(xen_platform_register);
+type_init(xen_platform_register_types)
diff --git a/hw/xgmac.c b/hw/xgmac.c
index d395b1cc2d..dd4bdc46f5 100644
--- a/hw/xgmac.c
+++ b/hw/xgmac.c
@@ -425,9 +425,9 @@ static TypeInfo xgmac_enet_info = {
.class_init = xgmac_enet_class_init,
};
-static void xgmac_enet_register(void)
+static void xgmac_enet_register_types(void)
{
type_register_static(&xgmac_enet_info);
}
-device_init(xgmac_enet_register)
+type_init(xgmac_enet_register_types)
diff --git a/hw/xilinx_axidma.c b/hw/xilinx_axidma.c
index e8a53123f9..85dfcbf2b9 100644
--- a/hw/xilinx_axidma.c
+++ b/hw/xilinx_axidma.c
@@ -508,9 +508,9 @@ static TypeInfo axidma_info = {
.class_init = axidma_class_init,
};
-static void xilinx_axidma_register(void)
+static void xilinx_axidma_register_types(void)
{
type_register_static(&axidma_info);
}
-device_init(xilinx_axidma_register)
+type_init(xilinx_axidma_register_types)
diff --git a/hw/xilinx_axienet.c b/hw/xilinx_axienet.c
index 1ce2db4510..7526273b4d 100644
--- a/hw/xilinx_axienet.c
+++ b/hw/xilinx_axienet.c
@@ -894,9 +894,10 @@ static TypeInfo xilinx_enet_info = {
.instance_size = sizeof(struct XilinxAXIEnet),
.class_init = xilinx_enet_class_init,
};
-static void xilinx_enet_register(void)
+
+static void xilinx_enet_register_types(void)
{
type_register_static(&xilinx_enet_info);
}
-device_init(xilinx_enet_register)
+type_init(xilinx_enet_register_types)
diff --git a/hw/xilinx_ethlite.c b/hw/xilinx_ethlite.c
index 499feef488..857b33d172 100644
--- a/hw/xilinx_ethlite.c
+++ b/hw/xilinx_ethlite.c
@@ -249,9 +249,9 @@ static TypeInfo xilinx_ethlite_info = {
.class_init = xilinx_ethlite_class_init,
};
-static void xilinx_ethlite_register(void)
+static void xilinx_ethlite_register_types(void)
{
type_register_static(&xilinx_ethlite_info);
}
-device_init(xilinx_ethlite_register)
+type_init(xilinx_ethlite_register_types)
diff --git a/hw/xilinx_intc.c b/hw/xilinx_intc.c
index 73eed6dc5f..553f8488f6 100644
--- a/hw/xilinx_intc.c
+++ b/hw/xilinx_intc.c
@@ -182,9 +182,9 @@ static TypeInfo xilinx_intc_info = {
.class_init = xilinx_intc_class_init,
};
-static void xilinx_intc_register(void)
+static void xilinx_intc_register_types(void)
{
type_register_static(&xilinx_intc_info);
}
-device_init(xilinx_intc_register)
+type_init(xilinx_intc_register_types)
diff --git a/hw/xilinx_timer.c b/hw/xilinx_timer.c
index c8236d2e2a..3ab2f2bb03 100644
--- a/hw/xilinx_timer.c
+++ b/hw/xilinx_timer.c
@@ -241,9 +241,9 @@ static TypeInfo xilinx_timer_info = {
.class_init = xilinx_timer_class_init,
};
-static void xilinx_timer_register(void)
+static void xilinx_timer_register_types(void)
{
type_register_static(&xilinx_timer_info);
}
-device_init(xilinx_timer_register)
+type_init(xilinx_timer_register_types)
diff --git a/hw/xilinx_uartlite.c b/hw/xilinx_uartlite.c
index 1c2b9087b4..aa0170db49 100644
--- a/hw/xilinx_uartlite.c
+++ b/hw/xilinx_uartlite.c
@@ -225,9 +225,9 @@ static TypeInfo xilinx_uartlite_info = {
.class_init = xilinx_uartlite_class_init,
};
-static void xilinx_uart_register(void)
+static void xilinx_uart_register_types(void)
{
type_register_static(&xilinx_uartlite_info);
}
-device_init(xilinx_uart_register)
+type_init(xilinx_uart_register_types)
diff --git a/hw/xio3130_downstream.c b/hw/xio3130_downstream.c
index 07e4fc1018..319624f212 100644
--- a/hw/xio3130_downstream.c
+++ b/hw/xio3130_downstream.c
@@ -203,12 +203,12 @@ static TypeInfo xio3130_downstream_info = {
.class_init = xio3130_downstream_class_init,
};
-static void xio3130_downstream_register(void)
+static void xio3130_downstream_register_types(void)
{
type_register_static(&xio3130_downstream_info);
}
-device_init(xio3130_downstream_register);
+type_init(xio3130_downstream_register_types)
/*
* Local variables:
diff --git a/hw/xio3130_upstream.c b/hw/xio3130_upstream.c
index 7887c92fcc..34a99bba08 100644
--- a/hw/xio3130_upstream.c
+++ b/hw/xio3130_upstream.c
@@ -177,12 +177,12 @@ static TypeInfo xio3130_upstream_info = {
.class_init = xio3130_upstream_class_init,
};
-static void xio3130_upstream_register(void)
+static void xio3130_upstream_register_types(void)
{
type_register_static(&xio3130_upstream_info);
}
-device_init(xio3130_upstream_register);
+type_init(xio3130_upstream_register_types)
/*
diff --git a/hw/zaurus.c b/hw/zaurus.c
index 055df9b4e2..72838ec440 100644
--- a/hw/zaurus.c
+++ b/hw/zaurus.c
@@ -243,11 +243,12 @@ static TypeInfo scoop_sysbus_info = {
.class_init = scoop_sysbus_class_init,
};
-static void scoop_register(void)
+static void scoop_register_types(void)
{
type_register_static(&scoop_sysbus_info);
}
-device_init(scoop_register);
+
+type_init(scoop_register_types)
/* Write the bootloader parameters memory area. */