target-arm:
* New board model: xlnx-ep108 * Some more preparation for AArch64 EL2/EL3 * Fix bugs in access checking for generic counter registers * Remove a stray '+' sign -----BEGIN PGP SIGNATURE----- Version: GnuPG v1 iQIcBAABCAAGBQJVWjnBAAoJEDwlJe0UNgzeH68QAIEQiV1A22NuQfmF/EJ2SnQ9 kRc55WJRe1YH2O1UtyYut+VBa+JhtJ4GX57hbp5/dzCdbL1UAuBPUXpPxUdWfD9Z F0wzfQOlbNJoPwMdLRvBPVUEXd/U4c2/W0YA3y8+AfoCIa+khPzvcdyQUWleRhZT Xbpfmp8FyKetrxLP+ALj/lu1fWkpZ7RydKN0lY1LlGHeXEJyKvSzlXnTcmlrb12A 2IoozO3Jmhr86g4K7fRfnDoggRR2alVTLncl1pw+GzNxTxm0rn/oH0+XYsVo7MRu 2QnScwxWY7p1R7LONdx5a2WKJOuSSCrskX7mcekZgNKMgYbO/W1rUe+xIkWHJ70B 6fhf62/gLLtCI04IyIB8wjFfruvIXX/KmRwzLwhEWtE8qDB1J4t5Fv9ZVs3OhSwP c5kOPyEB5cUxyX9ZN9K7pNH3amQN005d5onDXV1tCU2fgsLLQnFVNMBtECrOb0O5 UX27rdyk6Ere4mizZqz4j5QyQyilrdRRi/Deum1XcNqJluo1RF1olmJKjSwKNMZa 54aPIHW6NdqV0J8E4fiWRvygwNELChkmSIUVizxmZB2XytGFqQNqzRvK42+4HKnn lC6m3p1B/hOOUvG6UgMntdkxYFycyCVn2EjzugtwmX8eTq/fgwM83bsWS3d7Zjy2 5G1std6fNl8AoK9Ra9xt =MuVm -----END PGP SIGNATURE----- Merge remote-tracking branch 'remotes/pmaydell/tags/pull-target-arm-20150518-3' into staging target-arm: * New board model: xlnx-ep108 * Some more preparation for AArch64 EL2/EL3 * Fix bugs in access checking for generic counter registers * Remove a stray '+' sign # gpg: Signature made Mon May 18 20:13:05 2015 BST using RSA key ID 14360CDE # gpg: Good signature from "Peter Maydell <peter.maydell@linaro.org>" * remotes/pmaydell/tags/pull-target-arm-20150518-3: (21 commits) target-arm: Remove unneeded '+' target-arm: Correct accessfn for CNTV_TVAL_EL0 target-arm: Correct accessfn for CNTP_{CT}VAL_EL0 target-arm: Add WFx syndrome function target-arm: Add EL3 and EL2 TCR checking target-arm: Add TTBR regime function and use linux-user/arm: Correct TARGET_NR_timerfd to TARGET_NR_timerfd_create arm: xlnx-ep108: Add bootloading arm: xlnx-ep108: Add external RAM arm: Add xlnx-ep108 machine arm: xlnx-zynqmp: Add UART support char: cadence_uart: Split state struct and type into header char: cadence_uart: Clean up variable names arm: xlnx-zynqmp: Add GEM support net: cadence_gem: Split state struct and type into header net: cadence_gem: Clean up variable names arm: xlnx-zynqmp: Connect CPU Timers to GIC arm: xlnx-zynqmp: Add GIC arm: Introduce Xilinx ZynqMP SoC target-arm: cpu64: Add support for Cortex-A53 ... Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
This commit is contained in:
commit
62bf3df432
|
@ -3,4 +3,4 @@
|
||||||
# We support all the 32 bit boards so need all their config
|
# We support all the 32 bit boards so need all their config
|
||||||
include arm-softmmu.mak
|
include arm-softmmu.mak
|
||||||
|
|
||||||
# Currently no 64-bit specific config requirements
|
CONFIG_XLNX_ZYNQMP=y
|
||||||
|
|
|
@ -10,3 +10,4 @@ obj-$(CONFIG_DIGIC) += digic.o
|
||||||
obj-y += omap1.o omap2.o strongarm.o
|
obj-y += omap1.o omap2.o strongarm.o
|
||||||
obj-$(CONFIG_ALLWINNER_A10) += allwinner-a10.o cubieboard.o
|
obj-$(CONFIG_ALLWINNER_A10) += allwinner-a10.o cubieboard.o
|
||||||
obj-$(CONFIG_STM32F205_SOC) += stm32f205_soc.o
|
obj-$(CONFIG_STM32F205_SOC) += stm32f205_soc.o
|
||||||
|
obj-$(CONFIG_XLNX_ZYNQMP) += xlnx-zynqmp.o xlnx-ep108.o
|
||||||
|
|
82
hw/arm/xlnx-ep108.c
Normal file
82
hw/arm/xlnx-ep108.c
Normal file
|
@ -0,0 +1,82 @@
|
||||||
|
/*
|
||||||
|
* Xilinx ZynqMP EP108 board
|
||||||
|
*
|
||||||
|
* Copyright (C) 2015 Xilinx Inc
|
||||||
|
* Written by Peter Crosthwaite <peter.crosthwaite@xilinx.com>
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify it
|
||||||
|
* under the terms of the GNU General Public License as published by the
|
||||||
|
* Free Software Foundation; either version 2 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||||
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||||
|
* for more details.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "hw/arm/xlnx-zynqmp.h"
|
||||||
|
#include "hw/boards.h"
|
||||||
|
#include "qemu/error-report.h"
|
||||||
|
#include "exec/address-spaces.h"
|
||||||
|
|
||||||
|
typedef struct XlnxEP108 {
|
||||||
|
XlnxZynqMPState soc;
|
||||||
|
MemoryRegion ddr_ram;
|
||||||
|
} XlnxEP108;
|
||||||
|
|
||||||
|
/* Max 2GB RAM */
|
||||||
|
#define EP108_MAX_RAM_SIZE 0x80000000ull
|
||||||
|
|
||||||
|
static struct arm_boot_info xlnx_ep108_binfo;
|
||||||
|
|
||||||
|
static void xlnx_ep108_init(MachineState *machine)
|
||||||
|
{
|
||||||
|
XlnxEP108 *s = g_new0(XlnxEP108, 1);
|
||||||
|
Error *err = NULL;
|
||||||
|
|
||||||
|
object_initialize(&s->soc, sizeof(s->soc), TYPE_XLNX_ZYNQMP);
|
||||||
|
object_property_add_child(OBJECT(machine), "soc", OBJECT(&s->soc),
|
||||||
|
&error_abort);
|
||||||
|
|
||||||
|
object_property_set_bool(OBJECT(&s->soc), true, "realized", &err);
|
||||||
|
if (err) {
|
||||||
|
error_report("%s", error_get_pretty(err));
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (machine->ram_size > EP108_MAX_RAM_SIZE) {
|
||||||
|
error_report("WARNING: RAM size " RAM_ADDR_FMT " above max supported, "
|
||||||
|
"reduced to %llx", machine->ram_size, EP108_MAX_RAM_SIZE);
|
||||||
|
machine->ram_size = EP108_MAX_RAM_SIZE;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (machine->ram_size <= 0x08000000) {
|
||||||
|
qemu_log("WARNING: RAM size " RAM_ADDR_FMT " is small for EP108",
|
||||||
|
machine->ram_size);
|
||||||
|
}
|
||||||
|
|
||||||
|
memory_region_allocate_system_memory(&s->ddr_ram, NULL, "ddr-ram",
|
||||||
|
machine->ram_size);
|
||||||
|
memory_region_add_subregion(get_system_memory(), 0, &s->ddr_ram);
|
||||||
|
|
||||||
|
xlnx_ep108_binfo.ram_size = machine->ram_size;
|
||||||
|
xlnx_ep108_binfo.kernel_filename = machine->kernel_filename;
|
||||||
|
xlnx_ep108_binfo.kernel_cmdline = machine->kernel_cmdline;
|
||||||
|
xlnx_ep108_binfo.initrd_filename = machine->initrd_filename;
|
||||||
|
xlnx_ep108_binfo.loader_start = 0;
|
||||||
|
arm_load_kernel(&s->soc.cpu[0], &xlnx_ep108_binfo);
|
||||||
|
}
|
||||||
|
|
||||||
|
static QEMUMachine xlnx_ep108_machine = {
|
||||||
|
.name = "xlnx-ep108",
|
||||||
|
.desc = "Xilinx ZynqMP EP108 board",
|
||||||
|
.init = xlnx_ep108_init,
|
||||||
|
};
|
||||||
|
|
||||||
|
static void xlnx_ep108_machine_init(void)
|
||||||
|
{
|
||||||
|
qemu_register_machine(&xlnx_ep108_machine);
|
||||||
|
}
|
||||||
|
|
||||||
|
machine_init(xlnx_ep108_machine_init);
|
211
hw/arm/xlnx-zynqmp.c
Normal file
211
hw/arm/xlnx-zynqmp.c
Normal file
|
@ -0,0 +1,211 @@
|
||||||
|
/*
|
||||||
|
* Xilinx Zynq MPSoC emulation
|
||||||
|
*
|
||||||
|
* Copyright (C) 2015 Xilinx Inc
|
||||||
|
* Written by Peter Crosthwaite <peter.crosthwaite@xilinx.com>
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify it
|
||||||
|
* under the terms of the GNU General Public License as published by the
|
||||||
|
* Free Software Foundation; either version 2 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||||
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||||
|
* for more details.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "hw/arm/xlnx-zynqmp.h"
|
||||||
|
#include "hw/intc/arm_gic_common.h"
|
||||||
|
#include "exec/address-spaces.h"
|
||||||
|
|
||||||
|
#define GIC_NUM_SPI_INTR 160
|
||||||
|
|
||||||
|
#define ARM_PHYS_TIMER_PPI 30
|
||||||
|
#define ARM_VIRT_TIMER_PPI 27
|
||||||
|
|
||||||
|
#define GIC_BASE_ADDR 0xf9000000
|
||||||
|
#define GIC_DIST_ADDR 0xf9010000
|
||||||
|
#define GIC_CPU_ADDR 0xf9020000
|
||||||
|
|
||||||
|
static const uint64_t gem_addr[XLNX_ZYNQMP_NUM_GEMS] = {
|
||||||
|
0xFF0B0000, 0xFF0C0000, 0xFF0D0000, 0xFF0E0000,
|
||||||
|
};
|
||||||
|
|
||||||
|
static const int gem_intr[XLNX_ZYNQMP_NUM_GEMS] = {
|
||||||
|
57, 59, 61, 63,
|
||||||
|
};
|
||||||
|
|
||||||
|
static const uint64_t uart_addr[XLNX_ZYNQMP_NUM_UARTS] = {
|
||||||
|
0xFF000000, 0xFF010000,
|
||||||
|
};
|
||||||
|
|
||||||
|
static const int uart_intr[XLNX_ZYNQMP_NUM_UARTS] = {
|
||||||
|
21, 22,
|
||||||
|
};
|
||||||
|
|
||||||
|
typedef struct XlnxZynqMPGICRegion {
|
||||||
|
int region_index;
|
||||||
|
uint32_t address;
|
||||||
|
} XlnxZynqMPGICRegion;
|
||||||
|
|
||||||
|
static const XlnxZynqMPGICRegion xlnx_zynqmp_gic_regions[] = {
|
||||||
|
{ .region_index = 0, .address = GIC_DIST_ADDR, },
|
||||||
|
{ .region_index = 1, .address = GIC_CPU_ADDR, },
|
||||||
|
};
|
||||||
|
|
||||||
|
static inline int arm_gic_ppi_index(int cpu_nr, int ppi_index)
|
||||||
|
{
|
||||||
|
return GIC_NUM_SPI_INTR + cpu_nr * GIC_INTERNAL + ppi_index;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void xlnx_zynqmp_init(Object *obj)
|
||||||
|
{
|
||||||
|
XlnxZynqMPState *s = XLNX_ZYNQMP(obj);
|
||||||
|
int i;
|
||||||
|
|
||||||
|
for (i = 0; i < XLNX_ZYNQMP_NUM_CPUS; i++) {
|
||||||
|
object_initialize(&s->cpu[i], sizeof(s->cpu[i]),
|
||||||
|
"cortex-a53-" TYPE_ARM_CPU);
|
||||||
|
object_property_add_child(obj, "cpu[*]", OBJECT(&s->cpu[i]),
|
||||||
|
&error_abort);
|
||||||
|
}
|
||||||
|
|
||||||
|
object_initialize(&s->gic, sizeof(s->gic), TYPE_ARM_GIC);
|
||||||
|
qdev_set_parent_bus(DEVICE(&s->gic), sysbus_get_default());
|
||||||
|
|
||||||
|
for (i = 0; i < XLNX_ZYNQMP_NUM_GEMS; i++) {
|
||||||
|
object_initialize(&s->gem[i], sizeof(s->gem[i]), TYPE_CADENCE_GEM);
|
||||||
|
qdev_set_parent_bus(DEVICE(&s->gem[i]), sysbus_get_default());
|
||||||
|
}
|
||||||
|
|
||||||
|
for (i = 0; i < XLNX_ZYNQMP_NUM_UARTS; i++) {
|
||||||
|
object_initialize(&s->uart[i], sizeof(s->uart[i]), TYPE_CADENCE_UART);
|
||||||
|
qdev_set_parent_bus(DEVICE(&s->uart[i]), sysbus_get_default());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void xlnx_zynqmp_realize(DeviceState *dev, Error **errp)
|
||||||
|
{
|
||||||
|
XlnxZynqMPState *s = XLNX_ZYNQMP(dev);
|
||||||
|
MemoryRegion *system_memory = get_system_memory();
|
||||||
|
uint8_t i;
|
||||||
|
qemu_irq gic_spi[GIC_NUM_SPI_INTR];
|
||||||
|
Error *err = NULL;
|
||||||
|
|
||||||
|
qdev_prop_set_uint32(DEVICE(&s->gic), "num-irq", GIC_NUM_SPI_INTR + 32);
|
||||||
|
qdev_prop_set_uint32(DEVICE(&s->gic), "revision", 2);
|
||||||
|
qdev_prop_set_uint32(DEVICE(&s->gic), "num-cpu", XLNX_ZYNQMP_NUM_CPUS);
|
||||||
|
object_property_set_bool(OBJECT(&s->gic), true, "realized", &err);
|
||||||
|
if (err) {
|
||||||
|
error_propagate((errp), (err));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
assert(ARRAY_SIZE(xlnx_zynqmp_gic_regions) == XLNX_ZYNQMP_GIC_REGIONS);
|
||||||
|
for (i = 0; i < XLNX_ZYNQMP_GIC_REGIONS; i++) {
|
||||||
|
SysBusDevice *gic = SYS_BUS_DEVICE(&s->gic);
|
||||||
|
const XlnxZynqMPGICRegion *r = &xlnx_zynqmp_gic_regions[i];
|
||||||
|
MemoryRegion *mr = sysbus_mmio_get_region(gic, r->region_index);
|
||||||
|
uint32_t addr = r->address;
|
||||||
|
int j;
|
||||||
|
|
||||||
|
sysbus_mmio_map(gic, r->region_index, addr);
|
||||||
|
|
||||||
|
for (j = 0; j < XLNX_ZYNQMP_GIC_ALIASES; j++) {
|
||||||
|
MemoryRegion *alias = &s->gic_mr[i][j];
|
||||||
|
|
||||||
|
addr += XLNX_ZYNQMP_GIC_REGION_SIZE;
|
||||||
|
memory_region_init_alias(alias, OBJECT(s), "zynqmp-gic-alias", mr,
|
||||||
|
0, XLNX_ZYNQMP_GIC_REGION_SIZE);
|
||||||
|
memory_region_add_subregion(system_memory, addr, alias);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (i = 0; i < XLNX_ZYNQMP_NUM_CPUS; i++) {
|
||||||
|
qemu_irq irq;
|
||||||
|
|
||||||
|
object_property_set_int(OBJECT(&s->cpu[i]), QEMU_PSCI_CONDUIT_SMC,
|
||||||
|
"psci-conduit", &error_abort);
|
||||||
|
if (i > 0) {
|
||||||
|
/* Secondary CPUs start in PSCI powered-down state */
|
||||||
|
object_property_set_bool(OBJECT(&s->cpu[i]), true,
|
||||||
|
"start-powered-off", &error_abort);
|
||||||
|
}
|
||||||
|
|
||||||
|
object_property_set_int(OBJECT(&s->cpu[i]), GIC_BASE_ADDR,
|
||||||
|
"reset-cbar", &err);
|
||||||
|
if (err) {
|
||||||
|
error_propagate((errp), (err));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
object_property_set_bool(OBJECT(&s->cpu[i]), true, "realized", &err);
|
||||||
|
if (err) {
|
||||||
|
error_propagate((errp), (err));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
sysbus_connect_irq(SYS_BUS_DEVICE(&s->gic), i,
|
||||||
|
qdev_get_gpio_in(DEVICE(&s->cpu[i]), ARM_CPU_IRQ));
|
||||||
|
irq = qdev_get_gpio_in(DEVICE(&s->gic),
|
||||||
|
arm_gic_ppi_index(i, ARM_PHYS_TIMER_PPI));
|
||||||
|
qdev_connect_gpio_out(DEVICE(&s->cpu[i]), 0, irq);
|
||||||
|
irq = qdev_get_gpio_in(DEVICE(&s->gic),
|
||||||
|
arm_gic_ppi_index(i, ARM_VIRT_TIMER_PPI));
|
||||||
|
qdev_connect_gpio_out(DEVICE(&s->cpu[i]), 1, irq);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (i = 0; i < GIC_NUM_SPI_INTR; i++) {
|
||||||
|
gic_spi[i] = qdev_get_gpio_in(DEVICE(&s->gic), i);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (i = 0; i < XLNX_ZYNQMP_NUM_GEMS; i++) {
|
||||||
|
NICInfo *nd = &nd_table[i];
|
||||||
|
|
||||||
|
if (nd->used) {
|
||||||
|
qemu_check_nic_model(nd, TYPE_CADENCE_GEM);
|
||||||
|
qdev_set_nic_properties(DEVICE(&s->gem[i]), nd);
|
||||||
|
}
|
||||||
|
object_property_set_bool(OBJECT(&s->gem[i]), true, "realized", &err);
|
||||||
|
if (err) {
|
||||||
|
error_propagate((errp), (err));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
sysbus_mmio_map(SYS_BUS_DEVICE(&s->gem[i]), 0, gem_addr[i]);
|
||||||
|
sysbus_connect_irq(SYS_BUS_DEVICE(&s->gem[i]), 0,
|
||||||
|
gic_spi[gem_intr[i]]);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (i = 0; i < XLNX_ZYNQMP_NUM_UARTS; i++) {
|
||||||
|
object_property_set_bool(OBJECT(&s->uart[i]), true, "realized", &err);
|
||||||
|
if (err) {
|
||||||
|
error_propagate((errp), (err));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
sysbus_mmio_map(SYS_BUS_DEVICE(&s->uart[i]), 0, uart_addr[i]);
|
||||||
|
sysbus_connect_irq(SYS_BUS_DEVICE(&s->uart[i]), 0,
|
||||||
|
gic_spi[uart_intr[i]]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void xlnx_zynqmp_class_init(ObjectClass *oc, void *data)
|
||||||
|
{
|
||||||
|
DeviceClass *dc = DEVICE_CLASS(oc);
|
||||||
|
|
||||||
|
dc->realize = xlnx_zynqmp_realize;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const TypeInfo xlnx_zynqmp_type_info = {
|
||||||
|
.name = TYPE_XLNX_ZYNQMP,
|
||||||
|
.parent = TYPE_DEVICE,
|
||||||
|
.instance_size = sizeof(XlnxZynqMPState),
|
||||||
|
.instance_init = xlnx_zynqmp_init,
|
||||||
|
.class_init = xlnx_zynqmp_class_init,
|
||||||
|
};
|
||||||
|
|
||||||
|
static void xlnx_zynqmp_register_types(void)
|
||||||
|
{
|
||||||
|
type_register_static(&xlnx_zynqmp_type_info);
|
||||||
|
}
|
||||||
|
|
||||||
|
type_init(xlnx_zynqmp_register_types)
|
|
@ -16,9 +16,7 @@
|
||||||
* with this program; if not, see <http://www.gnu.org/licenses/>.
|
* with this program; if not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "hw/sysbus.h"
|
#include "hw/char/cadence_uart.h"
|
||||||
#include "sysemu/char.h"
|
|
||||||
#include "qemu/timer.h"
|
|
||||||
|
|
||||||
#ifdef CADENCE_UART_ERR_DEBUG
|
#ifdef CADENCE_UART_ERR_DEBUG
|
||||||
#define DB_PRINT(...) do { \
|
#define DB_PRINT(...) do { \
|
||||||
|
@ -85,8 +83,6 @@
|
||||||
#define LOCAL_LOOPBACK (0x2 << UART_MR_CHMODE_SH)
|
#define LOCAL_LOOPBACK (0x2 << UART_MR_CHMODE_SH)
|
||||||
#define REMOTE_LOOPBACK (0x3 << UART_MR_CHMODE_SH)
|
#define REMOTE_LOOPBACK (0x3 << UART_MR_CHMODE_SH)
|
||||||
|
|
||||||
#define RX_FIFO_SIZE 16
|
|
||||||
#define TX_FIFO_SIZE 16
|
|
||||||
#define UART_INPUT_CLK 50000000
|
#define UART_INPUT_CLK 50000000
|
||||||
|
|
||||||
#define R_CR (0x00/4)
|
#define R_CR (0x00/4)
|
||||||
|
@ -108,38 +104,18 @@
|
||||||
#define R_PWID (0x40/4)
|
#define R_PWID (0x40/4)
|
||||||
#define R_TTRIG (0x44/4)
|
#define R_TTRIG (0x44/4)
|
||||||
|
|
||||||
#define R_MAX (R_TTRIG + 1)
|
|
||||||
|
|
||||||
#define TYPE_CADENCE_UART "cadence_uart"
|
static void uart_update_status(CadenceUARTState *s)
|
||||||
#define CADENCE_UART(obj) OBJECT_CHECK(UartState, (obj), TYPE_CADENCE_UART)
|
|
||||||
|
|
||||||
typedef struct {
|
|
||||||
/*< private >*/
|
|
||||||
SysBusDevice parent_obj;
|
|
||||||
/*< public >*/
|
|
||||||
|
|
||||||
MemoryRegion iomem;
|
|
||||||
uint32_t r[R_MAX];
|
|
||||||
uint8_t rx_fifo[RX_FIFO_SIZE];
|
|
||||||
uint8_t tx_fifo[TX_FIFO_SIZE];
|
|
||||||
uint32_t rx_wpos;
|
|
||||||
uint32_t rx_count;
|
|
||||||
uint32_t tx_count;
|
|
||||||
uint64_t char_tx_time;
|
|
||||||
CharDriverState *chr;
|
|
||||||
qemu_irq irq;
|
|
||||||
QEMUTimer *fifo_trigger_handle;
|
|
||||||
} UartState;
|
|
||||||
|
|
||||||
static void uart_update_status(UartState *s)
|
|
||||||
{
|
{
|
||||||
s->r[R_SR] = 0;
|
s->r[R_SR] = 0;
|
||||||
|
|
||||||
s->r[R_SR] |= s->rx_count == RX_FIFO_SIZE ? UART_SR_INTR_RFUL : 0;
|
s->r[R_SR] |= s->rx_count == CADENCE_UART_RX_FIFO_SIZE ? UART_SR_INTR_RFUL
|
||||||
|
: 0;
|
||||||
s->r[R_SR] |= !s->rx_count ? UART_SR_INTR_REMPTY : 0;
|
s->r[R_SR] |= !s->rx_count ? UART_SR_INTR_REMPTY : 0;
|
||||||
s->r[R_SR] |= s->rx_count >= s->r[R_RTRIG] ? UART_SR_INTR_RTRIG : 0;
|
s->r[R_SR] |= s->rx_count >= s->r[R_RTRIG] ? UART_SR_INTR_RTRIG : 0;
|
||||||
|
|
||||||
s->r[R_SR] |= s->tx_count == TX_FIFO_SIZE ? UART_SR_INTR_TFUL : 0;
|
s->r[R_SR] |= s->tx_count == CADENCE_UART_TX_FIFO_SIZE ? UART_SR_INTR_TFUL
|
||||||
|
: 0;
|
||||||
s->r[R_SR] |= !s->tx_count ? UART_SR_INTR_TEMPTY : 0;
|
s->r[R_SR] |= !s->tx_count ? UART_SR_INTR_TEMPTY : 0;
|
||||||
s->r[R_SR] |= s->tx_count >= s->r[R_TTRIG] ? UART_SR_TTRIG : 0;
|
s->r[R_SR] |= s->tx_count >= s->r[R_TTRIG] ? UART_SR_TTRIG : 0;
|
||||||
|
|
||||||
|
@ -150,14 +126,14 @@ static void uart_update_status(UartState *s)
|
||||||
|
|
||||||
static void fifo_trigger_update(void *opaque)
|
static void fifo_trigger_update(void *opaque)
|
||||||
{
|
{
|
||||||
UartState *s = (UartState *)opaque;
|
CadenceUARTState *s = opaque;
|
||||||
|
|
||||||
s->r[R_CISR] |= UART_INTR_TIMEOUT;
|
s->r[R_CISR] |= UART_INTR_TIMEOUT;
|
||||||
|
|
||||||
uart_update_status(s);
|
uart_update_status(s);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void uart_rx_reset(UartState *s)
|
static void uart_rx_reset(CadenceUARTState *s)
|
||||||
{
|
{
|
||||||
s->rx_wpos = 0;
|
s->rx_wpos = 0;
|
||||||
s->rx_count = 0;
|
s->rx_count = 0;
|
||||||
|
@ -166,12 +142,12 @@ static void uart_rx_reset(UartState *s)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void uart_tx_reset(UartState *s)
|
static void uart_tx_reset(CadenceUARTState *s)
|
||||||
{
|
{
|
||||||
s->tx_count = 0;
|
s->tx_count = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void uart_send_breaks(UartState *s)
|
static void uart_send_breaks(CadenceUARTState *s)
|
||||||
{
|
{
|
||||||
int break_enabled = 1;
|
int break_enabled = 1;
|
||||||
|
|
||||||
|
@ -181,7 +157,7 @@ static void uart_send_breaks(UartState *s)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void uart_parameters_setup(UartState *s)
|
static void uart_parameters_setup(CadenceUARTState *s)
|
||||||
{
|
{
|
||||||
QEMUSerialSetParams ssp;
|
QEMUSerialSetParams ssp;
|
||||||
unsigned int baud_rate, packet_size;
|
unsigned int baud_rate, packet_size;
|
||||||
|
@ -236,20 +212,20 @@ static void uart_parameters_setup(UartState *s)
|
||||||
|
|
||||||
static int uart_can_receive(void *opaque)
|
static int uart_can_receive(void *opaque)
|
||||||
{
|
{
|
||||||
UartState *s = (UartState *)opaque;
|
CadenceUARTState *s = opaque;
|
||||||
int ret = MAX(RX_FIFO_SIZE, TX_FIFO_SIZE);
|
int ret = MAX(CADENCE_UART_RX_FIFO_SIZE, CADENCE_UART_TX_FIFO_SIZE);
|
||||||
uint32_t ch_mode = s->r[R_MR] & UART_MR_CHMODE;
|
uint32_t ch_mode = s->r[R_MR] & UART_MR_CHMODE;
|
||||||
|
|
||||||
if (ch_mode == NORMAL_MODE || ch_mode == ECHO_MODE) {
|
if (ch_mode == NORMAL_MODE || ch_mode == ECHO_MODE) {
|
||||||
ret = MIN(ret, RX_FIFO_SIZE - s->rx_count);
|
ret = MIN(ret, CADENCE_UART_RX_FIFO_SIZE - s->rx_count);
|
||||||
}
|
}
|
||||||
if (ch_mode == REMOTE_LOOPBACK || ch_mode == ECHO_MODE) {
|
if (ch_mode == REMOTE_LOOPBACK || ch_mode == ECHO_MODE) {
|
||||||
ret = MIN(ret, TX_FIFO_SIZE - s->tx_count);
|
ret = MIN(ret, CADENCE_UART_TX_FIFO_SIZE - s->tx_count);
|
||||||
}
|
}
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void uart_ctrl_update(UartState *s)
|
static void uart_ctrl_update(CadenceUARTState *s)
|
||||||
{
|
{
|
||||||
if (s->r[R_CR] & UART_CR_TXRST) {
|
if (s->r[R_CR] & UART_CR_TXRST) {
|
||||||
uart_tx_reset(s);
|
uart_tx_reset(s);
|
||||||
|
@ -268,7 +244,7 @@ static void uart_ctrl_update(UartState *s)
|
||||||
|
|
||||||
static void uart_write_rx_fifo(void *opaque, const uint8_t *buf, int size)
|
static void uart_write_rx_fifo(void *opaque, const uint8_t *buf, int size)
|
||||||
{
|
{
|
||||||
UartState *s = (UartState *)opaque;
|
CadenceUARTState *s = opaque;
|
||||||
uint64_t new_rx_time = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
|
uint64_t new_rx_time = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
|
@ -276,12 +252,12 @@ static void uart_write_rx_fifo(void *opaque, const uint8_t *buf, int size)
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (s->rx_count == RX_FIFO_SIZE) {
|
if (s->rx_count == CADENCE_UART_RX_FIFO_SIZE) {
|
||||||
s->r[R_CISR] |= UART_INTR_ROVR;
|
s->r[R_CISR] |= UART_INTR_ROVR;
|
||||||
} else {
|
} else {
|
||||||
for (i = 0; i < size; i++) {
|
for (i = 0; i < size; i++) {
|
||||||
s->rx_fifo[s->rx_wpos] = buf[i];
|
s->rx_fifo[s->rx_wpos] = buf[i];
|
||||||
s->rx_wpos = (s->rx_wpos + 1) % RX_FIFO_SIZE;
|
s->rx_wpos = (s->rx_wpos + 1) % CADENCE_UART_RX_FIFO_SIZE;
|
||||||
s->rx_count++;
|
s->rx_count++;
|
||||||
}
|
}
|
||||||
timer_mod(s->fifo_trigger_handle, new_rx_time +
|
timer_mod(s->fifo_trigger_handle, new_rx_time +
|
||||||
|
@ -293,7 +269,7 @@ static void uart_write_rx_fifo(void *opaque, const uint8_t *buf, int size)
|
||||||
static gboolean cadence_uart_xmit(GIOChannel *chan, GIOCondition cond,
|
static gboolean cadence_uart_xmit(GIOChannel *chan, GIOCondition cond,
|
||||||
void *opaque)
|
void *opaque)
|
||||||
{
|
{
|
||||||
UartState *s = opaque;
|
CadenceUARTState *s = opaque;
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
/* instant drain the fifo when there's no back-end */
|
/* instant drain the fifo when there's no back-end */
|
||||||
|
@ -320,14 +296,15 @@ static gboolean cadence_uart_xmit(GIOChannel *chan, GIOCondition cond,
|
||||||
return FALSE;
|
return FALSE;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void uart_write_tx_fifo(UartState *s, const uint8_t *buf, int size)
|
static void uart_write_tx_fifo(CadenceUARTState *s, const uint8_t *buf,
|
||||||
|
int size)
|
||||||
{
|
{
|
||||||
if ((s->r[R_CR] & UART_CR_TX_DIS) || !(s->r[R_CR] & UART_CR_TX_EN)) {
|
if ((s->r[R_CR] & UART_CR_TX_DIS) || !(s->r[R_CR] & UART_CR_TX_EN)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (size > TX_FIFO_SIZE - s->tx_count) {
|
if (size > CADENCE_UART_TX_FIFO_SIZE - s->tx_count) {
|
||||||
size = TX_FIFO_SIZE - s->tx_count;
|
size = CADENCE_UART_TX_FIFO_SIZE - s->tx_count;
|
||||||
/*
|
/*
|
||||||
* This can only be a guest error via a bad tx fifo register push,
|
* This can only be a guest error via a bad tx fifo register push,
|
||||||
* as can_receive() should stop remote loop and echo modes ever getting
|
* as can_receive() should stop remote loop and echo modes ever getting
|
||||||
|
@ -345,7 +322,7 @@ static void uart_write_tx_fifo(UartState *s, const uint8_t *buf, int size)
|
||||||
|
|
||||||
static void uart_receive(void *opaque, const uint8_t *buf, int size)
|
static void uart_receive(void *opaque, const uint8_t *buf, int size)
|
||||||
{
|
{
|
||||||
UartState *s = (UartState *)opaque;
|
CadenceUARTState *s = opaque;
|
||||||
uint32_t ch_mode = s->r[R_MR] & UART_MR_CHMODE;
|
uint32_t ch_mode = s->r[R_MR] & UART_MR_CHMODE;
|
||||||
|
|
||||||
if (ch_mode == NORMAL_MODE || ch_mode == ECHO_MODE) {
|
if (ch_mode == NORMAL_MODE || ch_mode == ECHO_MODE) {
|
||||||
|
@ -358,7 +335,7 @@ static void uart_receive(void *opaque, const uint8_t *buf, int size)
|
||||||
|
|
||||||
static void uart_event(void *opaque, int event)
|
static void uart_event(void *opaque, int event)
|
||||||
{
|
{
|
||||||
UartState *s = (UartState *)opaque;
|
CadenceUARTState *s = opaque;
|
||||||
uint8_t buf = '\0';
|
uint8_t buf = '\0';
|
||||||
|
|
||||||
if (event == CHR_EVENT_BREAK) {
|
if (event == CHR_EVENT_BREAK) {
|
||||||
|
@ -368,15 +345,15 @@ static void uart_event(void *opaque, int event)
|
||||||
uart_update_status(s);
|
uart_update_status(s);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void uart_read_rx_fifo(UartState *s, uint32_t *c)
|
static void uart_read_rx_fifo(CadenceUARTState *s, uint32_t *c)
|
||||||
{
|
{
|
||||||
if ((s->r[R_CR] & UART_CR_RX_DIS) || !(s->r[R_CR] & UART_CR_RX_EN)) {
|
if ((s->r[R_CR] & UART_CR_RX_DIS) || !(s->r[R_CR] & UART_CR_RX_EN)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (s->rx_count) {
|
if (s->rx_count) {
|
||||||
uint32_t rx_rpos =
|
uint32_t rx_rpos = (CADENCE_UART_RX_FIFO_SIZE + s->rx_wpos -
|
||||||
(RX_FIFO_SIZE + s->rx_wpos - s->rx_count) % RX_FIFO_SIZE;
|
s->rx_count) % CADENCE_UART_RX_FIFO_SIZE;
|
||||||
*c = s->rx_fifo[rx_rpos];
|
*c = s->rx_fifo[rx_rpos];
|
||||||
s->rx_count--;
|
s->rx_count--;
|
||||||
|
|
||||||
|
@ -393,7 +370,7 @@ static void uart_read_rx_fifo(UartState *s, uint32_t *c)
|
||||||
static void uart_write(void *opaque, hwaddr offset,
|
static void uart_write(void *opaque, hwaddr offset,
|
||||||
uint64_t value, unsigned size)
|
uint64_t value, unsigned size)
|
||||||
{
|
{
|
||||||
UartState *s = (UartState *)opaque;
|
CadenceUARTState *s = opaque;
|
||||||
|
|
||||||
DB_PRINT(" offset:%x data:%08x\n", (unsigned)offset, (unsigned)value);
|
DB_PRINT(" offset:%x data:%08x\n", (unsigned)offset, (unsigned)value);
|
||||||
offset >>= 2;
|
offset >>= 2;
|
||||||
|
@ -437,11 +414,11 @@ static void uart_write(void *opaque, hwaddr offset,
|
||||||
static uint64_t uart_read(void *opaque, hwaddr offset,
|
static uint64_t uart_read(void *opaque, hwaddr offset,
|
||||||
unsigned size)
|
unsigned size)
|
||||||
{
|
{
|
||||||
UartState *s = (UartState *)opaque;
|
CadenceUARTState *s = opaque;
|
||||||
uint32_t c = 0;
|
uint32_t c = 0;
|
||||||
|
|
||||||
offset >>= 2;
|
offset >>= 2;
|
||||||
if (offset >= R_MAX) {
|
if (offset >= CADENCE_UART_R_MAX) {
|
||||||
c = 0;
|
c = 0;
|
||||||
} else if (offset == R_TX_RX) {
|
} else if (offset == R_TX_RX) {
|
||||||
uart_read_rx_fifo(s, &c);
|
uart_read_rx_fifo(s, &c);
|
||||||
|
@ -461,7 +438,7 @@ static const MemoryRegionOps uart_ops = {
|
||||||
|
|
||||||
static void cadence_uart_reset(DeviceState *dev)
|
static void cadence_uart_reset(DeviceState *dev)
|
||||||
{
|
{
|
||||||
UartState *s = CADENCE_UART(dev);
|
CadenceUARTState *s = CADENCE_UART(dev);
|
||||||
|
|
||||||
s->r[R_CR] = 0x00000128;
|
s->r[R_CR] = 0x00000128;
|
||||||
s->r[R_IMR] = 0;
|
s->r[R_IMR] = 0;
|
||||||
|
@ -478,7 +455,7 @@ static void cadence_uart_reset(DeviceState *dev)
|
||||||
|
|
||||||
static void cadence_uart_realize(DeviceState *dev, Error **errp)
|
static void cadence_uart_realize(DeviceState *dev, Error **errp)
|
||||||
{
|
{
|
||||||
UartState *s = CADENCE_UART(dev);
|
CadenceUARTState *s = CADENCE_UART(dev);
|
||||||
|
|
||||||
s->fifo_trigger_handle = timer_new_ns(QEMU_CLOCK_VIRTUAL,
|
s->fifo_trigger_handle = timer_new_ns(QEMU_CLOCK_VIRTUAL,
|
||||||
fifo_trigger_update, s);
|
fifo_trigger_update, s);
|
||||||
|
@ -495,7 +472,7 @@ static void cadence_uart_realize(DeviceState *dev, Error **errp)
|
||||||
static void cadence_uart_init(Object *obj)
|
static void cadence_uart_init(Object *obj)
|
||||||
{
|
{
|
||||||
SysBusDevice *sbd = SYS_BUS_DEVICE(obj);
|
SysBusDevice *sbd = SYS_BUS_DEVICE(obj);
|
||||||
UartState *s = CADENCE_UART(obj);
|
CadenceUARTState *s = CADENCE_UART(obj);
|
||||||
|
|
||||||
memory_region_init_io(&s->iomem, obj, &uart_ops, s, "uart", 0x1000);
|
memory_region_init_io(&s->iomem, obj, &uart_ops, s, "uart", 0x1000);
|
||||||
sysbus_init_mmio(sbd, &s->iomem);
|
sysbus_init_mmio(sbd, &s->iomem);
|
||||||
|
@ -506,7 +483,7 @@ static void cadence_uart_init(Object *obj)
|
||||||
|
|
||||||
static int cadence_uart_post_load(void *opaque, int version_id)
|
static int cadence_uart_post_load(void *opaque, int version_id)
|
||||||
{
|
{
|
||||||
UartState *s = opaque;
|
CadenceUARTState *s = opaque;
|
||||||
|
|
||||||
uart_parameters_setup(s);
|
uart_parameters_setup(s);
|
||||||
uart_update_status(s);
|
uart_update_status(s);
|
||||||
|
@ -519,13 +496,15 @@ static const VMStateDescription vmstate_cadence_uart = {
|
||||||
.minimum_version_id = 2,
|
.minimum_version_id = 2,
|
||||||
.post_load = cadence_uart_post_load,
|
.post_load = cadence_uart_post_load,
|
||||||
.fields = (VMStateField[]) {
|
.fields = (VMStateField[]) {
|
||||||
VMSTATE_UINT32_ARRAY(r, UartState, R_MAX),
|
VMSTATE_UINT32_ARRAY(r, CadenceUARTState, CADENCE_UART_R_MAX),
|
||||||
VMSTATE_UINT8_ARRAY(rx_fifo, UartState, RX_FIFO_SIZE),
|
VMSTATE_UINT8_ARRAY(rx_fifo, CadenceUARTState,
|
||||||
VMSTATE_UINT8_ARRAY(tx_fifo, UartState, RX_FIFO_SIZE),
|
CADENCE_UART_RX_FIFO_SIZE),
|
||||||
VMSTATE_UINT32(rx_count, UartState),
|
VMSTATE_UINT8_ARRAY(tx_fifo, CadenceUARTState,
|
||||||
VMSTATE_UINT32(tx_count, UartState),
|
CADENCE_UART_TX_FIFO_SIZE),
|
||||||
VMSTATE_UINT32(rx_wpos, UartState),
|
VMSTATE_UINT32(rx_count, CadenceUARTState),
|
||||||
VMSTATE_TIMER_PTR(fifo_trigger_handle, UartState),
|
VMSTATE_UINT32(tx_count, CadenceUARTState),
|
||||||
|
VMSTATE_UINT32(rx_wpos, CadenceUARTState),
|
||||||
|
VMSTATE_TIMER_PTR(fifo_trigger_handle, CadenceUARTState),
|
||||||
VMSTATE_END_OF_LIST()
|
VMSTATE_END_OF_LIST()
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -544,7 +523,7 @@ static void cadence_uart_class_init(ObjectClass *klass, void *data)
|
||||||
static const TypeInfo cadence_uart_info = {
|
static const TypeInfo cadence_uart_info = {
|
||||||
.name = TYPE_CADENCE_UART,
|
.name = TYPE_CADENCE_UART,
|
||||||
.parent = TYPE_SYS_BUS_DEVICE,
|
.parent = TYPE_SYS_BUS_DEVICE,
|
||||||
.instance_size = sizeof(UartState),
|
.instance_size = sizeof(CadenceUARTState),
|
||||||
.instance_init = cadence_uart_init,
|
.instance_init = cadence_uart_init,
|
||||||
.class_init = cadence_uart_class_init,
|
.class_init = cadence_uart_class_init,
|
||||||
};
|
};
|
||||||
|
|
|
@ -24,8 +24,7 @@
|
||||||
|
|
||||||
#include <zlib.h> /* For crc32 */
|
#include <zlib.h> /* For crc32 */
|
||||||
|
|
||||||
#include "hw/sysbus.h"
|
#include "hw/net/cadence_gem.h"
|
||||||
#include "net/net.h"
|
|
||||||
#include "net/checksum.h"
|
#include "net/checksum.h"
|
||||||
|
|
||||||
#ifdef CADENCE_GEM_ERR_DEBUG
|
#ifdef CADENCE_GEM_ERR_DEBUG
|
||||||
|
@ -141,8 +140,6 @@
|
||||||
#define GEM_DESCONF6 (0x00000294/4)
|
#define GEM_DESCONF6 (0x00000294/4)
|
||||||
#define GEM_DESCONF7 (0x00000298/4)
|
#define GEM_DESCONF7 (0x00000298/4)
|
||||||
|
|
||||||
#define GEM_MAXREG (0x00000640/4) /* Last valid GEM address */
|
|
||||||
|
|
||||||
/*****************************************/
|
/*****************************************/
|
||||||
#define GEM_NWCTRL_TXSTART 0x00000200 /* Transmit Enable */
|
#define GEM_NWCTRL_TXSTART 0x00000200 /* Transmit Enable */
|
||||||
#define GEM_NWCTRL_TXENA 0x00000008 /* Transmit Enable */
|
#define GEM_NWCTRL_TXENA 0x00000008 /* Transmit Enable */
|
||||||
|
@ -349,44 +346,6 @@ static inline void rx_desc_set_sar(unsigned *desc, int sar_idx)
|
||||||
desc[1] |= R_DESC_1_RX_SAR_MATCH;
|
desc[1] |= R_DESC_1_RX_SAR_MATCH;
|
||||||
}
|
}
|
||||||
|
|
||||||
#define TYPE_CADENCE_GEM "cadence_gem"
|
|
||||||
#define GEM(obj) OBJECT_CHECK(GemState, (obj), TYPE_CADENCE_GEM)
|
|
||||||
|
|
||||||
typedef struct GemState {
|
|
||||||
SysBusDevice parent_obj;
|
|
||||||
|
|
||||||
MemoryRegion iomem;
|
|
||||||
NICState *nic;
|
|
||||||
NICConf conf;
|
|
||||||
qemu_irq irq;
|
|
||||||
|
|
||||||
/* GEM registers backing store */
|
|
||||||
uint32_t regs[GEM_MAXREG];
|
|
||||||
/* Mask of register bits which are write only */
|
|
||||||
uint32_t regs_wo[GEM_MAXREG];
|
|
||||||
/* Mask of register bits which are read only */
|
|
||||||
uint32_t regs_ro[GEM_MAXREG];
|
|
||||||
/* Mask of register bits which are clear on read */
|
|
||||||
uint32_t regs_rtc[GEM_MAXREG];
|
|
||||||
/* Mask of register bits which are write 1 to clear */
|
|
||||||
uint32_t regs_w1c[GEM_MAXREG];
|
|
||||||
|
|
||||||
/* PHY registers backing store */
|
|
||||||
uint16_t phy_regs[32];
|
|
||||||
|
|
||||||
uint8_t phy_loop; /* Are we in phy loopback? */
|
|
||||||
|
|
||||||
/* The current DMA descriptor pointers */
|
|
||||||
uint32_t rx_desc_addr;
|
|
||||||
uint32_t tx_desc_addr;
|
|
||||||
|
|
||||||
uint8_t can_rx_state; /* Debug only */
|
|
||||||
|
|
||||||
unsigned rx_desc[2];
|
|
||||||
|
|
||||||
bool sar_active[4];
|
|
||||||
} GemState;
|
|
||||||
|
|
||||||
/* The broadcast MAC address: 0xFFFFFFFFFFFF */
|
/* The broadcast MAC address: 0xFFFFFFFFFFFF */
|
||||||
static const uint8_t broadcast_addr[] = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF };
|
static const uint8_t broadcast_addr[] = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF };
|
||||||
|
|
||||||
|
@ -395,7 +354,7 @@ static const uint8_t broadcast_addr[] = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF };
|
||||||
* One time initialization.
|
* One time initialization.
|
||||||
* Set masks to identify which register bits have magical clear properties
|
* Set masks to identify which register bits have magical clear properties
|
||||||
*/
|
*/
|
||||||
static void gem_init_register_masks(GemState *s)
|
static void gem_init_register_masks(CadenceGEMState *s)
|
||||||
{
|
{
|
||||||
/* Mask of register bits which are read only */
|
/* Mask of register bits which are read only */
|
||||||
memset(&s->regs_ro[0], 0, sizeof(s->regs_ro));
|
memset(&s->regs_ro[0], 0, sizeof(s->regs_ro));
|
||||||
|
@ -430,7 +389,7 @@ static void gem_init_register_masks(GemState *s)
|
||||||
* phy_update_link:
|
* phy_update_link:
|
||||||
* Make the emulated PHY link state match the QEMU "interface" state.
|
* Make the emulated PHY link state match the QEMU "interface" state.
|
||||||
*/
|
*/
|
||||||
static void phy_update_link(GemState *s)
|
static void phy_update_link(CadenceGEMState *s)
|
||||||
{
|
{
|
||||||
DB_PRINT("down %d\n", qemu_get_queue(s->nic)->link_down);
|
DB_PRINT("down %d\n", qemu_get_queue(s->nic)->link_down);
|
||||||
|
|
||||||
|
@ -450,7 +409,7 @@ static void phy_update_link(GemState *s)
|
||||||
|
|
||||||
static int gem_can_receive(NetClientState *nc)
|
static int gem_can_receive(NetClientState *nc)
|
||||||
{
|
{
|
||||||
GemState *s;
|
CadenceGEMState *s;
|
||||||
|
|
||||||
s = qemu_get_nic_opaque(nc);
|
s = qemu_get_nic_opaque(nc);
|
||||||
|
|
||||||
|
@ -483,7 +442,7 @@ static int gem_can_receive(NetClientState *nc)
|
||||||
* gem_update_int_status:
|
* gem_update_int_status:
|
||||||
* Raise or lower interrupt based on current status.
|
* Raise or lower interrupt based on current status.
|
||||||
*/
|
*/
|
||||||
static void gem_update_int_status(GemState *s)
|
static void gem_update_int_status(CadenceGEMState *s)
|
||||||
{
|
{
|
||||||
if (s->regs[GEM_ISR]) {
|
if (s->regs[GEM_ISR]) {
|
||||||
DB_PRINT("asserting int. (0x%08x)\n", s->regs[GEM_ISR]);
|
DB_PRINT("asserting int. (0x%08x)\n", s->regs[GEM_ISR]);
|
||||||
|
@ -495,7 +454,7 @@ static void gem_update_int_status(GemState *s)
|
||||||
* gem_receive_updatestats:
|
* gem_receive_updatestats:
|
||||||
* Increment receive statistics.
|
* Increment receive statistics.
|
||||||
*/
|
*/
|
||||||
static void gem_receive_updatestats(GemState *s, const uint8_t *packet,
|
static void gem_receive_updatestats(CadenceGEMState *s, const uint8_t *packet,
|
||||||
unsigned bytes)
|
unsigned bytes)
|
||||||
{
|
{
|
||||||
uint64_t octets;
|
uint64_t octets;
|
||||||
|
@ -586,7 +545,7 @@ static unsigned calc_mac_hash(const uint8_t *mac)
|
||||||
* GEM_RM_PROMISCUOUS_ACCEPT, GEM_RX_BROADCAST_ACCEPT,
|
* GEM_RM_PROMISCUOUS_ACCEPT, GEM_RX_BROADCAST_ACCEPT,
|
||||||
* GEM_RX_MULTICAST_HASH_ACCEPT or GEM_RX_UNICAST_HASH_ACCEPT
|
* GEM_RX_MULTICAST_HASH_ACCEPT or GEM_RX_UNICAST_HASH_ACCEPT
|
||||||
*/
|
*/
|
||||||
static int gem_mac_address_filter(GemState *s, const uint8_t *packet)
|
static int gem_mac_address_filter(CadenceGEMState *s, const uint8_t *packet)
|
||||||
{
|
{
|
||||||
uint8_t *gem_spaddr;
|
uint8_t *gem_spaddr;
|
||||||
int i;
|
int i;
|
||||||
|
@ -636,7 +595,7 @@ static int gem_mac_address_filter(GemState *s, const uint8_t *packet)
|
||||||
return GEM_RX_REJECT;
|
return GEM_RX_REJECT;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void gem_get_rx_desc(GemState *s)
|
static void gem_get_rx_desc(CadenceGEMState *s)
|
||||||
{
|
{
|
||||||
DB_PRINT("read descriptor 0x%x\n", (unsigned)s->rx_desc_addr);
|
DB_PRINT("read descriptor 0x%x\n", (unsigned)s->rx_desc_addr);
|
||||||
/* read current descriptor */
|
/* read current descriptor */
|
||||||
|
@ -660,7 +619,7 @@ static void gem_get_rx_desc(GemState *s)
|
||||||
*/
|
*/
|
||||||
static ssize_t gem_receive(NetClientState *nc, const uint8_t *buf, size_t size)
|
static ssize_t gem_receive(NetClientState *nc, const uint8_t *buf, size_t size)
|
||||||
{
|
{
|
||||||
GemState *s;
|
CadenceGEMState *s;
|
||||||
unsigned rxbufsize, bytes_to_copy;
|
unsigned rxbufsize, bytes_to_copy;
|
||||||
unsigned rxbuf_offset;
|
unsigned rxbuf_offset;
|
||||||
uint8_t rxbuf[2048];
|
uint8_t rxbuf[2048];
|
||||||
|
@ -810,7 +769,7 @@ static ssize_t gem_receive(NetClientState *nc, const uint8_t *buf, size_t size)
|
||||||
* gem_transmit_updatestats:
|
* gem_transmit_updatestats:
|
||||||
* Increment transmit statistics.
|
* Increment transmit statistics.
|
||||||
*/
|
*/
|
||||||
static void gem_transmit_updatestats(GemState *s, const uint8_t *packet,
|
static void gem_transmit_updatestats(CadenceGEMState *s, const uint8_t *packet,
|
||||||
unsigned bytes)
|
unsigned bytes)
|
||||||
{
|
{
|
||||||
uint64_t octets;
|
uint64_t octets;
|
||||||
|
@ -856,7 +815,7 @@ static void gem_transmit_updatestats(GemState *s, const uint8_t *packet,
|
||||||
* gem_transmit:
|
* gem_transmit:
|
||||||
* Fish packets out of the descriptor ring and feed them to QEMU
|
* Fish packets out of the descriptor ring and feed them to QEMU
|
||||||
*/
|
*/
|
||||||
static void gem_transmit(GemState *s)
|
static void gem_transmit(CadenceGEMState *s)
|
||||||
{
|
{
|
||||||
unsigned desc[2];
|
unsigned desc[2];
|
||||||
hwaddr packet_desc_addr;
|
hwaddr packet_desc_addr;
|
||||||
|
@ -976,7 +935,7 @@ static void gem_transmit(GemState *s)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void gem_phy_reset(GemState *s)
|
static void gem_phy_reset(CadenceGEMState *s)
|
||||||
{
|
{
|
||||||
memset(&s->phy_regs[0], 0, sizeof(s->phy_regs));
|
memset(&s->phy_regs[0], 0, sizeof(s->phy_regs));
|
||||||
s->phy_regs[PHY_REG_CONTROL] = 0x1140;
|
s->phy_regs[PHY_REG_CONTROL] = 0x1140;
|
||||||
|
@ -1004,7 +963,7 @@ static void gem_phy_reset(GemState *s)
|
||||||
static void gem_reset(DeviceState *d)
|
static void gem_reset(DeviceState *d)
|
||||||
{
|
{
|
||||||
int i;
|
int i;
|
||||||
GemState *s = GEM(d);
|
CadenceGEMState *s = CADENCE_GEM(d);
|
||||||
|
|
||||||
DB_PRINT("\n");
|
DB_PRINT("\n");
|
||||||
|
|
||||||
|
@ -1032,13 +991,13 @@ static void gem_reset(DeviceState *d)
|
||||||
gem_update_int_status(s);
|
gem_update_int_status(s);
|
||||||
}
|
}
|
||||||
|
|
||||||
static uint16_t gem_phy_read(GemState *s, unsigned reg_num)
|
static uint16_t gem_phy_read(CadenceGEMState *s, unsigned reg_num)
|
||||||
{
|
{
|
||||||
DB_PRINT("reg: %d value: 0x%04x\n", reg_num, s->phy_regs[reg_num]);
|
DB_PRINT("reg: %d value: 0x%04x\n", reg_num, s->phy_regs[reg_num]);
|
||||||
return s->phy_regs[reg_num];
|
return s->phy_regs[reg_num];
|
||||||
}
|
}
|
||||||
|
|
||||||
static void gem_phy_write(GemState *s, unsigned reg_num, uint16_t val)
|
static void gem_phy_write(CadenceGEMState *s, unsigned reg_num, uint16_t val)
|
||||||
{
|
{
|
||||||
DB_PRINT("reg: %d value: 0x%04x\n", reg_num, val);
|
DB_PRINT("reg: %d value: 0x%04x\n", reg_num, val);
|
||||||
|
|
||||||
|
@ -1072,10 +1031,10 @@ static void gem_phy_write(GemState *s, unsigned reg_num, uint16_t val)
|
||||||
*/
|
*/
|
||||||
static uint64_t gem_read(void *opaque, hwaddr offset, unsigned size)
|
static uint64_t gem_read(void *opaque, hwaddr offset, unsigned size)
|
||||||
{
|
{
|
||||||
GemState *s;
|
CadenceGEMState *s;
|
||||||
uint32_t retval;
|
uint32_t retval;
|
||||||
|
|
||||||
s = (GemState *)opaque;
|
s = (CadenceGEMState *)opaque;
|
||||||
|
|
||||||
offset >>= 2;
|
offset >>= 2;
|
||||||
retval = s->regs[offset];
|
retval = s->regs[offset];
|
||||||
|
@ -1120,7 +1079,7 @@ static uint64_t gem_read(void *opaque, hwaddr offset, unsigned size)
|
||||||
static void gem_write(void *opaque, hwaddr offset, uint64_t val,
|
static void gem_write(void *opaque, hwaddr offset, uint64_t val,
|
||||||
unsigned size)
|
unsigned size)
|
||||||
{
|
{
|
||||||
GemState *s = (GemState *)opaque;
|
CadenceGEMState *s = (CadenceGEMState *)opaque;
|
||||||
uint32_t readonly;
|
uint32_t readonly;
|
||||||
|
|
||||||
DB_PRINT("offset: 0x%04x write: 0x%08x ", (unsigned)offset, (unsigned)val);
|
DB_PRINT("offset: 0x%04x write: 0x%08x ", (unsigned)offset, (unsigned)val);
|
||||||
|
@ -1226,7 +1185,7 @@ static NetClientInfo net_gem_info = {
|
||||||
static int gem_init(SysBusDevice *sbd)
|
static int gem_init(SysBusDevice *sbd)
|
||||||
{
|
{
|
||||||
DeviceState *dev = DEVICE(sbd);
|
DeviceState *dev = DEVICE(sbd);
|
||||||
GemState *s = GEM(dev);
|
CadenceGEMState *s = CADENCE_GEM(dev);
|
||||||
|
|
||||||
DB_PRINT("\n");
|
DB_PRINT("\n");
|
||||||
|
|
||||||
|
@ -1248,18 +1207,18 @@ static const VMStateDescription vmstate_cadence_gem = {
|
||||||
.version_id = 2,
|
.version_id = 2,
|
||||||
.minimum_version_id = 2,
|
.minimum_version_id = 2,
|
||||||
.fields = (VMStateField[]) {
|
.fields = (VMStateField[]) {
|
||||||
VMSTATE_UINT32_ARRAY(regs, GemState, GEM_MAXREG),
|
VMSTATE_UINT32_ARRAY(regs, CadenceGEMState, CADENCE_GEM_MAXREG),
|
||||||
VMSTATE_UINT16_ARRAY(phy_regs, GemState, 32),
|
VMSTATE_UINT16_ARRAY(phy_regs, CadenceGEMState, 32),
|
||||||
VMSTATE_UINT8(phy_loop, GemState),
|
VMSTATE_UINT8(phy_loop, CadenceGEMState),
|
||||||
VMSTATE_UINT32(rx_desc_addr, GemState),
|
VMSTATE_UINT32(rx_desc_addr, CadenceGEMState),
|
||||||
VMSTATE_UINT32(tx_desc_addr, GemState),
|
VMSTATE_UINT32(tx_desc_addr, CadenceGEMState),
|
||||||
VMSTATE_BOOL_ARRAY(sar_active, GemState, 4),
|
VMSTATE_BOOL_ARRAY(sar_active, CadenceGEMState, 4),
|
||||||
VMSTATE_END_OF_LIST(),
|
VMSTATE_END_OF_LIST(),
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
static Property gem_properties[] = {
|
static Property gem_properties[] = {
|
||||||
DEFINE_NIC_PROPERTIES(GemState, conf),
|
DEFINE_NIC_PROPERTIES(CadenceGEMState, conf),
|
||||||
DEFINE_PROP_END_OF_LIST(),
|
DEFINE_PROP_END_OF_LIST(),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -1277,7 +1236,7 @@ static void gem_class_init(ObjectClass *klass, void *data)
|
||||||
static const TypeInfo gem_info = {
|
static const TypeInfo gem_info = {
|
||||||
.name = TYPE_CADENCE_GEM,
|
.name = TYPE_CADENCE_GEM,
|
||||||
.parent = TYPE_SYS_BUS_DEVICE,
|
.parent = TYPE_SYS_BUS_DEVICE,
|
||||||
.instance_size = sizeof(GemState),
|
.instance_size = sizeof(CadenceGEMState),
|
||||||
.class_init = gem_class_init,
|
.class_init = gem_class_init,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
58
include/hw/arm/xlnx-zynqmp.h
Normal file
58
include/hw/arm/xlnx-zynqmp.h
Normal file
|
@ -0,0 +1,58 @@
|
||||||
|
/*
|
||||||
|
* Xilinx Zynq MPSoC emulation
|
||||||
|
*
|
||||||
|
* Copyright (C) 2015 Xilinx Inc
|
||||||
|
* Written by Peter Crosthwaite <peter.crosthwaite@xilinx.com>
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify it
|
||||||
|
* under the terms of the GNU General Public License as published by the
|
||||||
|
* Free Software Foundation; either version 2 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||||
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||||
|
* for more details.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef XLNX_ZYNQMP_H
|
||||||
|
|
||||||
|
#include "qemu-common.h"
|
||||||
|
#include "hw/arm/arm.h"
|
||||||
|
#include "hw/intc/arm_gic.h"
|
||||||
|
#include "hw/net/cadence_gem.h"
|
||||||
|
#include "hw/char/cadence_uart.h"
|
||||||
|
|
||||||
|
#define TYPE_XLNX_ZYNQMP "xlnx,zynqmp"
|
||||||
|
#define XLNX_ZYNQMP(obj) OBJECT_CHECK(XlnxZynqMPState, (obj), \
|
||||||
|
TYPE_XLNX_ZYNQMP)
|
||||||
|
|
||||||
|
#define XLNX_ZYNQMP_NUM_CPUS 4
|
||||||
|
#define XLNX_ZYNQMP_NUM_GEMS 4
|
||||||
|
#define XLNX_ZYNQMP_NUM_UARTS 2
|
||||||
|
|
||||||
|
#define XLNX_ZYNQMP_GIC_REGIONS 2
|
||||||
|
|
||||||
|
/* ZynqMP maps the ARM GIC regions (GICC, GICD ...) at consecutive 64k offsets
|
||||||
|
* and under-decodes the 64k region. This mirrors the 4k regions to every 4k
|
||||||
|
* aligned address in the 64k region. To implement each GIC region needs a
|
||||||
|
* number of memory region aliases.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#define XLNX_ZYNQMP_GIC_REGION_SIZE 0x4000
|
||||||
|
#define XLNX_ZYNQMP_GIC_ALIASES (0x10000 / XLNX_ZYNQMP_GIC_REGION_SIZE - 1)
|
||||||
|
|
||||||
|
typedef struct XlnxZynqMPState {
|
||||||
|
/*< private >*/
|
||||||
|
DeviceState parent_obj;
|
||||||
|
|
||||||
|
/*< public >*/
|
||||||
|
ARMCPU cpu[XLNX_ZYNQMP_NUM_CPUS];
|
||||||
|
GICState gic;
|
||||||
|
MemoryRegion gic_mr[XLNX_ZYNQMP_GIC_REGIONS][XLNX_ZYNQMP_GIC_ALIASES];
|
||||||
|
CadenceGEMState gem[XLNX_ZYNQMP_NUM_GEMS];
|
||||||
|
CadenceUARTState uart[XLNX_ZYNQMP_NUM_UARTS];
|
||||||
|
} XlnxZynqMPState;
|
||||||
|
|
||||||
|
#define XLNX_ZYNQMP_H
|
||||||
|
#endif
|
53
include/hw/char/cadence_uart.h
Normal file
53
include/hw/char/cadence_uart.h
Normal file
|
@ -0,0 +1,53 @@
|
||||||
|
/*
|
||||||
|
* Device model for Cadence UART
|
||||||
|
*
|
||||||
|
* Copyright (c) 2010 Xilinx Inc.
|
||||||
|
* Copyright (c) 2012 Peter A.G. Crosthwaite (peter.crosthwaite@petalogix.com)
|
||||||
|
* Copyright (c) 2012 PetaLogix Pty Ltd.
|
||||||
|
* Written by Haibing Ma
|
||||||
|
* M.Habib
|
||||||
|
*
|
||||||
|
* 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.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License along
|
||||||
|
* with this program; if not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef CADENCE_UART_H
|
||||||
|
|
||||||
|
#include "hw/sysbus.h"
|
||||||
|
#include "sysemu/char.h"
|
||||||
|
#include "qemu/timer.h"
|
||||||
|
|
||||||
|
#define CADENCE_UART_RX_FIFO_SIZE 16
|
||||||
|
#define CADENCE_UART_TX_FIFO_SIZE 16
|
||||||
|
|
||||||
|
#define CADENCE_UART_R_MAX (0x48/4)
|
||||||
|
|
||||||
|
#define TYPE_CADENCE_UART "cadence_uart"
|
||||||
|
#define CADENCE_UART(obj) OBJECT_CHECK(CadenceUARTState, (obj), \
|
||||||
|
TYPE_CADENCE_UART)
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
/*< private >*/
|
||||||
|
SysBusDevice parent_obj;
|
||||||
|
|
||||||
|
/*< public >*/
|
||||||
|
MemoryRegion iomem;
|
||||||
|
uint32_t r[CADENCE_UART_R_MAX];
|
||||||
|
uint8_t rx_fifo[CADENCE_UART_RX_FIFO_SIZE];
|
||||||
|
uint8_t tx_fifo[CADENCE_UART_TX_FIFO_SIZE];
|
||||||
|
uint32_t rx_wpos;
|
||||||
|
uint32_t rx_count;
|
||||||
|
uint32_t tx_count;
|
||||||
|
uint64_t char_tx_time;
|
||||||
|
CharDriverState *chr;
|
||||||
|
qemu_irq irq;
|
||||||
|
QEMUTimer *fifo_trigger_handle;
|
||||||
|
} CadenceUARTState;
|
||||||
|
|
||||||
|
#define CADENCE_UART_H
|
||||||
|
#endif
|
73
include/hw/net/cadence_gem.h
Normal file
73
include/hw/net/cadence_gem.h
Normal file
|
@ -0,0 +1,73 @@
|
||||||
|
/*
|
||||||
|
* QEMU Cadence GEM emulation
|
||||||
|
*
|
||||||
|
* Copyright (c) 2011 Xilinx, Inc.
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
* in the Software without restriction, including without limitation the rights
|
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
* copies of the Software, and to permit persons to whom the Software is
|
||||||
|
* furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in
|
||||||
|
* all copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||||
|
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
* THE SOFTWARE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef CADENCE_GEM_H
|
||||||
|
|
||||||
|
#define TYPE_CADENCE_GEM "cadence_gem"
|
||||||
|
#define CADENCE_GEM(obj) OBJECT_CHECK(CadenceGEMState, (obj), TYPE_CADENCE_GEM)
|
||||||
|
|
||||||
|
#include "net/net.h"
|
||||||
|
#include "hw/sysbus.h"
|
||||||
|
|
||||||
|
#define CADENCE_GEM_MAXREG (0x00000640/4) /* Last valid GEM address */
|
||||||
|
|
||||||
|
typedef struct CadenceGEMState {
|
||||||
|
/*< private >*/
|
||||||
|
SysBusDevice parent_obj;
|
||||||
|
|
||||||
|
/*< public >*/
|
||||||
|
MemoryRegion iomem;
|
||||||
|
NICState *nic;
|
||||||
|
NICConf conf;
|
||||||
|
qemu_irq irq;
|
||||||
|
|
||||||
|
/* GEM registers backing store */
|
||||||
|
uint32_t regs[CADENCE_GEM_MAXREG];
|
||||||
|
/* Mask of register bits which are write only */
|
||||||
|
uint32_t regs_wo[CADENCE_GEM_MAXREG];
|
||||||
|
/* Mask of register bits which are read only */
|
||||||
|
uint32_t regs_ro[CADENCE_GEM_MAXREG];
|
||||||
|
/* Mask of register bits which are clear on read */
|
||||||
|
uint32_t regs_rtc[CADENCE_GEM_MAXREG];
|
||||||
|
/* Mask of register bits which are write 1 to clear */
|
||||||
|
uint32_t regs_w1c[CADENCE_GEM_MAXREG];
|
||||||
|
|
||||||
|
/* PHY registers backing store */
|
||||||
|
uint16_t phy_regs[32];
|
||||||
|
|
||||||
|
uint8_t phy_loop; /* Are we in phy loopback? */
|
||||||
|
|
||||||
|
/* The current DMA descriptor pointers */
|
||||||
|
uint32_t rx_desc_addr;
|
||||||
|
uint32_t tx_desc_addr;
|
||||||
|
|
||||||
|
uint8_t can_rx_state; /* Debug only */
|
||||||
|
|
||||||
|
unsigned rx_desc[2];
|
||||||
|
|
||||||
|
bool sar_active[4];
|
||||||
|
} CadenceGEMState;
|
||||||
|
|
||||||
|
#define CADENCE_GEM_H
|
||||||
|
#endif
|
|
@ -354,7 +354,7 @@
|
||||||
#define TARGET_NR_kexec_load (347)
|
#define TARGET_NR_kexec_load (347)
|
||||||
#define TARGET_NR_utimensat (348)
|
#define TARGET_NR_utimensat (348)
|
||||||
#define TARGET_NR_signalfd (349)
|
#define TARGET_NR_signalfd (349)
|
||||||
#define TARGET_NR_timerfd (350)
|
#define TARGET_NR_timerfd_create (350)
|
||||||
#define TARGET_NR_eventfd (351)
|
#define TARGET_NR_eventfd (351)
|
||||||
#define TARGET_NR_fallocate (352)
|
#define TARGET_NR_fallocate (352)
|
||||||
#define TARGET_NR_timerfd_settime (353)
|
#define TARGET_NR_timerfd_settime (353)
|
||||||
|
|
|
@ -38,22 +38,22 @@ static inline void unset_feature(CPUARMState *env, int feature)
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifndef CONFIG_USER_ONLY
|
#ifndef CONFIG_USER_ONLY
|
||||||
static uint64_t a57_l2ctlr_read(CPUARMState *env, const ARMCPRegInfo *ri)
|
static uint64_t a57_a53_l2ctlr_read(CPUARMState *env, const ARMCPRegInfo *ri)
|
||||||
{
|
{
|
||||||
/* Number of processors is in [25:24]; otherwise we RAZ */
|
/* Number of processors is in [25:24]; otherwise we RAZ */
|
||||||
return (smp_cpus - 1) << 24;
|
return (smp_cpus - 1) << 24;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
static const ARMCPRegInfo cortexa57_cp_reginfo[] = {
|
static const ARMCPRegInfo cortex_a57_a53_cp_reginfo[] = {
|
||||||
#ifndef CONFIG_USER_ONLY
|
#ifndef CONFIG_USER_ONLY
|
||||||
{ .name = "L2CTLR_EL1", .state = ARM_CP_STATE_AA64,
|
{ .name = "L2CTLR_EL1", .state = ARM_CP_STATE_AA64,
|
||||||
.opc0 = 3, .opc1 = 1, .crn = 11, .crm = 0, .opc2 = 2,
|
.opc0 = 3, .opc1 = 1, .crn = 11, .crm = 0, .opc2 = 2,
|
||||||
.access = PL1_RW, .readfn = a57_l2ctlr_read,
|
.access = PL1_RW, .readfn = a57_a53_l2ctlr_read,
|
||||||
.writefn = arm_cp_write_ignore },
|
.writefn = arm_cp_write_ignore },
|
||||||
{ .name = "L2CTLR",
|
{ .name = "L2CTLR",
|
||||||
.cp = 15, .opc1 = 1, .crn = 9, .crm = 0, .opc2 = 2,
|
.cp = 15, .opc1 = 1, .crn = 9, .crm = 0, .opc2 = 2,
|
||||||
.access = PL1_RW, .readfn = a57_l2ctlr_read,
|
.access = PL1_RW, .readfn = a57_a53_l2ctlr_read,
|
||||||
.writefn = arm_cp_write_ignore },
|
.writefn = arm_cp_write_ignore },
|
||||||
#endif
|
#endif
|
||||||
{ .name = "L2ECTLR_EL1", .state = ARM_CP_STATE_AA64,
|
{ .name = "L2ECTLR_EL1", .state = ARM_CP_STATE_AA64,
|
||||||
|
@ -140,7 +140,57 @@ static void aarch64_a57_initfn(Object *obj)
|
||||||
cpu->ccsidr[1] = 0x201fe012; /* 48KB L1 icache */
|
cpu->ccsidr[1] = 0x201fe012; /* 48KB L1 icache */
|
||||||
cpu->ccsidr[2] = 0x70ffe07a; /* 2048KB L2 cache */
|
cpu->ccsidr[2] = 0x70ffe07a; /* 2048KB L2 cache */
|
||||||
cpu->dcz_blocksize = 4; /* 64 bytes */
|
cpu->dcz_blocksize = 4; /* 64 bytes */
|
||||||
define_arm_cp_regs(cpu, cortexa57_cp_reginfo);
|
define_arm_cp_regs(cpu, cortex_a57_a53_cp_reginfo);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void aarch64_a53_initfn(Object *obj)
|
||||||
|
{
|
||||||
|
ARMCPU *cpu = ARM_CPU(obj);
|
||||||
|
|
||||||
|
cpu->dtb_compatible = "arm,cortex-a53";
|
||||||
|
set_feature(&cpu->env, ARM_FEATURE_V8);
|
||||||
|
set_feature(&cpu->env, ARM_FEATURE_VFP4);
|
||||||
|
set_feature(&cpu->env, ARM_FEATURE_NEON);
|
||||||
|
set_feature(&cpu->env, ARM_FEATURE_GENERIC_TIMER);
|
||||||
|
set_feature(&cpu->env, ARM_FEATURE_AARCH64);
|
||||||
|
set_feature(&cpu->env, ARM_FEATURE_CBAR_RO);
|
||||||
|
set_feature(&cpu->env, ARM_FEATURE_V8_AES);
|
||||||
|
set_feature(&cpu->env, ARM_FEATURE_V8_SHA1);
|
||||||
|
set_feature(&cpu->env, ARM_FEATURE_V8_SHA256);
|
||||||
|
set_feature(&cpu->env, ARM_FEATURE_V8_PMULL);
|
||||||
|
set_feature(&cpu->env, ARM_FEATURE_CRC);
|
||||||
|
cpu->midr = 0x410fd034;
|
||||||
|
cpu->reset_fpsid = 0x41034070;
|
||||||
|
cpu->mvfr0 = 0x10110222;
|
||||||
|
cpu->mvfr1 = 0x12111111;
|
||||||
|
cpu->mvfr2 = 0x00000043;
|
||||||
|
cpu->ctr = 0x84448004; /* L1Ip = VIPT */
|
||||||
|
cpu->reset_sctlr = 0x00c50838;
|
||||||
|
cpu->id_pfr0 = 0x00000131;
|
||||||
|
cpu->id_pfr1 = 0x00011011;
|
||||||
|
cpu->id_dfr0 = 0x03010066;
|
||||||
|
cpu->id_afr0 = 0x00000000;
|
||||||
|
cpu->id_mmfr0 = 0x10101105;
|
||||||
|
cpu->id_mmfr1 = 0x40000000;
|
||||||
|
cpu->id_mmfr2 = 0x01260000;
|
||||||
|
cpu->id_mmfr3 = 0x02102211;
|
||||||
|
cpu->id_isar0 = 0x02101110;
|
||||||
|
cpu->id_isar1 = 0x13112111;
|
||||||
|
cpu->id_isar2 = 0x21232042;
|
||||||
|
cpu->id_isar3 = 0x01112131;
|
||||||
|
cpu->id_isar4 = 0x00011142;
|
||||||
|
cpu->id_isar5 = 0x00011121;
|
||||||
|
cpu->id_aa64pfr0 = 0x00002222;
|
||||||
|
cpu->id_aa64dfr0 = 0x10305106;
|
||||||
|
cpu->id_aa64isar0 = 0x00011120;
|
||||||
|
cpu->id_aa64mmfr0 = 0x00001122; /* 40 bit physical addr */
|
||||||
|
cpu->dbgdidr = 0x3516d000;
|
||||||
|
cpu->clidr = 0x0a200023;
|
||||||
|
cpu->ccsidr[0] = 0x700fe01a; /* 32KB L1 dcache */
|
||||||
|
cpu->ccsidr[1] = 0x201fe00a; /* 32KB L1 icache */
|
||||||
|
cpu->ccsidr[2] = 0x707fe07a; /* 1024KB L2 cache */
|
||||||
|
cpu->dcz_blocksize = 4; /* 64 bytes */
|
||||||
|
define_arm_cp_regs(cpu, cortex_a57_a53_cp_reginfo);
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef CONFIG_USER_ONLY
|
#ifdef CONFIG_USER_ONLY
|
||||||
|
@ -170,6 +220,7 @@ typedef struct ARMCPUInfo {
|
||||||
|
|
||||||
static const ARMCPUInfo aarch64_cpus[] = {
|
static const ARMCPUInfo aarch64_cpus[] = {
|
||||||
{ .name = "cortex-a57", .initfn = aarch64_a57_initfn },
|
{ .name = "cortex-a57", .initfn = aarch64_a57_initfn },
|
||||||
|
{ .name = "cortex-a53", .initfn = aarch64_a53_initfn },
|
||||||
#ifdef CONFIG_USER_ONLY
|
#ifdef CONFIG_USER_ONLY
|
||||||
{ .name = "any", .initfn = aarch64_any_initfn },
|
{ .name = "any", .initfn = aarch64_any_initfn },
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -1249,7 +1249,7 @@ static void gt_tval_write(CPUARMState *env, const ARMCPRegInfo *ri,
|
||||||
int timeridx = ri->crm & 1;
|
int timeridx = ri->crm & 1;
|
||||||
|
|
||||||
env->cp15.c14_timer[timeridx].cval = gt_get_countervalue(env) +
|
env->cp15.c14_timer[timeridx].cval = gt_get_countervalue(env) +
|
||||||
+ sextract64(value, 0, 32);
|
sextract64(value, 0, 32);
|
||||||
gt_recalc_timer(arm_env_get_cpu(env), timeridx);
|
gt_recalc_timer(arm_env_get_cpu(env), timeridx);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1353,6 +1353,7 @@ static const ARMCPRegInfo generic_timer_cp_reginfo[] = {
|
||||||
{ .name = "CNTP_TVAL_EL0", .state = ARM_CP_STATE_AA64,
|
{ .name = "CNTP_TVAL_EL0", .state = ARM_CP_STATE_AA64,
|
||||||
.opc0 = 3, .opc1 = 3, .crn = 14, .crm = 2, .opc2 = 0,
|
.opc0 = 3, .opc1 = 3, .crn = 14, .crm = 2, .opc2 = 0,
|
||||||
.type = ARM_CP_NO_RAW | ARM_CP_IO, .access = PL1_RW | PL0_R,
|
.type = ARM_CP_NO_RAW | ARM_CP_IO, .access = PL1_RW | PL0_R,
|
||||||
|
.accessfn = gt_ptimer_access,
|
||||||
.readfn = gt_tval_read, .writefn = gt_tval_write,
|
.readfn = gt_tval_read, .writefn = gt_tval_write,
|
||||||
},
|
},
|
||||||
{ .name = "CNTV_TVAL", .cp = 15, .crn = 14, .crm = 3, .opc1 = 0, .opc2 = 0,
|
{ .name = "CNTV_TVAL", .cp = 15, .crn = 14, .crm = 3, .opc1 = 0, .opc2 = 0,
|
||||||
|
@ -1363,6 +1364,7 @@ static const ARMCPRegInfo generic_timer_cp_reginfo[] = {
|
||||||
{ .name = "CNTV_TVAL_EL0", .state = ARM_CP_STATE_AA64,
|
{ .name = "CNTV_TVAL_EL0", .state = ARM_CP_STATE_AA64,
|
||||||
.opc0 = 3, .opc1 = 3, .crn = 14, .crm = 3, .opc2 = 0,
|
.opc0 = 3, .opc1 = 3, .crn = 14, .crm = 3, .opc2 = 0,
|
||||||
.type = ARM_CP_NO_RAW | ARM_CP_IO, .access = PL1_RW | PL0_R,
|
.type = ARM_CP_NO_RAW | ARM_CP_IO, .access = PL1_RW | PL0_R,
|
||||||
|
.accessfn = gt_vtimer_access,
|
||||||
.readfn = gt_tval_read, .writefn = gt_tval_write,
|
.readfn = gt_tval_read, .writefn = gt_tval_write,
|
||||||
},
|
},
|
||||||
/* The counter itself */
|
/* The counter itself */
|
||||||
|
@ -1401,7 +1403,7 @@ static const ARMCPRegInfo generic_timer_cp_reginfo[] = {
|
||||||
.access = PL1_RW | PL0_R,
|
.access = PL1_RW | PL0_R,
|
||||||
.type = ARM_CP_IO,
|
.type = ARM_CP_IO,
|
||||||
.fieldoffset = offsetof(CPUARMState, cp15.c14_timer[GTIMER_PHYS].cval),
|
.fieldoffset = offsetof(CPUARMState, cp15.c14_timer[GTIMER_PHYS].cval),
|
||||||
.resetvalue = 0, .accessfn = gt_vtimer_access,
|
.resetvalue = 0, .accessfn = gt_ptimer_access,
|
||||||
.writefn = gt_cval_write, .raw_writefn = raw_write,
|
.writefn = gt_cval_write, .raw_writefn = raw_write,
|
||||||
},
|
},
|
||||||
{ .name = "CNTV_CVAL", .cp = 15, .crm = 14, .opc1 = 3,
|
{ .name = "CNTV_CVAL", .cp = 15, .crm = 14, .opc1 = 3,
|
||||||
|
@ -4913,6 +4915,21 @@ static inline TCR *regime_tcr(CPUARMState *env, ARMMMUIdx mmu_idx)
|
||||||
return &env->cp15.tcr_el[regime_el(env, mmu_idx)];
|
return &env->cp15.tcr_el[regime_el(env, mmu_idx)];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Return the TTBR associated with this translation regime */
|
||||||
|
static inline uint64_t regime_ttbr(CPUARMState *env, ARMMMUIdx mmu_idx,
|
||||||
|
int ttbrn)
|
||||||
|
{
|
||||||
|
if (mmu_idx == ARMMMUIdx_S2NS) {
|
||||||
|
/* TODO: return VTTBR_EL2 */
|
||||||
|
g_assert_not_reached();
|
||||||
|
}
|
||||||
|
if (ttbrn == 0) {
|
||||||
|
return env->cp15.ttbr0_el[regime_el(env, mmu_idx)];
|
||||||
|
} else {
|
||||||
|
return env->cp15.ttbr1_el[regime_el(env, mmu_idx)];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/* Return true if the translation regime is using LPAE format page tables */
|
/* Return true if the translation regime is using LPAE format page tables */
|
||||||
static inline bool regime_using_lpae_format(CPUARMState *env,
|
static inline bool regime_using_lpae_format(CPUARMState *env,
|
||||||
ARMMMUIdx mmu_idx)
|
ARMMMUIdx mmu_idx)
|
||||||
|
@ -5111,7 +5128,6 @@ static bool get_level1_table_address(CPUARMState *env, ARMMMUIdx mmu_idx,
|
||||||
uint32_t *table, uint32_t address)
|
uint32_t *table, uint32_t address)
|
||||||
{
|
{
|
||||||
/* Note that we can only get here for an AArch32 PL0/PL1 lookup */
|
/* Note that we can only get here for an AArch32 PL0/PL1 lookup */
|
||||||
int el = regime_el(env, mmu_idx);
|
|
||||||
TCR *tcr = regime_tcr(env, mmu_idx);
|
TCR *tcr = regime_tcr(env, mmu_idx);
|
||||||
|
|
||||||
if (address & tcr->mask) {
|
if (address & tcr->mask) {
|
||||||
|
@ -5119,13 +5135,13 @@ static bool get_level1_table_address(CPUARMState *env, ARMMMUIdx mmu_idx,
|
||||||
/* Translation table walk disabled for TTBR1 */
|
/* Translation table walk disabled for TTBR1 */
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
*table = env->cp15.ttbr1_el[el] & 0xffffc000;
|
*table = regime_ttbr(env, mmu_idx, 1) & 0xffffc000;
|
||||||
} else {
|
} else {
|
||||||
if (tcr->raw_tcr & TTBCR_PD0) {
|
if (tcr->raw_tcr & TTBCR_PD0) {
|
||||||
/* Translation table walk disabled for TTBR0 */
|
/* Translation table walk disabled for TTBR0 */
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
*table = env->cp15.ttbr0_el[el] & tcr->base_mask;
|
*table = regime_ttbr(env, mmu_idx, 0) & tcr->base_mask;
|
||||||
}
|
}
|
||||||
*table |= (address >> 18) & 0x3ffc;
|
*table |= (address >> 18) & 0x3ffc;
|
||||||
return true;
|
return true;
|
||||||
|
@ -5431,21 +5447,34 @@ static int get_phys_addr_lpae(CPUARMState *env, target_ulong address,
|
||||||
int32_t tbi = 0;
|
int32_t tbi = 0;
|
||||||
TCR *tcr = regime_tcr(env, mmu_idx);
|
TCR *tcr = regime_tcr(env, mmu_idx);
|
||||||
int ap, ns, xn, pxn;
|
int ap, ns, xn, pxn;
|
||||||
|
uint32_t el = regime_el(env, mmu_idx);
|
||||||
|
bool ttbr1_valid = true;
|
||||||
|
|
||||||
/* TODO:
|
/* TODO:
|
||||||
* This code assumes we're either a 64-bit EL1 or a 32-bit PL1;
|
* This code does not handle the different format TCR for VTCR_EL2.
|
||||||
* it doesn't handle the different format TCR for TCR_EL2, TCR_EL3,
|
* This code also does not support shareability levels.
|
||||||
* and VTCR_EL2, or the fact that those regimes don't have a split
|
* Attribute and permission bit handling should also be checked when adding
|
||||||
* TTBR0/TTBR1. Attribute and permission bit handling should also
|
* support for those page table walks.
|
||||||
* be checked when adding support for those page table walks.
|
|
||||||
*/
|
*/
|
||||||
if (arm_el_is_aa64(env, regime_el(env, mmu_idx))) {
|
if (arm_el_is_aa64(env, el)) {
|
||||||
va_size = 64;
|
va_size = 64;
|
||||||
if (extract64(address, 55, 1))
|
if (el > 1) {
|
||||||
tbi = extract64(tcr->raw_tcr, 38, 1);
|
tbi = extract64(tcr->raw_tcr, 20, 1);
|
||||||
else
|
} else {
|
||||||
tbi = extract64(tcr->raw_tcr, 37, 1);
|
if (extract64(address, 55, 1)) {
|
||||||
|
tbi = extract64(tcr->raw_tcr, 38, 1);
|
||||||
|
} else {
|
||||||
|
tbi = extract64(tcr->raw_tcr, 37, 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
tbi *= 8;
|
tbi *= 8;
|
||||||
|
|
||||||
|
/* If we are in 64-bit EL2 or EL3 then there is no TTBR1, so mark it
|
||||||
|
* invalid.
|
||||||
|
*/
|
||||||
|
if (el > 1) {
|
||||||
|
ttbr1_valid = false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Determine whether this address is in the region controlled by
|
/* Determine whether this address is in the region controlled by
|
||||||
|
@ -5466,13 +5495,14 @@ static int get_phys_addr_lpae(CPUARMState *env, target_ulong address,
|
||||||
if (t0sz && !extract64(address, va_size - t0sz, t0sz - tbi)) {
|
if (t0sz && !extract64(address, va_size - t0sz, t0sz - tbi)) {
|
||||||
/* there is a ttbr0 region and we are in it (high bits all zero) */
|
/* there is a ttbr0 region and we are in it (high bits all zero) */
|
||||||
ttbr_select = 0;
|
ttbr_select = 0;
|
||||||
} else if (t1sz && !extract64(~address, va_size - t1sz, t1sz - tbi)) {
|
} else if (ttbr1_valid && t1sz &&
|
||||||
|
!extract64(~address, va_size - t1sz, t1sz - tbi)) {
|
||||||
/* there is a ttbr1 region and we are in it (high bits all one) */
|
/* there is a ttbr1 region and we are in it (high bits all one) */
|
||||||
ttbr_select = 1;
|
ttbr_select = 1;
|
||||||
} else if (!t0sz) {
|
} else if (!t0sz) {
|
||||||
/* ttbr0 region is "everything not in the ttbr1 region" */
|
/* ttbr0 region is "everything not in the ttbr1 region" */
|
||||||
ttbr_select = 0;
|
ttbr_select = 0;
|
||||||
} else if (!t1sz) {
|
} else if (!t1sz && ttbr1_valid) {
|
||||||
/* ttbr1 region is "everything not in the ttbr0 region" */
|
/* ttbr1 region is "everything not in the ttbr0 region" */
|
||||||
ttbr_select = 1;
|
ttbr_select = 1;
|
||||||
} else {
|
} else {
|
||||||
|
@ -5489,7 +5519,7 @@ static int get_phys_addr_lpae(CPUARMState *env, target_ulong address,
|
||||||
* we will always flush the TLB any time the ASID is changed).
|
* we will always flush the TLB any time the ASID is changed).
|
||||||
*/
|
*/
|
||||||
if (ttbr_select == 0) {
|
if (ttbr_select == 0) {
|
||||||
ttbr = A32_BANKED_CURRENT_REG_GET(env, ttbr0);
|
ttbr = regime_ttbr(env, mmu_idx, 0);
|
||||||
epd = extract32(tcr->raw_tcr, 7, 1);
|
epd = extract32(tcr->raw_tcr, 7, 1);
|
||||||
tsz = t0sz;
|
tsz = t0sz;
|
||||||
|
|
||||||
|
@ -5501,7 +5531,10 @@ static int get_phys_addr_lpae(CPUARMState *env, target_ulong address,
|
||||||
granule_sz = 11;
|
granule_sz = 11;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
ttbr = A32_BANKED_CURRENT_REG_GET(env, ttbr1);
|
/* We should only be here if TTBR1 is valid */
|
||||||
|
assert(ttbr1_valid);
|
||||||
|
|
||||||
|
ttbr = regime_ttbr(env, mmu_idx, 1);
|
||||||
epd = extract32(tcr->raw_tcr, 23, 1);
|
epd = extract32(tcr->raw_tcr, 23, 1);
|
||||||
tsz = t1sz;
|
tsz = t1sz;
|
||||||
|
|
||||||
|
@ -5519,7 +5552,9 @@ static int get_phys_addr_lpae(CPUARMState *env, target_ulong address,
|
||||||
*/
|
*/
|
||||||
|
|
||||||
if (epd) {
|
if (epd) {
|
||||||
/* Translation table walk disabled => Translation fault on TLB miss */
|
/* Translation table walk disabled => Translation fault on TLB miss
|
||||||
|
* Note: This is always 0 on 64-bit EL2 and EL3.
|
||||||
|
*/
|
||||||
goto do_fault;
|
goto do_fault;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -347,6 +347,12 @@ static inline uint32_t syn_breakpoint(int same_el)
|
||||||
| ARM_EL_IL | 0x22;
|
| ARM_EL_IL | 0x22;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static inline uint32_t syn_wfx(int cv, int cond, int ti)
|
||||||
|
{
|
||||||
|
return (EC_WFX_TRAP << ARM_EL_EC_SHIFT) |
|
||||||
|
(cv << 24) | (cond << 20) | ti;
|
||||||
|
}
|
||||||
|
|
||||||
/* Update a QEMU watchpoint based on the information the guest has set in the
|
/* Update a QEMU watchpoint based on the information the guest has set in the
|
||||||
* DBGWCR<n>_EL1 and DBGWVR<n>_EL1 registers.
|
* DBGWCR<n>_EL1 and DBGWVR<n>_EL1 registers.
|
||||||
*/
|
*/
|
||||||
|
|
Loading…
Reference in a new issue