diff options
author | Peter Maydell <peter.maydell@linaro.org> | 2021-09-01 17:45:38 +0100 |
---|---|---|
committer | Peter Maydell <peter.maydell@linaro.org> | 2021-09-01 17:45:38 +0100 |
commit | 079b1252e9de384385c9da910262312ec2e574c8 (patch) | |
tree | b18dfec9ce1edd5412bac5bc612f0c2989eacb01 /hw | |
parent | 4c41a1c595e1ce3fe29f3b7bb22ff7402be9c77d (diff) | |
parent | 683754c7b61f9e2ff098720ec80c9ab86c54663d (diff) | |
download | qemu-079b1252e9de384385c9da910262312ec2e574c8.zip qemu-079b1252e9de384385c9da910262312ec2e574c8.tar.gz qemu-079b1252e9de384385c9da910262312ec2e574c8.tar.bz2 |
Merge remote-tracking branch 'remotes/pmaydell/tags/pull-target-arm-20210901' into staging
* Refactor M-profile systick to use Clocks instead of system_clock_scale global
* clock: Provide builtin multiplier/divider
* Add A64FX processor model
* Enable MVE emulation in Cortex-M55
* hw: Add compat machines for 6.2
* hw/intc/arm_gicv3: Replace mis-used MEMTX_* constants by booleans
* hw/arm/raspi: Remove deprecated raspi2/raspi3 aliases
# gpg: Signature made Wed 01 Sep 2021 11:35:57 BST
# gpg: using RSA key E1A5C593CD419DE28E8315CF3C2525ED14360CDE
# gpg: issuer "peter.maydell@linaro.org"
# gpg: Good signature from "Peter Maydell <peter.maydell@linaro.org>" [ultimate]
# gpg: aka "Peter Maydell <pmaydell@gmail.com>" [ultimate]
# gpg: aka "Peter Maydell <pmaydell@chiark.greenend.org.uk>" [ultimate]
# Primary key fingerprint: E1A5 C593 CD41 9DE2 8E83 15CF 3C25 25ED 1436 0CDE
* remotes/pmaydell/tags/pull-target-arm-20210901: (51 commits)
arm: Remove system_clock_scale global
hw/timer/stellaris-gptm: Use Clock input instead of system_clock_scale
hw/arm/stellaris: Split stellaris-gptm into its own file
hw/arm/stellaris: Fix code style issues in GPTM code
hw/timer/armv7m_systick: Use clock inputs instead of system_clock_scale
hw/arm/msf2-soc: Wire up refclk
hw/arm/msf2: Use Clock input to MSF2_SOC instead of m3clk property
hw/arm/msf2_soc: Don't allocate separate MemoryRegions
hw/arm/stellaris: Wire sysclk up to armv7m
hw/arm/stellaris: split stellaris_sys_init()
hw/arm/nrf51: Wire up sysclk
hw/arm/stm32vldiscovery: Delete trailing blank line
hw/arm/stm32f405: Wire up sysclk and refclk
hw/arm/stm32f205: Wire up sysclk and refclk
hw/arm/stm32f100: Wire up sysclk and refclk
hw/arm: Don't allocate separate MemoryRegions in stm32 SoC realize
clock: Provide builtin multiplier/divider
hw/arm/mps2.c: Connect up armv7m clocks
armsse: Wire up systick cpuclk clock
hw/arm/armv7m: Create input clocks
...
Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
Diffstat (limited to 'hw')
33 files changed, 1279 insertions, 845 deletions
diff --git a/hw/arm/Kconfig b/hw/arm/Kconfig index dc050b5..78fdd1b 100644 --- a/hw/arm/Kconfig +++ b/hw/arm/Kconfig @@ -235,6 +235,7 @@ config STELLARIS select SSI_SD select STELLARIS_INPUT select STELLARIS_ENET # ethernet + select STELLARIS_GPTM # general purpose timer module select UNIMP config STM32VLDISCOVERY diff --git a/hw/arm/armsse.c b/hw/arm/armsse.c index a1456cb..aecdeb9 100644 --- a/hw/arm/armsse.c +++ b/hw/arm/armsse.c @@ -689,17 +689,6 @@ static void armsse_forward_sec_resp_cfg(ARMSSE *s) qdev_connect_gpio_out(dev_splitter, 2, s->sec_resp_cfg_in); } -static void armsse_mainclk_update(void *opaque, ClockEvent event) -{ - ARMSSE *s = ARM_SSE(opaque); - - /* - * Set system_clock_scale from our Clock input; this is what - * controls the tick rate of the CPU SysTick timer. - */ - system_clock_scale = clock_ticks_to_ns(s->mainclk, 1); -} - static void armsse_init(Object *obj) { ARMSSE *s = ARM_SSE(obj); @@ -711,8 +700,7 @@ static void armsse_init(Object *obj) assert(info->sram_banks <= MAX_SRAM_BANKS); assert(info->num_cpus <= SSE_MAX_CPUS); - s->mainclk = qdev_init_clock_in(DEVICE(s), "MAINCLK", - armsse_mainclk_update, s, ClockUpdate); + s->mainclk = qdev_init_clock_in(DEVICE(s), "MAINCLK", NULL, NULL, 0); s->s32kclk = qdev_init_clock_in(DEVICE(s), "S32KCLK", NULL, NULL, 0); memory_region_init(&s->container, obj, "armsse-container", UINT64_MAX); @@ -995,6 +983,9 @@ static void armsse_realize(DeviceState *dev, Error **errp) int j; char *gpioname; + qdev_connect_clock_in(cpudev, "cpuclk", s->mainclk); + /* The SSE subsystems do not wire up a systick refclk */ + qdev_prop_set_uint32(cpudev, "num-irq", s->exp_numirq + NUM_SSE_IRQS); /* * In real hardware the initial Secure VTOR is set from the INITSVTOR* @@ -1651,9 +1642,6 @@ static void armsse_realize(DeviceState *dev, Error **errp) * devices in the ARMSSE. */ sysbus_init_mmio(SYS_BUS_DEVICE(s), &s->container); - - /* Set initial system_clock_scale from MAINCLK */ - armsse_mainclk_update(s, ClockUpdate); } static void armsse_idau_check(IDAUInterface *ii, uint32_t address, diff --git a/hw/arm/armv7m.c b/hw/arm/armv7m.c index 9ce5c30..8d08db8 100644 --- a/hw/arm/armv7m.c +++ b/hw/arm/armv7m.c @@ -14,11 +14,14 @@ #include "hw/arm/boot.h" #include "hw/loader.h" #include "hw/qdev-properties.h" +#include "hw/qdev-clock.h" #include "elf.h" #include "sysemu/reset.h" #include "qemu/error-report.h" #include "qemu/module.h" +#include "qemu/log.h" #include "target/arm/idau.h" +#include "migration/vmstate.h" /* Bitbanded IO. Each word corresponds to a single bit. */ @@ -124,6 +127,122 @@ static const hwaddr bitband_output_addr[ARMV7M_NUM_BITBANDS] = { 0x22000000, 0x42000000 }; +static MemTxResult v7m_sysreg_ns_write(void *opaque, hwaddr addr, + uint64_t value, unsigned size, + MemTxAttrs attrs) +{ + MemoryRegion *mr = opaque; + + if (attrs.secure) { + /* S accesses to the alias act like NS accesses to the real region */ + attrs.secure = 0; + return memory_region_dispatch_write(mr, addr, value, + size_memop(size) | MO_TE, attrs); + } else { + /* NS attrs are RAZ/WI for privileged, and BusFault for user */ + if (attrs.user) { + return MEMTX_ERROR; + } + return MEMTX_OK; + } +} + +static MemTxResult v7m_sysreg_ns_read(void *opaque, hwaddr addr, + uint64_t *data, unsigned size, + MemTxAttrs attrs) +{ + MemoryRegion *mr = opaque; + + if (attrs.secure) { + /* S accesses to the alias act like NS accesses to the real region */ + attrs.secure = 0; + return memory_region_dispatch_read(mr, addr, data, + size_memop(size) | MO_TE, attrs); + } else { + /* NS attrs are RAZ/WI for privileged, and BusFault for user */ + if (attrs.user) { + return MEMTX_ERROR; + } + *data = 0; + return MEMTX_OK; + } +} + +static const MemoryRegionOps v7m_sysreg_ns_ops = { + .read_with_attrs = v7m_sysreg_ns_read, + .write_with_attrs = v7m_sysreg_ns_write, + .endianness = DEVICE_NATIVE_ENDIAN, +}; + +static MemTxResult v7m_systick_write(void *opaque, hwaddr addr, + uint64_t value, unsigned size, + MemTxAttrs attrs) +{ + ARMv7MState *s = opaque; + MemoryRegion *mr; + + /* Direct the access to the correct systick */ + mr = sysbus_mmio_get_region(SYS_BUS_DEVICE(&s->systick[attrs.secure]), 0); + return memory_region_dispatch_write(mr, addr, value, + size_memop(size) | MO_TE, attrs); +} + +static MemTxResult v7m_systick_read(void *opaque, hwaddr addr, + uint64_t *data, unsigned size, + MemTxAttrs attrs) +{ + ARMv7MState *s = opaque; + MemoryRegion *mr; + + /* Direct the access to the correct systick */ + mr = sysbus_mmio_get_region(SYS_BUS_DEVICE(&s->systick[attrs.secure]), 0); + return memory_region_dispatch_read(mr, addr, data, size_memop(size) | MO_TE, + attrs); +} + +static const MemoryRegionOps v7m_systick_ops = { + .read_with_attrs = v7m_systick_read, + .write_with_attrs = v7m_systick_write, + .endianness = DEVICE_NATIVE_ENDIAN, +}; + +/* + * Unassigned portions of the PPB space are RAZ/WI for privileged + * accesses, and fault for non-privileged accesses. + */ +static MemTxResult ppb_default_read(void *opaque, hwaddr addr, + uint64_t *data, unsigned size, + MemTxAttrs attrs) +{ + qemu_log_mask(LOG_UNIMP, "Read of unassigned area of PPB: offset 0x%x\n", + (uint32_t)addr); + if (attrs.user) { + return MEMTX_ERROR; + } + *data = 0; + return MEMTX_OK; +} + +static MemTxResult ppb_default_write(void *opaque, hwaddr addr, + uint64_t value, unsigned size, + MemTxAttrs attrs) +{ + qemu_log_mask(LOG_UNIMP, "Write of unassigned area of PPB: offset 0x%x\n", + (uint32_t)addr); + if (attrs.user) { + return MEMTX_ERROR; + } + return MEMTX_OK; +} + +static const MemoryRegionOps ppb_default_ops = { + .read_with_attrs = ppb_default_read, + .write_with_attrs = ppb_default_write, + .endianness = DEVICE_NATIVE_ENDIAN, + .valid.min_access_size = 1, + .valid.max_access_size = 8, +}; + static void armv7m_instance_init(Object *obj) { ARMv7MState *s = ARMV7M(obj); @@ -137,10 +256,20 @@ static void armv7m_instance_init(Object *obj) object_property_add_alias(obj, "num-irq", OBJECT(&s->nvic), "num-irq"); + object_initialize_child(obj, "systick-reg-ns", &s->systick[M_REG_NS], + TYPE_SYSTICK); + /* + * We can't initialize the secure systick here, as we don't know + * yet if we need it. + */ + for (i = 0; i < ARRAY_SIZE(s->bitband); i++) { object_initialize_child(obj, "bitband[*]", &s->bitband[i], TYPE_BITBAND); } + + s->refclk = qdev_init_clock_in(DEVICE(obj), "refclk", NULL, NULL, 0); + s->cpuclk = qdev_init_clock_in(DEVICE(obj), "cpuclk", NULL, NULL, 0); } static void armv7m_realize(DeviceState *dev, Error **errp) @@ -223,13 +352,130 @@ static void armv7m_realize(DeviceState *dev, Error **errp) qdev_pass_gpios(DEVICE(&s->nvic), dev, "SYSRESETREQ"); qdev_pass_gpios(DEVICE(&s->nvic), dev, "NMI"); + /* + * We map various devices into the container MR at their architected + * addresses. In particular, we map everything corresponding to the + * "System PPB" space. This is the range from 0xe0000000 to 0xe00fffff + * and includes the NVIC, the System Control Space (system registers), + * the systick timer, and for CPUs with the Security extension an NS + * banked version of all of these. + * + * The default behaviour for unimplemented registers/ranges + * (for instance the Data Watchpoint and Trace unit at 0xe0001000) + * is to RAZ/WI for privileged access and BusFault for non-privileged + * access. + * + * The NVIC and System Control Space (SCS) starts at 0xe000e000 + * and looks like this: + * 0x004 - ICTR + * 0x010 - 0xff - systick + * 0x100..0x7ec - NVIC + * 0x7f0..0xcff - Reserved + * 0xd00..0xd3c - SCS registers + * 0xd40..0xeff - Reserved or Not implemented + * 0xf00 - STIR + * + * Some registers within this space are banked between security states. + * In v8M there is a second range 0xe002e000..0xe002efff which is the + * NonSecure alias SCS; secure accesses to this behave like NS accesses + * to the main SCS range, and non-secure accesses (including when + * the security extension is not implemented) are RAZ/WI. + * Note that both the main SCS range and the alias range are defined + * to be exempt from memory attribution (R_BLJT) and so the memory + * transaction attribute always matches the current CPU security + * state (attrs.secure == env->v7m.secure). In the v7m_sysreg_ns_ops + * wrappers we change attrs.secure to indicate the NS access; so + * generally code determining which banked register to use should + * use attrs.secure; code determining actual behaviour of the system + * should use env->v7m.secure. + * + * Within the PPB space, some MRs overlap, and the priority + * of overlapping regions is: + * - default region (for RAZ/WI and BusFault) : -1 + * - system register regions (provided by the NVIC) : 0 + * - systick : 1 + * This is because the systick device is a small block of registers + * in the middle of the other system control registers. + */ + + memory_region_init_io(&s->defaultmem, OBJECT(s), &ppb_default_ops, s, + "nvic-default", 0x100000); + memory_region_add_subregion_overlap(&s->container, 0xe0000000, + &s->defaultmem, -1); + /* Wire the NVIC up to the CPU */ sbd = SYS_BUS_DEVICE(&s->nvic); sysbus_connect_irq(sbd, 0, qdev_get_gpio_in(DEVICE(s->cpu), ARM_CPU_IRQ)); - memory_region_add_subregion(&s->container, 0xe0000000, + memory_region_add_subregion(&s->container, 0xe000e000, sysbus_mmio_get_region(sbd, 0)); + if (arm_feature(&s->cpu->env, ARM_FEATURE_V8)) { + /* Create the NS alias region for the NVIC sysregs */ + memory_region_init_io(&s->sysreg_ns_mem, OBJECT(s), + &v7m_sysreg_ns_ops, + sysbus_mmio_get_region(sbd, 0), + "nvic_sysregs_ns", 0x1000); + memory_region_add_subregion(&s->container, 0xe002e000, + &s->sysreg_ns_mem); + } + + /* Create and map the systick devices */ + qdev_connect_clock_in(DEVICE(&s->systick[M_REG_NS]), "refclk", s->refclk); + qdev_connect_clock_in(DEVICE(&s->systick[M_REG_NS]), "cpuclk", s->cpuclk); + if (!sysbus_realize(SYS_BUS_DEVICE(&s->systick[M_REG_NS]), errp)) { + return; + } + sysbus_connect_irq(SYS_BUS_DEVICE(&s->systick[M_REG_NS]), 0, + qdev_get_gpio_in_named(DEVICE(&s->nvic), + "systick-trigger", M_REG_NS)); + + if (arm_feature(&s->cpu->env, ARM_FEATURE_M_SECURITY)) { + /* + * We couldn't init the secure systick device in instance_init + * as we didn't know then if the CPU had the security extensions; + * so we have to do it here. + */ + object_initialize_child(OBJECT(dev), "systick-reg-s", + &s->systick[M_REG_S], TYPE_SYSTICK); + qdev_connect_clock_in(DEVICE(&s->systick[M_REG_S]), "refclk", + s->refclk); + qdev_connect_clock_in(DEVICE(&s->systick[M_REG_S]), "cpuclk", + s->cpuclk); + + if (!sysbus_realize(SYS_BUS_DEVICE(&s->systick[M_REG_S]), errp)) { + return; + } + sysbus_connect_irq(SYS_BUS_DEVICE(&s->systick[M_REG_S]), 0, + qdev_get_gpio_in_named(DEVICE(&s->nvic), + "systick-trigger", M_REG_S)); + } + + memory_region_init_io(&s->systickmem, OBJECT(s), + &v7m_systick_ops, s, + "v7m_systick", 0xe0); + + memory_region_add_subregion_overlap(&s->container, 0xe000e010, + &s->systickmem, 1); + if (arm_feature(&s->cpu->env, ARM_FEATURE_V8)) { + memory_region_init_io(&s->systick_ns_mem, OBJECT(s), + &v7m_sysreg_ns_ops, &s->systickmem, + "v7m_systick_ns", 0xe0); + memory_region_add_subregion_overlap(&s->container, 0xe002e010, + &s->systick_ns_mem, 1); + } + + /* If the CPU has RAS support, create the RAS register block */ + if (cpu_isar_feature(aa32_ras, s->cpu)) { + object_initialize_child(OBJECT(dev), "armv7m-ras", + &s->ras, TYPE_ARMV7M_RAS); + sbd = SYS_BUS_DEVICE(&s->ras); + if (!sysbus_realize(sbd, errp)) { + return; + } + memory_region_add_subregion_overlap(&s->container, 0xe0005000, + sysbus_mmio_get_region(sbd, 0), 1); + } for (i = 0; i < ARRAY_SIZE(s->bitband); i++) { if (s->enable_bitband) { @@ -269,11 +515,23 @@ static Property armv7m_properties[] = { DEFINE_PROP_END_OF_LIST(), }; +static const VMStateDescription vmstate_armv7m = { + .name = "armv7m", + .version_id = 1, + .minimum_version_id = 1, + .fields = (VMStateField[]) { + VMSTATE_CLOCK(refclk, SysTickState), + VMSTATE_CLOCK(cpuclk, SysTickState), + VMSTATE_END_OF_LIST() + } +}; + static void armv7m_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); dc->realize = armv7m_realize; + dc->vmsd = &vmstate_armv7m; device_class_set_props(dc, armv7m_properties); } diff --git a/hw/arm/mps2.c b/hw/arm/mps2.c index 81413b7..4634aa1 100644 --- a/hw/arm/mps2.c +++ b/hw/arm/mps2.c @@ -86,6 +86,7 @@ struct MPS2MachineState { CMSDKAPBWatchdog watchdog; CMSDKAPBTimer timer[2]; Clock *sysclk; + Clock *refclk; }; #define TYPE_MPS2_MACHINE "mps2" @@ -99,6 +100,15 @@ OBJECT_DECLARE_TYPE(MPS2MachineState, MPS2MachineClass, MPS2_MACHINE) /* Main SYSCLK frequency in Hz */ #define SYSCLK_FRQ 25000000 +/* + * The Application Notes don't say anything about how the + * systick reference clock is configured. (Quite possibly + * they don't have one at all.) This 1MHz clock matches the + * pre-existing behaviour that used to be hardcoded in the + * armv7m_systick implementation. + */ +#define REFCLK_FRQ (1 * 1000 * 1000) + /* Initialize the auxiliary RAM region @mr and map it into * the memory map at @base. */ @@ -146,6 +156,9 @@ static void mps2_common_init(MachineState *machine) mms->sysclk = clock_new(OBJECT(machine), "SYSCLK"); clock_set_hz(mms->sysclk, SYSCLK_FRQ); + mms->refclk = clock_new(OBJECT(machine), "REFCLK"); + clock_set_hz(mms->refclk, REFCLK_FRQ); + /* The FPGA images have an odd combination of different RAMs, * because in hardware they are different implementations and * connected to different buses, giving varying performance/size @@ -223,6 +236,8 @@ static void mps2_common_init(MachineState *machine) default: g_assert_not_reached(); } + qdev_connect_clock_in(armv7m, "cpuclk", mms->sysclk); + qdev_connect_clock_in(armv7m, "refclk", mms->refclk); qdev_prop_set_string(armv7m, "cpu-type", machine->cpu_type); qdev_prop_set_bit(armv7m, "enable-bitband", true); object_property_set_link(OBJECT(&mms->armv7m), "memory", @@ -424,8 +439,6 @@ static void mps2_common_init(MachineState *machine) qdev_get_gpio_in(armv7m, mmc->fpga_type == FPGA_AN511 ? 47 : 13)); - system_clock_scale = NANOSECONDS_PER_SECOND / SYSCLK_FRQ; - armv7m_load_kernel(ARM_CPU(first_cpu), machine->kernel_filename, 0x400000); } diff --git a/hw/arm/msf2-soc.c b/hw/arm/msf2-soc.c index 5cfe7ca..b5fe9f3 100644 --- a/hw/arm/msf2-soc.c +++ b/hw/arm/msf2-soc.c @@ -29,6 +29,7 @@ #include "hw/char/serial.h" #include "hw/arm/msf2-soc.h" #include "hw/misc/unimp.h" +#include "hw/qdev-clock.h" #include "sysemu/sysemu.h" #define MSF2_TIMER_BASE 0x40004000 @@ -73,6 +74,9 @@ static void m2sxxx_soc_initfn(Object *obj) } object_initialize_child(obj, "emac", &s->emac, TYPE_MSS_EMAC); + + s->m3clk = qdev_init_clock_in(DEVICE(obj), "m3clk", NULL, NULL, 0); + s->refclk = qdev_init_clock_in(DEVICE(obj), "refclk", NULL, NULL, 0); } static void m2sxxx_soc_realize(DeviceState *dev_soc, Error **errp) @@ -83,11 +87,34 @@ static void m2sxxx_soc_realize(DeviceState *dev_soc, Error **errp) int i; MemoryRegion *system_memory = get_system_memory(); - MemoryRegion *nvm = g_new(MemoryRegion, 1); - MemoryRegion *nvm_alias = g_new(MemoryRegion, 1); - MemoryRegion *sram = g_new(MemoryRegion, 1); - memory_region_init_rom(nvm, OBJECT(dev_soc), "MSF2.eNVM", s->envm_size, + if (!clock_has_source(s->m3clk)) { + error_setg(errp, "m3clk must be wired up by the board code"); + return; + } + + /* + * We use s->refclk internally and only define it with qdev_init_clock_in() + * so it is correctly parented and not leaked on an init/deinit; it is not + * intended as an externally exposed clock. + */ + if (clock_has_source(s->refclk)) { + error_setg(errp, "refclk must not be wired up by the board code"); + return; + } + + /* + * TODO: ideally we should model the SoC SYSTICK_CR register at 0xe0042038, + * which allows the guest to program the divisor between the m3clk and + * the systick refclk to either /4, /8, /16 or /32, as well as setting + * the value the guest can read in the STCALIB register. Currently we + * implement the divisor as a fixed /32, which matches the reset value + * of SYSTICK_CR. + */ + clock_set_mul_div(s->refclk, 32, 1); + clock_set_source(s->refclk, s->m3clk); + + memory_region_init_rom(&s->nvm, OBJECT(dev_soc), "MSF2.eNVM", s->envm_size, &error_fatal); /* * On power-on, the eNVM region 0x60000000 is automatically @@ -95,34 +122,28 @@ static void m2sxxx_soc_realize(DeviceState *dev_soc, Error **errp) * start address (0x0). We do not support remapping other eNVM, * eSRAM and DDR regions by guest(via Sysreg) currently. */ - memory_region_init_alias(nvm_alias, OBJECT(dev_soc), "MSF2.eNVM", nvm, 0, - s->envm_size); + memory_region_init_alias(&s->nvm_alias, OBJECT(dev_soc), "MSF2.eNVM", + &s->nvm, 0, s->envm_size); - memory_region_add_subregion(system_memory, ENVM_BASE_ADDRESS, nvm); - memory_region_add_subregion(system_memory, 0, nvm_alias); + memory_region_add_subregion(system_memory, ENVM_BASE_ADDRESS, &s->nvm); + memory_region_add_subregion(system_memory, 0, &s->nvm_alias); - memory_region_init_ram(sram, NULL, "MSF2.eSRAM", s->esram_size, + memory_region_init_ram(&s->sram, NULL, "MSF2.eSRAM", s->esram_size, &error_fatal); - memory_region_add_subregion(system_memory, SRAM_BASE_ADDRESS, sram); + memory_region_add_subregion(system_memory, SRAM_BASE_ADDRESS, &s->sram); armv7m = DEVICE(&s->armv7m); qdev_prop_set_uint32(armv7m, "num-irq", 81); qdev_prop_set_string(armv7m, "cpu-type", s->cpu_type); qdev_prop_set_bit(armv7m, "enable-bitband", true); + qdev_connect_clock_in(armv7m, "cpuclk", s->m3clk); + qdev_connect_clock_in(armv7m, "refclk", s->refclk); object_property_set_link(OBJECT(&s->armv7m), "memory", OBJECT(get_system_memory()), &error_abort); if (!sysbus_realize(SYS_BUS_DEVICE(&s->armv7m), errp)) { return; } - if (!s->m3clk) { - error_setg(errp, "Invalid m3clk value"); - error_append_hint(errp, "m3clk can not be zero\n"); - return; - } - - system_clock_scale = NANOSECONDS_PER_SECOND / s->m3clk; - for (i = 0; i < MSF2_NUM_UARTS; i++) { if (serial_hd(i)) { serial_mm_init(get_system_memory(), uart_addr[i], 2, @@ -132,8 +153,13 @@ static void m2sxxx_soc_realize(DeviceState *dev_soc, Error **errp) } dev = DEVICE(&s->timer); - /* APB0 clock is the timer input clock */ - qdev_prop_set_uint32(dev, "clock-frequency", s->m3clk / s->apb0div); + /* + * APB0 clock is the timer input clock. + * TODO: ideally the MSF2 timer device should use a Clock rather than a + * clock-frequency integer property. + */ + qdev_prop_set_uint32(dev, "clock-frequency", + clock_get_hz(s->m3clk) / s->apb0div); if (!sysbus_realize(SYS_BUS_DEVICE(&s->timer), errp)) { return; } @@ -210,8 +236,6 @@ static Property m2sxxx_soc_properties[] = { DEFINE_PROP_UINT64("eNVM-size", MSF2State, envm_size, MSF2_ENVM_MAX_SIZE), DEFINE_PROP_UINT64("eSRAM-size", MSF2State, esram_size, MSF2_ESRAM_MAX_SIZE), - /* Libero GUI shows 100Mhz as default for clocks */ - DEFINE_PROP_UINT32("m3clk", MSF2State, m3clk, 100 * 1000000), /* default divisors in Libero GUI */ DEFINE_PROP_UINT8("apb0div", MSF2State, apb0div, 2), DEFINE_PROP_UINT8("apb1div", MSF2State, apb1div, 2), diff --git a/hw/arm/msf2-som.c b/hw/arm/msf2-som.c index 343ec97..396e8b9 100644 --- a/hw/arm/msf2-som.c +++ b/hw/arm/msf2-som.c @@ -29,6 +29,7 @@ #include "hw/boards.h" #include "hw/qdev-properties.h" #include "hw/arm/boot.h" +#include "hw/qdev-clock.h" #include "exec/address-spaces.h" #include "hw/arm/msf2-soc.h" @@ -49,6 +50,7 @@ static void emcraft_sf2_s2s010_init(MachineState *machine) BusState *spi_bus; MemoryRegion *sysmem = get_system_memory(); MemoryRegion *ddr = g_new(MemoryRegion, 1); + Clock *m3clk; if (strcmp(machine->cpu_type, mc->default_cpu_type) != 0) { error_report("This board can only be used with CPU %s", @@ -72,7 +74,10 @@ static void emcraft_sf2_s2s010_init(MachineState *machine) * in Libero. CPU clock is divided by APB0 and APB1 divisors for * peripherals. Emcraft's SoM kit comes with these settings by default. */ - qdev_prop_set_uint32(dev, "m3clk", 142 * 1000000); + /* This clock doesn't need migration because it is fixed-frequency */ + m3clk = clock_new(OBJECT(machine), "m3clk"); + clock_set_hz(m3clk, 142 * 1000000); + qdev_connect_clock_in(dev, "m3clk", m3clk); qdev_prop_set_uint32(dev, "apb0div", 2); qdev_prop_set_uint32(dev, "apb1div", 2); diff --git a/hw/arm/netduino2.c b/hw/arm/netduino2.c index 1733b71..3365da1 100644 --- a/hw/arm/netduino2.c +++ b/hw/arm/netduino2.c @@ -26,6 +26,7 @@ #include "qapi/error.h" #include "hw/boards.h" #include "hw/qdev-properties.h" +#include "hw/qdev-clock.h" #include "qemu/error-report.h" #include "hw/arm/stm32f205_soc.h" #include "hw/arm/boot.h" @@ -36,16 +37,15 @@ static void netduino2_init(MachineState *machine) { DeviceState *dev; + Clock *sysclk; - /* - * TODO: ideally we would model the SoC RCC and let it handle - * system_clock_scale, including its ability to define different - * possible SYSCLK sources. - */ - system_clock_scale = NANOSECONDS_PER_SECOND / SYSCLK_FRQ; + /* This clock doesn't need migration because it is fixed-frequency */ + sysclk = clock_new(OBJECT(machine), "SYSCLK"); + clock_set_hz(sysclk, SYSCLK_FRQ); dev = qdev_new(TYPE_STM32F205_SOC); qdev_prop_set_string(dev, "cpu-type", ARM_CPU_TYPE_NAME("cortex-m3")); + qdev_connect_clock_in(dev, "sysclk", sysclk); sysbus_realize_and_unref(SYS_BUS_DEVICE(dev), &error_fatal); armv7m_load_kernel(ARM_CPU(first_cpu), machine->kernel_filename, diff --git a/hw/arm/netduinoplus2.c b/hw/arm/netduinoplus2.c index d3ad7a2..76cea8e 100644 --- a/hw/arm/netduinoplus2.c +++ b/hw/arm/netduinoplus2.c @@ -26,6 +26,7 @@ #include "qapi/error.h" #include "hw/boards.h" #include "hw/qdev-properties.h" +#include "hw/qdev-clock.h" #include "qemu/error-report.h" #include "hw/arm/stm32f405_soc.h" #include "hw/arm/boot.h" @@ -36,16 +37,15 @@ static void netduinoplus2_init(MachineState *machine) { DeviceState *dev; + Clock *sysclk; - /* - * TODO: ideally we would model the SoC RCC and let it handle - * system_clock_scale, including its ability to define different - * possible SYSCLK sources. - */ - system_clock_scale = NANOSECONDS_PER_SECOND / SYSCLK_FRQ; + /* This clock doesn't need migration because it is fixed-frequency */ + sysclk = clock_new(OBJECT(machine), "SYSCLK"); + clock_set_hz(sysclk, SYSCLK_FRQ); dev = qdev_new(TYPE_STM32F405_SOC); qdev_prop_set_string(dev, "cpu-type", ARM_CPU_TYPE_NAME("cortex-m4")); + qdev_connect_clock_in(dev, "sysclk", sysclk); sysbus_realize_and_unref(SYS_BUS_DEVICE(dev), &error_fatal); armv7m_load_kernel(ARM_CPU(first_cpu), diff --git a/hw/arm/nrf51_soc.c b/hw/arm/nrf51_soc.c index 9407c2f..34da0d6 100644 --- a/hw/arm/nrf51_soc.c +++ b/hw/arm/nrf51_soc.c @@ -12,6 +12,7 @@ #include "qapi/error.h" #include "hw/arm/boot.h" #include "hw/sysbus.h" +#include "hw/qdev-clock.h" #include "hw/misc/unimp.h" #include "qemu/log.h" @@ -66,7 +67,22 @@ static void nrf51_soc_realize(DeviceState *dev_soc, Error **errp) return; } - system_clock_scale = NANOSECONDS_PER_SECOND / HCLK_FRQ; + /* + * HCLK on this SoC is fixed, so we set up sysclk ourselves and + * the board shouldn't connect it. + */ + if (clock_has_source(s->sysclk)) { + error_setg(errp, "sysclk clock must not be wired up by the board code"); + return; + } + /* This clock doesn't need migration because it is fixed-frequency */ + clock_set_hz(s->sysclk, HCLK_FRQ); + qdev_connect_clock_in(DEVICE(&s->cpu), "cpuclk", s->sysclk); + /* + * This SoC has no systick device, so don't connect refclk. + * TODO: model the lack of systick (currently the armv7m object + * will always provide one). + */ object_property_set_link(OBJECT(&s->cpu), "memory", OBJECT(&s->container), &error_abort); @@ -191,6 +207,8 @@ static void nrf51_soc_init(Object *obj) TYPE_NRF51_TIMER); } + + s->sysclk = qdev_init_clock_in(DEVICE(s), "sysclk", NULL, NULL, 0); } static Property nrf51_soc_properties[] = { diff --git a/hw/arm/raspi.c b/hw/arm/raspi.c index 0ada91c..146d353 100644 --- a/hw/arm/raspi.c +++ b/hw/arm/raspi.c @@ -340,7 +340,6 @@ static void raspi2b_machine_class_init(ObjectClass *oc, void *data) MachineClass *mc = MACHINE_CLASS(oc); RaspiMachineClass *rmc = RASPI_MACHINE_CLASS(oc); - mc->alias = "raspi2"; rmc->board_rev = 0xa21041; raspi_machine_class_common_init(mc, rmc->board_rev); }; @@ -360,7 +359,6 @@ static void raspi3b_machine_class_init(ObjectClass *oc, void *data) MachineClass *mc = MACHINE_CLASS(oc); RaspiMachineClass *rmc = RASPI_MACHINE_CLASS(oc); - mc->alias = "raspi3"; rmc->board_rev = 0xa02082; raspi_machine_class_common_init(mc, rmc->board_rev); }; diff --git a/hw/arm/stellaris.c b/hw/arm/stellaris.c index ad48cf2..78827ac 100644 --- a/hw/arm/stellaris.c +++ b/hw/arm/stellaris.c @@ -26,6 +26,7 @@ #include "hw/watchdog/cmsdk-apb-watchdog.h" #include "migration/vmstate.h" #include "hw/misc/unimp.h" +#include "hw/timer/stellaris-gptm.h" #include "hw/qdev-clock.h" #include "qom/object.h" @@ -55,306 +56,6 @@ typedef const struct { uint32_t peripherals; } stellaris_board_info; -/* General purpose timer module. */ - -#define TYPE_STELLARIS_GPTM "stellaris-gptm" -OBJECT_DECLARE_SIMPLE_TYPE(gptm_state, STELLARIS_GPTM) - -struct gptm_state { - SysBusDevice parent_obj; - - MemoryRegion iomem; - uint32_t config; - uint32_t mode[2]; - uint32_t control; - uint32_t state; - uint32_t mask; - uint32_t load[2]; - uint32_t match[2]; - uint32_t prescale[2]; - uint32_t match_prescale[2]; - uint32_t rtc; - int64_t tick[2]; - struct gptm_state *opaque[2]; - QEMUTimer *timer[2]; - /* The timers have an alternate output used to trigger the ADC. */ - qemu_irq trigger; - qemu_irq irq; -}; - -static void gptm_update_irq(gptm_state *s) -{ - int level; - level = (s->state & s->mask) != 0; - qemu_set_irq(s->irq, level); -} - -static void gptm_stop(gptm_state *s, int n) -{ - timer_del(s->timer[n]); -} - -static void gptm_reload(gptm_state *s, int n, int reset) -{ - int64_t tick; - if (reset) - tick = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); - else - tick = s->tick[n]; - - if (s->config == 0) { - /* 32-bit CountDown. */ - uint32_t count; - count = s->load[0] | (s->load[1] << 16); - tick += (int64_t)count * system_clock_scale; - } else if (s->config == 1) { - /* 32-bit RTC. 1Hz tick. */ - tick += NANOSECONDS_PER_SECOND; - } else if (s->mode[n] == 0xa) { - /* PWM mode. Not implemented. */ - } else { - qemu_log_mask(LOG_UNIMP, - "GPTM: 16-bit timer mode unimplemented: 0x%x\n", - s->mode[n]); - return; - } - s->tick[n] = tick; - timer_mod(s->timer[n], tick); -} - -static void gptm_tick(void *opaque) -{ - gptm_state **p = (gptm_state **)opaque; - gptm_state *s; - int n; - - s = *p; - n = p - s->opaque; - if (s->config == 0) { - s->state |= 1; - if ((s->control & 0x20)) { - /* Output trigger. */ - qemu_irq_pulse(s->trigger); - } - if (s->mode[0] & 1) { - /* One-shot. */ - s->control &= ~1; - } else { - /* Periodic. */ - gptm_reload(s, 0, 0); - } - } else if (s->config == 1) { - /* RTC. */ - uint32_t match; - s->rtc++; - match = s->match[0] | (s->match[1] << 16); - if (s->rtc > match) - s->rtc = 0; - if (s->rtc == 0) { - s->state |= 8; - } - gptm_reload(s, 0, 0); - } else if (s->mode[n] == 0xa) { - /* PWM mode. Not implemented. */ - } else { - qemu_log_mask(LOG_UNIMP, - "GPTM: 16-bit timer mode unimplemented: 0x%x\n", - s->mode[n]); - } - gptm_update_irq(s); -} - -static uint64_t gptm_read(void *opaque, hwaddr offset, - unsigned size) -{ - gptm_state *s = (gptm_state *)opaque; - - switch (offset) { - case 0x00: /* CFG */ - return s->config; - case 0x04: /* TAMR */ - return s->mode[0]; - case 0x08: /* TBMR */ - return s->mode[1]; - case 0x0c: /* CTL */ - return s->control; - case 0x18: /* IMR */ - return s->mask; - case 0x1c: /* RIS */ - return s->state; - case 0x20: /* MIS */ - return s->state & s->mask; - case 0x24: /* CR */ - return 0; - case 0x28: /* TAILR */ - return s->load[0] | ((s->config < 4) ? (s->load[1] << 16) : 0); - case 0x2c: /* TBILR */ - return s->load[1]; - case 0x30: /* TAMARCHR */ - return s->match[0] | ((s->config < 4) ? (s->match[1] << 16) : 0); - case 0x34: /* TBMATCHR */ - return s->match[1]; - case 0x38: /* TAPR */ - return s->prescale[0]; - case 0x3c: /* TBPR */ - return s->prescale[1]; - case 0x40: /* TAPMR */ - return s->match_prescale[0]; - case 0x44: /* TBPMR */ - return s->match_prescale[1]; - case 0x48: /* TAR */ - if (s->config == 1) { - return s->rtc; - } - qemu_log_mask(LOG_UNIMP, - "GPTM: read of TAR but timer read not supported\n"); - return 0; - case 0x4c: /* TBR */ - qemu_log_mask(LOG_UNIMP, - "GPTM: read of TBR but timer read not supported\n"); - return 0; - default: - qemu_log_mask(LOG_GUEST_ERROR, - "GPTM: read at bad offset 0x02%" HWADDR_PRIx "\n", - offset); - return 0; - } -} - -static void gptm_write(void *opaque, hwaddr offset, - uint64_t value, unsigned size) -{ - gptm_state *s = (gptm_state *)opaque; - uint32_t oldval; - - /* The timers should be disabled before changing the configuration. - We take advantage of this and defer everything until the timer - is enabled. */ - switch (offset) { - case 0x00: /* CFG */ - s->config = value; - break; - case 0x04: /* TAMR */ - s->mode[0] = value; - break; - case 0x08: /* TBMR */ - s->mode[1] = value; - break; - case 0x0c: /* CTL */ - oldval = s->control; - s->control = value; - /* TODO: Implement pause. */ - if ((oldval ^ value) & 1) { - if (value & 1) { - gptm_reload(s, 0, 1); - } else { - gptm_stop(s, 0); - } - } - if (((oldval ^ value) & 0x100) && s->config >= 4) { - if (value & 0x100) { - gptm_reload(s, 1, 1); - } else { - gptm_stop(s, 1); - } - } - break; - case 0x18: /* IMR */ - s->mask = value & 0x77; - gptm_update_irq(s); - break; - case 0x24: /* CR */ - s->state &= ~value; - break; - case 0x28: /* TAILR */ - s->load[0] = value & 0xffff; - if (s->config < 4) { - s->load[1] = value >> 16; - } - break; - case 0x2c: /* TBILR */ - s->load[1] = value & 0xffff; - break; - case 0x30: /* TAMARCHR */ - s->match[0] = value & 0xffff; - if (s->config < 4) { - s->match[1] = value >> 16; - } - break; - case 0x34: /* TBMATCHR */ - s->match[1] = value >> 16; - break; - case 0x38: /* TAPR */ - s->prescale[0] = value; - break; - case 0x3c: /* TBPR */ - s->prescale[1] = value; - break; - case 0x40: /* TAPMR */ - s->match_prescale[0] = value; - break; - case 0x44: /* TBPMR */ - s->match_prescale[0] = value; - break; - default: - qemu_log_mask(LOG_GUEST_ERROR, - "GPTM: write at bad offset 0x02%" HWADDR_PRIx "\n", - offset); - } - gptm_update_irq(s); -} - -static const MemoryRegionOps gptm_ops = { - .read = gptm_read, - .write = gptm_write, - .endianness = DEVICE_NATIVE_ENDIAN, -}; - -static const VMStateDescription vmstate_stellaris_gptm = { - .name = "stellaris_gptm", - .version_id = 1, - .minimum_version_id = 1, - .fields = (VMStateField[]) { - VMSTATE_UINT32(config, gptm_state), - VMSTATE_UINT32_ARRAY(mode, gptm_state, 2), - VMSTATE_UINT32(control, gptm_state), - VMSTATE_UINT32(state, gptm_state), - VMSTATE_UINT32(mask, gptm_state), - VMSTATE_UNUSED(8), - VMSTATE_UINT32_ARRAY(load, gptm_state, 2), - VMSTATE_UINT32_ARRAY(match, gptm_state, 2), - VMSTATE_UINT32_ARRAY(prescale, gptm_state, 2), - VMSTATE_UINT32_ARRAY(match_prescale, gptm_state, 2), - VMSTATE_UINT32(rtc, gptm_state), - VMSTATE_INT64_ARRAY(tick, gptm_state, 2), - VMSTATE_TIMER_PTR_ARRAY(timer, gptm_state, 2), - VMSTATE_END_OF_LIST() - } -}; - -static void stellaris_gptm_init(Object *obj) -{ - DeviceState *dev = DEVICE(obj); - gptm_state *s = STELLARIS_GPTM(obj); - SysBusDevice *sbd = SYS_BUS_DEVICE(obj); - - sysbus_init_irq(sbd, &s->irq); - qdev_init_gpio_out(dev, &s->trigger, 1); - - memory_region_init_io(&s->iomem, obj, &gptm_ops, s, - "gptm", 0x1000); - sysbus_init_mmio(sbd, &s->iomem); - - s->opaque[0] = s->opaque[1] = s; -} - -static void stellaris_gptm_realize(DeviceState *dev, Error **errp) -{ - gptm_state *s = STELLARIS_GPTM(dev); - s->timer[0] = timer_new_ns(QEMU_CLOCK_VIRTUAL, gptm_tick, &s->opaque[0]); - s->timer[1] = timer_new_ns(QEMU_CLOCK_VIRTUAL, gptm_tick, &s->opaque[1]); -} - /* System controller. */ #define TYPE_STELLARIS_SYS "stellaris-sys" @@ -562,17 +263,18 @@ static bool ssys_use_rcc2(ssys_state *s) */ static void ssys_calculate_system_clock(ssys_state *s, bool propagate_clock) { + int period_ns; /* * SYSDIV field specifies divisor: 0 == /1, 1 == /2, etc. Input * clock is 200MHz, which is a period of 5 ns. Dividing the clock * frequency by X is the same as multiplying the period by X. */ if (ssys_use_rcc2(s)) { - system_clock_scale = 5 * (((s->rcc2 >> 23) & 0x3f) + 1); + period_ns = 5 * (((s->rcc2 >> 23) & 0x3f) + 1); } else { - system_clock_scale = 5 * (((s->rcc >> 23) & 0xf) + 1); + period_ns = 5 * (((s->rcc >> 23) & 0xf) + 1); } - clock_set_ns(s->sysclk, system_clock_scale); + clock_set_ns(s->sysclk, period_ns); if (propagate_clock) { clock_propagate(s->sysclk); } @@ -755,33 +457,6 @@ static void stellaris_sys_instance_init(Object *obj) s->sysclk = qdev_init_clock_out(DEVICE(s), "SYSCLK"); } -static DeviceState *stellaris_sys_init(uint32_t base, qemu_irq irq, - stellaris_board_info *board, - uint8_t *macaddr) -{ - DeviceState *dev = qdev_new(TYPE_STELLARIS_SYS); - SysBusDevice *sbd = SYS_BUS_DEVICE(dev); - - /* Most devices come preprogrammed with a MAC address in the user data. */ - qdev_prop_set_uint32(dev, "user0", - macaddr[0] | (macaddr[1] << 8) | (macaddr[2] << 16)); - qdev_prop_set_uint32(dev, "user1", - macaddr[3] | (macaddr[4] << 8) | (macaddr[5] << 16)); - qdev_prop_set_uint32(dev, "did0", board->did0); - qdev_prop_set_uint32(dev, "did1", board->did1); - qdev_prop_set_uint32(dev, "dc0", board->dc0); - qdev_prop_set_uint32(dev, "dc1", board->dc1); - qdev_prop_set_uint32(dev, "dc2", board->dc2); - qdev_prop_set_uint32(dev, "dc3", board->dc3); - qdev_prop_set_uint32(dev, "dc4", board->dc4); - - sysbus_realize_and_unref(sbd, &error_fatal); - sysbus_mmio_map(sbd, 0, base); - sysbus_connect_irq(sbd, 0, irq); - - return dev; -} - /* I2C controller. */ #define TYPE_STELLARIS_I2C "stellaris-i2c" @@ -1349,6 +1024,7 @@ static void stellaris_init(MachineState *ms, stellaris_board_info *board) DeviceState *ssys_dev; int i; int j; + const uint8_t *macaddr; MemoryRegion *sram = g_new(MemoryRegion, 1); MemoryRegion *flash = g_new(MemoryRegion, 1); @@ -1366,15 +1042,42 @@ static void stellaris_init(MachineState *ms, stellaris_board_info *board) &error_fatal); memory_region_add_subregion(system_memory, 0x20000000, sram); + /* + * Create the system-registers object early, because we will + * need its sysclk output. + */ + ssys_dev = qdev_new(TYPE_STELLARIS_SYS); + /* Most devices come preprogrammed with a MAC address in the user data. */ + macaddr = nd_table[0].macaddr.a; + qdev_prop_set_uint32(ssys_dev, "user0", + macaddr[0] | (macaddr[1] << 8) | (macaddr[2] << 16)); + qdev_prop_set_uint32(ssys_dev, "user1", + macaddr[3] | (macaddr[4] << 8) | (macaddr[5] << 16)); + qdev_prop_set_uint32(ssys_dev, "did0", board->did0); + qdev_prop_set_uint32(ssys_dev, "did1", board->did1); + qdev_prop_set_uint32(ssys_dev, "dc0", board->dc0); + qdev_prop_set_uint32(ssys_dev, "dc1", board->dc1); + qdev_prop_set_uint32(ssys_dev, "dc2", board->dc2); + qdev_prop_set_uint32(ssys_dev, "dc3", board->dc3); + qdev_prop_set_uint32(ssys_dev, "dc4", board->dc4); + sysbus_realize_and_unref(SYS_BUS_DEVICE(ssys_dev), &error_fatal); + nvic = qdev_new(TYPE_ARMV7M); qdev_prop_set_uint32(nvic, "num-irq", NUM_IRQ_LINES); qdev_prop_set_string(nvic, "cpu-type", ms->cpu_type); qdev_prop_set_bit(nvic, "enable-bitband", true); + qdev_connect_clock_in(nvic, "cpuclk", + qdev_get_clock_out(ssys_dev, "SYSCLK")); + /* This SoC does not connect the systick reference clock */ object_property_set_link(OBJECT(nvic), "memory", OBJECT(get_system_memory()), &error_abort); /* This will exit with an error if the user passed us a bad cpu_type */ sysbus_realize_and_unref(SYS_BUS_DEVICE(nvic), &error_fatal); + /* Now we can wire up the IRQ and MMIO of the system registers */ + sysbus_mmio_map(SYS_BUS_DEVICE(ssys_dev), 0, 0x400fe000); + sysbus_connect_irq(SYS_BUS_DEVICE(ssys_dev), 0, qdev_get_gpio_in(nvic, 28)); + if (board->dc1 & (1 << 16)) { dev = sysbus_create_varargs(TYPE_STELLARIS_ADC, 0x40038000, qdev_get_gpio_in(nvic, 14), @@ -1388,19 +1091,21 @@ static void stellaris_init(MachineState *ms, stellaris_board_info *board) } for (i = 0; i < 4; i++) { if (board->dc2 & (0x10000 << i)) { - dev = sysbus_create_simple(TYPE_STELLARIS_GPTM, - 0x40030000 + i * 0x1000, - qdev_get_gpio_in(nvic, timer_irq[i])); + SysBusDevice *sbd; + + dev = qdev_new(TYPE_STELLARIS_GPTM); + sbd = SYS_BUS_DEVICE(dev); + qdev_connect_clock_in(dev, "clk", + qdev_get_clock_out(ssys_dev, "SYSCLK")); + sysbus_realize_and_unref(sbd, &error_fatal); + sysbus_mmio_map(sbd, 0, 0x40030000 + i * 0x1000); + sysbus_connect_irq(sbd, 0, qdev_get_gpio_in(nvic, timer_irq[i])); /* TODO: This is incorrect, but we get away with it because the ADC output is only ever pulsed. */ qdev_connect_gpio_out(dev, 0, adc); } } - ssys_dev = stellaris_sys_init(0x400fe000, qdev_get_gpio_in(nvic, 28), - board, nd_table[0].macaddr.a); - - if (board->dc1 & (1 << 3)) { /* watchdog present */ dev = qdev_new(TYPE_LUMINARY_WATCHDOG); @@ -1642,22 +1347,6 @@ static const TypeInfo stellaris_i2c_info = { .class_init = stellaris_i2c_class_init, }; -static void stellaris_gptm_class_init(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - - dc->vmsd = &vmstate_stellaris_gptm; - dc->realize = stellaris_gptm_realize; -} - -static const TypeInfo stellaris_gptm_info = { - .name = TYPE_STELLARIS_GPTM, - .parent = TYPE_SYS_BUS_DEVICE, - .instance_size = sizeof(gptm_state), - .instance_init = stellaris_gptm_init, - .class_init = stellaris_gptm_class_init, -}; - static void stellaris_adc_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); @@ -1696,7 +1385,6 @@ static const TypeInfo stellaris_sys_info = { static void stellaris_register_types(void) { type_register_static(&stellaris_i2c_info); - type_register_static(&stellaris_gptm_info); type_register_static(&stellaris_adc_info); type_register_static(&stellaris_sys_info); } diff --git a/hw/arm/stm32f100_soc.c b/hw/arm/stm32f100_soc.c index 0c4a5c6..f7b344b 100644 --- a/hw/arm/stm32f100_soc.c +++ b/hw/arm/stm32f100_soc.c @@ -30,6 +30,7 @@ #include "exec/address-spaces.h" #include "hw/arm/stm32f100_soc.h" #include "hw/qdev-properties.h" +#include "hw/qdev-clock.h" #include "hw/misc/unimp.h" #include "sysemu/sysemu.h" @@ -57,6 +58,9 @@ static void stm32f100_soc_initfn(Object *obj) for (i = 0; i < STM_NUM_SPIS; i++) { object_initialize_child(obj, "spi[*]", &s->spi[i], TYPE_STM32F2XX_SPI); } + + s->sysclk = qdev_init_clock_in(DEVICE(s), "sysclk", NULL, NULL, 0); + s->refclk = qdev_init_clock_in(DEVICE(s), "refclk", NULL, NULL, 0); } static void stm32f100_soc_realize(DeviceState *dev_soc, Error **errp) @@ -67,31 +71,54 @@ static void stm32f100_soc_realize(DeviceState *dev_soc, Error **errp) int i; MemoryRegion *system_memory = get_system_memory(); - MemoryRegion *sram = g_new(MemoryRegion, 1); - MemoryRegion *flash = g_new(MemoryRegion, 1); - MemoryRegion *flash_alias = g_new(MemoryRegion, 1); + + /* + * We use s->refclk internally and only define it with qdev_init_clock_in() + * so it is correctly parented and not leaked on an init/deinit; it is not + * intended as an externally exposed clock. + */ + if (clock_has_source(s->refclk)) { + error_setg(errp, "refclk clock must not be wired up by the board code"); + return; + } + + if (!clock_has_source(s->sysclk)) { + error_setg(errp, "sysclk clock must be wired up by the board code"); + return; + } + + /* + * TODO: ideally we should model the SoC RCC and its ability to + * change the sysclk frequency and define different sysclk sources. + */ + + /* The refclk always runs at frequency HCLK / 8 */ + clock_set_mul_div(s->refclk, 8, 1); + clock_set_source(s->refclk, s->sysclk); /* * Init flash region * Flash starts at 0x08000000 and then is aliased to boot memory at 0x0 */ - memory_region_init_rom(flash, OBJECT(dev_soc), "STM32F100.flash", + memory_region_init_rom(&s->flash, OBJECT(dev_soc), "STM32F100.flash", FLASH_SIZE, &error_fatal); - memory_region_init_alias(flash_alias, OBJECT(dev_soc), - "STM32F100.flash.alias", flash, 0, FLASH_SIZE); - memory_region_add_subregion(system_memory, FLASH_BASE_ADDRESS, flash); - memory_region_add_subregion(system_memory, 0, flash_alias); + memory_region_init_alias(&s->flash_alias, OBJECT(dev_soc), + "STM32F100.flash.alias", &s->flash, 0, FLASH_SIZE); + memory_region_add_subregion(system_memory, FLASH_BASE_ADDRESS, &s->flash); + memory_region_add_subregion(system_memory, 0, &s->flash_alias); /* Init SRAM region */ - memory_region_init_ram(sram, NULL, "STM32F100.sram", SRAM_SIZE, + memory_region_init_ram(&s->sram, NULL, "STM32F100.sram", SRAM_SIZE, &error_fatal); - memory_region_add_subregion(system_memory, SRAM_BASE_ADDRESS, sram); + memory_region_add_subregion(system_memory, SRAM_BASE_ADDRESS, &s->sram); /* Init ARMv7m */ armv7m = DEVICE(&s->armv7m); qdev_prop_set_uint32(armv7m, "num-irq", 61); qdev_prop_set_string(armv7m, "cpu-type", s->cpu_type); qdev_prop_set_bit(armv7m, "enable-bitband", true); + qdev_connect_clock_in(armv7m, "cpuclk", s->sysclk); + qdev_connect_clock_in(armv7m, "refclk", s->refclk); object_property_set_link(OBJECT(&s->armv7m), "memory", OBJECT(get_system_memory()), &error_abort); if (!sysbus_realize(SYS_BUS_DEVICE(&s->armv7m), errp)) { diff --git a/hw/arm/stm32f205_soc.c b/hw/arm/stm32f205_soc.c index 9cd41bf..c6b75a3 100644 --- a/hw/arm/stm32f205_soc.c +++ b/hw/arm/stm32f205_soc.c @@ -29,6 +29,7 @@ #include "exec/address-spaces.h" #include "hw/arm/stm32f205_soc.h" #include "hw/qdev-properties.h" +#include "hw/qdev-clock.h" #include "sysemu/sysemu.h" /* At the moment only Timer 2 to 5 are modelled */ @@ -74,6 +75,9 @@ static void stm32f205_soc_initfn(Object *obj) for (i = 0; i < STM_NUM_SPIS; i++) { object_initialize_child(obj, "spi[*]", &s->spi[i], TYPE_STM32F2XX_SPI); } + + s->sysclk = qdev_init_clock_in(DEVICE(s), "sysclk", NULL, NULL, 0); + s->refclk = qdev_init_clock_in(DEVICE(s), "refclk", NULL, NULL, 0); } static void stm32f205_soc_realize(DeviceState *dev_soc, Error **errp) @@ -84,26 +88,49 @@ static void stm32f205_soc_realize(DeviceState *dev_soc, Error **errp) int i; MemoryRegion *system_memory = get_system_memory(); - MemoryRegion *sram = g_new(MemoryRegion, 1); - MemoryRegion *flash = g_new(MemoryRegion, 1); - MemoryRegion *flash_alias = g_new(MemoryRegion, 1); - memory_region_init_rom(flash, OBJECT(dev_soc), "STM32F205.flash", + /* + * We use s->refclk internally and only define it with qdev_init_clock_in() + * so it is correctly parented and not leaked on an init/deinit; it is not + * intended as an externally exposed clock. + */ + if (clock_has_source(s->refclk)) { + error_setg(errp, "refclk clock must not be wired up by the board code"); + return; + } + + if (!clock_has_source(s->sysclk)) { + error_setg(errp, "sysclk clock must be wired up by the board code"); + return; + } + + /* + * TODO: ideally we should model the SoC RCC and its ability to + * change the sysclk frequency and define different sysclk sources. + */ + + /* The refclk always runs at frequency HCLK / 8 */ + clock_set_mul_div(s->refclk, 8, 1); + clock_set_source(s->refclk, s->sysclk); + + memory_region_init_rom(&s->flash, OBJECT(dev_soc), "STM32F205.flash", FLASH_SIZE, &error_fatal); - memory_region_init_alias(flash_alias, OBJECT(dev_soc), - "STM32F205.flash.alias", flash, 0, FLASH_SIZE); + memory_region_init_alias(&s->flash_alias, OBJECT(dev_soc), + "STM32F205.flash.alias", &s->flash, 0, FLASH_SIZE); - memory_region_add_subregion(system_memory, FLASH_BASE_ADDRESS, flash); - memory_region_add_subregion(system_memory, 0, flash_alias); + memory_region_add_subregion(system_memory, FLASH_BASE_ADDRESS, &s->flash); + memory_region_add_subregion(system_memory, 0, &s->flash_alias); - memory_region_init_ram(sram, NULL, "STM32F205.sram", SRAM_SIZE, + memory_region_init_ram(&s->sram, NULL, "STM32F205.sram", SRAM_SIZE, &error_fatal); - memory_region_add_subregion(system_memory, SRAM_BASE_ADDRESS, sram); + memory_region_add_subregion(system_memory, SRAM_BASE_ADDRESS, &s->sram); armv7m = DEVICE(&s->armv7m); qdev_prop_set_uint32(armv7m, "num-irq", 96); qdev_prop_set_string(armv7m, "cpu-type", s->cpu_type); qdev_prop_set_bit(armv7m, "enable-bitband", true); + qdev_connect_clock_in(armv7m, "cpuclk", s->sysclk); + qdev_connect_clock_in(armv7m, "refclk", s->refclk); object_property_set_link(OBJECT(&s->armv7m), "memory", OBJECT(get_system_memory()), &error_abort); if (!sysbus_realize(SYS_BUS_DEVICE(&s->armv7m), errp)) { diff --git a/hw/arm/stm32f405_soc.c b/hw/arm/stm32f405_soc.c index cb04c11..0019b7f 100644 --- a/hw/arm/stm32f405_soc.c +++ b/hw/arm/stm32f405_soc.c @@ -28,6 +28,7 @@ #include "exec/address-spaces.h" #include "sysemu/sysemu.h" #include "hw/arm/stm32f405_soc.h" +#include "hw/qdev-clock.h" #include "hw/misc/unimp.h" #define SYSCFG_ADD 0x40013800 @@ -80,6 +81,9 @@ static void stm32f405_soc_initfn(Object *obj) } object_initialize_child(obj, "exti", &s->exti, TYPE_STM32F4XX_EXTI); + + s->sysclk = qdev_init_clock_in(DEVICE(s), "sysclk", NULL, NULL, 0); + s->refclk = qdev_init_clock_in(DEVICE(s), "refclk", NULL, NULL, 0); } static void stm32f405_soc_realize(DeviceState *dev_soc, Error **errp) @@ -91,6 +95,30 @@ static void stm32f405_soc_realize(DeviceState *dev_soc, Error **errp) Error *err = NULL; int i; + /* + * We use s->refclk internally and only define it with qdev_init_clock_in() + * so it is correctly parented and not leaked on an init/deinit; it is not + * intended as an externally exposed clock. + */ + if (clock_has_source(s->refclk)) { + error_setg(errp, "refclk clock must not be wired up by the board code"); + return; + } + + if (!clock_has_source(s->sysclk)) { + error_setg(errp, "sysclk clock must be wired up by the board code"); + return; + } + + /* + * TODO: ideally we should model the SoC RCC and its ability to + * change the sysclk frequency and define different sysclk sources. + */ + + /* The refclk always runs at frequency HCLK / 8 */ + clock_set_mul_div(s->refclk, 8, 1); + clock_set_source(s->refclk, s->sysclk); + memory_region_init_rom(&s->flash, OBJECT(dev_soc), "STM32F405.flash", FLASH_SIZE, &err); if (err != NULL) { @@ -116,6 +144,8 @@ static void stm32f405_soc_realize(DeviceState *dev_soc, Error **errp) qdev_prop_set_uint32(armv7m, "num-irq", 96); qdev_prop_set_string(armv7m, "cpu-type", s->cpu_type); qdev_prop_set_bit(armv7m, "enable-bitband", true); + qdev_connect_clock_in(armv7m, "cpuclk", s->sysclk); + qdev_connect_clock_in(armv7m, "refclk", s->refclk); object_property_set_link(OBJECT(&s->armv7m), "memory", OBJECT(system_memory), &error_abort); if (!sysbus_realize(SYS_BUS_DEVICE(&s->armv7m), errp)) { diff --git a/hw/arm/stm32vldiscovery.c b/hw/arm/stm32vldiscovery.c index 7e8191e..04036da 100644 --- a/hw/arm/stm32vldiscovery.c +++ b/hw/arm/stm32vldiscovery.c @@ -27,6 +27,7 @@ #include "qapi/error.h" #include "hw/boards.h" #include "hw/qdev-properties.h" +#include "hw/qdev-clock.h" #include "qemu/error-report.h" #include "hw/arm/stm32f100_soc.h" #include "hw/arm/boot.h" @@ -39,16 +40,15 @@ static void stm32vldiscovery_init(MachineState *machine) { DeviceState *dev; + Clock *sysclk; - /* - * TODO: ideally we would model the SoC RCC and let it handle - * system_clock_scale, including its ability to define different - * possible SYSCLK sources. - */ - system_clock_scale = NANOSECONDS_PER_SECOND / SYSCLK_FRQ; + /* This clock doesn't need migration because it is fixed-frequency */ + sysclk = clock_new(OBJECT(machine), "SYSCLK"); + clock_set_hz(sysclk, SYSCLK_FRQ); dev = qdev_new(TYPE_STM32F100_SOC); qdev_prop_set_string(dev, "cpu-type", ARM_CPU_TYPE_NAME("cortex-m3")); + qdev_connect_clock_in(dev, "sysclk", sysclk); sysbus_realize_and_unref(SYS_BUS_DEVICE(dev), &error_fatal); armv7m_load_kernel(ARM_CPU(first_cpu), @@ -63,4 +63,3 @@ static void stm32vldiscovery_machine_init(MachineClass *mc) } DEFINE_MACHINE("stm32vldiscovery", stm32vldiscovery_machine_init) - diff --git a/hw/arm/virt.c b/hw/arm/virt.c index 86c8a4c..73e9c6b 100644 --- a/hw/arm/virt.c +++ b/hw/arm/virt.c @@ -200,6 +200,7 @@ static const char *valid_cpus[] = { ARM_CPU_TYPE_NAME("cortex-a53"), ARM_CPU_TYPE_NAME("cortex-a57"), ARM_CPU_TYPE_NAME("cortex-a72"), + ARM_CPU_TYPE_NAME("a64fx"), ARM_CPU_TYPE_NAME("host"), ARM_CPU_TYPE_NAME("max"), }; @@ -2783,10 +2784,17 @@ static void machvirt_machine_init(void) } type_init(machvirt_machine_init); +static void virt_machine_6_2_options(MachineClass *mc) +{ +} +DEFINE_VIRT_MACHINE_AS_LATEST(6, 2) + static void virt_machine_6_1_options(MachineClass *mc) { + virt_machine_6_2_options(mc); + compat_props_add(mc->compat_props, hw_compat_6_1, hw_compat_6_1_len); } -DEFINE_VIRT_MACHINE_AS_LATEST(6, 1) +DEFINE_VIRT_MACHINE(6, 1) static void virt_machine_6_0_options(MachineClass *mc) { diff --git a/hw/core/clock-vmstate.c b/hw/core/clock-vmstate.c index 260b13f..9d9174f 100644 --- a/hw/core/clock-vmstate.c +++ b/hw/core/clock-vmstate.c @@ -14,12 +14,50 @@ #include "migration/vmstate.h" #include "hw/clock.h" +static bool muldiv_needed(void *opaque) +{ + Clock *clk = opaque; + + return clk->multiplier != 1 || clk->divider != 1; +} + +static int clock_pre_load(void *opaque) +{ + Clock *clk = opaque; + /* + * The initial out-of-reset settings of the Clock might have been + * configured by the device to be different from what we set + * in clock_initfn(), so we must here set the default values to + * be used if they are not in the inbound migration state. + */ + clk->multiplier = 1; + clk->divider = 1; + + return 0; +} + +const VMStateDescription vmstate_muldiv = { + .name = "clock/muldiv", + .version_id = 1, + .minimum_version_id = 1, + .needed = muldiv_needed, + .fields = (VMStateField[]) { + VMSTATE_UINT32(multiplier, Clock), + VMSTATE_UINT32(divider, Clock), + }, +}; + const VMStateDescription vmstate_clock = { .name = "clock", .version_id = 0, .minimum_version_id = 0, + .pre_load = clock_pre_load, .fields = (VMStateField[]) { VMSTATE_UINT64(period, Clock), VMSTATE_END_OF_LIST() - } + }, + .subsections = (const VMStateDescription*[]) { + &vmstate_muldiv, + NULL + }, }; diff --git a/hw/core/clock.c b/hw/core/clock.c index fc5a996..916875e 100644 --- a/hw/core/clock.c +++ b/hw/core/clock.c @@ -64,6 +64,15 @@ bool clock_set(Clock *clk, uint64_t period) return true; } +static uint64_t clock_get_child_period(Clock *clk) +{ + /* + * Return the period to be used for child clocks, which is the parent + * clock period adjusted for for multiplier and divider effects. + */ + return muldiv64(clk->period, clk->multiplier, clk->divider); +} + static void clock_call_callback(Clock *clk, ClockEvent event) { /* @@ -78,15 +87,16 @@ static void clock_call_callback(Clock *clk, ClockEvent event) static void clock_propagate_period(Clock *clk, bool call_callbacks) { Clock *child; + uint64_t child_period = clock_get_child_period(clk); QLIST_FOREACH(child, &clk->children, sibling) { - if (child->period != clk->period) { + if (child->period != child_period) { if (call_callbacks) { clock_call_callback(child, ClockPreUpdate); } - child->period = clk->period; + child->period = child_period; trace_clock_update(CLOCK_PATH(child), CLOCK_PATH(clk), - CLOCK_PERIOD_TO_HZ(clk->period), + CLOCK_PERIOD_TO_HZ(child->period), call_callbacks); if (call_callbacks) { clock_call_callback(child, ClockUpdate); @@ -110,7 +120,7 @@ void clock_set_source(Clock *clk, Clock *src) trace_clock_set_source(CLOCK_PATH(clk), CLOCK_PATH(src)); - clk->period = src->period; + clk->period = clock_get_child_period(src); QLIST_INSERT_HEAD(&src->children, clk, sibling); clk->source = src; clock_propagate_period(clk, false); @@ -133,10 +143,23 @@ char *clock_display_freq(Clock *clk) return freq_to_str(clock_get_hz(clk)); } +void clock_set_mul_div(Clock *clk, uint32_t multiplier, uint32_t divider) +{ + assert(divider != 0); + + trace_clock_set_mul_div(CLOCK_PATH(clk), clk->multiplier, multiplier, + clk->divider, divider); + clk->multiplier = multiplier; + clk->divider = divider; +} + static void clock_initfn(Object *obj) { Clock *clk = CLOCK(obj); + clk->multiplier = 1; + clk->divider = 1; + QLIST_INIT(&clk->children); } diff --git a/hw/core/machine.c b/hw/core/machine.c index 54e0405..067f42b 100644 --- a/hw/core/machine.c +++ b/hw/core/machine.c @@ -37,6 +37,9 @@ #include "hw/virtio/virtio.h" #include "hw/virtio/virtio-pci.h" +GlobalProperty hw_compat_6_1[] = {}; +const size_t hw_compat_6_1_len = G_N_ELEMENTS(hw_compat_6_1); + GlobalProperty hw_compat_6_0[] = { { "gpex-pcihost", "allow-unmapped-accesses", "false" }, { "i8042", "extended-state", "false"}, diff --git a/hw/core/trace-events b/hw/core/trace-events index 360ddeb..9b3ecce 100644 --- a/hw/core/trace-events +++ b/hw/core/trace-events @@ -34,3 +34,4 @@ clock_disconnect(const char *clk) "'%s'" clock_set(const char *clk, uint64_t old, uint64_t new) "'%s', %"PRIu64"Hz->%"PRIu64"Hz" clock_propagate(const char *clk) "'%s'" clock_update(const char *clk, const char *src, uint64_t hz, int cb) "'%s', src='%s', val=%"PRIu64"Hz cb=%d" +clock_set_mul_div(const char *clk, uint32_t oldmul, uint32_t mul, uint32_t olddiv, uint32_t div) "'%s', mul: %u -> %u, div: %u -> %u" diff --git a/hw/i386/pc.c b/hw/i386/pc.c index 102b223..1276bfe 100644 --- a/hw/i386/pc.c +++ b/hw/i386/pc.c @@ -93,6 +93,9 @@ #include "trace.h" #include CONFIG_DEVICES +GlobalProperty pc_compat_6_1[] = {}; +const size_t pc_compat_6_1_len = G_N_ELEMENTS(pc_compat_6_1); + GlobalProperty pc_compat_6_0[] = { { "qemu64" "-" TYPE_X86_CPU, "family", "6" }, { "qemu64" "-" TYPE_X86_CPU, "model", "6" }, diff --git a/hw/i386/pc_piix.c b/hw/i386/pc_piix.c index 1bc3016..c5da773 100644 --- a/hw/i386/pc_piix.c +++ b/hw/i386/pc_piix.c @@ -412,7 +412,7 @@ static void pc_i440fx_machine_options(MachineClass *m) machine_class_allow_dynamic_sysbus_dev(m, TYPE_VMBUS_BRIDGE); } -static void pc_i440fx_6_1_machine_options(MachineClass *m) +static void pc_i440fx_6_2_machine_options(MachineClass *m) { PCMachineClass *pcmc = PC_MACHINE_CLASS(m); pc_i440fx_machine_options(m); @@ -421,6 +421,18 @@ static void pc_i440fx_6_1_machine_options(MachineClass *m) pcmc->default_cpu_version = 1; } +DEFINE_I440FX_MACHINE(v6_2, "pc-i440fx-6.2", NULL, + pc_i440fx_6_2_machine_options); + +static void pc_i440fx_6_1_machine_options(MachineClass *m) +{ + pc_i440fx_6_2_machine_options(m); + m->alias = NULL; + m->is_default = false; + compat_props_add(m->compat_props, hw_compat_6_1, hw_compat_6_1_len); + compat_props_add(m->compat_props, pc_compat_6_1, pc_compat_6_1_len); +} + DEFINE_I440FX_MACHINE(v6_1, "pc-i440fx-6.1", NULL, pc_i440fx_6_1_machine_options); diff --git a/hw/i386/pc_q35.c b/hw/i386/pc_q35.c index eeb0b18..565fadc 100644 --- a/hw/i386/pc_q35.c +++ b/hw/i386/pc_q35.c @@ -354,7 +354,7 @@ static void pc_q35_machine_options(MachineClass *m) m->max_cpus = 288; } -static void pc_q35_6_1_machine_options(MachineClass *m) +static void pc_q35_6_2_machine_options(MachineClass *m) { PCMachineClass *pcmc = PC_MACHINE_CLASS(m); pc_q35_machine_options(m); @@ -362,6 +362,17 @@ static void pc_q35_6_1_machine_options(MachineClass *m) pcmc->default_cpu_version = 1; } +DEFINE_Q35_MACHINE(v6_2, "pc-q35-6.2", NULL, + pc_q35_6_2_machine_options); + +static void pc_q35_6_1_machine_options(MachineClass *m) +{ + pc_q35_6_2_machine_options(m); + m->alias = NULL; + compat_props_add(m->compat_props, hw_compat_6_1, hw_compat_6_1_len); + compat_props_add(m->compat_props, pc_compat_6_1, pc_compat_6_1_len); +} + DEFINE_Q35_MACHINE(v6_1, "pc-q35-6.1", NULL, pc_q35_6_1_machine_options); diff --git a/hw/intc/arm_gicv3_dist.c b/hw/intc/arm_gicv3_dist.c index b65f56f..5beb7c4 100644 --- a/hw/intc/arm_gicv3_dist.c +++ b/hw/intc/arm_gicv3_dist.c @@ -262,8 +262,21 @@ static void gicd_write_irouter(GICv3State *s, MemTxAttrs attrs, int irq, gicv3_update(s, irq, 1); } -static MemTxResult gicd_readb(GICv3State *s, hwaddr offset, - uint64_t *data, MemTxAttrs attrs) +/** + * gicd_readb + * gicd_readw + * gicd_readl + * gicd_readq + * gicd_writeb + * gicd_writew + * gicd_writel + * gicd_writeq + * + * Return %true if the operation succeeded, %false otherwise. + */ + +static bool gicd_readb(GICv3State *s, hwaddr offset, + uint64_t *data, MemTxAttrs attrs) { /* Most GICv3 distributor registers do not support byte accesses. */ switch (offset) { @@ -273,17 +286,17 @@ static MemTxResult gicd_readb(GICv3State *s, hwaddr offset, /* This GIC implementation always has affinity routing enabled, * so these registers are all RAZ/WI. */ - return MEMTX_OK; + return true; case GICD_IPRIORITYR ... GICD_IPRIORITYR + 0x3ff: *data = gicd_read_ipriorityr(s, attrs, offset - GICD_IPRIORITYR); - return MEMTX_OK; + return true; default: - return MEMTX_ERROR; + return false; } } -static MemTxResult gicd_writeb(GICv3State *s, hwaddr offset, - uint64_t value, MemTxAttrs attrs) +static bool gicd_writeb(GICv3State *s, hwaddr offset, + uint64_t value, MemTxAttrs attrs) { /* Most GICv3 distributor registers do not support byte accesses. */ switch (offset) { @@ -293,25 +306,25 @@ static MemTxResult gicd_writeb(GICv3State *s, hwaddr offset, /* This GIC implementation always has affinity routing enabled, * so these registers are all RAZ/WI. */ - return MEMTX_OK; + return true; case GICD_IPRIORITYR ... GICD_IPRIORITYR + 0x3ff: { int irq = offset - GICD_IPRIORITYR; if (irq < GIC_INTERNAL || irq >= s->num_irq) { - return MEMTX_OK; + return true; } gicd_write_ipriorityr(s, attrs, irq, value); gicv3_update(s, irq, 1); - return MEMTX_OK; + return true; } default: - return MEMTX_ERROR; + return false; } } -static MemTxResult gicd_readw(GICv3State *s, hwaddr offset, - uint64_t *data, MemTxAttrs attrs) +static bool gicd_readw(GICv3State *s, hwaddr offset, + uint64_t *data, MemTxAttrs attrs) { /* Only GICD_SETSPI_NSR, GICD_CLRSPI_NSR, GICD_SETSPI_SR and GICD_SETSPI_NSR * support 16 bit accesses, and those registers are all part of the @@ -319,11 +332,11 @@ static MemTxResult gicd_readw(GICv3State *s, hwaddr offset, * implement (ie for us GICD_TYPER.MBIS == 0), so for us they are * reserved. */ - return MEMTX_ERROR; + return false; } -static MemTxResult gicd_writew(GICv3State *s, hwaddr offset, - uint64_t value, MemTxAttrs attrs) +static bool gicd_writew(GICv3State *s, hwaddr offset, + uint64_t value, MemTxAttrs attrs) { /* Only GICD_SETSPI_NSR, GICD_CLRSPI_NSR, GICD_SETSPI_SR and GICD_SETSPI_NSR * support 16 bit accesses, and those registers are all part of the @@ -331,11 +344,11 @@ static MemTxResult gicd_writew(GICv3State *s, hwaddr offset, * implement (ie for us GICD_TYPER.MBIS == 0), so for us they are * reserved. */ - return MEMTX_ERROR; + return false; } -static MemTxResult gicd_readl(GICv3State *s, hwaddr offset, - uint64_t *data, MemTxAttrs attrs) +static bool gicd_readl(GICv3State *s, hwaddr offset, + uint64_t *data, MemTxAttrs attrs) { /* Almost all GICv3 distributor registers are 32-bit. * Note that WO registers must return an UNKNOWN value on reads, @@ -363,7 +376,7 @@ static MemTxResult gicd_readl(GICv3State *s, hwaddr offset, } else { *data = s->gicd_ctlr; } - return MEMTX_OK; + return true; case GICD_TYPER: { /* For this implementation: @@ -387,61 +400,61 @@ static MemTxResult gicd_readl(GICv3State *s, hwaddr offset, *data = (1 << 25) | (1 << 24) | (sec_extn << 10) | (0xf << 19) | itlinesnumber; - return MEMTX_OK; + return true; } case GICD_IIDR: /* We claim to be an ARM r0p0 with a zero ProductID. * This is the same as an r0p0 GIC-500. */ *data = gicv3_iidr(); - return MEMTX_OK; + return true; case GICD_STATUSR: /* RAZ/WI for us (this is an optional register and our implementation * does not track RO/WO/reserved violations to report them to the guest) */ *data = 0; - return MEMTX_OK; + return true; case GICD_IGROUPR ... GICD_IGROUPR + 0x7f: { int irq; if (!attrs.secure && !(s->gicd_ctlr & GICD_CTLR_DS)) { *data = 0; - return MEMTX_OK; + return true; } /* RAZ/WI for SGIs, PPIs, unimplemented irqs */ irq = (offset - GICD_IGROUPR) * 8; if (irq < GIC_INTERNAL || irq >= s->num_irq) { *data = 0; - return MEMTX_OK; + return true; } *data = *gic_bmp_ptr32(s->group, irq); - return MEMTX_OK; + return true; } case GICD_ISENABLER ... GICD_ISENABLER + 0x7f: *data = gicd_read_bitmap_reg(s, attrs, s->enabled, NULL, offset - GICD_ISENABLER); - return MEMTX_OK; + return true; case GICD_ICENABLER ... GICD_ICENABLER + 0x7f: *data = gicd_read_bitmap_reg(s, attrs, s->enabled, NULL, offset - GICD_ICENABLER); - return MEMTX_OK; + return true; case GICD_ISPENDR ... GICD_ISPENDR + 0x7f: *data = gicd_read_bitmap_reg(s, attrs, s->pending, mask_nsacr_ge1, offset - GICD_ISPENDR); - return MEMTX_OK; + return true; case GICD_ICPENDR ... GICD_ICPENDR + 0x7f: *data = gicd_read_bitmap_reg(s, attrs, s->pending, mask_nsacr_ge2, offset - GICD_ICPENDR); - return MEMTX_OK; + return true; case GICD_ISACTIVER ... GICD_ISACTIVER + 0x7f: *data = gicd_read_bitmap_reg(s, attrs, s->active, mask_nsacr_ge2, offset - GICD_ISACTIVER); - return MEMTX_OK; + return true; case GICD_ICACTIVER ... GICD_ICACTIVER + 0x7f: *data = gicd_read_bitmap_reg(s, attrs, s->active, mask_nsacr_ge2, offset - GICD_ICACTIVER); - return MEMTX_OK; + return true; case GICD_IPRIORITYR ... GICD_IPRIORITYR + 0x3ff: { int i, irq = offset - GICD_IPRIORITYR; @@ -452,12 +465,12 @@ static MemTxResult gicd_readl(GICv3State *s, hwaddr offset, value |= gicd_read_ipriorityr(s, attrs, i); } *data = value; - return MEMTX_OK; + return true; } case GICD_ITARGETSR ... GICD_ITARGETSR + 0x3ff: /* RAZ/WI since affinity routing is always enabled */ *data = 0; - return MEMTX_OK; + return true; case GICD_ICFGR ... GICD_ICFGR + 0xff: { /* Here only the even bits are used; odd bits are RES0 */ @@ -466,7 +479,7 @@ static MemTxResult gicd_readl(GICv3State *s, hwaddr offset, if (irq < GIC_INTERNAL || irq >= s->num_irq) { *data = 0; - return MEMTX_OK; + return true; } /* Since our edge_trigger bitmap is one bit per irq, we only need @@ -478,7 +491,7 @@ static MemTxResult gicd_readl(GICv3State *s, hwaddr offset, value = extract32(value, (irq & 0x1f) ? 16 : 0, 16); value = half_shuffle32(value) << 1; *data = value; - return MEMTX_OK; + return true; } case GICD_IGRPMODR ... GICD_IGRPMODR + 0xff: { @@ -489,16 +502,16 @@ static MemTxResult gicd_readl(GICv3State *s, hwaddr offset, * security enabled and this is an NS access */ *data = 0; - return MEMTX_OK; + return true; } /* RAZ/WI for SGIs, PPIs, unimplemented irqs */ irq = (offset - GICD_IGRPMODR) * 8; if (irq < GIC_INTERNAL || irq >= s->num_irq) { *data = 0; - return MEMTX_OK; + return true; } *data = *gic_bmp_ptr32(s->grpmod, irq); - return MEMTX_OK; + return true; } case GICD_NSACR ... GICD_NSACR + 0xff: { @@ -507,7 +520,7 @@ static MemTxResult gicd_readl(GICv3State *s, hwaddr offset, if (irq < GIC_INTERNAL || irq >= s->num_irq) { *data = 0; - return MEMTX_OK; + return true; } if ((s->gicd_ctlr & GICD_CTLR_DS) || !attrs.secure) { @@ -515,17 +528,17 @@ static MemTxResult gicd_readl(GICv3State *s, hwaddr offset, * security enabled and this is an NS access */ *data = 0; - return MEMTX_OK; + return true; } *data = s->gicd_nsacr[irq / 16]; - return MEMTX_OK; + return true; } case GICD_CPENDSGIR ... GICD_CPENDSGIR + 0xf: case GICD_SPENDSGIR ... GICD_SPENDSGIR + 0xf: /* RAZ/WI since affinity routing is always enabled */ *data = 0; - return MEMTX_OK; + return true; case GICD_IROUTER ... GICD_IROUTER + 0x1fdf: { uint64_t r; @@ -537,26 +550,26 @@ static MemTxResult gicd_readl(GICv3State *s, hwaddr offset, } else { *data = (uint32_t)r; } - return MEMTX_OK; + return true; } case GICD_IDREGS ... GICD_IDREGS + 0x2f: /* ID registers */ *data = gicv3_idreg(offset - GICD_IDREGS); - return MEMTX_OK; + return true; case GICD_SGIR: /* WO registers, return unknown value */ qemu_log_mask(LOG_GUEST_ERROR, "%s: invalid guest read from WO register at offset " TARGET_FMT_plx "\n", __func__, offset); *data = 0; - return MEMTX_OK; + return true; default: - return MEMTX_ERROR; + return false; } } -static MemTxResult gicd_writel(GICv3State *s, hwaddr offset, - uint64_t value, MemTxAttrs attrs) +static bool gicd_writel(GICv3State *s, hwaddr offset, + uint64_t value, MemTxAttrs attrs) { /* Almost all GICv3 distributor registers are 32-bit. Note that * RO registers must ignore writes, not abort. @@ -600,68 +613,68 @@ static MemTxResult gicd_writel(GICv3State *s, hwaddr offset, s->gicd_ctlr &= ~(GICD_CTLR_EN_GRP1S | GICD_CTLR_ARE_NS); } gicv3_full_update(s); - return MEMTX_OK; + return true; } case GICD_STATUSR: /* RAZ/WI for our implementation */ - return MEMTX_OK; + return true; case GICD_IGROUPR ... GICD_IGROUPR + 0x7f: { int irq; if (!attrs.secure && !(s->gicd_ctlr & GICD_CTLR_DS)) { - return MEMTX_OK; + return true; } /* RAZ/WI for SGIs, PPIs, unimplemented irqs */ irq = (offset - GICD_IGROUPR) * 8; if (irq < GIC_INTERNAL || irq >= s->num_irq) { - return MEMTX_OK; + return true; } *gic_bmp_ptr32(s->group, irq) = value; gicv3_update(s, irq, 32); - return MEMTX_OK; + return true; } case GICD_ISENABLER ... GICD_ISENABLER + 0x7f: gicd_write_set_bitmap_reg(s, attrs, s->enabled, NULL, offset - GICD_ISENABLER, value); - return MEMTX_OK; + return true; case GICD_ICENABLER ... GICD_ICENABLER + 0x7f: gicd_write_clear_bitmap_reg(s, attrs, s->enabled, NULL, offset - GICD_ICENABLER, value); - return MEMTX_OK; + return true; case GICD_ISPENDR ... GICD_ISPENDR + 0x7f: gicd_write_set_bitmap_reg(s, attrs, s->pending, mask_nsacr_ge1, offset - GICD_ISPENDR, value); - return MEMTX_OK; + return true; case GICD_ICPENDR ... GICD_ICPENDR + 0x7f: gicd_write_clear_bitmap_reg(s, attrs, s->pending, mask_nsacr_ge2, offset - GICD_ICPENDR, value); - return MEMTX_OK; + return true; case GICD_ISACTIVER ... GICD_ISACTIVER + 0x7f: gicd_write_set_bitmap_reg(s, attrs, s->active, NULL, offset - GICD_ISACTIVER, value); - return MEMTX_OK; + return true; case GICD_ICACTIVER ... GICD_ICACTIVER + 0x7f: gicd_write_clear_bitmap_reg(s, attrs, s->active, NULL, offset - GICD_ICACTIVER, value); - return MEMTX_OK; + return true; case GICD_IPRIORITYR ... GICD_IPRIORITYR + 0x3ff: { int i, irq = offset - GICD_IPRIORITYR; if (irq < GIC_INTERNAL || irq + 3 >= s->num_irq) { - return MEMTX_OK; + return true; } for (i = irq; i < irq + 4; i++, value >>= 8) { gicd_write_ipriorityr(s, attrs, i, value); } gicv3_update(s, irq, 4); - return MEMTX_OK; + return true; } case GICD_ITARGETSR ... GICD_ITARGETSR + 0x3ff: /* RAZ/WI since affinity routing is always enabled */ - return MEMTX_OK; + return true; case GICD_ICFGR ... GICD_ICFGR + 0xff: { /* Here only the odd bits are used; even bits are RES0 */ @@ -669,7 +682,7 @@ static MemTxResult gicd_writel(GICv3State *s, hwaddr offset, uint32_t mask, oldval; if (irq < GIC_INTERNAL || irq >= s->num_irq) { - return MEMTX_OK; + return true; } /* Since our edge_trigger bitmap is one bit per irq, our input @@ -687,7 +700,7 @@ static MemTxResult gicd_writel(GICv3State *s, hwaddr offset, oldval = *gic_bmp_ptr32(s->edge_trigger, (irq & ~0x1f)); value = (oldval & ~mask) | (value & mask); *gic_bmp_ptr32(s->edge_trigger, irq & ~0x1f) = value; - return MEMTX_OK; + return true; } case GICD_IGRPMODR ... GICD_IGRPMODR + 0xff: { @@ -697,16 +710,16 @@ static MemTxResult gicd_writel(GICv3State *s, hwaddr offset, /* RAZ/WI if security disabled, or if * security enabled and this is an NS access */ - return MEMTX_OK; + return true; } /* RAZ/WI for SGIs, PPIs, unimplemented irqs */ irq = (offset - GICD_IGRPMODR) * 8; if (irq < GIC_INTERNAL || irq >= s->num_irq) { - return MEMTX_OK; + return true; } *gic_bmp_ptr32(s->grpmod, irq) = value; gicv3_update(s, irq, 32); - return MEMTX_OK; + return true; } case GICD_NSACR ... GICD_NSACR + 0xff: { @@ -714,41 +727,41 @@ static MemTxResult gicd_writel(GICv3State *s, hwaddr offset, int irq = (offset - GICD_NSACR) * 4; if (irq < GIC_INTERNAL || irq >= s->num_irq) { - return MEMTX_OK; + return true; } if ((s->gicd_ctlr & GICD_CTLR_DS) || !attrs.secure) { /* RAZ/WI if security disabled, or if * security enabled and this is an NS access */ - return MEMTX_OK; + return true; } s->gicd_nsacr[irq / 16] = value; /* No update required as this only affects access permission checks */ - return MEMTX_OK; + return true; } case GICD_SGIR: /* RES0 if affinity routing is enabled */ - return MEMTX_OK; + return true; case GICD_CPENDSGIR ... GICD_CPENDSGIR + 0xf: case GICD_SPENDSGIR ... GICD_SPENDSGIR + 0xf: /* RAZ/WI since affinity routing is always enabled */ - return MEMTX_OK; + return true; case GICD_IROUTER ... GICD_IROUTER + 0x1fdf: { uint64_t r; int irq = (offset - GICD_IROUTER) / 8; if (irq < GIC_INTERNAL || irq >= s->num_irq) { - return MEMTX_OK; + return true; } /* Write half of the 64-bit register */ r = gicd_read_irouter(s, attrs, irq); r = deposit64(r, (offset & 7) ? 32 : 0, 32, value); gicd_write_irouter(s, attrs, irq, r); - return MEMTX_OK; + return true; } case GICD_IDREGS ... GICD_IDREGS + 0x2f: case GICD_TYPER: @@ -757,14 +770,14 @@ static MemTxResult gicd_writel(GICv3State *s, hwaddr offset, qemu_log_mask(LOG_GUEST_ERROR, "%s: invalid guest write to RO register at offset " TARGET_FMT_plx "\n", __func__, offset); - return MEMTX_OK; + return true; default: - return MEMTX_ERROR; + return false; } } -static MemTxResult gicd_writell(GICv3State *s, hwaddr offset, - uint64_t value, MemTxAttrs attrs) +static bool gicd_writeq(GICv3State *s, hwaddr offset, + uint64_t value, MemTxAttrs attrs) { /* Our only 64-bit registers are GICD_IROUTER<n> */ int irq; @@ -773,14 +786,14 @@ static MemTxResult gicd_writell(GICv3State *s, hwaddr offset, case GICD_IROUTER ... GICD_IROUTER + 0x1fdf: irq = (offset - GICD_IROUTER) / 8; gicd_write_irouter(s, attrs, irq, value); - return MEMTX_OK; + return true; default: - return MEMTX_ERROR; + return false; } } -static MemTxResult gicd_readll(GICv3State *s, hwaddr offset, - uint64_t *data, MemTxAttrs attrs) +static bool gicd_readq(GICv3State *s, hwaddr offset, + uint64_t *data, MemTxAttrs attrs) { /* Our only 64-bit registers are GICD_IROUTER<n> */ int irq; @@ -789,9 +802,9 @@ static MemTxResult gicd_readll(GICv3State *s, hwaddr offset, case GICD_IROUTER ... GICD_IROUTER + 0x1fdf: irq = (offset - GICD_IROUTER) / 8; *data = gicd_read_irouter(s, attrs, irq); - return MEMTX_OK; + return true; default: - return MEMTX_ERROR; + return false; } } @@ -799,7 +812,7 @@ MemTxResult gicv3_dist_read(void *opaque, hwaddr offset, uint64_t *data, unsigned size, MemTxAttrs attrs) { GICv3State *s = (GICv3State *)opaque; - MemTxResult r; + bool r; switch (size) { case 1: @@ -812,14 +825,14 @@ MemTxResult gicv3_dist_read(void *opaque, hwaddr offset, uint64_t *data, r = gicd_readl(s, offset, data, attrs); break; case 8: - r = gicd_readll(s, offset, data, attrs); + r = gicd_readq(s, offset, data, attrs); break; default: - r = MEMTX_ERROR; + r = false; break; } - if (r == MEMTX_ERROR) { + if (!r) { qemu_log_mask(LOG_GUEST_ERROR, "%s: invalid guest read at offset " TARGET_FMT_plx "size %u\n", __func__, offset, size); @@ -829,19 +842,18 @@ MemTxResult gicv3_dist_read(void *opaque, hwaddr offset, uint64_t *data, * trigger the guest-error logging but don't return it to * the caller, or we'll cause a spurious guest data abort. */ - r = MEMTX_OK; *data = 0; } else { trace_gicv3_dist_read(offset, *data, size, attrs.secure); } - return r; + return MEMTX_OK; } MemTxResult gicv3_dist_write(void *opaque, hwaddr offset, uint64_t data, unsigned size, MemTxAttrs attrs) { GICv3State *s = (GICv3State *)opaque; - MemTxResult r; + bool r; switch (size) { case 1: @@ -854,14 +866,14 @@ MemTxResult gicv3_dist_write(void *opaque, hwaddr offset, uint64_t data, r = gicd_writel(s, offset, data, attrs); break; case 8: - r = gicd_writell(s, offset, data, attrs); + r = gicd_writeq(s, offset, data, attrs); break; default: - r = MEMTX_ERROR; + r = false; break; } - if (r == MEMTX_ERROR) { + if (!r) { qemu_log_mask(LOG_GUEST_ERROR, "%s: invalid guest write at offset " TARGET_FMT_plx "size %u\n", __func__, offset, size); @@ -871,11 +883,10 @@ MemTxResult gicv3_dist_write(void *opaque, hwaddr offset, uint64_t data, * trigger the guest-error logging but don't return it to * the caller, or we'll cause a spurious guest data abort. */ - r = MEMTX_OK; } else { trace_gicv3_dist_write(offset, data, size, attrs.secure); } - return r; + return MEMTX_OK; } void gicv3_dist_set_irq(GICv3State *s, int irq, int level) diff --git a/hw/intc/armv7m_nvic.c b/hw/intc/armv7m_nvic.c index 1e7ddcb..13df002 100644 --- a/hw/intc/armv7m_nvic.c +++ b/hw/intc/armv7m_nvic.c @@ -2470,172 +2470,6 @@ static const MemoryRegionOps nvic_sysreg_ops = { .endianness = DEVICE_NATIVE_ENDIAN, }; -static MemTxResult nvic_sysreg_ns_write(void *opaque, hwaddr addr, - uint64_t value, unsigned size, - MemTxAttrs attrs) -{ - MemoryRegion *mr = opaque; - - if (attrs.secure) { - /* S accesses to the alias act like NS accesses to the real region */ - attrs.secure = 0; - return memory_region_dispatch_write(mr, addr, value, - size_memop(size) | MO_TE, attrs); - } else { - /* NS attrs are RAZ/WI for privileged, and BusFault for user */ - if (attrs.user) { - return MEMTX_ERROR; - } - return MEMTX_OK; - } -} - -static MemTxResult nvic_sysreg_ns_read(void *opaque, hwaddr addr, - uint64_t *data, unsigned size, - MemTxAttrs attrs) -{ - MemoryRegion *mr = opaque; - - if (attrs.secure) { - /* S accesses to the alias act like NS accesses to the real region */ - attrs.secure = 0; - return memory_region_dispatch_read(mr, addr, data, - size_memop(size) | MO_TE, attrs); - } else { - /* NS attrs are RAZ/WI for privileged, and BusFault for user */ - if (attrs.user) { - return MEMTX_ERROR; - } - *data = 0; - return MEMTX_OK; - } -} - -static const MemoryRegionOps nvic_sysreg_ns_ops = { - .read_with_attrs = nvic_sysreg_ns_read, - .write_with_attrs = nvic_sysreg_ns_write, - .endianness = DEVICE_NATIVE_ENDIAN, -}; - -static MemTxResult nvic_systick_write(void *opaque, hwaddr addr, - uint64_t value, unsigned size, - MemTxAttrs attrs) -{ - NVICState *s = opaque; - MemoryRegion *mr; - - /* Direct the access to the correct systick */ - mr = sysbus_mmio_get_region(SYS_BUS_DEVICE(&s->systick[attrs.secure]), 0); - return memory_region_dispatch_write(mr, addr, value, - size_memop(size) | MO_TE, attrs); -} - -static MemTxResult nvic_systick_read(void *opaque, hwaddr addr, - uint64_t *data, unsigned size, - MemTxAttrs attrs) -{ - NVICState *s = opaque; - MemoryRegion *mr; - - /* Direct the access to the correct systick */ - mr = sysbus_mmio_get_region(SYS_BUS_DEVICE(&s->systick[attrs.secure]), 0); - return memory_region_dispatch_read(mr, addr, data, size_memop(size) | MO_TE, - attrs); -} - -static const MemoryRegionOps nvic_systick_ops = { - .read_with_attrs = nvic_systick_read, - .write_with_attrs = nvic_systick_write, - .endianness = DEVICE_NATIVE_ENDIAN, -}; - - -static MemTxResult ras_read(void *opaque, hwaddr addr, - uint64_t *data, unsigned size, - MemTxAttrs attrs) -{ - if (attrs.user) { - return MEMTX_ERROR; - } - - switch (addr) { - case 0xe10: /* ERRIIDR */ - /* architect field = Arm; product/variant/revision 0 */ - *data = 0x43b; - break; - case 0xfc8: /* ERRDEVID */ - /* Minimal RAS: we implement 0 error record indexes */ - *data = 0; - break; - default: - qemu_log_mask(LOG_UNIMP, "Read RAS register offset 0x%x\n", - (uint32_t)addr); - *data = 0; - break; - } - return MEMTX_OK; -} - -static MemTxResult ras_write(void *opaque, hwaddr addr, - uint64_t value, unsigned size, - MemTxAttrs attrs) -{ - if (attrs.user) { - return MEMTX_ERROR; - } - - switch (addr) { - default: - qemu_log_mask(LOG_UNIMP, "Write to RAS register offset 0x%x\n", - (uint32_t)addr); - break; - } - return MEMTX_OK; -} - -static const MemoryRegionOps ras_ops = { - .read_with_attrs = ras_read, - .write_with_attrs = ras_write, - .endianness = DEVICE_NATIVE_ENDIAN, -}; - -/* - * Unassigned portions of the PPB space are RAZ/WI for privileged - * accesses, and fault for non-privileged accesses. - */ -static MemTxResult ppb_default_read(void *opaque, hwaddr addr, - uint64_t *data, unsigned size, - MemTxAttrs attrs) -{ - qemu_log_mask(LOG_UNIMP, "Read of unassigned area of PPB: offset 0x%x\n", - (uint32_t)addr); - if (attrs.user) { - return MEMTX_ERROR; - } - *data = 0; - return MEMTX_OK; -} - -static MemTxResult ppb_default_write(void *opaque, hwaddr addr, - uint64_t value, unsigned size, - MemTxAttrs attrs) -{ - qemu_log_mask(LOG_UNIMP, "Write of unassigned area of PPB: offset 0x%x\n", - (uint32_t)addr); - if (attrs.user) { - return MEMTX_ERROR; - } - return MEMTX_OK; -} - -static const MemoryRegionOps ppb_default_ops = { - .read_with_attrs = ppb_default_read, - .write_with_attrs = ppb_default_write, - .endianness = DEVICE_NATIVE_ENDIAN, - .valid.min_access_size = 1, - .valid.max_access_size = 8, -}; - static int nvic_post_load(void *opaque, int version_id) { NVICState *s = opaque; @@ -2851,108 +2685,14 @@ static void armv7m_nvic_realize(DeviceState *dev, Error **errp) s->num_prio_bits = arm_feature(&s->cpu->env, ARM_FEATURE_V7) ? 8 : 2; - if (!sysbus_realize(SYS_BUS_DEVICE(&s->systick[M_REG_NS]), errp)) { - return; - } - sysbus_connect_irq(SYS_BUS_DEVICE(&s->systick[M_REG_NS]), 0, - qdev_get_gpio_in_named(dev, "systick-trigger", - M_REG_NS)); - - if (arm_feature(&s->cpu->env, ARM_FEATURE_M_SECURITY)) { - /* We couldn't init the secure systick device in instance_init - * as we didn't know then if the CPU had the security extensions; - * so we have to do it here. - */ - object_initialize_child(OBJECT(dev), "systick-reg-s", - &s->systick[M_REG_S], TYPE_SYSTICK); - - if (!sysbus_realize(SYS_BUS_DEVICE(&s->systick[M_REG_S]), errp)) { - return; - } - sysbus_connect_irq(SYS_BUS_DEVICE(&s->systick[M_REG_S]), 0, - qdev_get_gpio_in_named(dev, "systick-trigger", - M_REG_S)); - } - /* - * This device provides a single sysbus memory region which - * represents the whole of the "System PPB" space. This is the - * range from 0xe0000000 to 0xe00fffff and includes the NVIC, - * the System Control Space (system registers), the systick timer, - * and for CPUs with the Security extension an NS banked version - * of all of these. - * - * The default behaviour for unimplemented registers/ranges - * (for instance the Data Watchpoint and Trace unit at 0xe0001000) - * is to RAZ/WI for privileged access and BusFault for non-privileged - * access. - * - * The NVIC and System Control Space (SCS) starts at 0xe000e000 - * and looks like this: - * 0x004 - ICTR - * 0x010 - 0xff - systick - * 0x100..0x7ec - NVIC - * 0x7f0..0xcff - Reserved - * 0xd00..0xd3c - SCS registers - * 0xd40..0xeff - Reserved or Not implemented - * 0xf00 - STIR - * - * Some registers within this space are banked between security states. - * In v8M there is a second range 0xe002e000..0xe002efff which is the - * NonSecure alias SCS; secure accesses to this behave like NS accesses - * to the main SCS range, and non-secure accesses (including when - * the security extension is not implemented) are RAZ/WI. - * Note that both the main SCS range and the alias range are defined - * to be exempt from memory attribution (R_BLJT) and so the memory - * transaction attribute always matches the current CPU security - * state (attrs.secure == env->v7m.secure). In the nvic_sysreg_ns_ops - * wrappers we change attrs.secure to indicate the NS access; so - * generally code determining which banked register to use should - * use attrs.secure; code determining actual behaviour of the system - * should use env->v7m.secure. - * - * The container covers the whole PPB space. Within it the priority - * of overlapping regions is: - * - default region (for RAZ/WI and BusFault) : -1 - * - system register regions : 0 - * - systick : 1 - * This is because the systick device is a small block of registers - * in the middle of the other system control registers. + * This device provides a single memory region which covers the + * sysreg/NVIC registers from 0xE000E000 .. 0xE000EFFF, with the + * exception of the systick timer registers 0xE000E010 .. 0xE000E0FF. */ - memory_region_init(&s->container, OBJECT(s), "nvic", 0x100000); - memory_region_init_io(&s->defaultmem, OBJECT(s), &ppb_default_ops, s, - "nvic-default", 0x100000); - memory_region_add_subregion_overlap(&s->container, 0, &s->defaultmem, -1); memory_region_init_io(&s->sysregmem, OBJECT(s), &nvic_sysreg_ops, s, "nvic_sysregs", 0x1000); - memory_region_add_subregion(&s->container, 0xe000, &s->sysregmem); - - memory_region_init_io(&s->systickmem, OBJECT(s), - &nvic_systick_ops, s, - "nvic_systick", 0xe0); - - memory_region_add_subregion_overlap(&s->container, 0xe010, - &s->systickmem, 1); - - if (arm_feature(&s->cpu->env, ARM_FEATURE_V8)) { - memory_region_init_io(&s->sysreg_ns_mem, OBJECT(s), - &nvic_sysreg_ns_ops, &s->sysregmem, - "nvic_sysregs_ns", 0x1000); - memory_region_add_subregion(&s->container, 0x2e000, &s->sysreg_ns_mem); - memory_region_init_io(&s->systick_ns_mem, OBJECT(s), - &nvic_sysreg_ns_ops, &s->systickmem, - "nvic_systick_ns", 0xe0); - memory_region_add_subregion_overlap(&s->container, 0x2e010, - &s->systick_ns_mem, 1); - } - - if (cpu_isar_feature(aa32_ras, s->cpu)) { - memory_region_init_io(&s->ras_mem, OBJECT(s), - &ras_ops, s, "nvic_ras", 0x1000); - memory_region_add_subregion(&s->container, 0x5000, &s->ras_mem); - } - - sysbus_init_mmio(SYS_BUS_DEVICE(dev), &s->container); + sysbus_init_mmio(SYS_BUS_DEVICE(dev), &s->sysregmem); } static void armv7m_nvic_instance_init(Object *obj) @@ -2961,12 +2701,6 @@ static void armv7m_nvic_instance_init(Object *obj) NVICState *nvic = NVIC(obj); SysBusDevice *sbd = SYS_BUS_DEVICE(obj); - object_initialize_child(obj, "systick-reg-ns", &nvic->systick[M_REG_NS], - TYPE_SYSTICK); - /* We can't initialize the secure systick here, as we don't know - * yet if we need it. - */ - sysbus_init_irq(sbd, &nvic->excpout); qdev_init_gpio_out_named(dev, &nvic->sysresetreq, "SYSRESETREQ", 1); qdev_init_gpio_in_named(dev, nvic_systick_trigger, "systick-trigger", diff --git a/hw/misc/armv7m_ras.c b/hw/misc/armv7m_ras.c new file mode 100644 index 0000000..de24922 --- /dev/null +++ b/hw/misc/armv7m_ras.c @@ -0,0 +1,93 @@ +/* + * Arm M-profile RAS (Reliability, Availability and Serviceability) block + * + * Copyright (c) 2021 Linaro Limited + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 or + * (at your option) any later version. + */ + +#include "qemu/osdep.h" +#include "hw/misc/armv7m_ras.h" +#include "qemu/log.h" + +static MemTxResult ras_read(void *opaque, hwaddr addr, + uint64_t *data, unsigned size, + MemTxAttrs attrs) +{ + if (attrs.user) { + return MEMTX_ERROR; + } + + switch (addr) { + case 0xe10: /* ERRIIDR */ + /* architect field = Arm; product/variant/revision 0 */ + *data = 0x43b; + break; + case 0xfc8: /* ERRDEVID */ + /* Minimal RAS: we implement 0 error record indexes */ + *data = 0; + break; + default: + qemu_log_mask(LOG_UNIMP, "Read RAS register offset 0x%x\n", + (uint32_t)addr); + *data = 0; + break; + } + return MEMTX_OK; +} + +static MemTxResult ras_write(void *opaque, hwaddr addr, + uint64_t value, unsigned size, + MemTxAttrs attrs) +{ + if (attrs.user) { + return MEMTX_ERROR; + } + + switch (addr) { + default: + qemu_log_mask(LOG_UNIMP, "Write to RAS register offset 0x%x\n", + (uint32_t)addr); + break; + } + return MEMTX_OK; +} + +static const MemoryRegionOps ras_ops = { + .read_with_attrs = ras_read, + .write_with_attrs = ras_write, + .endianness = DEVICE_NATIVE_ENDIAN, +}; + + +static void armv7m_ras_init(Object *obj) +{ + SysBusDevice *sbd = SYS_BUS_DEVICE(obj); + ARMv7MRAS *s = ARMV7M_RAS(obj); + + memory_region_init_io(&s->iomem, obj, &ras_ops, + s, "armv7m-ras", 0x1000); + sysbus_init_mmio(sbd, &s->iomem); +} + +static void armv7m_ras_class_init(ObjectClass *klass, void *data) +{ + /* This device has no state: no need for vmstate or reset */ +} + +static const TypeInfo armv7m_ras_info = { + .name = TYPE_ARMV7M_RAS, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(ARMv7MRAS), + .instance_init = armv7m_ras_init, + .class_init = armv7m_ras_class_init, +}; + +static void armv7m_ras_register_types(void) +{ + type_register_static(&armv7m_ras_info); +} + +type_init(armv7m_ras_register_types); diff --git a/hw/misc/meson.build b/hw/misc/meson.build index a53b849..3f41a3a5 100644 --- a/hw/misc/meson.build +++ b/hw/misc/meson.build @@ -17,6 +17,8 @@ softmmu_ss.add(when: 'CONFIG_INTEGRATOR_DEBUG', if_true: files('arm_integrator_d softmmu_ss.add(when: 'CONFIG_A9SCU', if_true: files('a9scu.c')) softmmu_ss.add(when: 'CONFIG_ARM11SCU', if_true: files('arm11scu.c')) +softmmu_ss.add(when: 'CONFIG_ARM_V7M', if_true: files('armv7m_ras.c')) + # Mac devices softmmu_ss.add(when: 'CONFIG_MOS6522', if_true: files('mos6522.c')) diff --git a/hw/ppc/spapr.c b/hw/ppc/spapr.c index 81699d4..d39fd4e 100644 --- a/hw/ppc/spapr.c +++ b/hw/ppc/spapr.c @@ -4686,14 +4686,25 @@ static void spapr_machine_latest_class_options(MachineClass *mc) type_init(spapr_machine_register_##suffix) /* + * pseries-6.2 + */ +static void spapr_machine_6_2_class_options(MachineClass *mc) +{ + /* Defaults for the latest behaviour inherited from the base class */ +} + +DEFINE_SPAPR_MACHINE(6_2, "6.2", true); + +/* * pseries-6.1 */ static void spapr_machine_6_1_class_options(MachineClass *mc) { - /* Defaults for the latest behaviour inherited from the base class */ + spapr_machine_6_2_class_options(mc); + compat_props_add(mc->compat_props, hw_compat_6_1, hw_compat_6_1_len); } -DEFINE_SPAPR_MACHINE(6_1, "6.1", true); +DEFINE_SPAPR_MACHINE(6_1, "6.1", false); /* * pseries-6.0 diff --git a/hw/s390x/s390-virtio-ccw.c b/hw/s390x/s390-virtio-ccw.c index e4b18ae..4d25278 100644 --- a/hw/s390x/s390-virtio-ccw.c +++ b/hw/s390x/s390-virtio-ccw.c @@ -791,14 +791,26 @@ bool css_migration_enabled(void) } \ type_init(ccw_machine_register_##suffix) +static void ccw_machine_6_2_instance_options(MachineState *machine) +{ +} + +static void ccw_machine_6_2_class_options(MachineClass *mc) +{ +} +DEFINE_CCW_MACHINE(6_2, "6.2", true); + static void ccw_machine_6_1_instance_options(MachineState *machine) { + ccw_machine_6_2_instance_options(machine); } static void ccw_machine_6_1_class_options(MachineClass *mc) { + ccw_machine_6_2_class_options(mc); + compat_props_add(mc->compat_props, hw_compat_6_1, hw_compat_6_1_len); } -DEFINE_CCW_MACHINE(6_1, "6.1", true); +DEFINE_CCW_MACHINE(6_1, "6.1", false); static void ccw_machine_6_0_instance_options(MachineState *machine) { diff --git a/hw/timer/Kconfig b/hw/timer/Kconfig index bac2511..1e73da7 100644 --- a/hw/timer/Kconfig +++ b/hw/timer/Kconfig @@ -52,5 +52,8 @@ config SSE_COUNTER config SSE_TIMER bool +config STELLARIS_GPTM + bool + config AVR_TIMER16 bool diff --git a/hw/timer/armv7m_systick.c b/hw/timer/armv7m_systick.c index 2f19201..3bd951d 100644 --- a/hw/timer/armv7m_systick.c +++ b/hw/timer/armv7m_systick.c @@ -14,28 +14,32 @@ #include "migration/vmstate.h" #include "hw/irq.h" #include "hw/sysbus.h" +#include "hw/qdev-clock.h" #include "qemu/timer.h" #include "qemu/log.h" #include "qemu/module.h" +#include "qapi/error.h" #include "trace.h" -/* qemu timers run at 1GHz. We want something closer to 1MHz. */ -#define SYSTICK_SCALE 1000ULL - #define SYSTICK_ENABLE (1 << 0) #define SYSTICK_TICKINT (1 << 1) #define SYSTICK_CLKSOURCE (1 << 2) #define SYSTICK_COUNTFLAG (1 << 16) -int system_clock_scale; +#define SYSCALIB_NOREF (1U << 31) +#define SYSCALIB_SKEW (1U << 30) +#define SYSCALIB_TENMS ((1U << 24) - 1) -/* Conversion factor from qemu timer to SysTick frequencies. */ -static inline int64_t systick_scale(SysTickState *s) +static void systick_set_period_from_clock(SysTickState *s) { + /* + * Set the ptimer period from whichever clock is selected. + * Must be called from within a ptimer transaction block. + */ if (s->control & SYSTICK_CLKSOURCE) { - return system_clock_scale; + ptimer_set_period_from_clock(s->ptimer, s->cpuclk, 1); } else { - return 1000; + ptimer_set_period_from_clock(s->ptimer, s->refclk, 1); } } @@ -82,7 +86,28 @@ static MemTxResult systick_read(void *opaque, hwaddr addr, uint64_t *data, val = ptimer_get_count(s->ptimer); break; case 0xc: /* SysTick Calibration Value. */ - val = 10000; + /* + * In real hardware it is possible to make this register report + * a different value from what the reference clock is actually + * running at. We don't model that (which usually happens due + * to integration errors in the real hardware) and instead always + * report the theoretical correct value as described in the + * knowledgebase article at + * https://developer.arm.com/documentation/ka001325/latest + * If necessary, we could implement an extra QOM property on this + * device to force the STCALIB value to something different from + * the "correct" value. + */ + if (!clock_has_source(s->refclk)) { + val = SYSCALIB_NOREF; + break; + } + val = clock_ns_to_ticks(s->refclk, 10 * SCALE_MS) - 1; + val &= SYSCALIB_TENMS; + if (clock_ticks_to_ns(s->refclk, val + 1) != 10 * SCALE_MS) { + /* report that tick count does not yield exactly 10ms */ + val |= SYSCALIB_SKEW; + } break; default: val = 0; @@ -114,6 +139,11 @@ static MemTxResult systick_write(void *opaque, hwaddr addr, { uint32_t oldval; + if (!clock_has_source(s->refclk)) { + /* This bit is always 1 if there is no external refclk */ + value |= SYSTICK_CLKSOURCE; + } + ptimer_transaction_begin(s->ptimer); oldval = s->control; s->control &= 0xfffffff8; @@ -121,19 +151,14 @@ static MemTxResult systick_write(void *opaque, hwaddr addr, if ((oldval ^ value) & SYSTICK_ENABLE) { if (value & SYSTICK_ENABLE) { - /* - * Always reload the period in case board code has - * changed system_clock_scale. If we ever replace that - * global with a more sensible API then we might be able - * to set the period only when it actually changes. - */ - ptimer_set_period(s->ptimer, systick_scale(s)); ptimer_run(s->ptimer, 0); } else { ptimer_stop(s->ptimer); } - } else if ((oldval ^ value) & SYSTICK_CLKSOURCE) { - ptimer_set_period(s->ptimer, systick_scale(s)); + } + + if ((oldval ^ value) & SYSTICK_CLKSOURCE) { + systick_set_period_from_clock(s); } ptimer_transaction_commit(s->ptimer); break; @@ -176,20 +201,42 @@ static void systick_reset(DeviceState *dev) { SysTickState *s = SYSTICK(dev); - /* - * Forgetting to set system_clock_scale is always a board code - * bug. We can't check this earlier because for some boards - * (like stellaris) it is not yet configured at the point where - * the systick device is realized. - */ - assert(system_clock_scale != 0); - ptimer_transaction_begin(s->ptimer); s->control = 0; + if (!clock_has_source(s->refclk)) { + /* This bit is always 1 if there is no external refclk */ + s->control |= SYSTICK_CLKSOURCE; + } ptimer_stop(s->ptimer); ptimer_set_count(s->ptimer, 0); ptimer_set_limit(s->ptimer, 0, 0); - ptimer_set_period(s->ptimer, systick_scale(s)); + systick_set_period_from_clock(s); + ptimer_transaction_commit(s->ptimer); +} + +static void systick_cpuclk_update(void *opaque, ClockEvent event) +{ + SysTickState *s = SYSTICK(opaque); + + if (!(s->control & SYSTICK_CLKSOURCE)) { + /* currently using refclk, we can ignore cpuclk changes */ + } + + ptimer_transaction_begin(s->ptimer); + ptimer_set_period_from_clock(s->ptimer, s->cpuclk, 1); + ptimer_transaction_commit(s->ptimer); +} + +static void systick_refclk_update(void *opaque, ClockEvent event) +{ + SysTickState *s = SYSTICK(opaque); + + if (s->control & SYSTICK_CLKSOURCE) { + /* currently using cpuclk, we can ignore refclk changes */ + } + + ptimer_transaction_begin(s->ptimer); + ptimer_set_period_from_clock(s->ptimer, s->refclk, 1); ptimer_transaction_commit(s->ptimer); } @@ -201,6 +248,11 @@ static void systick_instance_init(Object *obj) memory_region_init_io(&s->iomem, obj, &systick_ops, s, "systick", 0xe0); sysbus_init_mmio(sbd, &s->iomem); sysbus_init_irq(sbd, &s->irq); + + s->refclk = qdev_init_clock_in(DEVICE(obj), "refclk", + systick_refclk_update, s, ClockUpdate); + s->cpuclk = qdev_init_clock_in(DEVICE(obj), "cpuclk", + systick_cpuclk_update, s, ClockUpdate); } static void systick_realize(DeviceState *dev, Error **errp) @@ -211,13 +263,21 @@ static void systick_realize(DeviceState *dev, Error **errp) PTIMER_POLICY_NO_COUNTER_ROUND_DOWN | PTIMER_POLICY_NO_IMMEDIATE_RELOAD | PTIMER_POLICY_TRIGGER_ONLY_ON_DECREMENT); + + if (!clock_has_source(s->cpuclk)) { + error_setg(errp, "systick: cpuclk must be connected"); + return; + } + /* It's OK not to connect the refclk */ } static const VMStateDescription vmstate_systick = { .name = "armv7m_systick", - .version_id = 2, - .minimum_version_id = 2, + .version_id = 3, + .minimum_version_id = 3, .fields = (VMStateField[]) { + VMSTATE_CLOCK(refclk, SysTickState), + VMSTATE_CLOCK(cpuclk, SysTickState), VMSTATE_UINT32(control, SysTickState), VMSTATE_INT64(tick, SysTickState), VMSTATE_PTIMER(ptimer, SysTickState), diff --git a/hw/timer/meson.build b/hw/timer/meson.build index 1aa3cd2..e67478a 100644 --- a/hw/timer/meson.build +++ b/hw/timer/meson.build @@ -31,6 +31,7 @@ softmmu_ss.add(when: 'CONFIG_SH_TIMER', if_true: files('sh_timer.c')) softmmu_ss.add(when: 'CONFIG_SLAVIO', if_true: files('slavio_timer.c')) softmmu_ss.add(when: 'CONFIG_SSE_COUNTER', if_true: files('sse-counter.c')) softmmu_ss.add(when: 'CONFIG_SSE_TIMER', if_true: files('sse-timer.c')) +softmmu_ss.add(when: 'CONFIG_STELLARIS_GPTM', if_true: files('stellaris-gptm.c')) softmmu_ss.add(when: 'CONFIG_STM32F2XX_TIMER', if_true: files('stm32f2xx_timer.c')) softmmu_ss.add(when: 'CONFIG_XILINX', if_true: files('xilinx_timer.c')) specific_ss.add(when: 'CONFIG_IBEX', if_true: files('ibex_timer.c')) diff --git a/hw/timer/stellaris-gptm.c b/hw/timer/stellaris-gptm.c new file mode 100644 index 0000000..fd71c79 --- /dev/null +++ b/hw/timer/stellaris-gptm.c @@ -0,0 +1,332 @@ +/* + * Luminary Micro Stellaris General Purpose Timer Module + * + * Copyright (c) 2006 CodeSourcery. + * Written by Paul Brook + * + * This code is licensed under the GPL. + */ + +#include "qemu/osdep.h" +#include "qemu/log.h" +#include "qemu/timer.h" +#include "qapi/error.h" +#include "migration/vmstate.h" +#include "hw/qdev-clock.h" +#include "hw/timer/stellaris-gptm.h" + +static void gptm_update_irq(gptm_state *s) +{ + int level; + level = (s->state & s->mask) != 0; + qemu_set_irq(s->irq, level); +} + +static void gptm_stop(gptm_state *s, int n) +{ + timer_del(s->timer[n]); +} + +static void gptm_reload(gptm_state *s, int n, int reset) +{ + int64_t tick; + if (reset) { + tick = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); + } else { + tick = s->tick[n]; + } + + if (s->config == 0) { + /* 32-bit CountDown. */ + uint32_t count; + count = s->load[0] | (s->load[1] << 16); + tick += clock_ticks_to_ns(s->clk, count); + } else if (s->config == 1) { + /* 32-bit RTC. 1Hz tick. */ + tick += NANOSECONDS_PER_SECOND; + } else if (s->mode[n] == 0xa) { + /* PWM mode. Not implemented. */ + } else { + qemu_log_mask(LOG_UNIMP, + "GPTM: 16-bit timer mode unimplemented: 0x%x\n", + s->mode[n]); + return; + } + s->tick[n] = tick; + timer_mod(s->timer[n], tick); +} + +static void gptm_tick(void *opaque) +{ + gptm_state **p = (gptm_state **)opaque; + gptm_state *s; + int n; + + s = *p; + n = p - s->opaque; + if (s->config == 0) { + s->state |= 1; + if ((s->control & 0x20)) { + /* Output trigger. */ + qemu_irq_pulse(s->trigger); + } + if (s->mode[0] & 1) { + /* One-shot. */ + s->control &= ~1; + } else { + /* Periodic. */ + gptm_reload(s, 0, 0); + } + } else if (s->config == 1) { + /* RTC. */ + uint32_t match; + s->rtc++; + match = s->match[0] | (s->match[1] << 16); + if (s->rtc > match) + s->rtc = 0; + if (s->rtc == 0) { + s->state |= 8; + } + gptm_reload(s, 0, 0); + } else if (s->mode[n] == 0xa) { + /* PWM mode. Not implemented. */ + } else { + qemu_log_mask(LOG_UNIMP, + "GPTM: 16-bit timer mode unimplemented: 0x%x\n", + s->mode[n]); + } + gptm_update_irq(s); +} + +static uint64_t gptm_read(void *opaque, hwaddr offset, + unsigned size) +{ + gptm_state *s = (gptm_state *)opaque; + + switch (offset) { + case 0x00: /* CFG */ + return s->config; + case 0x04: /* TAMR */ + return s->mode[0]; + case 0x08: /* TBMR */ + return s->mode[1]; + case 0x0c: /* CTL */ + return s->control; + case 0x18: /* IMR */ + return s->mask; + case 0x1c: /* RIS */ + return s->state; + case 0x20: /* MIS */ + return s->state & s->mask; + case 0x24: /* CR */ + return 0; + case 0x28: /* TAILR */ + return s->load[0] | ((s->config < 4) ? (s->load[1] << 16) : 0); + case 0x2c: /* TBILR */ + return s->load[1]; + case 0x30: /* TAMARCHR */ + return s->match[0] | ((s->config < 4) ? (s->match[1] << 16) : 0); + case 0x34: /* TBMATCHR */ + return s->match[1]; + case 0x38: /* TAPR */ + return s->prescale[0]; + case 0x3c: /* TBPR */ + return s->prescale[1]; + case 0x40: /* TAPMR */ + return s->match_prescale[0]; + case 0x44: /* TBPMR */ + return s->match_prescale[1]; + case 0x48: /* TAR */ + if (s->config == 1) { + return s->rtc; + } + qemu_log_mask(LOG_UNIMP, + "GPTM: read of TAR but timer read not supported\n"); + return 0; + case 0x4c: /* TBR */ + qemu_log_mask(LOG_UNIMP, + "GPTM: read of TBR but timer read not supported\n"); + return 0; + default: + qemu_log_mask(LOG_GUEST_ERROR, + "GPTM: read at bad offset 0x02%" HWADDR_PRIx "\n", + offset); + return 0; + } +} + +static void gptm_write(void *opaque, hwaddr offset, + uint64_t value, unsigned size) +{ + gptm_state *s = (gptm_state *)opaque; + uint32_t oldval; + + /* + * The timers should be disabled before changing the configuration. + * We take advantage of this and defer everything until the timer + * is enabled. + */ + switch (offset) { + case 0x00: /* CFG */ + s->config = value; + break; + case 0x04: /* TAMR */ + s->mode[0] = value; + break; + case 0x08: /* TBMR */ + s->mode[1] = value; + break; + case 0x0c: /* CTL */ + oldval = s->control; + s->control = value; + /* TODO: Implement pause. */ + if ((oldval ^ value) & 1) { + if (value & 1) { + gptm_reload(s, 0, 1); + } else { + gptm_stop(s, 0); + } + } + if (((oldval ^ value) & 0x100) && s->config >= 4) { + if (value & 0x100) { + gptm_reload(s, 1, 1); + } else { + gptm_stop(s, 1); + } + } + break; + case 0x18: /* IMR */ + s->mask = value & 0x77; + gptm_update_irq(s); + break; + case 0x24: /* CR */ + s->state &= ~value; + break; + case 0x28: /* TAILR */ + s->load[0] = value & 0xffff; + if (s->config < 4) { + s->load[1] = value >> 16; + } + break; + case 0x2c: /* TBILR */ + s->load[1] = value & 0xffff; + break; + case 0x30: /* TAMARCHR */ + s->match[0] = value & 0xffff; + if (s->config < 4) { + s->match[1] = value >> 16; + } + break; + case 0x34: /* TBMATCHR */ + s->match[1] = value >> 16; + break; + case 0x38: /* TAPR */ + s->prescale[0] = value; + break; + case 0x3c: /* TBPR */ + s->prescale[1] = value; + break; + case 0x40: /* TAPMR */ + s->match_prescale[0] = value; + break; + case 0x44: /* TBPMR */ + s->match_prescale[0] = value; + break; + default: + qemu_log_mask(LOG_GUEST_ERROR, + "GPTM: write at bad offset 0x02%" HWADDR_PRIx "\n", + offset); + } + gptm_update_irq(s); +} + +static const MemoryRegionOps gptm_ops = { + .read = gptm_read, + .write = gptm_write, + .endianness = DEVICE_NATIVE_ENDIAN, +}; + +static const VMStateDescription vmstate_stellaris_gptm = { + .name = "stellaris_gptm", + .version_id = 2, + .minimum_version_id = 2, + .fields = (VMStateField[]) { + VMSTATE_UINT32(config, gptm_state), + VMSTATE_UINT32_ARRAY(mode, gptm_state, 2), + VMSTATE_UINT32(control, gptm_state), + VMSTATE_UINT32(state, gptm_state), + VMSTATE_UINT32(mask, gptm_state), + VMSTATE_UNUSED(8), + VMSTATE_UINT32_ARRAY(load, gptm_state, 2), + VMSTATE_UINT32_ARRAY(match, gptm_state, 2), + VMSTATE_UINT32_ARRAY(prescale, gptm_state, 2), + VMSTATE_UINT32_ARRAY(match_prescale, gptm_state, 2), + VMSTATE_UINT32(rtc, gptm_state), + VMSTATE_INT64_ARRAY(tick, gptm_state, 2), + VMSTATE_TIMER_PTR_ARRAY(timer, gptm_state, 2), + VMSTATE_CLOCK(clk, gptm_state), + VMSTATE_END_OF_LIST() + } +}; + +static void stellaris_gptm_init(Object *obj) +{ + DeviceState *dev = DEVICE(obj); + gptm_state *s = STELLARIS_GPTM(obj); + SysBusDevice *sbd = SYS_BUS_DEVICE(obj); + + sysbus_init_irq(sbd, &s->irq); + qdev_init_gpio_out(dev, &s->trigger, 1); + + memory_region_init_io(&s->iomem, obj, &gptm_ops, s, + "gptm", 0x1000); + sysbus_init_mmio(sbd, &s->iomem); + + s->opaque[0] = s->opaque[1] = s; + + /* + * TODO: in an ideal world we would model the effects of changing + * the input clock frequency while the countdown timer is active. + * The best way to do this would be to convert the device to use + * ptimer instead of hand-rolling its own timer. This would also + * make it easy to implement reading the current count from the + * TAR and TBR registers. + */ + s->clk = qdev_init_clock_in(dev, "clk", NULL, NULL, 0); +} + +static void stellaris_gptm_realize(DeviceState *dev, Error **errp) +{ + gptm_state *s = STELLARIS_GPTM(dev); + + if (!clock_has_source(s->clk)) { + error_setg(errp, "stellaris-gptm: clk must be connected"); + return; + } + + s->timer[0] = timer_new_ns(QEMU_CLOCK_VIRTUAL, gptm_tick, &s->opaque[0]); + s->timer[1] = timer_new_ns(QEMU_CLOCK_VIRTUAL, gptm_tick, &s->opaque[1]); +} + +static void stellaris_gptm_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + + dc->vmsd = &vmstate_stellaris_gptm; + dc->realize = stellaris_gptm_realize; +} + +static const TypeInfo stellaris_gptm_info = { + .name = TYPE_STELLARIS_GPTM, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(gptm_state), + .instance_init = stellaris_gptm_init, + .class_init = stellaris_gptm_class_init, +}; + +static void stellaris_gptm_register_types(void) +{ + type_register_static(&stellaris_gptm_info); +} + +type_init(stellaris_gptm_register_types) |