aboutsummaryrefslogtreecommitdiff
path: root/hw
diff options
context:
space:
mode:
authorRichard Henderson <richard.henderson@linaro.org>2024-07-01 10:41:45 -0700
committerRichard Henderson <richard.henderson@linaro.org>2024-07-01 10:41:45 -0700
commitc80a339587fe4148292c260716482dd2f86d4476 (patch)
treed98724b8d23d91da62f2da44905f0dc5ee2cf031 /hw
parent1152a0414944f03231f3177207d379d58125890e (diff)
parent58c782de557beb496bfb4c5ade721bbbd2480c72 (diff)
downloadqemu-c80a339587fe4148292c260716482dd2f86d4476.zip
qemu-c80a339587fe4148292c260716482dd2f86d4476.tar.gz
qemu-c80a339587fe4148292c260716482dd2f86d4476.tar.bz2
Merge tag 'pull-target-arm-20240701' of https://git.linaro.org/people/pmaydell/qemu-arm into staging
target-arm queue: * tests/avocado: update firmware for sbsa-ref and use all cores * hw/arm/smmu-common: Replace smmu_iommu_mr with smmu_find_sdev * arm: Fix VCMLA Dd, Dn, Dm[idx] * arm: Fix SQDMULH (by element) with Q=0 * arm: Fix FJCVTZS vs flush-to-zero * arm: More conversion of A64 AdvSIMD to decodetree * arm: Enable FEAT_Debugv8p8 for -cpu max * MAINTAINERS: Update family name for Patrick Leis * hw/arm/xilinx_zynq: Add boot-mode property * docs/system/arm: Add a doc for zynq board * hw/misc: In STM32L4x5 EXTI, correct configurable interrupts * tests/qtest: fix minor issues in STM32L4x5 tests # -----BEGIN PGP SIGNATURE----- # # iQJNBAABCAA3FiEE4aXFk81BneKOgxXPPCUl7RQ2DN4FAmaC1BMZHHBldGVyLm1h # eWRlbGxAbGluYXJvLm9yZwAKCRA8JSXtFDYM3nDOEACCoewjO2FJ4RFXMSmgr0Zf # jxWliu7osw7oeG4ZNq1+xMiXeW0vyS54eW41TMki1f98N/yK8v55BM8kBBvDvZaz # R5DUXpN+MtwD9A62md3B2c4mFXHqk1UOGbKi4btbtFj4lS8pV51mPmApzBUr2iTj # w6dCLciLOt87NWgtLECXsZ3evn+VlTRc+Hmfp1M/C/Rf2Qx3zis/CFHGQsZLGwzG # 2WhTpU1BKeOfsQa1VbSX6un14d72/JATFZN3rSgMbOEbvsCEeP+rnkzX57ejGyxV # 4DUx69gEAqS5bOfkQHLwy82WsunD/oIgp+GpYaYgINHzh6UkEsPoymrHAaPgV1Vh # g0TaBtbv2p89RFY1C2W2Mi4ICQ14a+oIV9FPvDsOE8Wq+wDAy/ZxZs7G6flxqods # s4JvcMqB3kUNBZaMsFVXTKdqT1PufICS+gx0VsKdKDwXcOHwMS10nTlEOPzqvoBA # phAsEbjnjWVhf03XTfCus+l5NT96lswCzPcUovb3CitSc2A1KUye3TyzHnxIqmOt # Owcl+Oiso++cgYzr/BCveTAYKYoRZzVcq5jCl4bBUH/8sLrRDbT0cpFpcMk72eE9 # VhR00kbkDfL3nKrulLsG8FeUlisX5+oGb3G5AdPtU9sqJPJMmBGaF+KniI0wi7VN # 5teHq08upLMF5JAjiKzZIA== # =faXD # -----END PGP SIGNATURE----- # gpg: Signature made Mon 01 Jul 2024 09:06:43 AM PDT # gpg: using RSA key E1A5C593CD419DE28E8315CF3C2525ED14360CDE # gpg: issuer "peter.maydell@linaro.org" # gpg: Good signature from "Peter Maydell <peter.maydell@linaro.org>" [full] # gpg: aka "Peter Maydell <pmaydell@gmail.com>" [full] # gpg: aka "Peter Maydell <pmaydell@chiark.greenend.org.uk>" [full] # gpg: aka "Peter Maydell <peter@archaic.org.uk>" [unknown] * tag 'pull-target-arm-20240701' of https://git.linaro.org/people/pmaydell/qemu-arm: (29 commits) tests/qtest: Ensure STM32L4x5 EXTI state is correct at the end of QTests hw/misc: In STM32L4x5 EXTI, correct configurable interrupts tests/qtest: Fix STM32L4x5 SYSCFG irq line 15 state assumption docs/system/arm: Add a doc for zynq board hw/arm/xilinx_zynq: Add boot-mode property hw/misc/zynq_slcr: Add boot-mode property MAINTAINERS: Update my family name target/arm: Enable FEAT_Debugv8p8 for -cpu max target/arm: Move initialization of debug ID registers target/arm: Fix indentation target/arm: Delete dead code from disas_simd_indexed target/arm: Convert FCMLA to decodetree target/arm: Convert FCADD to decodetree target/arm: Add data argument to do_fp3_vector target/arm: Convert BFMMLA, SMMLA, UMMLA, USMMLA to decodetree target/arm: Convert BFMLALB, BFMLALT to decodetree target/arm: Convert BFDOT to decodetree target/arm: Convert SUDOT, USDOT to decodetree target/arm: Convert SDOT, UDOT to decodetree target/arm: Convert SQRDMLAH, SQRDMLSH to decodetree ... Signed-off-by: Richard Henderson <richard.henderson@linaro.org>
Diffstat (limited to 'hw')
-rw-r--r--hw/arm/bcm2835_peripherals.c15
-rw-r--r--hw/arm/smmu-common.c8
-rw-r--r--hw/arm/smmuv3.c12
-rw-r--r--hw/arm/xilinx_zynq.c31
-rw-r--r--hw/misc/bcm2835_property.c87
-rw-r--r--hw/misc/stm32l4x5_exti.c28
-rw-r--r--hw/misc/zynq_slcr.c22
-rw-r--r--hw/nvram/bcm2835_otp.c187
-rw-r--r--hw/nvram/meson.build1
9 files changed, 360 insertions, 31 deletions
diff --git a/hw/arm/bcm2835_peripherals.c b/hw/arm/bcm2835_peripherals.c
index 1695d8b..ac153a9 100644
--- a/hw/arm/bcm2835_peripherals.c
+++ b/hw/arm/bcm2835_peripherals.c
@@ -116,6 +116,10 @@ static void raspi_peripherals_base_init(Object *obj)
object_property_add_const_link(OBJECT(&s->fb), "dma-mr",
OBJECT(&s->gpu_bus_mr));
+ /* OTP */
+ object_initialize_child(obj, "bcm2835-otp", &s->otp,
+ TYPE_BCM2835_OTP);
+
/* Property channel */
object_initialize_child(obj, "property", &s->property,
TYPE_BCM2835_PROPERTY);
@@ -128,6 +132,8 @@ static void raspi_peripherals_base_init(Object *obj)
OBJECT(&s->fb));
object_property_add_const_link(OBJECT(&s->property), "dma-mr",
OBJECT(&s->gpu_bus_mr));
+ object_property_add_const_link(OBJECT(&s->property), "otp",
+ OBJECT(&s->otp));
/* Extended Mass Media Controller */
object_initialize_child(obj, "sdhci", &s->sdhci, TYPE_SYSBUS_SDHCI);
@@ -374,6 +380,14 @@ void bcm_soc_peripherals_common_realize(DeviceState *dev, Error **errp)
sysbus_connect_irq(SYS_BUS_DEVICE(&s->fb), 0,
qdev_get_gpio_in(DEVICE(&s->mboxes), MBOX_CHAN_FB));
+ /* OTP */
+ if (!sysbus_realize(SYS_BUS_DEVICE(&s->otp), errp)) {
+ return;
+ }
+
+ memory_region_add_subregion(&s->peri_mr, OTP_OFFSET,
+ sysbus_mmio_get_region(SYS_BUS_DEVICE(&s->otp), 0));
+
/* Property channel */
if (!sysbus_realize(SYS_BUS_DEVICE(&s->property), errp)) {
return;
@@ -500,7 +514,6 @@ void bcm_soc_peripherals_common_realize(DeviceState *dev, Error **errp)
create_unimp(s, &s->i2s, "bcm2835-i2s", I2S_OFFSET, 0x100);
create_unimp(s, &s->smi, "bcm2835-smi", SMI_OFFSET, 0x100);
create_unimp(s, &s->bscsl, "bcm2835-spis", BSC_SL_OFFSET, 0x100);
- create_unimp(s, &s->otp, "bcm2835-otp", OTP_OFFSET, 0x80);
create_unimp(s, &s->dbus, "bcm2835-dbus", DBUS_OFFSET, 0x8000);
create_unimp(s, &s->ave0, "bcm2835-ave0", AVE0_OFFSET, 0x8000);
create_unimp(s, &s->v3d, "bcm2835-v3d", V3D_OFFSET, 0x1000);
diff --git a/hw/arm/smmu-common.c b/hw/arm/smmu-common.c
index 1ce706b..b6601cc 100644
--- a/hw/arm/smmu-common.c
+++ b/hw/arm/smmu-common.c
@@ -620,20 +620,16 @@ static const PCIIOMMUOps smmu_ops = {
.get_address_space = smmu_find_add_as,
};
-IOMMUMemoryRegion *smmu_iommu_mr(SMMUState *s, uint32_t sid)
+SMMUDevice *smmu_find_sdev(SMMUState *s, uint32_t sid)
{
uint8_t bus_n, devfn;
SMMUPciBus *smmu_bus;
- SMMUDevice *smmu;
bus_n = PCI_BUS_NUM(sid);
smmu_bus = smmu_find_smmu_pcibus(s, bus_n);
if (smmu_bus) {
devfn = SMMU_PCI_DEVFN(sid);
- smmu = smmu_bus->pbdev[devfn];
- if (smmu) {
- return &smmu->iommu;
- }
+ return smmu_bus->pbdev[devfn];
}
return NULL;
}
diff --git a/hw/arm/smmuv3.c b/hw/arm/smmuv3.c
index 2d1e0d5..445e04d 100644
--- a/hw/arm/smmuv3.c
+++ b/hw/arm/smmuv3.c
@@ -1218,20 +1218,18 @@ static int smmuv3_cmdq_consume(SMMUv3State *s)
case SMMU_CMD_CFGI_STE:
{
uint32_t sid = CMD_SID(&cmd);
- IOMMUMemoryRegion *mr = smmu_iommu_mr(bs, sid);
- SMMUDevice *sdev;
+ SMMUDevice *sdev = smmu_find_sdev(bs, sid);
if (CMD_SSEC(&cmd)) {
cmd_error = SMMU_CERROR_ILL;
break;
}
- if (!mr) {
+ if (!sdev) {
break;
}
trace_smmuv3_cmdq_cfgi_ste(sid);
- sdev = container_of(mr, SMMUDevice, iommu);
smmuv3_flush_config(sdev);
break;
@@ -1260,20 +1258,18 @@ static int smmuv3_cmdq_consume(SMMUv3State *s)
case SMMU_CMD_CFGI_CD_ALL:
{
uint32_t sid = CMD_SID(&cmd);
- IOMMUMemoryRegion *mr = smmu_iommu_mr(bs, sid);
- SMMUDevice *sdev;
+ SMMUDevice *sdev = smmu_find_sdev(bs, sid);
if (CMD_SSEC(&cmd)) {
cmd_error = SMMU_CERROR_ILL;
break;
}
- if (!mr) {
+ if (!sdev) {
break;
}
trace_smmuv3_cmdq_cfgi_cd(sid);
- sdev = container_of(mr, SMMUDevice, iommu);
smmuv3_flush_config(sdev);
break;
}
diff --git a/hw/arm/xilinx_zynq.c b/hw/arm/xilinx_zynq.c
index c79661b..3c56b9a 100644
--- a/hw/arm/xilinx_zynq.c
+++ b/hw/arm/xilinx_zynq.c
@@ -38,6 +38,7 @@
#include "qom/object.h"
#include "exec/tswap.h"
#include "target/arm/cpu-qom.h"
+#include "qapi/visitor.h"
#define TYPE_ZYNQ_MACHINE MACHINE_TYPE_NAME("xilinx-zynq-a9")
OBJECT_DECLARE_SIMPLE_TYPE(ZynqMachineState, ZYNQ_MACHINE)
@@ -90,6 +91,7 @@ struct ZynqMachineState {
MachineState parent;
Clock *ps_clk;
ARMCPU *cpu[ZYNQ_MAX_CPUS];
+ uint8_t boot_mode;
};
static void zynq_write_board_setup(ARMCPU *cpu,
@@ -176,6 +178,27 @@ static inline int zynq_init_spi_flashes(uint32_t base_addr, qemu_irq irq,
return unit;
}
+static void zynq_set_boot_mode(Object *obj, const char *str,
+ Error **errp)
+{
+ ZynqMachineState *m = ZYNQ_MACHINE(obj);
+ uint8_t mode = 0;
+
+ if (!strncasecmp(str, "qspi", 4)) {
+ mode = 1;
+ } else if (!strncasecmp(str, "sd", 2)) {
+ mode = 5;
+ } else if (!strncasecmp(str, "nor", 3)) {
+ mode = 2;
+ } else if (!strncasecmp(str, "jtag", 4)) {
+ mode = 0;
+ } else {
+ error_setg(errp, "%s boot mode not supported", str);
+ return;
+ }
+ m->boot_mode = mode;
+}
+
static void zynq_init(MachineState *machine)
{
ZynqMachineState *zynq_machine = ZYNQ_MACHINE(machine);
@@ -241,6 +264,7 @@ static void zynq_init(MachineState *machine)
/* Create slcr, keep a pointer to connect clocks */
slcr = qdev_new("xilinx-zynq_slcr");
qdev_connect_clock_in(slcr, "ps_clk", zynq_machine->ps_clk);
+ qdev_prop_set_uint8(slcr, "boot-mode", zynq_machine->boot_mode);
sysbus_realize_and_unref(SYS_BUS_DEVICE(slcr), &error_fatal);
sysbus_mmio_map(SYS_BUS_DEVICE(slcr), 0, 0xF8000000);
@@ -373,6 +397,7 @@ static void zynq_machine_class_init(ObjectClass *oc, void *data)
NULL
};
MachineClass *mc = MACHINE_CLASS(oc);
+ ObjectProperty *prop;
mc->desc = "Xilinx Zynq Platform Baseboard for Cortex-A9";
mc->init = zynq_init;
mc->max_cpus = ZYNQ_MAX_CPUS;
@@ -380,6 +405,12 @@ static void zynq_machine_class_init(ObjectClass *oc, void *data)
mc->ignore_memory_transaction_failures = true;
mc->valid_cpu_types = valid_cpu_types;
mc->default_ram_id = "zynq.ext_ram";
+ prop = object_class_property_add_str(oc, "boot-mode", NULL,
+ zynq_set_boot_mode);
+ object_class_property_set_description(oc, "boot-mode",
+ "Supported boot modes:"
+ " jtag qspi sd nor");
+ object_property_set_default_str(prop, "qspi");
}
static const TypeInfo zynq_machine_type = {
diff --git a/hw/misc/bcm2835_property.c b/hw/misc/bcm2835_property.c
index bdd9a6b..63de3db 100644
--- a/hw/misc/bcm2835_property.c
+++ b/hw/misc/bcm2835_property.c
@@ -32,6 +32,7 @@ static void bcm2835_property_mbox_push(BCM2835PropertyState *s, uint32_t value)
uint32_t tmp;
int n;
uint32_t offset, length, color;
+ uint32_t start_num, number, otp_row;
/*
* Copy the current state of the framebuffer config; we will update
@@ -322,6 +323,89 @@ static void bcm2835_property_mbox_push(BCM2835PropertyState *s, uint32_t value)
0);
resplen = VCHI_BUSADDR_SIZE;
break;
+
+ /* Customer OTP */
+
+ case RPI_FWREQ_GET_CUSTOMER_OTP:
+ start_num = ldl_le_phys(&s->dma_as, value + 12);
+ number = ldl_le_phys(&s->dma_as, value + 16);
+
+ resplen = 8 + 4 * number;
+
+ for (n = start_num; n < start_num + number &&
+ n < BCM2835_OTP_CUSTOMER_OTP_LEN; n++) {
+ otp_row = bcm2835_otp_get_row(s->otp,
+ BCM2835_OTP_CUSTOMER_OTP + n);
+ stl_le_phys(&s->dma_as,
+ value + 20 + ((n - start_num) << 2), otp_row);
+ }
+ break;
+ case RPI_FWREQ_SET_CUSTOMER_OTP:
+ start_num = ldl_le_phys(&s->dma_as, value + 12);
+ number = ldl_le_phys(&s->dma_as, value + 16);
+
+ resplen = 4;
+
+ /* Magic numbers to permanently lock customer OTP */
+ if (start_num == BCM2835_OTP_LOCK_NUM1 &&
+ number == BCM2835_OTP_LOCK_NUM2) {
+ bcm2835_otp_set_row(s->otp,
+ BCM2835_OTP_ROW_32,
+ BCM2835_OTP_ROW_32_LOCK);
+ break;
+ }
+
+ /* If row 32 has the lock bit, don't allow further writes */
+ if (bcm2835_otp_get_row(s->otp, BCM2835_OTP_ROW_32) &
+ BCM2835_OTP_ROW_32_LOCK) {
+ break;
+ }
+
+ for (n = start_num; n < start_num + number &&
+ n < BCM2835_OTP_CUSTOMER_OTP_LEN; n++) {
+ otp_row = ldl_le_phys(&s->dma_as,
+ value + 20 + ((n - start_num) << 2));
+ bcm2835_otp_set_row(s->otp,
+ BCM2835_OTP_CUSTOMER_OTP + n, otp_row);
+ }
+ break;
+
+ /* Device-specific private key */
+
+ case RPI_FWREQ_GET_PRIVATE_KEY:
+ start_num = ldl_le_phys(&s->dma_as, value + 12);
+ number = ldl_le_phys(&s->dma_as, value + 16);
+
+ resplen = 8 + 4 * number;
+
+ for (n = start_num; n < start_num + number &&
+ n < BCM2835_OTP_PRIVATE_KEY_LEN; n++) {
+ otp_row = bcm2835_otp_get_row(s->otp,
+ BCM2835_OTP_PRIVATE_KEY + n);
+ stl_le_phys(&s->dma_as,
+ value + 20 + ((n - start_num) << 2), otp_row);
+ }
+ break;
+ case RPI_FWREQ_SET_PRIVATE_KEY:
+ start_num = ldl_le_phys(&s->dma_as, value + 12);
+ number = ldl_le_phys(&s->dma_as, value + 16);
+
+ resplen = 4;
+
+ /* If row 32 has the lock bit, don't allow further writes */
+ if (bcm2835_otp_get_row(s->otp, BCM2835_OTP_ROW_32) &
+ BCM2835_OTP_ROW_32_LOCK) {
+ break;
+ }
+
+ for (n = start_num; n < start_num + number &&
+ n < BCM2835_OTP_PRIVATE_KEY_LEN; n++) {
+ otp_row = ldl_le_phys(&s->dma_as,
+ value + 20 + ((n - start_num) << 2));
+ bcm2835_otp_set_row(s->otp,
+ BCM2835_OTP_PRIVATE_KEY + n, otp_row);
+ }
+ break;
default:
qemu_log_mask(LOG_UNIMP,
"bcm2835_property: unhandled tag 0x%08x\n", tag);
@@ -449,6 +533,9 @@ static void bcm2835_property_realize(DeviceState *dev, Error **errp)
s->dma_mr = MEMORY_REGION(obj);
address_space_init(&s->dma_as, s->dma_mr, TYPE_BCM2835_PROPERTY "-memory");
+ obj = object_property_get_link(OBJECT(dev), "otp", &error_abort);
+ s->otp = BCM2835_OTP(obj);
+
/* TODO: connect to MAC address of USB NIC device, once we emulate it */
qemu_macaddr_default_if_unset(&s->macaddr);
diff --git a/hw/misc/stm32l4x5_exti.c b/hw/misc/stm32l4x5_exti.c
index 495a000..6a2ec62 100644
--- a/hw/misc/stm32l4x5_exti.c
+++ b/hw/misc/stm32l4x5_exti.c
@@ -88,6 +88,7 @@ static void stm32l4x5_exti_reset_hold(Object *obj, ResetType type)
s->ftsr[bank] = 0x00000000;
s->swier[bank] = 0x00000000;
s->pr[bank] = 0x00000000;
+ s->irq_levels[bank] = 0x00000000;
}
}
@@ -102,27 +103,23 @@ static void stm32l4x5_exti_set_irq(void *opaque, int irq, int level)
/* Shift the value to enable access in x2 registers. */
irq %= EXTI_MAX_IRQ_PER_BANK;
+ if (level == extract32(s->irq_levels[bank], irq, 1)) {
+ /* No change in IRQ line state: do nothing */
+ return;
+ }
+ s->irq_levels[bank] = deposit32(s->irq_levels[bank], irq, 1, level);
+
/* If the interrupt is masked, pr won't be raised */
if (!extract32(s->imr[bank], irq, 1)) {
return;
}
- if (((1 << irq) & s->rtsr[bank]) && level) {
- /* Rising Edge */
- s->pr[bank] |= 1 << irq;
- qemu_irq_pulse(s->irq[oirq]);
- } else if (((1 << irq) & s->ftsr[bank]) && !level) {
- /* Falling Edge */
+ if ((level && extract32(s->rtsr[bank], irq, 1)) ||
+ (!level && extract32(s->ftsr[bank], irq, 1))) {
+
s->pr[bank] |= 1 << irq;
qemu_irq_pulse(s->irq[oirq]);
}
- /*
- * In the following situations :
- * - falling edge but rising trigger selected
- * - rising edge but falling trigger selected
- * - no trigger selected
- * No action is required
- */
}
static uint64_t stm32l4x5_exti_read(void *opaque, hwaddr addr,
@@ -255,8 +252,8 @@ static void stm32l4x5_exti_init(Object *obj)
static const VMStateDescription vmstate_stm32l4x5_exti = {
.name = TYPE_STM32L4X5_EXTI,
- .version_id = 1,
- .minimum_version_id = 1,
+ .version_id = 2,
+ .minimum_version_id = 2,
.fields = (VMStateField[]) {
VMSTATE_UINT32_ARRAY(imr, Stm32l4x5ExtiState, EXTI_NUM_REGISTER),
VMSTATE_UINT32_ARRAY(emr, Stm32l4x5ExtiState, EXTI_NUM_REGISTER),
@@ -264,6 +261,7 @@ static const VMStateDescription vmstate_stm32l4x5_exti = {
VMSTATE_UINT32_ARRAY(ftsr, Stm32l4x5ExtiState, EXTI_NUM_REGISTER),
VMSTATE_UINT32_ARRAY(swier, Stm32l4x5ExtiState, EXTI_NUM_REGISTER),
VMSTATE_UINT32_ARRAY(pr, Stm32l4x5ExtiState, EXTI_NUM_REGISTER),
+ VMSTATE_UINT32_ARRAY(irq_levels, Stm32l4x5ExtiState, EXTI_NUM_REGISTER),
VMSTATE_END_OF_LIST()
}
};
diff --git a/hw/misc/zynq_slcr.c b/hw/misc/zynq_slcr.c
index 3412ff0..ad814c3 100644
--- a/hw/misc/zynq_slcr.c
+++ b/hw/misc/zynq_slcr.c
@@ -24,6 +24,8 @@
#include "hw/registerfields.h"
#include "hw/qdev-clock.h"
#include "qom/object.h"
+#include "hw/qdev-properties.h"
+#include "qapi/error.h"
#ifndef ZYNQ_SLCR_ERR_DEBUG
#define ZYNQ_SLCR_ERR_DEBUG 0
@@ -121,6 +123,7 @@ REG32(RST_REASON, 0x250)
REG32(REBOOT_STATUS, 0x258)
REG32(BOOT_MODE, 0x25c)
+ FIELD(BOOT_MODE, BOOT_MODE, 0, 4)
REG32(APU_CTRL, 0x300)
REG32(WDT_CLK_SEL, 0x304)
@@ -195,6 +198,7 @@ struct ZynqSLCRState {
Clock *ps_clk;
Clock *uart0_ref_clk;
Clock *uart1_ref_clk;
+ uint8_t boot_mode;
};
/*
@@ -371,7 +375,7 @@ static void zynq_slcr_reset_init(Object *obj, ResetType type)
s->regs[R_FPGA_RST_CTRL] = 0x01F33F0F;
s->regs[R_RST_REASON] = 0x00000040;
- s->regs[R_BOOT_MODE] = 0x00000001;
+ s->regs[R_BOOT_MODE] = s->boot_mode & R_BOOT_MODE_BOOT_MODE_MASK;
/* 0x700 - 0x7D4 */
for (i = 0; i < 54; i++) {
@@ -588,6 +592,15 @@ static const ClockPortInitArray zynq_slcr_clocks = {
QDEV_CLOCK_END
};
+static void zynq_slcr_realize(DeviceState *dev, Error **errp)
+{
+ ZynqSLCRState *s = ZYNQ_SLCR(dev);
+
+ if (s->boot_mode > 0xF) {
+ error_setg(errp, "Invalid boot mode %d specified", s->boot_mode);
+ }
+}
+
static void zynq_slcr_init(Object *obj)
{
ZynqSLCRState *s = ZYNQ_SLCR(obj);
@@ -610,15 +623,22 @@ static const VMStateDescription vmstate_zynq_slcr = {
}
};
+static Property zynq_slcr_props[] = {
+ DEFINE_PROP_UINT8("boot-mode", ZynqSLCRState, boot_mode, 1),
+ DEFINE_PROP_END_OF_LIST(),
+};
+
static void zynq_slcr_class_init(ObjectClass *klass, void *data)
{
DeviceClass *dc = DEVICE_CLASS(klass);
ResettableClass *rc = RESETTABLE_CLASS(klass);
dc->vmsd = &vmstate_zynq_slcr;
+ dc->realize = zynq_slcr_realize;
rc->phases.enter = zynq_slcr_reset_init;
rc->phases.hold = zynq_slcr_reset_hold;
rc->phases.exit = zynq_slcr_reset_exit;
+ device_class_set_props(dc, zynq_slcr_props);
}
static const TypeInfo zynq_slcr_info = {
diff --git a/hw/nvram/bcm2835_otp.c b/hw/nvram/bcm2835_otp.c
new file mode 100644
index 0000000..c4aed28
--- /dev/null
+++ b/hw/nvram/bcm2835_otp.c
@@ -0,0 +1,187 @@
+/*
+ * BCM2835 One-Time Programmable (OTP) Memory
+ *
+ * The OTP implementation is mostly a stub except for the OTP rows
+ * which are accessed directly by other peripherals such as the mailbox.
+ *
+ * The OTP registers are unimplemented due to lack of documentation.
+ *
+ * Copyright (c) 2024 Rayhan Faizel <rayhan.faizel@gmail.com>
+ *
+ * SPDX-License-Identifier: MIT
+ */
+
+#include "qemu/osdep.h"
+#include "qemu/log.h"
+#include "hw/nvram/bcm2835_otp.h"
+#include "migration/vmstate.h"
+
+/* OTP rows are 1-indexed */
+uint32_t bcm2835_otp_get_row(BCM2835OTPState *s, unsigned int row)
+{
+ assert(row <= BCM2835_OTP_ROW_COUNT && row >= 1);
+
+ return s->otp_rows[row - 1];
+}
+
+void bcm2835_otp_set_row(BCM2835OTPState *s, unsigned int row,
+ uint32_t value)
+{
+ assert(row <= BCM2835_OTP_ROW_COUNT && row >= 1);
+
+ /* Real OTP rows work as e-fuses */
+ s->otp_rows[row - 1] |= value;
+}
+
+static uint64_t bcm2835_otp_read(void *opaque, hwaddr addr, unsigned size)
+{
+ switch (addr) {
+ case BCM2835_OTP_BOOTMODE_REG:
+ qemu_log_mask(LOG_UNIMP,
+ "bcm2835_otp: BCM2835_OTP_BOOTMODE_REG\n");
+ break;
+ case BCM2835_OTP_CONFIG_REG:
+ qemu_log_mask(LOG_UNIMP,
+ "bcm2835_otp: BCM2835_OTP_CONFIG_REG\n");
+ break;
+ case BCM2835_OTP_CTRL_LO_REG:
+ qemu_log_mask(LOG_UNIMP,
+ "bcm2835_otp: BCM2835_OTP_CTRL_LO_REG\n");
+ break;
+ case BCM2835_OTP_CTRL_HI_REG:
+ qemu_log_mask(LOG_UNIMP,
+ "bcm2835_otp: BCM2835_OTP_CTRL_HI_REG\n");
+ break;
+ case BCM2835_OTP_STATUS_REG:
+ qemu_log_mask(LOG_UNIMP,
+ "bcm2835_otp: BCM2835_OTP_STATUS_REG\n");
+ break;
+ case BCM2835_OTP_BITSEL_REG:
+ qemu_log_mask(LOG_UNIMP,
+ "bcm2835_otp: BCM2835_OTP_BITSEL_REG\n");
+ break;
+ case BCM2835_OTP_DATA_REG:
+ qemu_log_mask(LOG_UNIMP,
+ "bcm2835_otp: BCM2835_OTP_DATA_REG\n");
+ break;
+ case BCM2835_OTP_ADDR_REG:
+ qemu_log_mask(LOG_UNIMP,
+ "bcm2835_otp: BCM2835_OTP_ADDR_REG\n");
+ break;
+ case BCM2835_OTP_WRITE_DATA_READ_REG:
+ qemu_log_mask(LOG_UNIMP,
+ "bcm2835_otp: BCM2835_OTP_WRITE_DATA_READ_REG\n");
+ break;
+ case BCM2835_OTP_INIT_STATUS_REG:
+ qemu_log_mask(LOG_UNIMP,
+ "bcm2835_otp: BCM2835_OTP_INIT_STATUS_REG\n");
+ break;
+ default:
+ qemu_log_mask(LOG_GUEST_ERROR,
+ "%s: Bad offset 0x%" HWADDR_PRIx "\n", __func__, addr);
+ }
+
+ return 0;
+}
+
+static void bcm2835_otp_write(void *opaque, hwaddr addr,
+ uint64_t value, unsigned int size)
+{
+ switch (addr) {
+ case BCM2835_OTP_BOOTMODE_REG:
+ qemu_log_mask(LOG_UNIMP,
+ "bcm2835_otp: BCM2835_OTP_BOOTMODE_REG\n");
+ break;
+ case BCM2835_OTP_CONFIG_REG:
+ qemu_log_mask(LOG_UNIMP,
+ "bcm2835_otp: BCM2835_OTP_CONFIG_REG\n");
+ break;
+ case BCM2835_OTP_CTRL_LO_REG:
+ qemu_log_mask(LOG_UNIMP,
+ "bcm2835_otp: BCM2835_OTP_CTRL_LO_REG\n");
+ break;
+ case BCM2835_OTP_CTRL_HI_REG:
+ qemu_log_mask(LOG_UNIMP,
+ "bcm2835_otp: BCM2835_OTP_CTRL_HI_REG\n");
+ break;
+ case BCM2835_OTP_STATUS_REG:
+ qemu_log_mask(LOG_UNIMP,
+ "bcm2835_otp: BCM2835_OTP_STATUS_REG\n");
+ break;
+ case BCM2835_OTP_BITSEL_REG:
+ qemu_log_mask(LOG_UNIMP,
+ "bcm2835_otp: BCM2835_OTP_BITSEL_REG\n");
+ break;
+ case BCM2835_OTP_DATA_REG:
+ qemu_log_mask(LOG_UNIMP,
+ "bcm2835_otp: BCM2835_OTP_DATA_REG\n");
+ break;
+ case BCM2835_OTP_ADDR_REG:
+ qemu_log_mask(LOG_UNIMP,
+ "bcm2835_otp: BCM2835_OTP_ADDR_REG\n");
+ break;
+ case BCM2835_OTP_WRITE_DATA_READ_REG:
+ qemu_log_mask(LOG_UNIMP,
+ "bcm2835_otp: BCM2835_OTP_WRITE_DATA_READ_REG\n");
+ break;
+ case BCM2835_OTP_INIT_STATUS_REG:
+ qemu_log_mask(LOG_UNIMP,
+ "bcm2835_otp: BCM2835_OTP_INIT_STATUS_REG\n");
+ break;
+ default:
+ qemu_log_mask(LOG_GUEST_ERROR,
+ "%s: Bad offset 0x%" HWADDR_PRIx "\n", __func__, addr);
+ }
+}
+
+static const MemoryRegionOps bcm2835_otp_ops = {
+ .read = bcm2835_otp_read,
+ .write = bcm2835_otp_write,
+ .endianness = DEVICE_NATIVE_ENDIAN,
+ .impl = {
+ .min_access_size = 4,
+ .max_access_size = 4,
+ },
+};
+
+static void bcm2835_otp_realize(DeviceState *dev, Error **errp)
+{
+ BCM2835OTPState *s = BCM2835_OTP(dev);
+ memory_region_init_io(&s->iomem, OBJECT(dev), &bcm2835_otp_ops, s,
+ TYPE_BCM2835_OTP, 0x80);
+ sysbus_init_mmio(SYS_BUS_DEVICE(dev), &s->iomem);
+
+ memset(s->otp_rows, 0x00, sizeof(s->otp_rows));
+}
+
+static const VMStateDescription vmstate_bcm2835_otp = {
+ .name = TYPE_BCM2835_OTP,
+ .version_id = 1,
+ .minimum_version_id = 1,
+ .fields = (const VMStateField[]) {
+ VMSTATE_UINT32_ARRAY(otp_rows, BCM2835OTPState, BCM2835_OTP_ROW_COUNT),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
+static void bcm2835_otp_class_init(ObjectClass *klass, void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(klass);
+
+ dc->realize = bcm2835_otp_realize;
+ dc->vmsd = &vmstate_bcm2835_otp;
+}
+
+static const TypeInfo bcm2835_otp_info = {
+ .name = TYPE_BCM2835_OTP,
+ .parent = TYPE_SYS_BUS_DEVICE,
+ .instance_size = sizeof(BCM2835OTPState),
+ .class_init = bcm2835_otp_class_init,
+};
+
+static void bcm2835_otp_register_types(void)
+{
+ type_register_static(&bcm2835_otp_info);
+}
+
+type_init(bcm2835_otp_register_types)
diff --git a/hw/nvram/meson.build b/hw/nvram/meson.build
index 4996c72..10f3639 100644
--- a/hw/nvram/meson.build
+++ b/hw/nvram/meson.build
@@ -1,5 +1,6 @@
system_ss.add(files('fw_cfg-interface.c'))
system_ss.add(files('fw_cfg.c'))
+system_ss.add(when: 'CONFIG_RASPI', if_true: files('bcm2835_otp.c'))
system_ss.add(when: 'CONFIG_CHRP_NVRAM', if_true: files('chrp_nvram.c'))
system_ss.add(when: 'CONFIG_DS1225Y', if_true: files('ds1225y.c'))
system_ss.add(when: 'CONFIG_NMC93XX_EEPROM', if_true: files('eeprom93xx.c'))