target-arm queue:

* Support u-boot 'noload' images for Arm (as used by NetBSD/evbarm GENERIC kernel)
  * hw/misc/tz-mpc: Fix value of BLK_MAX register
  * target/arm: Emit barriers for A32/T32 load-acquire/store-release insns
  * nRF51 SoC: add timer, GPIO, RNG peripherals
  * hw/arm/allwinner-a10: Add the 'A' SRAM and the SRAM controller
  * cpus.c: Fix race condition in cpu_stop_current()
  * hw/arm: versal: Plug memory leaks
  * Allow M profile boards to run even if -kernel not specified
  * gdbstub: Add multiprocess extension support for use when the
    board has multiple CPUs of different types (like the Xilinx Zynq boards)
  * target/arm: Don't decode S bit in SVE brk[ab] merging insns
  * target/arm: Convert ARM_TBFLAG_* to FIELDs
 -----BEGIN PGP SIGNATURE-----
 Version: GnuPG v1
 
 iQIcBAABCAAGBQJcM36AAAoJEDwlJe0UNgzepuMP/A6umcXRrO+vOZgkW+cvJ8cD
 JkDdb8H/u3S6zqNokABI3Ya/areX1P30sRV7e7mC5IsknVNZe0MqQX6TW5477HMP
 Oz/m1AbyByWMLVILFiWfte5dtRRLfs3axzrmhu6HwJXe0NIUiYQofoJzCZEDMxDn
 71cehgeNkUGA36HViPyqzHZYADFkCX3Tfmh1FEh2jD7taK9GNsff8p6cHTb05W7d
 wWk68PS8VKTb5VrYH6SyiAHW8gBVrrUkYlkPKHzemK5fwlgDOSfxVLthf8mo08SH
 QxEXI430tagdmrGNO/nKOTA2NQwMzvCk/OLf0Qwg9I9F9pYtiOJ7nXXbtqDC8eKy
 DdHsL57W0F7sFkoVt+YNHSeylyLRluDh+D+Q7OHnlvwsEYmecqsWkW/A2CYC0uWs
 8ajxPBNpGG1lIvo63YK5/4kOy0DE/6ISljYOSlYYg3iXeAZPkQZMTlUxoYmJQ+Zr
 h1tLg1N9SuyQK5g5Uuluw2GwgzIv/Bt1LFo7pnvsA2X6PKiv6nno40T8q0Lw6ah4
 lmAUWx0OUilTrvQwterHlr6hfWu2RLiRoxCg06a3C93YlRjsR3vZOBeQ5ByaE+ho
 5ItKn58EerO+UaweVoc6MDhJFPC8b16Eee281BCec8Ks4GR1tIcpP/0z2lUwhBu6
 hoPmkoPtFtu1dKBgF8Ma
 =x1jv
 -----END PGP SIGNATURE-----

Merge remote-tracking branch 'remotes/pmaydell/tags/pull-target-arm-20190107' into staging

target-arm queue:
 * Support u-boot 'noload' images for Arm (as used by NetBSD/evbarm GENERIC kernel)
 * hw/misc/tz-mpc: Fix value of BLK_MAX register
 * target/arm: Emit barriers for A32/T32 load-acquire/store-release insns
 * nRF51 SoC: add timer, GPIO, RNG peripherals
 * hw/arm/allwinner-a10: Add the 'A' SRAM and the SRAM controller
 * cpus.c: Fix race condition in cpu_stop_current()
 * hw/arm: versal: Plug memory leaks
 * Allow M profile boards to run even if -kernel not specified
 * gdbstub: Add multiprocess extension support for use when the
   board has multiple CPUs of different types (like the Xilinx Zynq boards)
 * target/arm: Don't decode S bit in SVE brk[ab] merging insns
 * target/arm: Convert ARM_TBFLAG_* to FIELDs

# gpg: Signature made Mon 07 Jan 2019 16:29:52 GMT
# gpg:                using RSA key 3C2525ED14360CDE
# gpg: Good signature from "Peter Maydell <peter.maydell@linaro.org>"
# gpg:                 aka "Peter Maydell <pmaydell@gmail.com>"
# gpg:                 aka "Peter Maydell <pmaydell@chiark.greenend.org.uk>"
# Primary key fingerprint: E1A5 C593 CD41 9DE2 8E83  15CF 3C25 25ED 1436 0CDE

* remotes/pmaydell/tags/pull-target-arm-20190107: (37 commits)
  Support u-boot noload images for arm as used by, NetBSD/evbarm GENERIC kernel.
  hw/misc/tz-mpc: Fix value of BLK_MAX register
  target/arm: Emit barriers for A32/T32 load-acquire/store-release insns
  arm: Add Clock peripheral stub to NRF51 SOC
  tests/microbit-test: Add Tests for nRF51 Timer
  arm: Instantiate NRF51 Timers
  hw/timer/nrf51_timer: Add nRF51 Timer peripheral
  tests/microbit-test: Add Tests for nRF51 GPIO
  arm: Instantiate NRF51 general purpose I/O
  hw/gpio/nrf51_gpio: Add nRF51 GPIO peripheral
  arm: Instantiate NRF51 random number generator
  hw/misc/nrf51_rng: Add NRF51 random number generator peripheral
  arm: Add header to host common definition for nRF51 SOC peripherals
  qtest: Add set_irq_in command to set IRQ/GPIO level
  hw/arm/allwinner-a10: Add the 'A' SRAM and the SRAM controller
  cpus.c: Fix race condition in cpu_stop_current()
  MAINTAINERS: Add ARM-related files for hw/[misc|input|timer]/
  hw/arm: versal: Plug memory leaks
  Revert "armv7m: Guard against no -kernel argument"
  arm/xlnx-zynqmp: put APUs and RPUs in separate CPU clusters
  ...

Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
This commit is contained in:
Peter Maydell 2019-01-07 16:56:32 +00:00
commit c102d9471f
48 changed files with 2567 additions and 271 deletions

View file

@ -22,6 +22,7 @@
#include "hw/sysbus.h"
#include "hw/devices.h"
#include "hw/arm/allwinner-a10.h"
#include "hw/misc/unimp.h"
static void aw_a10_init(Object *obj)
{
@ -85,6 +86,11 @@ static void aw_a10_realize(DeviceState *dev, Error **errp)
sysbus_connect_irq(sysbusdev, 4, s->irq[67]);
sysbus_connect_irq(sysbusdev, 5, s->irq[68]);
memory_region_init_ram(&s->sram_a, OBJECT(dev), "sram A", 48 * KiB,
&error_fatal);
memory_region_add_subregion(get_system_memory(), 0x00000000, &s->sram_a);
create_unimplemented_device("a10-sram-ctrl", 0x01c00000, 4 * KiB);
/* FIXME use qdev NIC properties instead of nd_table[] */
if (nd_table[0].used) {
qemu_check_nic_model(&nd_table[0], TYPE_AW_EMAC);

View file

@ -285,11 +285,6 @@ void armv7m_load_kernel(ARMCPU *cpu, const char *kernel_filename, int mem_size)
big_endian = 0;
#endif
if (!kernel_filename && !qtest_enabled()) {
error_report("Guest image must be specified (using -kernel)");
exit(1);
}
if (arm_feature(&cpu->env, ARM_FEATURE_EL3)) {
asidx = ARMASIdx_S;
} else {

View file

@ -30,8 +30,9 @@
* Documentation/arm/Booting and Documentation/arm64/booting.txt
* They have different preferred image load offsets from system RAM base.
*/
#define KERNEL_ARGS_ADDR 0x100
#define KERNEL_LOAD_ADDR 0x00010000
#define KERNEL_ARGS_ADDR 0x100
#define KERNEL_NOLOAD_ADDR 0x02000000
#define KERNEL_LOAD_ADDR 0x00010000
#define KERNEL64_LOAD_ADDR 0x00080000
#define ARM64_TEXT_OFFSET_OFFSET 8
@ -1082,7 +1083,8 @@ void arm_load_kernel(ARMCPU *cpu, struct arm_boot_info *info)
}
entry = elf_entry;
if (kernel_size < 0) {
kernel_size = load_uimage_as(info->kernel_filename, &entry, NULL,
uint64_t loadaddr = info->loader_start + KERNEL_NOLOAD_ADDR;
kernel_size = load_uimage_as(info->kernel_filename, &entry, &loadaddr,
&is_linux, NULL, NULL, as);
}
if (arm_feature(&cpu->env, ARM_FEATURE_AARCH64) && kernel_size < 0) {

View file

@ -21,35 +21,46 @@
#include "qemu/log.h"
#include "cpu.h"
#include "hw/arm/nrf51.h"
#include "hw/arm/nrf51_soc.h"
#define IOMEM_BASE 0x40000000
#define IOMEM_SIZE 0x20000000
#define FICR_BASE 0x10000000
#define FICR_SIZE 0x000000fc
#define FLASH_BASE 0x00000000
#define SRAM_BASE 0x20000000
#define PRIVATE_BASE 0xF0000000
#define PRIVATE_SIZE 0x10000000
/*
* The size and base is for the NRF51822 part. If other parts
* are supported in the future, add a sub-class of NRF51SoC for
* the specific variants
*/
#define NRF51822_FLASH_SIZE (256 * 1024)
#define NRF51822_SRAM_SIZE (16 * 1024)
#define NRF51822_FLASH_SIZE (256 * NRF51_PAGE_SIZE)
#define NRF51822_SRAM_SIZE (16 * NRF51_PAGE_SIZE)
#define BASE_TO_IRQ(base) ((base >> 12) & 0x1F)
static uint64_t clock_read(void *opaque, hwaddr addr, unsigned int size)
{
qemu_log_mask(LOG_UNIMP, "%s: 0x%" HWADDR_PRIx " [%u]\n",
__func__, addr, size);
return 1;
}
static void clock_write(void *opaque, hwaddr addr, uint64_t data,
unsigned int size)
{
qemu_log_mask(LOG_UNIMP, "%s: 0x%" HWADDR_PRIx " <- 0x%" PRIx64 " [%u]\n",
__func__, addr, data, size);
}
static const MemoryRegionOps clock_ops = {
.read = clock_read,
.write = clock_write
};
static void nrf51_soc_realize(DeviceState *dev_soc, Error **errp)
{
NRF51State *s = NRF51_SOC(dev_soc);
MemoryRegion *mr;
Error *err = NULL;
uint8_t i = 0;
hwaddr base_addr = 0;
if (!s->board_memory) {
error_setg(errp, "memory property was not set");
@ -76,14 +87,14 @@ static void nrf51_soc_realize(DeviceState *dev_soc, Error **errp)
error_propagate(errp, err);
return;
}
memory_region_add_subregion(&s->container, FLASH_BASE, &s->flash);
memory_region_add_subregion(&s->container, NRF51_FLASH_BASE, &s->flash);
memory_region_init_ram(&s->sram, NULL, "nrf51.sram", s->sram_size, &err);
if (err) {
error_propagate(errp, err);
return;
}
memory_region_add_subregion(&s->container, SRAM_BASE, &s->sram);
memory_region_add_subregion(&s->container, NRF51_SRAM_BASE, &s->sram);
/* UART */
object_property_set_bool(OBJECT(&s->uart), true, "realized", &err);
@ -92,19 +103,71 @@ static void nrf51_soc_realize(DeviceState *dev_soc, Error **errp)
return;
}
mr = sysbus_mmio_get_region(SYS_BUS_DEVICE(&s->uart), 0);
memory_region_add_subregion_overlap(&s->container, UART_BASE, mr, 0);
memory_region_add_subregion_overlap(&s->container, NRF51_UART_BASE, mr, 0);
sysbus_connect_irq(SYS_BUS_DEVICE(&s->uart), 0,
qdev_get_gpio_in(DEVICE(&s->cpu),
BASE_TO_IRQ(UART_BASE)));
BASE_TO_IRQ(NRF51_UART_BASE)));
create_unimplemented_device("nrf51_soc.io", IOMEM_BASE, IOMEM_SIZE);
create_unimplemented_device("nrf51_soc.ficr", FICR_BASE, FICR_SIZE);
/* RNG */
object_property_set_bool(OBJECT(&s->rng), true, "realized", &err);
if (err) {
error_propagate(errp, err);
return;
}
mr = sysbus_mmio_get_region(SYS_BUS_DEVICE(&s->rng), 0);
memory_region_add_subregion_overlap(&s->container, NRF51_RNG_BASE, mr, 0);
sysbus_connect_irq(SYS_BUS_DEVICE(&s->rng), 0,
qdev_get_gpio_in(DEVICE(&s->cpu),
BASE_TO_IRQ(NRF51_RNG_BASE)));
/* GPIO */
object_property_set_bool(OBJECT(&s->gpio), true, "realized", &err);
if (err) {
error_propagate(errp, err);
return;
}
mr = sysbus_mmio_get_region(SYS_BUS_DEVICE(&s->gpio), 0);
memory_region_add_subregion_overlap(&s->container, NRF51_GPIO_BASE, mr, 0);
/* Pass all GPIOs to the SOC layer so they are available to the board */
qdev_pass_gpios(DEVICE(&s->gpio), dev_soc, NULL);
/* TIMER */
for (i = 0; i < NRF51_NUM_TIMERS; i++) {
object_property_set_bool(OBJECT(&s->timer[i]), true, "realized", &err);
if (err) {
error_propagate(errp, err);
return;
}
base_addr = NRF51_TIMER_BASE + i * NRF51_TIMER_SIZE;
sysbus_mmio_map(SYS_BUS_DEVICE(&s->timer[i]), 0, base_addr);
sysbus_connect_irq(SYS_BUS_DEVICE(&s->timer[i]), 0,
qdev_get_gpio_in(DEVICE(&s->cpu),
BASE_TO_IRQ(base_addr)));
}
/* STUB Peripherals */
memory_region_init_io(&s->clock, NULL, &clock_ops, NULL,
"nrf51_soc.clock", 0x1000);
memory_region_add_subregion_overlap(&s->container,
NRF51_IOMEM_BASE, &s->clock, -1);
create_unimplemented_device("nrf51_soc.io", NRF51_IOMEM_BASE,
NRF51_IOMEM_SIZE);
create_unimplemented_device("nrf51_soc.ficr", NRF51_FICR_BASE,
NRF51_FICR_SIZE);
create_unimplemented_device("nrf51_soc.private",
PRIVATE_BASE, PRIVATE_SIZE);
NRF51_PRIVATE_BASE, NRF51_PRIVATE_SIZE);
}
static void nrf51_soc_init(Object *obj)
{
uint8_t i = 0;
NRF51State *s = NRF51_SOC(obj);
memory_region_init(&s->container, obj, "nrf51-container", UINT64_MAX);
@ -119,6 +182,18 @@ static void nrf51_soc_init(Object *obj)
TYPE_NRF51_UART);
object_property_add_alias(obj, "serial0", OBJECT(&s->uart), "chardev",
&error_abort);
sysbus_init_child_obj(obj, "rng", &s->rng, sizeof(s->rng),
TYPE_NRF51_RNG);
sysbus_init_child_obj(obj, "gpio", &s->gpio, sizeof(s->gpio),
TYPE_NRF51_GPIO);
for (i = 0; i < NRF51_NUM_TIMERS; i++) {
sysbus_init_child_obj(obj, "timer[*]", &s->timer[i],
sizeof(s->timer[i]), TYPE_NRF51_TIMER);
}
}
static Property nrf51_soc_properties[] = {

View file

@ -130,6 +130,7 @@ static void fdt_add_gic_nodes(VersalVirt *s)
2, MM_GIC_APU_REDIST_0_SIZE);
qemu_fdt_setprop_cell(s->fdt, nodename, "#interrupt-cells", 3);
qemu_fdt_setprop_string(s->fdt, nodename, "compatible", "arm,gic-v3");
g_free(nodename);
}
static void fdt_add_timer_nodes(VersalVirt *s)
@ -364,6 +365,7 @@ static void create_virtio_regions(VersalVirt *s)
sysbus_connect_irq(SYS_BUS_DEVICE(dev), 0, pic_irq);
mr = sysbus_mmio_get_region(SYS_BUS_DEVICE(dev), 0);
memory_region_add_subregion(&s->soc.mr_ps, base, mr);
g_free(name);
}
for (i = 0; i < NUM_VIRTIO_TRANSPORT; i++) {

View file

@ -178,12 +178,19 @@ static void xlnx_zynqmp_create_rpu(XlnxZynqMPState *s, const char *boot_cpu,
int i;
int num_rpus = MIN(smp_cpus - XLNX_ZYNQMP_NUM_APU_CPUS, XLNX_ZYNQMP_NUM_RPU_CPUS);
object_initialize_child(OBJECT(s), "rpu-cluster", &s->rpu_cluster,
sizeof(s->rpu_cluster), TYPE_CPU_CLUSTER,
&error_abort, NULL);
qdev_prop_set_uint32(DEVICE(&s->rpu_cluster), "cluster-id", 1);
qdev_init_nofail(DEVICE(&s->rpu_cluster));
for (i = 0; i < num_rpus; i++) {
char *name;
object_initialize(&s->rpu_cpu[i], sizeof(s->rpu_cpu[i]),
"cortex-r5f-" TYPE_ARM_CPU);
object_property_add_child(OBJECT(s), "rpu-cpu[*]",
object_property_add_child(OBJECT(&s->rpu_cluster), "rpu-cpu[*]",
OBJECT(&s->rpu_cpu[i]), &error_abort);
name = object_get_canonical_path_component(OBJECT(&s->rpu_cpu[i]));
@ -213,10 +220,16 @@ static void xlnx_zynqmp_init(Object *obj)
int i;
int num_apus = MIN(smp_cpus, XLNX_ZYNQMP_NUM_APU_CPUS);
object_initialize_child(obj, "apu-cluster", &s->apu_cluster,
sizeof(s->apu_cluster), TYPE_CPU_CLUSTER,
&error_abort, NULL);
qdev_prop_set_uint32(DEVICE(&s->apu_cluster), "cluster-id", 0);
for (i = 0; i < num_apus; i++) {
object_initialize_child(obj, "apu-cpu[*]", &s->apu_cpu[i],
sizeof(s->apu_cpu[i]),
"cortex-a53-" TYPE_ARM_CPU, &error_abort, NULL);
object_initialize_child(OBJECT(&s->apu_cluster), "apu-cpu[*]",
&s->apu_cpu[i], sizeof(s->apu_cpu[i]),
"cortex-a53-" TYPE_ARM_CPU, &error_abort,
NULL);
}
sysbus_init_child_obj(obj, "gic", &s->gic, sizeof(s->gic),
@ -333,6 +346,8 @@ static void xlnx_zynqmp_realize(DeviceState *dev, Error **errp)
qdev_prop_set_bit(DEVICE(&s->gic),
"has-virtualization-extensions", s->virt);
qdev_init_nofail(DEVICE(&s->apu_cluster));
/* Realize APUs before realizing the GIC. KVM requires this. */
for (i = 0; i < num_apus; i++) {
char *name;

View file

@ -613,13 +613,26 @@ static int load_uboot_image(const char *filename, hwaddr *ep, hwaddr *loadaddr,
goto out;
if (hdr->ih_type != image_type) {
fprintf(stderr, "Wrong image type %d, expected %d\n", hdr->ih_type,
image_type);
goto out;
if (!(image_type == IH_TYPE_KERNEL &&
hdr->ih_type == IH_TYPE_KERNEL_NOLOAD)) {
fprintf(stderr, "Wrong image type %d, expected %d\n", hdr->ih_type,
image_type);
goto out;
}
}
/* TODO: Implement other image types. */
switch (hdr->ih_type) {
case IH_TYPE_KERNEL_NOLOAD:
if (!loadaddr || *loadaddr == LOAD_UIMAGE_LOADADDR_INVALID) {
fprintf(stderr, "this image format (kernel_noload) cannot be "
"loaded on this machine type");
goto out;
}
hdr->ih_load = *loadaddr + sizeof(*hdr);
hdr->ih_ep += hdr->ih_load;
/* fall through */
case IH_TYPE_KERNEL:
address = hdr->ih_load;
if (translate_fn) {

View file

@ -124,6 +124,7 @@
#define IH_TYPE_SCRIPT 6 /* Script file */
#define IH_TYPE_FILESYSTEM 7 /* Filesystem Image (any type) */
#define IH_TYPE_FLATDT 8 /* Binary Flat Device Tree Blob */
#define IH_TYPE_KERNEL_NOLOAD 14 /* OS Kernel Image (noload) */
/*
* Compression Types

View file

@ -2,4 +2,4 @@ obj-$(CONFIG_ARM11MPCORE) += arm11mpcore.o
obj-$(CONFIG_REALVIEW) += realview_mpcore.o
obj-$(CONFIG_A9MPCORE) += a9mpcore.o
obj-$(CONFIG_A15MPCORE) += a15mpcore.o
common-obj-y += core.o
common-obj-y += core.o cluster.o

50
hw/cpu/cluster.c Normal file
View file

@ -0,0 +1,50 @@
/*
* QEMU CPU cluster
*
* Copyright (c) 2018 GreenSocs SAS
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, see
* <http://www.gnu.org/licenses/gpl-2.0.html>
*/
#include "qemu/osdep.h"
#include "hw/cpu/cluster.h"
#include "qapi/error.h"
#include "qemu/module.h"
static Property cpu_cluster_properties[] = {
DEFINE_PROP_UINT32("cluster-id", CPUClusterState, cluster_id, 0),
DEFINE_PROP_END_OF_LIST()
};
static void cpu_cluster_class_init(ObjectClass *klass, void *data)
{
DeviceClass *dc = DEVICE_CLASS(klass);
dc->props = cpu_cluster_properties;
}
static const TypeInfo cpu_cluster_type_info = {
.name = TYPE_CPU_CLUSTER,
.parent = TYPE_DEVICE,
.instance_size = sizeof(CPUClusterState),
.class_init = cpu_cluster_class_init,
};
static void cpu_cluster_register_types(void)
{
type_register_static(&cpu_cluster_type_info);
}
type_init(cpu_cluster_register_types)

View file

@ -8,3 +8,4 @@ common-obj-$(CONFIG_GPIO_KEY) += gpio_key.o
obj-$(CONFIG_OMAP) += omap_gpio.o
obj-$(CONFIG_IMX) += imx_gpio.o
obj-$(CONFIG_RASPI) += bcm2835_gpio.o
obj-$(CONFIG_NRF51_SOC) += nrf51_gpio.o

300
hw/gpio/nrf51_gpio.c Normal file
View file

@ -0,0 +1,300 @@
/*
* nRF51 System-on-Chip general purpose input/output register definition
*
* Reference Manual: http://infocenter.nordicsemi.com/pdf/nRF51_RM_v3.0.pdf
* Product Spec: http://infocenter.nordicsemi.com/pdf/nRF51822_PS_v3.1.pdf
*
* Copyright 2018 Steffen Görtz <contrib@steffen-goertz.de>
*
* This code is licensed under the GPL version 2 or later. See
* the COPYING file in the top-level directory.
*/
#include "qemu/osdep.h"
#include "qemu/log.h"
#include "hw/gpio/nrf51_gpio.h"
#include "trace.h"
/*
* Check if the output driver is connected to the direction switch
* given the current configuration and logic level.
* It is not differentiated between standard and "high"(-power) drive modes.
*/
static bool is_connected(uint32_t config, uint32_t level)
{
bool state;
uint32_t drive_config = extract32(config, 8, 3);
switch (drive_config) {
case 0 ... 3:
state = true;
break;
case 4 ... 5:
state = level != 0;
break;
case 6 ... 7:
state = level == 0;
break;
default:
g_assert_not_reached();
break;
}
return state;
}
static void update_output_irq(NRF51GPIOState *s, size_t i,
bool connected, bool level)
{
int64_t irq_level = connected ? level : -1;
bool old_connected = extract32(s->old_out_connected, i, 1);
bool old_level = extract32(s->old_out, i, 1);
if ((old_connected != connected) || (old_level != level)) {
qemu_set_irq(s->output[i], irq_level);
trace_nrf51_gpio_update_output_irq(i, irq_level);
}
s->old_out = deposit32(s->old_out, i, 1, level);
s->old_out_connected = deposit32(s->old_out_connected, i, 1, connected);
}
static void update_state(NRF51GPIOState *s)
{
uint32_t pull;
size_t i;
bool connected_out, dir, connected_in, out, input;
for (i = 0; i < NRF51_GPIO_PINS; i++) {
pull = extract32(s->cnf[i], 2, 2);
dir = extract32(s->cnf[i], 0, 1);
connected_in = extract32(s->in_mask, i, 1);
out = extract32(s->out, i, 1);
input = !extract32(s->cnf[i], 1, 1);
connected_out = is_connected(s->cnf[i], out) && dir;
update_output_irq(s, i, connected_out, out);
/* Pin both driven externally and internally */
if (connected_out && connected_in) {
qemu_log_mask(LOG_GUEST_ERROR, "GPIO pin %zu short circuited\n", i);
}
/*
* Input buffer disconnected from internal/external drives, so
* pull-up/pull-down becomes relevant
*/
if (!input || (input && !connected_in && !connected_out)) {
if (pull == NRF51_GPIO_PULLDOWN) {
s->in = deposit32(s->in, i, 1, 0);
} else if (pull == NRF51_GPIO_PULLUP) {
s->in = deposit32(s->in, i, 1, 1);
}
}
/* Self stimulation through internal output driver */
if (connected_out && !connected_in && input) {
s->in = deposit32(s->in, i, 1, out);
}
}
}
/*
* Direction is exposed in both the DIR register and the DIR bit
* of each PINs CNF configuration register. Reflect bits for pins in DIR
* to individual pin configuration registers.
*/
static void reflect_dir_bit_in_cnf(NRF51GPIOState *s)
{
size_t i;
uint32_t value = s->dir;
for (i = 0; i < NRF51_GPIO_PINS; i++) {
s->cnf[i] = (s->cnf[i] & ~(1UL)) | ((value >> i) & 0x01);
}
}
static uint64_t nrf51_gpio_read(void *opaque, hwaddr offset, unsigned int size)
{
NRF51GPIOState *s = NRF51_GPIO(opaque);
uint64_t r = 0;
size_t idx;
switch (offset) {
case NRF51_GPIO_REG_OUT ... NRF51_GPIO_REG_OUTCLR:
r = s->out;
break;
case NRF51_GPIO_REG_IN:
r = s->in;
break;
case NRF51_GPIO_REG_DIR ... NRF51_GPIO_REG_DIRCLR:
r = s->dir;
break;
case NRF51_GPIO_REG_CNF_START ... NRF51_GPIO_REG_CNF_END:
idx = (offset - NRF51_GPIO_REG_CNF_START) / 4;
r = s->cnf[idx];
break;
default:
qemu_log_mask(LOG_GUEST_ERROR,
"%s: bad read offset 0x%" HWADDR_PRIx "\n",
__func__, offset);
}
trace_nrf51_gpio_read(offset, r);
return r;
}
static void nrf51_gpio_write(void *opaque, hwaddr offset,
uint64_t value, unsigned int size)
{
NRF51GPIOState *s = NRF51_GPIO(opaque);
size_t idx;
trace_nrf51_gpio_write(offset, value);
switch (offset) {
case NRF51_GPIO_REG_OUT:
s->out = value;
break;
case NRF51_GPIO_REG_OUTSET:
s->out |= value;
break;
case NRF51_GPIO_REG_OUTCLR:
s->out &= ~value;
break;
case NRF51_GPIO_REG_DIR:
s->dir = value;
reflect_dir_bit_in_cnf(s);
break;
case NRF51_GPIO_REG_DIRSET:
s->dir |= value;
reflect_dir_bit_in_cnf(s);
break;
case NRF51_GPIO_REG_DIRCLR:
s->dir &= ~value;
reflect_dir_bit_in_cnf(s);
break;
case NRF51_GPIO_REG_CNF_START ... NRF51_GPIO_REG_CNF_END:
idx = (offset - NRF51_GPIO_REG_CNF_START) / 4;
s->cnf[idx] = value;
/*
* direction is exposed in both the DIR register and the DIR bit
* of each PINs CNF configuration register.
*/
s->dir = (s->dir & ~(1UL << idx)) | ((value & 0x01) << idx);
break;
default:
qemu_log_mask(LOG_GUEST_ERROR,
"%s: bad write offset 0x%" HWADDR_PRIx "\n",
__func__, offset);
}
update_state(s);
}
static const MemoryRegionOps gpio_ops = {
.read = nrf51_gpio_read,
.write = nrf51_gpio_write,
.endianness = DEVICE_LITTLE_ENDIAN,
.impl.min_access_size = 4,
.impl.max_access_size = 4,
};
static void nrf51_gpio_set(void *opaque, int line, int value)
{
NRF51GPIOState *s = NRF51_GPIO(opaque);
trace_nrf51_gpio_set(line, value);
assert(line >= 0 && line < NRF51_GPIO_PINS);
s->in_mask = deposit32(s->in_mask, line, 1, value >= 0);
if (value >= 0) {
s->in = deposit32(s->in, line, 1, value != 0);
}
update_state(s);
}
static void nrf51_gpio_reset(DeviceState *dev)
{
NRF51GPIOState *s = NRF51_GPIO(dev);
size_t i;
s->out = 0;
s->old_out = 0;
s->old_out_connected = 0;
s->in = 0;
s->in_mask = 0;
s->dir = 0;
for (i = 0; i < NRF51_GPIO_PINS; i++) {
s->cnf[i] = 0x00000002;
}
}
static const VMStateDescription vmstate_nrf51_gpio = {
.name = TYPE_NRF51_GPIO,
.version_id = 1,
.minimum_version_id = 1,
.fields = (VMStateField[]) {
VMSTATE_UINT32(out, NRF51GPIOState),
VMSTATE_UINT32(in, NRF51GPIOState),
VMSTATE_UINT32(in_mask, NRF51GPIOState),
VMSTATE_UINT32(dir, NRF51GPIOState),
VMSTATE_UINT32_ARRAY(cnf, NRF51GPIOState, NRF51_GPIO_PINS),
VMSTATE_UINT32(old_out, NRF51GPIOState),
VMSTATE_UINT32(old_out_connected, NRF51GPIOState),
VMSTATE_END_OF_LIST()
}
};
static void nrf51_gpio_init(Object *obj)
{
NRF51GPIOState *s = NRF51_GPIO(obj);
memory_region_init_io(&s->mmio, obj, &gpio_ops, s,
TYPE_NRF51_GPIO, NRF51_GPIO_SIZE);
sysbus_init_mmio(SYS_BUS_DEVICE(obj), &s->mmio);
qdev_init_gpio_in(DEVICE(s), nrf51_gpio_set, NRF51_GPIO_PINS);
qdev_init_gpio_out(DEVICE(s), s->output, NRF51_GPIO_PINS);
}
static void nrf51_gpio_class_init(ObjectClass *klass, void *data)
{
DeviceClass *dc = DEVICE_CLASS(klass);
dc->vmsd = &vmstate_nrf51_gpio;
dc->reset = nrf51_gpio_reset;
dc->desc = "nRF51 GPIO";
}
static const TypeInfo nrf51_gpio_info = {
.name = TYPE_NRF51_GPIO,
.parent = TYPE_SYS_BUS_DEVICE,
.instance_size = sizeof(NRF51GPIOState),
.instance_init = nrf51_gpio_init,
.class_init = nrf51_gpio_class_init
};
static void nrf51_gpio_register_types(void)
{
type_register_static(&nrf51_gpio_info);
}
type_init(nrf51_gpio_register_types)

7
hw/gpio/trace-events Normal file
View file

@ -0,0 +1,7 @@
# See docs/devel/tracing.txt for syntax documentation.
# hw/gpio/nrf51_gpio.c
nrf51_gpio_read(uint64_t offset, uint64_t r) "offset 0x%" PRIx64 " value 0x%" PRIx64
nrf51_gpio_write(uint64_t offset, uint64_t value) "offset 0x%" PRIx64 " value 0x%" PRIx64
nrf51_gpio_set(int64_t line, int64_t value) "line %" PRIi64 " value %" PRIi64
nrf51_gpio_update_output_irq(int64_t line, int64_t value) "line %" PRIi64 " value %" PRIi64

View file

@ -156,7 +156,7 @@ void microblaze_load_kernel(MicroBlazeCPU *cpu, hwaddr ddr_base,
/* If it wasn't an ELF image, try an u-boot image. */
if (kernel_size < 0) {
hwaddr uentry, loadaddr;
hwaddr uentry, loadaddr = LOAD_UIMAGE_LOADADDR_INVALID;
kernel_size = load_uimage(kernel_filename, &uentry, &loadaddr, 0,
NULL, NULL);

View file

@ -74,3 +74,4 @@ obj-$(CONFIG_PVPANIC) += pvpanic.o
obj-$(CONFIG_AUX) += auxbus.o
obj-$(CONFIG_ASPEED_SOC) += aspeed_scu.o aspeed_sdmc.o
obj-$(CONFIG_MSF2) += msf2-sysreg.o
obj-$(CONFIG_NRF51_SOC) += nrf51_rng.o

262
hw/misc/nrf51_rng.c Normal file
View file

@ -0,0 +1,262 @@
/*
* nRF51 Random Number Generator
*
* Reference Manual: http://infocenter.nordicsemi.com/pdf/nRF51_RM_v3.0.1.pdf
*
* Copyright 2018 Steffen Görtz <contrib@steffen-goertz.de>
*
* This code is licensed under the GPL version 2 or later. See
* the COPYING file in the top-level directory.
*/
#include "qemu/osdep.h"
#include "qemu/log.h"
#include "qapi/error.h"
#include "hw/arm/nrf51.h"
#include "hw/misc/nrf51_rng.h"
#include "crypto/random.h"
static void update_irq(NRF51RNGState *s)
{
bool irq = s->interrupt_enabled && s->event_valrdy;
qemu_set_irq(s->irq, irq);
}
static uint64_t rng_read(void *opaque, hwaddr offset, unsigned int size)
{
NRF51RNGState *s = NRF51_RNG(opaque);
uint64_t r = 0;
switch (offset) {
case NRF51_RNG_EVENT_VALRDY:
r = s->event_valrdy;
break;
case NRF51_RNG_REG_SHORTS:
r = s->shortcut_stop_on_valrdy;
break;
case NRF51_RNG_REG_INTEN:
case NRF51_RNG_REG_INTENSET:
case NRF51_RNG_REG_INTENCLR:
r = s->interrupt_enabled;
break;
case NRF51_RNG_REG_CONFIG:
r = s->filter_enabled;
break;
case NRF51_RNG_REG_VALUE:
r = s->value;
break;
default:
qemu_log_mask(LOG_GUEST_ERROR,
"%s: bad read offset 0x%" HWADDR_PRIx "\n",
__func__, offset);
}
return r;
}
static int64_t calc_next_timeout(NRF51RNGState *s)
{
int64_t timeout = qemu_clock_get_us(QEMU_CLOCK_VIRTUAL);
if (s->filter_enabled) {
timeout += s->period_filtered_us;
} else {
timeout += s->period_unfiltered_us;
}
return timeout;
}
static void rng_update_timer(NRF51RNGState *s)
{
if (s->active) {
timer_mod(&s->timer, calc_next_timeout(s));
} else {
timer_del(&s->timer);
}
}
static void rng_write(void *opaque, hwaddr offset,
uint64_t value, unsigned int size)
{
NRF51RNGState *s = NRF51_RNG(opaque);
switch (offset) {
case NRF51_RNG_TASK_START:
if (value == NRF51_TRIGGER_TASK) {
s->active = 1;
rng_update_timer(s);
}
break;
case NRF51_RNG_TASK_STOP:
if (value == NRF51_TRIGGER_TASK) {
s->active = 0;
rng_update_timer(s);
}
break;
case NRF51_RNG_EVENT_VALRDY:
if (value == NRF51_EVENT_CLEAR) {
s->event_valrdy = 0;
}
break;
case NRF51_RNG_REG_SHORTS:
s->shortcut_stop_on_valrdy =
(value & BIT_MASK(NRF51_RNG_REG_SHORTS_VALRDY_STOP)) ? 1 : 0;
break;
case NRF51_RNG_REG_INTEN:
s->interrupt_enabled =
(value & BIT_MASK(NRF51_RNG_REG_INTEN_VALRDY)) ? 1 : 0;
break;
case NRF51_RNG_REG_INTENSET:
if (value & BIT_MASK(NRF51_RNG_REG_INTEN_VALRDY)) {
s->interrupt_enabled = 1;
}
break;
case NRF51_RNG_REG_INTENCLR:
if (value & BIT_MASK(NRF51_RNG_REG_INTEN_VALRDY)) {
s->interrupt_enabled = 0;
}
break;
case NRF51_RNG_REG_CONFIG:
s->filter_enabled =
(value & BIT_MASK(NRF51_RNG_REG_CONFIG_DECEN)) ? 1 : 0;
break;
default:
qemu_log_mask(LOG_GUEST_ERROR,
"%s: bad write offset 0x%" HWADDR_PRIx "\n",
__func__, offset);
}
update_irq(s);
}
static const MemoryRegionOps rng_ops = {
.read = rng_read,
.write = rng_write,
.endianness = DEVICE_LITTLE_ENDIAN,
.impl.min_access_size = 4,
.impl.max_access_size = 4
};
static void nrf51_rng_timer_expire(void *opaque)
{
NRF51RNGState *s = NRF51_RNG(opaque);
qcrypto_random_bytes(&s->value, 1, &error_abort);
s->event_valrdy = 1;
qemu_set_irq(s->eep_valrdy, 1);
if (s->shortcut_stop_on_valrdy) {
s->active = 0;
}
rng_update_timer(s);
update_irq(s);
}
static void nrf51_rng_tep_start(void *opaque, int n, int level)
{
NRF51RNGState *s = NRF51_RNG(opaque);
if (level) {
s->active = 1;
rng_update_timer(s);
}
}
static void nrf51_rng_tep_stop(void *opaque, int n, int level)
{
NRF51RNGState *s = NRF51_RNG(opaque);
if (level) {
s->active = 0;
rng_update_timer(s);
}
}
static void nrf51_rng_init(Object *obj)
{
NRF51RNGState *s = NRF51_RNG(obj);
SysBusDevice *sbd = SYS_BUS_DEVICE(obj);
memory_region_init_io(&s->mmio, obj, &rng_ops, s,
TYPE_NRF51_RNG, NRF51_RNG_SIZE);
sysbus_init_mmio(sbd, &s->mmio);
timer_init_us(&s->timer, QEMU_CLOCK_VIRTUAL, nrf51_rng_timer_expire, s);
sysbus_init_irq(sbd, &s->irq);
/* Tasks */
qdev_init_gpio_in_named(DEVICE(s), nrf51_rng_tep_start, "tep_start", 1);
qdev_init_gpio_in_named(DEVICE(s), nrf51_rng_tep_stop, "tep_stop", 1);
/* Events */
qdev_init_gpio_out_named(DEVICE(s), &s->eep_valrdy, "eep_valrdy", 1);
}
static void nrf51_rng_reset(DeviceState *dev)
{
NRF51RNGState *s = NRF51_RNG(dev);
s->value = 0;
s->active = 0;
s->event_valrdy = 0;
s->shortcut_stop_on_valrdy = 0;
s->interrupt_enabled = 0;
s->filter_enabled = 0;
rng_update_timer(s);
}
static Property nrf51_rng_properties[] = {
DEFINE_PROP_UINT16("period_unfiltered_us", NRF51RNGState,
period_unfiltered_us, 167),
DEFINE_PROP_UINT16("period_filtered_us", NRF51RNGState,
period_filtered_us, 660),
DEFINE_PROP_END_OF_LIST(),
};
static const VMStateDescription vmstate_rng = {
.name = "nrf51_soc.rng",
.version_id = 1,
.minimum_version_id = 1,
.fields = (VMStateField[]) {
VMSTATE_UINT32(active, NRF51RNGState),
VMSTATE_UINT32(event_valrdy, NRF51RNGState),
VMSTATE_UINT32(shortcut_stop_on_valrdy, NRF51RNGState),
VMSTATE_UINT32(interrupt_enabled, NRF51RNGState),
VMSTATE_UINT32(filter_enabled, NRF51RNGState),
VMSTATE_END_OF_LIST()
}
};
static void nrf51_rng_class_init(ObjectClass *klass, void *data)
{
DeviceClass *dc = DEVICE_CLASS(klass);
dc->props = nrf51_rng_properties;
dc->vmsd = &vmstate_rng;
dc->reset = nrf51_rng_reset;
}
static const TypeInfo nrf51_rng_info = {
.name = TYPE_NRF51_RNG,
.parent = TYPE_SYS_BUS_DEVICE,
.instance_size = sizeof(NRF51RNGState),
.instance_init = nrf51_rng_init,
.class_init = nrf51_rng_class_init
};
static void nrf51_rng_register_types(void)
{
type_register_static(&nrf51_rng_info);
}
type_init(nrf51_rng_register_types)

View file

@ -150,7 +150,7 @@ static MemTxResult tz_mpc_reg_read(void *opaque, hwaddr addr,
r = s->ctrl;
break;
case A_BLK_MAX:
r = s->blk_max;
r = s->blk_max - 1;
break;
case A_BLK_CFG:
/* We are never in "init in progress state", so this just indicates

View file

@ -161,7 +161,7 @@ void nios2_load_kernel(Nios2CPU *cpu, hwaddr ddr_base,
/* If it wasn't an ELF image, try an u-boot image. */
if (kernel_size < 0) {
hwaddr uentry, loadaddr;
hwaddr uentry, loadaddr = LOAD_UIMAGE_LOADADDR_INVALID;
kernel_size = load_uimage(kernel_filename, &uentry, &loadaddr, 0,
NULL, NULL);

View file

@ -995,6 +995,7 @@ void ppce500_init(MachineState *machine)
* Hrm. No ELF image? Try a uImage, maybe someone is giving us an
* ePAPR compliant kernel
*/
loadaddr = LOAD_UIMAGE_LOADADDR_INVALID;
payload_size = load_uimage(filename, &bios_entry, &loadaddr, NULL,
NULL, NULL);
if (payload_size < 0) {

View file

@ -179,7 +179,7 @@ static void bamboo_init(MachineState *machine)
CPUPPCState *env;
uint64_t elf_entry;
uint64_t elf_lowaddr;
hwaddr loadaddr = 0;
hwaddr loadaddr = LOAD_UIMAGE_LOADADDR_INVALID;
target_long initrd_size = 0;
DeviceState *dev;
int success;

View file

@ -402,7 +402,7 @@ static void sam460ex_init(MachineState *machine)
CPUPPCState *env;
PPC4xxI2CState *i2c[2];
hwaddr entry = UBOOT_ENTRY;
hwaddr loadaddr = 0;
hwaddr loadaddr = LOAD_UIMAGE_LOADADDR_INVALID;
target_long initrd_size = 0;
DeviceState *dev;
SysBusDevice *sbdev;

View file

@ -23,6 +23,7 @@ common-obj-$(CONFIG_IMX) += imx_gpt.o
common-obj-$(CONFIG_LM32) += lm32_timer.o
common-obj-$(CONFIG_MILKYMIST) += milkymist-sysctl.o
common-obj-$(CONFIG_XLNX_ZYNQMP) += xlnx-zynqmp-rtc.o
common-obj-$(CONFIG_NRF51_SOC) += nrf51_timer.o
obj-$(CONFIG_ALTERA_TIMER) += altera_timer.o
obj-$(CONFIG_EXYNOS4) += exynos4210_mct.o

393
hw/timer/nrf51_timer.c Normal file
View file

@ -0,0 +1,393 @@
/*
* nRF51 System-on-Chip Timer peripheral
*
* Reference Manual: http://infocenter.nordicsemi.com/pdf/nRF51_RM_v3.0.pdf
* Product Spec: http://infocenter.nordicsemi.com/pdf/nRF51822_PS_v3.1.pdf
*
* Copyright 2018 Steffen Görtz <contrib@steffen-goertz.de>
* Copyright (c) 2019 Red Hat, Inc.
*
* This code is licensed under the GPL version 2 or later. See
* the COPYING file in the top-level directory.
*/
#include "qemu/osdep.h"
#include "qemu/log.h"
#include "hw/arm/nrf51.h"
#include "hw/timer/nrf51_timer.h"
#include "trace.h"
#define TIMER_CLK_FREQ 16000000UL
static uint32_t const bitwidths[] = {16, 8, 24, 32};
static uint32_t ns_to_ticks(NRF51TimerState *s, int64_t ns)
{
uint32_t freq = TIMER_CLK_FREQ >> s->prescaler;
return muldiv64(ns, freq, NANOSECONDS_PER_SECOND);
}
static int64_t ticks_to_ns(NRF51TimerState *s, uint32_t ticks)
{
uint32_t freq = TIMER_CLK_FREQ >> s->prescaler;
return muldiv64(ticks, NANOSECONDS_PER_SECOND, freq);
}
/* Returns number of ticks since last call */
static uint32_t update_counter(NRF51TimerState *s, int64_t now)
{
uint32_t ticks = ns_to_ticks(s, now - s->update_counter_ns);
s->counter = (s->counter + ticks) % BIT(bitwidths[s->bitmode]);
s->update_counter_ns = now;
return ticks;
}
/* Assumes s->counter is up-to-date */
static void rearm_timer(NRF51TimerState *s, int64_t now)
{
int64_t min_ns = INT64_MAX;
size_t i;
for (i = 0; i < NRF51_TIMER_REG_COUNT; i++) {
int64_t delta_ns;
if (s->events_compare[i]) {
continue; /* already expired, ignore it for now */
}
if (s->cc[i] <= s->counter) {
delta_ns = ticks_to_ns(s, BIT(bitwidths[s->bitmode]) -
s->counter + s->cc[i]);
} else {
delta_ns = ticks_to_ns(s, s->cc[i] - s->counter);
}
if (delta_ns < min_ns) {
min_ns = delta_ns;
}
}
if (min_ns != INT64_MAX) {
timer_mod_ns(&s->timer, now + min_ns);
}
}
static void update_irq(NRF51TimerState *s)
{
bool flag = false;
size_t i;
for (i = 0; i < NRF51_TIMER_REG_COUNT; i++) {
flag |= s->events_compare[i] && extract32(s->inten, 16 + i, 1);
}
qemu_set_irq(s->irq, flag);
}
static void timer_expire(void *opaque)
{
NRF51TimerState *s = NRF51_TIMER(opaque);
int64_t now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
uint32_t cc_remaining[NRF51_TIMER_REG_COUNT];
bool should_stop = false;
uint32_t ticks;
size_t i;
for (i = 0; i < NRF51_TIMER_REG_COUNT; i++) {
if (s->cc[i] > s->counter) {
cc_remaining[i] = s->cc[i] - s->counter;
} else {
cc_remaining[i] = BIT(bitwidths[s->bitmode]) -
s->counter + s->cc[i];
}
}
ticks = update_counter(s, now);
for (i = 0; i < NRF51_TIMER_REG_COUNT; i++) {
if (cc_remaining[i] <= ticks) {
s->events_compare[i] = 1;
if (s->shorts & BIT(i)) {
s->timer_start_ns = now;
s->update_counter_ns = s->timer_start_ns;
s->counter = 0;
}
should_stop |= s->shorts & BIT(i + 8);
}
}
update_irq(s);
if (should_stop) {
s->running = false;
timer_del(&s->timer);
} else {
rearm_timer(s, now);
}
}
static void counter_compare(NRF51TimerState *s)
{
uint32_t counter = s->counter;
size_t i;
for (i = 0; i < NRF51_TIMER_REG_COUNT; i++) {
if (counter == s->cc[i]) {
s->events_compare[i] = 1;
if (s->shorts & BIT(i)) {
s->counter = 0;
}
}
}
}
static uint64_t nrf51_timer_read(void *opaque, hwaddr offset, unsigned int size)
{
NRF51TimerState *s = NRF51_TIMER(opaque);
uint64_t r = 0;
switch (offset) {
case NRF51_TIMER_EVENT_COMPARE_0 ... NRF51_TIMER_EVENT_COMPARE_3:
r = s->events_compare[(offset - NRF51_TIMER_EVENT_COMPARE_0) / 4];
break;
case NRF51_TIMER_REG_SHORTS:
r = s->shorts;
break;
case NRF51_TIMER_REG_INTENSET:
r = s->inten;
break;
case NRF51_TIMER_REG_INTENCLR:
r = s->inten;
break;
case NRF51_TIMER_REG_MODE:
r = s->mode;
break;
case NRF51_TIMER_REG_BITMODE:
r = s->bitmode;
break;
case NRF51_TIMER_REG_PRESCALER:
r = s->prescaler;
break;
case NRF51_TIMER_REG_CC0 ... NRF51_TIMER_REG_CC3:
r = s->cc[(offset - NRF51_TIMER_REG_CC0) / 4];
break;
default:
qemu_log_mask(LOG_GUEST_ERROR,
"%s: bad read offset 0x%" HWADDR_PRIx "\n",
__func__, offset);
}
trace_nrf51_timer_read(offset, r, size);
return r;
}
static void nrf51_timer_write(void *opaque, hwaddr offset,
uint64_t value, unsigned int size)
{
NRF51TimerState *s = NRF51_TIMER(opaque);
uint64_t now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
size_t idx;
trace_nrf51_timer_write(offset, value, size);
switch (offset) {
case NRF51_TIMER_TASK_START:
if (value == NRF51_TRIGGER_TASK && s->mode == NRF51_TIMER_TIMER) {
s->running = true;
s->timer_start_ns = now - ticks_to_ns(s, s->counter);
s->update_counter_ns = s->timer_start_ns;
rearm_timer(s, now);
}
break;
case NRF51_TIMER_TASK_STOP:
case NRF51_TIMER_TASK_SHUTDOWN:
if (value == NRF51_TRIGGER_TASK) {
s->running = false;
timer_del(&s->timer);
}
break;
case NRF51_TIMER_TASK_COUNT:
if (value == NRF51_TRIGGER_TASK && s->mode == NRF51_TIMER_COUNTER) {
s->counter = (s->counter + 1) % BIT(bitwidths[s->bitmode]);
counter_compare(s);
}
break;
case NRF51_TIMER_TASK_CLEAR:
if (value == NRF51_TRIGGER_TASK) {
s->timer_start_ns = now;
s->update_counter_ns = s->timer_start_ns;
s->counter = 0;
if (s->running) {
rearm_timer(s, now);
}
}
break;
case NRF51_TIMER_TASK_CAPTURE_0 ... NRF51_TIMER_TASK_CAPTURE_3:
if (value == NRF51_TRIGGER_TASK) {
if (s->running) {
timer_expire(s); /* update counter and all state */
}
idx = (offset - NRF51_TIMER_TASK_CAPTURE_0) / 4;
s->cc[idx] = s->counter;
}
break;
case NRF51_TIMER_EVENT_COMPARE_0 ... NRF51_TIMER_EVENT_COMPARE_3:
if (value == NRF51_EVENT_CLEAR) {
s->events_compare[(offset - NRF51_TIMER_EVENT_COMPARE_0) / 4] = 0;
if (s->running) {
timer_expire(s); /* update counter and all state */
}
}
break;
case NRF51_TIMER_REG_SHORTS:
s->shorts = value & NRF51_TIMER_REG_SHORTS_MASK;
break;
case NRF51_TIMER_REG_INTENSET:
s->inten |= value & NRF51_TIMER_REG_INTEN_MASK;
break;
case NRF51_TIMER_REG_INTENCLR:
s->inten &= ~(value & NRF51_TIMER_REG_INTEN_MASK);
break;
case NRF51_TIMER_REG_MODE:
s->mode = value;
break;
case NRF51_TIMER_REG_BITMODE:
if (s->mode == NRF51_TIMER_TIMER && s->running) {
qemu_log_mask(LOG_GUEST_ERROR,
"%s: erroneous change of BITMODE while timer is running\n",
__func__);
}
s->bitmode = value & NRF51_TIMER_REG_BITMODE_MASK;
break;
case NRF51_TIMER_REG_PRESCALER:
if (s->mode == NRF51_TIMER_TIMER && s->running) {
qemu_log_mask(LOG_GUEST_ERROR,
"%s: erroneous change of PRESCALER while timer is running\n",
__func__);
}
s->prescaler = value & NRF51_TIMER_REG_PRESCALER_MASK;
break;
case NRF51_TIMER_REG_CC0 ... NRF51_TIMER_REG_CC3:
if (s->running) {
timer_expire(s); /* update counter */
}
idx = (offset - NRF51_TIMER_REG_CC0) / 4;
s->cc[idx] = value % BIT(bitwidths[s->bitmode]);
if (s->running) {
rearm_timer(s, now);
}
break;
default:
qemu_log_mask(LOG_GUEST_ERROR,
"%s: bad write offset 0x%" HWADDR_PRIx "\n",
__func__, offset);
}
update_irq(s);
}
static const MemoryRegionOps rng_ops = {
.read = nrf51_timer_read,
.write = nrf51_timer_write,
.endianness = DEVICE_LITTLE_ENDIAN,
.impl.min_access_size = 4,
.impl.max_access_size = 4,
};
static void nrf51_timer_init(Object *obj)
{
NRF51TimerState *s = NRF51_TIMER(obj);
SysBusDevice *sbd = SYS_BUS_DEVICE(obj);
memory_region_init_io(&s->iomem, obj, &rng_ops, s,
TYPE_NRF51_TIMER, NRF51_TIMER_SIZE);
sysbus_init_mmio(sbd, &s->iomem);
sysbus_init_irq(sbd, &s->irq);
timer_init_ns(&s->timer, QEMU_CLOCK_VIRTUAL, timer_expire, s);
}
static void nrf51_timer_reset(DeviceState *dev)
{
NRF51TimerState *s = NRF51_TIMER(dev);
timer_del(&s->timer);
s->timer_start_ns = 0x00;
s->update_counter_ns = 0x00;
s->counter = 0x00;
s->running = false;
memset(s->events_compare, 0x00, sizeof(s->events_compare));
memset(s->cc, 0x00, sizeof(s->cc));
s->shorts = 0x00;
s->inten = 0x00;
s->mode = 0x00;
s->bitmode = 0x00;
s->prescaler = 0x00;
}
static int nrf51_timer_post_load(void *opaque, int version_id)
{
NRF51TimerState *s = NRF51_TIMER(opaque);
if (s->running && s->mode == NRF51_TIMER_TIMER) {
timer_expire(s);
}
return 0;
}
static const VMStateDescription vmstate_nrf51_timer = {
.name = TYPE_NRF51_TIMER,
.version_id = 1,
.post_load = nrf51_timer_post_load,
.fields = (VMStateField[]) {
VMSTATE_TIMER(timer, NRF51TimerState),
VMSTATE_INT64(timer_start_ns, NRF51TimerState),
VMSTATE_INT64(update_counter_ns, NRF51TimerState),
VMSTATE_UINT32(counter, NRF51TimerState),
VMSTATE_BOOL(running, NRF51TimerState),
VMSTATE_UINT8_ARRAY(events_compare, NRF51TimerState,
NRF51_TIMER_REG_COUNT),
VMSTATE_UINT32_ARRAY(cc, NRF51TimerState, NRF51_TIMER_REG_COUNT),
VMSTATE_UINT32(shorts, NRF51TimerState),
VMSTATE_UINT32(inten, NRF51TimerState),
VMSTATE_UINT32(mode, NRF51TimerState),
VMSTATE_UINT32(bitmode, NRF51TimerState),
VMSTATE_UINT32(prescaler, NRF51TimerState),
VMSTATE_END_OF_LIST()
}
};
static void nrf51_timer_class_init(ObjectClass *klass, void *data)
{
DeviceClass *dc = DEVICE_CLASS(klass);
dc->reset = nrf51_timer_reset;
dc->vmsd = &vmstate_nrf51_timer;
}
static const TypeInfo nrf51_timer_info = {
.name = TYPE_NRF51_TIMER,
.parent = TYPE_SYS_BUS_DEVICE,
.instance_size = sizeof(NRF51TimerState),
.instance_init = nrf51_timer_init,
.class_init = nrf51_timer_class_init
};
static void nrf51_timer_register_types(void)
{
type_register_static(&nrf51_timer_info);
}
type_init(nrf51_timer_register_types)

View file

@ -72,3 +72,8 @@ sun4v_rtc_write(uint64_t addr, uint64_t value) "write: addr 0x%" PRIx64 " value
# hw/timer/xlnx-zynqmp-rtc.c
xlnx_zynqmp_rtc_gettime(int year, int month, int day, int hour, int min, int sec) "Get time from host: %d-%d-%d %2d:%02d:%02d"
# hw/timer/nrf51_timer.c
nrf51_timer_read(uint64_t addr, uint32_t value, unsigned size) "read addr 0x%" PRIx64 " data 0x%" PRIx32 " size %u"
nrf51_timer_write(uint64_t addr, uint32_t value, unsigned size) "write addr 0x%" PRIx64 " data 0x%" PRIx32 " size %u"