aboutsummaryrefslogtreecommitdiff
path: root/hw
diff options
context:
space:
mode:
authorPeter Maydell <peter.maydell@linaro.org>2024-01-18 12:48:17 +0000
committerPeter Maydell <peter.maydell@linaro.org>2024-01-18 12:48:17 +0000
commit88cf5fec91e50cd34bc002b633b4116228db0bc8 (patch)
treeac169c6e62de305d245d7f7e59e992a46946de03 /hw
parentf94e74a7e29482582cbb98acd0b3b10142c7712a (diff)
parent410c2a4d75f52f6a2fe978eda5a9b6f854afe5ea (diff)
downloadqemu-88cf5fec91e50cd34bc002b633b4116228db0bc8.zip
qemu-88cf5fec91e50cd34bc002b633b4116228db0bc8.tar.gz
qemu-88cf5fec91e50cd34bc002b633b4116228db0bc8.tar.bz2
Merge tag 'pull-target-arm-20240118' of https://git.linaro.org/people/pmaydell/qemu-arm into staging
target-arm queue: * docs/devel/docs: Document .hx file syntax * arm_pamax() no longer needs to do feature propagation * docs/system/arm/virt.rst: Improve 'highmem' option docs * STM32L4x5 Implement SYSCFG and EXTI devices * hw/timer: fix systick trace message * hw/arm/virt: Consolidate valid CPU types * load_elf: fix iterator's type for elf file processing # -----BEGIN PGP SIGNATURE----- # # iQJNBAABCAA3FiEE4aXFk81BneKOgxXPPCUl7RQ2DN4FAmWpHM4ZHHBldGVyLm1h # eWRlbGxAbGluYXJvLm9yZwAKCRA8JSXtFDYM3pZxD/sGIXvTeoOCsum7OFpArKoQ # J+wcy74pO526IDzjudgtwP8kFW09oVblMPgrt/68F9LY4Oa7sDNAZX/Xqlhs/hdJ # SVbOXArRmyLvgLpn8KVii9xk9iI/olMGt0S6KcXAErdgFud+JcCevbS0D5fAF4Ua # /G/4ldnwr+WcYUA5IIoi02ymSBm5VNeH2bKu0MPS3xpizjzgOFxWTBYwq3zkZYWD # w5GjH9+F+IC67CiAlCLvuQBqpGLdRwFBttU05hLtGXuSlnvS+FtJTooI7gGD17CR # 2wTa7qF716qDN1lNSIvxA6t8/dWNMIYCZYdlxJml476WzP3jECpth2WFWqE0G3yg # Orr7sFVB8X6JmtlR34srW6e3CZA3t+4FIWqcdELFLi5IQtJeer90jqQ9xwx4SttJ # nsHdy5M8txWSa61yAaDTXMID/smVlC7sWTKJrR9kV7v5+b9OPQ/R8k0mCODl5Aer # mzAVuCvUQVYK3j7fzprGrlldla57s3v78OAhqACLgKflK0+aJSJjglulPrSMK1z5 # bRPS5jLZjFwEi2VaLVg3LPJiBMDj1s/wAl0ycfCQSv2oEzvmpkw+Ar1HDc2NFe+d # 9dunbdhAZJMwh+ABIg7iMj+l0ncOXDa4DS+6BnjRxfECCa172u3viq1HATkLLAFI # GTkcJ5hIQzNEeg9ob0MDIg== # =Rfpe # -----END PGP SIGNATURE----- # gpg: Signature made Thu 18 Jan 2024 12:42:54 GMT # gpg: using RSA key E1A5C593CD419DE28E8315CF3C2525ED14360CDE # gpg: issuer "peter.maydell@linaro.org" # gpg: Good signature from "Peter Maydell <peter.maydell@linaro.org>" [ultimate] # gpg: aka "Peter Maydell <pmaydell@gmail.com>" [ultimate] # gpg: aka "Peter Maydell <pmaydell@chiark.greenend.org.uk>" [ultimate] # gpg: aka "Peter Maydell <peter@archaic.org.uk>" [ultimate] # Primary key fingerprint: E1A5 C593 CD41 9DE2 8E83 15CF 3C25 25ED 1436 0CDE * tag 'pull-target-arm-20240118' of https://git.linaro.org/people/pmaydell/qemu-arm: load_elf: fix iterator's type for elf file processing hw/arm/virt: Consolidate valid CPU types hw/timer: fix systick trace message tests/qtest: Add STM32L4x5 SYSCFG QTest testcase hw/arm: Connect STM32L4x5 SYSCFG to STM32L4x5 SoC hw/misc: Implement STM32L4x5 SYSCFG tests/qtest: Add STM32L4x5 EXTI QTest testcase hw/arm: Connect STM32L4x5 EXTI to STM32L4x5 SoC hw/misc: Implement STM32L4x5 EXTI docs/system/arm/virt.rst: Improve 'highmem' option docs target/arm: arm_pamax() no longer needs to do feature propagation docs/devel/docs: Document .hx file syntax Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
Diffstat (limited to 'hw')
-rw-r--r--hw/arm/Kconfig2
-rw-r--r--hw/arm/stm32l4x5_soc.c73
-rw-r--r--hw/arm/virt.c8
-rw-r--r--hw/misc/Kconfig6
-rw-r--r--hw/misc/meson.build2
-rw-r--r--hw/misc/stm32l4x5_exti.c290
-rw-r--r--hw/misc/stm32l4x5_syscfg.c266
-rw-r--r--hw/misc/trace-events11
-rw-r--r--hw/timer/trace-events2
9 files changed, 655 insertions, 5 deletions
diff --git a/hw/arm/Kconfig b/hw/arm/Kconfig
index 39d2554..218b454 100644
--- a/hw/arm/Kconfig
+++ b/hw/arm/Kconfig
@@ -459,6 +459,8 @@ config STM32L4X5_SOC
bool
select ARM_V7M
select OR_IRQ
+ select STM32L4X5_SYSCFG
+ select STM32L4X5_EXTI
config XLNX_ZYNQMP_ARM
bool
diff --git a/hw/arm/stm32l4x5_soc.c b/hw/arm/stm32l4x5_soc.c
index 159d531..f470ff7 100644
--- a/hw/arm/stm32l4x5_soc.c
+++ b/hw/arm/stm32l4x5_soc.c
@@ -36,10 +36,53 @@
#define SRAM2_BASE_ADDRESS 0x10000000
#define SRAM2_SIZE (32 * KiB)
+#define EXTI_ADDR 0x40010400
+#define SYSCFG_ADDR 0x40010000
+
+#define NUM_EXTI_IRQ 40
+/* Match exti line connections with their CPU IRQ number */
+/* See Vector Table (Reference Manual p.396) */
+static const int exti_irq[NUM_EXTI_IRQ] = {
+ 6, /* GPIO[0] */
+ 7, /* GPIO[1] */
+ 8, /* GPIO[2] */
+ 9, /* GPIO[3] */
+ 10, /* GPIO[4] */
+ 23, 23, 23, 23, 23, /* GPIO[5..9] */
+ 40, 40, 40, 40, 40, 40, /* GPIO[10..15] */
+ 1, /* PVD */
+ 67, /* OTG_FS_WKUP, Direct */
+ 41, /* RTC_ALARM */
+ 2, /* RTC_TAMP_STAMP2/CSS_LSE */
+ 3, /* RTC wakeup timer */
+ 63, /* COMP1 */
+ 63, /* COMP2 */
+ 31, /* I2C1 wakeup, Direct */
+ 33, /* I2C2 wakeup, Direct */
+ 72, /* I2C3 wakeup, Direct */
+ 37, /* USART1 wakeup, Direct */
+ 38, /* USART2 wakeup, Direct */
+ 39, /* USART3 wakeup, Direct */
+ 52, /* UART4 wakeup, Direct */
+ 53, /* UART4 wakeup, Direct */
+ 70, /* LPUART1 wakeup, Direct */
+ 65, /* LPTIM1, Direct */
+ 66, /* LPTIM2, Direct */
+ 76, /* SWPMI1 wakeup, Direct */
+ 1, /* PVM1 wakeup */
+ 1, /* PVM2 wakeup */
+ 1, /* PVM3 wakeup */
+ 1, /* PVM4 wakeup */
+ 78 /* LCD wakeup, Direct */
+};
+
static void stm32l4x5_soc_initfn(Object *obj)
{
Stm32l4x5SocState *s = STM32L4X5_SOC(obj);
+ object_initialize_child(obj, "exti", &s->exti, TYPE_STM32L4X5_EXTI);
+ object_initialize_child(obj, "syscfg", &s->syscfg, TYPE_STM32L4X5_SYSCFG);
+
s->sysclk = qdev_init_clock_in(DEVICE(s), "sysclk", NULL, NULL, 0);
s->refclk = qdev_init_clock_in(DEVICE(s), "refclk", NULL, NULL, 0);
}
@@ -51,6 +94,7 @@ static void stm32l4x5_soc_realize(DeviceState *dev_soc, Error **errp)
const Stm32l4x5SocClass *sc = STM32L4X5_SOC_GET_CLASS(dev_soc);
MemoryRegion *system_memory = get_system_memory();
DeviceState *armv7m;
+ SysBusDevice *busdev;
/*
* We use s->refclk internally and only define it with qdev_init_clock_in()
@@ -113,6 +157,33 @@ static void stm32l4x5_soc_realize(DeviceState *dev_soc, Error **errp)
return;
}
+ /* System configuration controller */
+ busdev = SYS_BUS_DEVICE(&s->syscfg);
+ if (!sysbus_realize(busdev, errp)) {
+ return;
+ }
+ sysbus_mmio_map(busdev, 0, SYSCFG_ADDR);
+ /*
+ * TODO: when the GPIO device is implemented, connect it
+ * to SYCFG using `qdev_connect_gpio_out`, NUM_GPIOS and
+ * GPIO_NUM_PINS.
+ */
+
+ /* EXTI device */
+ busdev = SYS_BUS_DEVICE(&s->exti);
+ if (!sysbus_realize(busdev, errp)) {
+ return;
+ }
+ sysbus_mmio_map(busdev, 0, EXTI_ADDR);
+ for (unsigned i = 0; i < NUM_EXTI_IRQ; i++) {
+ sysbus_connect_irq(busdev, i, qdev_get_gpio_in(armv7m, exti_irq[i]));
+ }
+
+ for (unsigned i = 0; i < 16; i++) {
+ qdev_connect_gpio_out(DEVICE(&s->syscfg), i,
+ qdev_get_gpio_in(DEVICE(&s->exti), i));
+ }
+
/* APB1 BUS */
create_unimplemented_device("TIM2", 0x40000000, 0x400);
create_unimplemented_device("TIM3", 0x40000400, 0x400);
@@ -150,10 +221,8 @@ static void stm32l4x5_soc_realize(DeviceState *dev_soc, Error **errp)
/* RESERVED: 0x40009800, 0x6800 */
/* APB2 BUS */
- create_unimplemented_device("SYSCFG", 0x40010000, 0x30);
create_unimplemented_device("VREFBUF", 0x40010030, 0x1D0);
create_unimplemented_device("COMP", 0x40010200, 0x200);
- create_unimplemented_device("EXTI", 0x40010400, 0x400);
/* RESERVED: 0x40010800, 0x1400 */
create_unimplemented_device("FIREWALL", 0x40011C00, 0x400);
/* RESERVED: 0x40012000, 0x800 */
diff --git a/hw/arm/virt.c b/hw/arm/virt.c
index 2793121..5cbc69d 100644
--- a/hw/arm/virt.c
+++ b/hw/arm/virt.c
@@ -2905,6 +2905,7 @@ static void virt_machine_class_init(ObjectClass *oc, void *data)
#ifdef CONFIG_TCG
ARM_CPU_TYPE_NAME("cortex-a7"),
ARM_CPU_TYPE_NAME("cortex-a15"),
+#ifdef TARGET_AARCH64
ARM_CPU_TYPE_NAME("cortex-a35"),
ARM_CPU_TYPE_NAME("cortex-a55"),
ARM_CPU_TYPE_NAME("cortex-a72"),
@@ -2914,12 +2915,15 @@ static void virt_machine_class_init(ObjectClass *oc, void *data)
ARM_CPU_TYPE_NAME("neoverse-n1"),
ARM_CPU_TYPE_NAME("neoverse-v1"),
ARM_CPU_TYPE_NAME("neoverse-n2"),
-#endif
+#endif /* TARGET_AARCH64 */
+#endif /* CONFIG_TCG */
+#ifdef TARGET_AARCH64
ARM_CPU_TYPE_NAME("cortex-a53"),
ARM_CPU_TYPE_NAME("cortex-a57"),
#if defined(CONFIG_KVM) || defined(CONFIG_HVF)
ARM_CPU_TYPE_NAME("host"),
-#endif
+#endif /* CONFIG_KVM || CONFIG_HVF */
+#endif /* TARGET_AARCH64 */
ARM_CPU_TYPE_NAME("max"),
NULL
};
diff --git a/hw/misc/Kconfig b/hw/misc/Kconfig
index cc8a8c1..4fc6b29 100644
--- a/hw/misc/Kconfig
+++ b/hw/misc/Kconfig
@@ -87,6 +87,12 @@ config STM32F4XX_SYSCFG
config STM32F4XX_EXTI
bool
+config STM32L4X5_EXTI
+ bool
+
+config STM32L4X5_SYSCFG
+ bool
+
config MIPS_ITU
bool
diff --git a/hw/misc/meson.build b/hw/misc/meson.build
index 36c20d5..2ca2ce4 100644
--- a/hw/misc/meson.build
+++ b/hw/misc/meson.build
@@ -110,6 +110,8 @@ system_ss.add(when: 'CONFIG_XLNX_VERSAL_TRNG', if_true: files(
system_ss.add(when: 'CONFIG_STM32F2XX_SYSCFG', if_true: files('stm32f2xx_syscfg.c'))
system_ss.add(when: 'CONFIG_STM32F4XX_SYSCFG', if_true: files('stm32f4xx_syscfg.c'))
system_ss.add(when: 'CONFIG_STM32F4XX_EXTI', if_true: files('stm32f4xx_exti.c'))
+system_ss.add(when: 'CONFIG_STM32L4X5_EXTI', if_true: files('stm32l4x5_exti.c'))
+system_ss.add(when: 'CONFIG_STM32L4X5_SYSCFG', if_true: files('stm32l4x5_syscfg.c'))
system_ss.add(when: 'CONFIG_MPS2_FPGAIO', if_true: files('mps2-fpgaio.c'))
system_ss.add(when: 'CONFIG_MPS2_SCC', if_true: files('mps2-scc.c'))
diff --git a/hw/misc/stm32l4x5_exti.c b/hw/misc/stm32l4x5_exti.c
new file mode 100644
index 0000000..9fd8591
--- /dev/null
+++ b/hw/misc/stm32l4x5_exti.c
@@ -0,0 +1,290 @@
+/*
+ * STM32L4x5 EXTI (Extended interrupts and events controller)
+ *
+ * Copyright (c) 2023 Arnaud Minier <arnaud.minier@telecom-paris.fr>
+ * Copyright (c) 2023 Samuel Tardieu <samuel.tardieu@telecom-paris.fr>
+ * Copyright (c) 2023 Inès Varhol <ines.varhol@telecom-paris.fr>
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2 or later.
+ * See the COPYING file in the top-level directory.
+ *
+ * This work is based on the stm32f4xx_exti by Alistair Francis.
+ * Original code is licensed under the MIT License:
+ *
+ * Copyright (c) 2014 Alistair Francis <alistair@alistair23.me>
+ */
+
+/*
+ * The reference used is the STMicroElectronics RM0351 Reference manual
+ * for STM32L4x5 and STM32L4x6 advanced Arm ® -based 32-bit MCUs.
+ * https://www.st.com/en/microcontrollers-microprocessors/stm32l4x5/documentation.html
+ */
+
+#include "qemu/osdep.h"
+#include "qemu/log.h"
+#include "trace.h"
+#include "hw/irq.h"
+#include "migration/vmstate.h"
+#include "hw/misc/stm32l4x5_exti.h"
+
+#define EXTI_IMR1 0x00
+#define EXTI_EMR1 0x04
+#define EXTI_RTSR1 0x08
+#define EXTI_FTSR1 0x0C
+#define EXTI_SWIER1 0x10
+#define EXTI_PR1 0x14
+#define EXTI_IMR2 0x20
+#define EXTI_EMR2 0x24
+#define EXTI_RTSR2 0x28
+#define EXTI_FTSR2 0x2C
+#define EXTI_SWIER2 0x30
+#define EXTI_PR2 0x34
+
+#define EXTI_NUM_GPIO_EVENT_IN_LINES 16
+#define EXTI_MAX_IRQ_PER_BANK 32
+#define EXTI_IRQS_BANK0 32
+#define EXTI_IRQS_BANK1 8
+
+static const unsigned irqs_per_bank[EXTI_NUM_REGISTER] = {
+ EXTI_IRQS_BANK0,
+ EXTI_IRQS_BANK1,
+};
+
+static const uint32_t exti_romask[EXTI_NUM_REGISTER] = {
+ 0xff820000, /* 0b11111111_10000010_00000000_00000000 */
+ 0x00000087, /* 0b00000000_00000000_00000000_10000111 */
+};
+
+static unsigned regbank_index_by_irq(unsigned irq)
+{
+ return irq >= EXTI_MAX_IRQ_PER_BANK ? 1 : 0;
+}
+
+static unsigned regbank_index_by_addr(hwaddr addr)
+{
+ return addr >= EXTI_IMR2 ? 1 : 0;
+}
+
+static unsigned valid_mask(unsigned bank)
+{
+ return MAKE_64BIT_MASK(0, irqs_per_bank[bank]);
+}
+
+static unsigned configurable_mask(unsigned bank)
+{
+ return valid_mask(bank) & ~exti_romask[bank];
+}
+
+static void stm32l4x5_exti_reset_hold(Object *obj)
+{
+ Stm32l4x5ExtiState *s = STM32L4X5_EXTI(obj);
+
+ for (unsigned bank = 0; bank < EXTI_NUM_REGISTER; bank++) {
+ s->imr[bank] = exti_romask[bank];
+ s->emr[bank] = 0x00000000;
+ s->rtsr[bank] = 0x00000000;
+ s->ftsr[bank] = 0x00000000;
+ s->swier[bank] = 0x00000000;
+ s->pr[bank] = 0x00000000;
+ }
+}
+
+static void stm32l4x5_exti_set_irq(void *opaque, int irq, int level)
+{
+ Stm32l4x5ExtiState *s = opaque;
+ const unsigned bank = regbank_index_by_irq(irq);
+ const int oirq = irq;
+
+ trace_stm32l4x5_exti_set_irq(irq, level);
+
+ /* Shift the value to enable access in x2 registers. */
+ irq %= EXTI_MAX_IRQ_PER_BANK;
+
+ /* 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 */
+ 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,
+ unsigned int size)
+{
+ Stm32l4x5ExtiState *s = opaque;
+ uint32_t r = 0;
+ const unsigned bank = regbank_index_by_addr(addr);
+
+ switch (addr) {
+ case EXTI_IMR1:
+ case EXTI_IMR2:
+ r = s->imr[bank];
+ break;
+ case EXTI_EMR1:
+ case EXTI_EMR2:
+ r = s->emr[bank];
+ break;
+ case EXTI_RTSR1:
+ case EXTI_RTSR2:
+ r = s->rtsr[bank];
+ break;
+ case EXTI_FTSR1:
+ case EXTI_FTSR2:
+ r = s->ftsr[bank];
+ break;
+ case EXTI_SWIER1:
+ case EXTI_SWIER2:
+ r = s->swier[bank];
+ break;
+ case EXTI_PR1:
+ case EXTI_PR2:
+ r = s->pr[bank];
+ break;
+
+ default:
+ qemu_log_mask(LOG_GUEST_ERROR,
+ "STM32L4X5_exti_read: Bad offset 0x%" HWADDR_PRIx "\n",
+ addr);
+ break;
+ }
+
+ trace_stm32l4x5_exti_read(addr, r);
+
+ return r;
+}
+
+static void stm32l4x5_exti_write(void *opaque, hwaddr addr,
+ uint64_t val64, unsigned int size)
+{
+ Stm32l4x5ExtiState *s = opaque;
+ const unsigned bank = regbank_index_by_addr(addr);
+
+ trace_stm32l4x5_exti_write(addr, val64);
+
+ switch (addr) {
+ case EXTI_IMR1:
+ case EXTI_IMR2:
+ s->imr[bank] = val64 & valid_mask(bank);
+ return;
+ case EXTI_EMR1:
+ case EXTI_EMR2:
+ s->emr[bank] = val64 & valid_mask(bank);
+ return;
+ case EXTI_RTSR1:
+ case EXTI_RTSR2:
+ s->rtsr[bank] = val64 & configurable_mask(bank);
+ return;
+ case EXTI_FTSR1:
+ case EXTI_FTSR2:
+ s->ftsr[bank] = val64 & configurable_mask(bank);
+ return;
+ case EXTI_SWIER1:
+ case EXTI_SWIER2: {
+ const uint32_t set = val64 & configurable_mask(bank);
+ const uint32_t pend = set & ~s->swier[bank] & s->imr[bank] &
+ ~s->pr[bank];
+ s->swier[bank] = set;
+ s->pr[bank] |= pend;
+ for (unsigned i = 0; i < irqs_per_bank[bank]; i++) {
+ if (extract32(pend, i, 1)) {
+ qemu_irq_pulse(s->irq[i + 32 * bank]);
+ }
+ }
+ return;
+ }
+ case EXTI_PR1:
+ case EXTI_PR2: {
+ const uint32_t cleared = s->pr[bank] & val64 & configurable_mask(bank);
+ /* This bit is cleared by writing a 1 to it */
+ s->pr[bank] &= ~cleared;
+ /* Software triggered interrupts are cleared as well */
+ s->swier[bank] &= ~cleared;
+ return;
+ }
+ default:
+ qemu_log_mask(LOG_GUEST_ERROR,
+ "STM32L4X5_exti_write: Bad offset 0x%" HWADDR_PRIx "\n",
+ addr);
+ }
+}
+
+static const MemoryRegionOps stm32l4x5_exti_ops = {
+ .read = stm32l4x5_exti_read,
+ .write = stm32l4x5_exti_write,
+ .endianness = DEVICE_NATIVE_ENDIAN,
+ .impl.min_access_size = 4,
+ .impl.max_access_size = 4,
+ .impl.unaligned = false,
+ .valid.min_access_size = 4,
+ .valid.max_access_size = 4,
+ .valid.unaligned = false,
+};
+
+static void stm32l4x5_exti_init(Object *obj)
+{
+ Stm32l4x5ExtiState *s = STM32L4X5_EXTI(obj);
+
+ for (size_t i = 0; i < EXTI_NUM_INTERRUPT_OUT_LINES; i++) {
+ sysbus_init_irq(SYS_BUS_DEVICE(obj), &s->irq[i]);
+ }
+
+ memory_region_init_io(&s->mmio, obj, &stm32l4x5_exti_ops, s,
+ TYPE_STM32L4X5_EXTI, 0x400);
+ sysbus_init_mmio(SYS_BUS_DEVICE(obj), &s->mmio);
+
+ qdev_init_gpio_in(DEVICE(obj), stm32l4x5_exti_set_irq,
+ EXTI_NUM_GPIO_EVENT_IN_LINES);
+}
+
+static const VMStateDescription vmstate_stm32l4x5_exti = {
+ .name = TYPE_STM32L4X5_EXTI,
+ .version_id = 1,
+ .minimum_version_id = 1,
+ .fields = (VMStateField[]) {
+ VMSTATE_UINT32_ARRAY(imr, Stm32l4x5ExtiState, EXTI_NUM_REGISTER),
+ VMSTATE_UINT32_ARRAY(emr, Stm32l4x5ExtiState, EXTI_NUM_REGISTER),
+ VMSTATE_UINT32_ARRAY(rtsr, Stm32l4x5ExtiState, EXTI_NUM_REGISTER),
+ 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_END_OF_LIST()
+ }
+};
+
+static void stm32l4x5_exti_class_init(ObjectClass *klass, void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(klass);
+ ResettableClass *rc = RESETTABLE_CLASS(klass);
+
+ dc->vmsd = &vmstate_stm32l4x5_exti;
+ rc->phases.hold = stm32l4x5_exti_reset_hold;
+}
+
+static const TypeInfo stm32l4x5_exti_types[] = {
+ {
+ .name = TYPE_STM32L4X5_EXTI,
+ .parent = TYPE_SYS_BUS_DEVICE,
+ .instance_size = sizeof(Stm32l4x5ExtiState),
+ .instance_init = stm32l4x5_exti_init,
+ .class_init = stm32l4x5_exti_class_init,
+ }
+};
+
+DEFINE_TYPES(stm32l4x5_exti_types)
diff --git a/hw/misc/stm32l4x5_syscfg.c b/hw/misc/stm32l4x5_syscfg.c
new file mode 100644
index 0000000..fd68cb8
--- /dev/null
+++ b/hw/misc/stm32l4x5_syscfg.c
@@ -0,0 +1,266 @@
+/*
+ * STM32L4x5 SYSCFG (System Configuration Controller)
+ *
+ * Copyright (c) 2023 Arnaud Minier <arnaud.minier@telecom-paris.fr>
+ * Copyright (c) 2023 Inès Varhol <ines.varhol@telecom-paris.fr>
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2 or later.
+ * See the COPYING file in the top-level directory.
+ *
+ * This work is based on the stm32f4xx_syscfg by Alistair Francis.
+ * Original code is licensed under the MIT License:
+ *
+ * Copyright (c) 2014 Alistair Francis <alistair@alistair23.me>
+ */
+
+/*
+ * The reference used is the STMicroElectronics RM0351 Reference manual
+ * for STM32L4x5 and STM32L4x6 advanced Arm ® -based 32-bit MCUs.
+ * https://www.st.com/en/microcontrollers-microprocessors/stm32l4x5/documentation.html
+ */
+
+#include "qemu/osdep.h"
+#include "qemu/log.h"
+#include "trace.h"
+#include "hw/irq.h"
+#include "migration/vmstate.h"
+#include "hw/misc/stm32l4x5_syscfg.h"
+
+#define SYSCFG_MEMRMP 0x00
+#define SYSCFG_CFGR1 0x04
+#define SYSCFG_EXTICR1 0x08
+#define SYSCFG_EXTICR2 0x0C
+#define SYSCFG_EXTICR3 0x10
+#define SYSCFG_EXTICR4 0x14
+#define SYSCFG_SCSR 0x18
+#define SYSCFG_CFGR2 0x1C
+#define SYSCFG_SWPR 0x20
+#define SYSCFG_SKR 0x24
+#define SYSCFG_SWPR2 0x28
+
+/* 00000000_00000000_00000001_00000111 */
+#define ACTIVABLE_BITS_MEMRP 0x00000107
+
+/* 11111100_11111111_00000001_00000000 */
+#define ACTIVABLE_BITS_CFGR1 0xFCFF0100
+/* 00000000_00000000_00000000_00000001 */
+#define FIREWALL_DISABLE_CFGR1 0x00000001
+
+/* 00000000_00000000_11111111_11111111 */
+#define ACTIVABLE_BITS_EXTICR 0x0000FFFF
+
+/* 00000000_00000000_00000000_00000011 */
+/* #define ACTIVABLE_BITS_SCSR 0x00000003 */
+
+/* 00000000_00000000_00000000_00001111 */
+#define ECC_LOCK_CFGR2 0x0000000F
+/* 00000000_00000000_00000001_00000000 */
+#define SRAM2_PARITY_ERROR_FLAG_CFGR2 0x00000100
+
+/* 00000000_00000000_00000000_11111111 */
+#define ACTIVABLE_BITS_SKR 0x000000FF
+
+#define NUM_LINES_PER_EXTICR_REG 4
+
+static void stm32l4x5_syscfg_hold_reset(Object *obj)
+{
+ Stm32l4x5SyscfgState *s = STM32L4X5_SYSCFG(obj);
+
+ s->memrmp = 0x00000000;
+ s->cfgr1 = 0x7C000001;
+ s->exticr[0] = 0x00000000;
+ s->exticr[1] = 0x00000000;
+ s->exticr[2] = 0x00000000;
+ s->exticr[3] = 0x00000000;
+ s->scsr = 0x00000000;
+ s->cfgr2 = 0x00000000;
+ s->swpr = 0x00000000;
+ s->skr = 0x00000000;
+ s->swpr2 = 0x00000000;
+}
+
+static void stm32l4x5_syscfg_set_irq(void *opaque, int irq, int level)
+{
+ Stm32l4x5SyscfgState *s = opaque;
+ const uint8_t gpio = irq / GPIO_NUM_PINS;
+ const int line = irq % GPIO_NUM_PINS;
+
+ const int exticr_reg = line / NUM_LINES_PER_EXTICR_REG;
+ const int startbit = (line % NUM_LINES_PER_EXTICR_REG) * 4;
+
+ g_assert(gpio < NUM_GPIOS);
+ trace_stm32l4x5_syscfg_set_irq(gpio, line, level);
+
+ if (extract32(s->exticr[exticr_reg], startbit, 4) == gpio) {
+ trace_stm32l4x5_syscfg_forward_exti(line);
+ qemu_set_irq(s->gpio_out[line], level);
+ }
+}
+
+static uint64_t stm32l4x5_syscfg_read(void *opaque, hwaddr addr,
+ unsigned int size)
+{
+ Stm32l4x5SyscfgState *s = opaque;
+
+ trace_stm32l4x5_syscfg_read(addr);
+
+ switch (addr) {
+ case SYSCFG_MEMRMP:
+ return s->memrmp;
+ case SYSCFG_CFGR1:
+ return s->cfgr1;
+ case SYSCFG_EXTICR1...SYSCFG_EXTICR4:
+ return s->exticr[(addr - SYSCFG_EXTICR1) / 4];
+ case SYSCFG_SCSR:
+ return s->scsr;
+ case SYSCFG_CFGR2:
+ return s->cfgr2;
+ case SYSCFG_SWPR:
+ return s->swpr;
+ case SYSCFG_SKR:
+ return s->skr;
+ case SYSCFG_SWPR2:
+ return s->swpr2;
+ default:
+ qemu_log_mask(LOG_GUEST_ERROR,
+ "%s: Bad offset 0x%" HWADDR_PRIx "\n", __func__, addr);
+ return 0;
+ }
+}
+static void stm32l4x5_syscfg_write(void *opaque, hwaddr addr,
+ uint64_t value, unsigned int size)
+{
+ Stm32l4x5SyscfgState *s = opaque;
+
+ trace_stm32l4x5_syscfg_write(addr, value);
+
+ switch (addr) {
+ case SYSCFG_MEMRMP:
+ qemu_log_mask(LOG_UNIMP,
+ "%s: Changing the memory mapping isn't supported\n",
+ __func__);
+ s->memrmp = value & ACTIVABLE_BITS_MEMRP;
+ return;
+ case SYSCFG_CFGR1:
+ qemu_log_mask(LOG_UNIMP,
+ "%s: Functions in CFGRx aren't supported\n",
+ __func__);
+ /* bit 0 (firewall dis.) is cleared by software, set only by reset. */
+ s->cfgr1 = (s->cfgr1 & value & FIREWALL_DISABLE_CFGR1) |
+ (value & ACTIVABLE_BITS_CFGR1);
+ return;
+ case SYSCFG_EXTICR1...SYSCFG_EXTICR4:
+ s->exticr[(addr - SYSCFG_EXTICR1) / 4] =
+ (value & ACTIVABLE_BITS_EXTICR);
+ return;
+ case SYSCFG_SCSR:
+ qemu_log_mask(LOG_UNIMP,
+ "%s: Erasing SRAM2 isn't supported\n",
+ __func__);
+ /*
+ * only non reserved bits are :
+ * bit 0 (write-protected by a passkey), bit 1 (meant to be read)
+ * so it serves no purpose yet to add :
+ * s->scsr = value & 0x3;
+ */
+ return;
+ case SYSCFG_CFGR2:
+ qemu_log_mask(LOG_UNIMP,
+ "%s: Functions in CFGRx aren't supported\n",
+ __func__);
+ /* bit 8 (SRAM2 PEF) is cleared by software by writing a '1'.*/
+ /* bits[3:0] (ECC Lock) are set by software, cleared only by reset.*/
+ s->cfgr2 = (s->cfgr2 | (value & ECC_LOCK_CFGR2)) &
+ ~(value & SRAM2_PARITY_ERROR_FLAG_CFGR2);
+ return;
+ case SYSCFG_SWPR:
+ qemu_log_mask(LOG_UNIMP,
+ "%s: Write protecting SRAM2 isn't supported\n",
+ __func__);
+ /* These bits are set by software and cleared only by reset.*/
+ s->swpr |= value;
+ return;
+ case SYSCFG_SKR:
+ qemu_log_mask(LOG_UNIMP,
+ "%s: Erasing SRAM2 isn't supported\n",
+ __func__);
+ s->skr = value & ACTIVABLE_BITS_SKR;
+ return;
+ case SYSCFG_SWPR2:
+ qemu_log_mask(LOG_UNIMP,
+ "%s: Write protecting SRAM2 isn't supported\n",
+ __func__);
+ /* These bits are set by software and cleared only by reset.*/
+ s->swpr2 |= value;
+ return;
+ default:
+ qemu_log_mask(LOG_GUEST_ERROR,
+ "%s: Bad offset 0x%" HWADDR_PRIx "\n", __func__, addr);
+ }
+}
+
+static const MemoryRegionOps stm32l4x5_syscfg_ops = {
+ .read = stm32l4x5_syscfg_read,
+ .write = stm32l4x5_syscfg_write,
+ .endianness = DEVICE_NATIVE_ENDIAN,
+ .impl.min_access_size = 4,
+ .impl.max_access_size = 4,
+ .impl.unaligned = false,
+ .valid.min_access_size = 4,
+ .valid.max_access_size = 4,
+ .valid.unaligned = false,
+};
+
+static void stm32l4x5_syscfg_init(Object *obj)
+{
+ Stm32l4x5SyscfgState *s = STM32L4X5_SYSCFG(obj);
+
+ memory_region_init_io(&s->mmio, obj, &stm32l4x5_syscfg_ops, s,
+ TYPE_STM32L4X5_SYSCFG, 0x400);
+ sysbus_init_mmio(SYS_BUS_DEVICE(obj), &s->mmio);
+
+ qdev_init_gpio_in(DEVICE(obj), stm32l4x5_syscfg_set_irq,
+ GPIO_NUM_PINS * NUM_GPIOS);
+ qdev_init_gpio_out(DEVICE(obj), s->gpio_out, GPIO_NUM_PINS);
+}
+
+static const VMStateDescription vmstate_stm32l4x5_syscfg = {
+ .name = TYPE_STM32L4X5_SYSCFG,
+ .version_id = 1,
+ .minimum_version_id = 1,
+ .fields = (VMStateField[]) {
+ VMSTATE_UINT32(memrmp, Stm32l4x5SyscfgState),
+ VMSTATE_UINT32(cfgr1, Stm32l4x5SyscfgState),
+ VMSTATE_UINT32_ARRAY(exticr, Stm32l4x5SyscfgState,
+ SYSCFG_NUM_EXTICR),
+ VMSTATE_UINT32(scsr, Stm32l4x5SyscfgState),
+ VMSTATE_UINT32(cfgr2, Stm32l4x5SyscfgState),
+ VMSTATE_UINT32(swpr, Stm32l4x5SyscfgState),
+ VMSTATE_UINT32(skr, Stm32l4x5SyscfgState),
+ VMSTATE_UINT32(swpr2, Stm32l4x5SyscfgState),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
+static void stm32l4x5_syscfg_class_init(ObjectClass *klass, void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(klass);
+ ResettableClass *rc = RESETTABLE_CLASS(klass);
+
+ dc->vmsd = &vmstate_stm32l4x5_syscfg;
+ rc->phases.hold = stm32l4x5_syscfg_hold_reset;
+}
+
+static const TypeInfo stm32l4x5_syscfg_info[] = {
+ {
+ .name = TYPE_STM32L4X5_SYSCFG,
+ .parent = TYPE_SYS_BUS_DEVICE,
+ .instance_size = sizeof(Stm32l4x5SyscfgState),
+ .instance_init = stm32l4x5_syscfg_init,
+ .class_init = stm32l4x5_syscfg_class_init,
+ }
+};
+
+DEFINE_TYPES(stm32l4x5_syscfg_info)
diff --git a/hw/misc/trace-events b/hw/misc/trace-events
index 8572550..5f5bc92 100644
--- a/hw/misc/trace-events
+++ b/hw/misc/trace-events
@@ -163,6 +163,17 @@ stm32f4xx_exti_set_irq(int irq, int level) "Set EXTI: %d to %d"
stm32f4xx_exti_read(uint64_t addr) "reg read: addr: 0x%" PRIx64 " "
stm32f4xx_exti_write(uint64_t addr, uint64_t data) "reg write: addr: 0x%" PRIx64 " val: 0x%" PRIx64 ""
+# stm32l4x5_syscfg.c
+stm32l4x5_syscfg_set_irq(int gpio, int line, int level) "irq from GPIO: %d, line: %d, level: %d"
+stm32l4x5_syscfg_forward_exti(int irq) "irq %d forwarded to EXTI"
+stm32l4x5_syscfg_read(uint64_t addr) "reg read: addr: 0x%" PRIx64 " "
+stm32l4x5_syscfg_write(uint64_t addr, uint64_t data) "reg write: addr: 0x%" PRIx64 " val: 0x%" PRIx64 ""
+
+# stm32l4x5_exti.c
+stm32l4x5_exti_set_irq(int irq, int level) "Set EXTI: %d to %d"
+stm32l4x5_exti_read(uint64_t addr, uint64_t data) "reg read: addr: 0x%" PRIx64 " val: 0x%" PRIx64 ""
+stm32l4x5_exti_write(uint64_t addr, uint64_t data) "reg write: addr: 0x%" PRIx64 " val: 0x%" PRIx64 ""
+
# tz-mpc.c
tz_mpc_reg_read(uint32_t offset, uint64_t data, unsigned size) "TZ MPC regs read: offset 0x%x data 0x%" PRIx64 " size %u"
tz_mpc_reg_write(uint32_t offset, uint64_t data, unsigned size) "TZ MPC regs write: offset 0x%x data 0x%" PRIx64 " size %u"
diff --git a/hw/timer/trace-events b/hw/timer/trace-events
index 3eccef8..8145e18 100644
--- a/hw/timer/trace-events
+++ b/hw/timer/trace-events
@@ -35,7 +35,7 @@ aspeed_timer_read(uint64_t offset, unsigned size, uint64_t value) "From 0x%" PRI
# armv7m_systick.c
systick_reload(void) "systick reload"
-systick_timer_tick(void) "systick reload"
+systick_timer_tick(void) "systick tick"
systick_read(uint64_t addr, uint32_t value, unsigned size) "systick read addr 0x%" PRIx64 " data 0x%" PRIx32 " size %u"
systick_write(uint64_t addr, uint32_t value, unsigned size) "systick write addr 0x%" PRIx64 " data 0x%" PRIx32 " size %u"