mirror of
https://github.com/Motorhead1991/qemu.git
synced 2025-08-04 08:13:54 -06:00
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:
commit
c102d9471f
48 changed files with 2567 additions and 271 deletions
|
@ -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);
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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[] = {
|
||||
|
|
|
@ -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++) {
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
50
hw/cpu/cluster.c
Normal 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)
|
|
@ -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
300
hw/gpio/nrf51_gpio.c
Normal 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
7
hw/gpio/trace-events
Normal 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
|
|
@ -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);
|
||||
|
|
|
@ -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
262
hw/misc/nrf51_rng.c
Normal 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)
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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
393
hw/timer/nrf51_timer.c
Normal 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)
|
|
@ -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"
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue