All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH qemu 1/1] Implement STM32L4x5 EXTI
  2023-11-21 14:56 [PATCH qemu 0/1] Implement the STM32L4x5 EXTI device ~aminier
@ 2023-11-11 14:33 ` ~aminier
  2023-11-21 21:07   ` Philippe Mathieu-Daudé
  2023-11-21 15:19 ` [PATCH qemu 0/1] Implement the STM32L4x5 EXTI device Philippe Mathieu-Daudé
  1 sibling, 1 reply; 4+ messages in thread
From: ~aminier @ 2023-11-11 14:33 UTC (permalink / raw)
  To: qemu-devel
  Cc: qemu-arm, alistair, philmd, peter.maydell, ines.varhol, arnaud.minier

From: Arnaud Minier <arnaud.minier@telecom-paris.fr>

Signed-off-by: Arnaud Minier <arnaud.minier@telecom-paris.fr>
Signed-off-by: Inès Varhol <ines.varhol@telecom-paris.fr>
---
 hw/arm/Kconfig                    |   1 +
 hw/arm/stm32l4x5_soc.c            |  65 +++++-
 hw/misc/Kconfig                   |   3 +
 hw/misc/meson.build               |   1 +
 hw/misc/stm32l4x5_exti.c          | 329 ++++++++++++++++++++++++++++++
 hw/misc/trace-events              |   5 +
 include/hw/arm/stm32l4x5_soc.h    |   3 +
 include/hw/misc/stm32l4x5_exti.h  |  64 ++++++
 tests/qtest/meson.build           |   5 +
 tests/qtest/stm32l4x5_exti-test.c | 102 +++++++++
 10 files changed, 576 insertions(+), 2 deletions(-)
 create mode 100644 hw/misc/stm32l4x5_exti.c
 create mode 100644 include/hw/misc/stm32l4x5_exti.h
 create mode 100644 tests/qtest/stm32l4x5_exti-test.c

diff --git a/hw/arm/Kconfig b/hw/arm/Kconfig
index b95576fb0c..28d378ed83 100644
--- a/hw/arm/Kconfig
+++ b/hw/arm/Kconfig
@@ -458,6 +458,7 @@ config STM32L4X5_SOC
     bool
     select ARM_V7M
     select OR_IRQ
+    select STM32L4X5_EXTI
 
 config XLNX_ZYNQMP_ARM
     bool
diff --git a/hw/arm/stm32l4x5_soc.c b/hw/arm/stm32l4x5_soc.c
index 198d3f6d3e..6f2a1b34b3 100644
--- a/hw/arm/stm32l4x5_soc.c
+++ b/hw/arm/stm32l4x5_soc.c
@@ -43,10 +43,51 @@
 #define SRAM2_BASE_ADDRESS 0x10000000
 #define SRAM2_SIZE (32 * KiB)
 
+static const hwaddr exti_addr = 0x40010400;
+
+#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);
+
     s->sysclk = qdev_init_clock_in(DEVICE(s), "sysclk", NULL, NULL, 0);
     s->refclk = qdev_init_clock_in(DEVICE(s), "refclk", NULL, NULL, 0);
 }
@@ -57,7 +98,9 @@ static void stm32l4x5_soc_realize(DeviceState *dev_soc, Error **errp)
     Stm32l4x5SocState *s = STM32L4X5_SOC(dev_soc);
     const Stm32l4x5SocClass *sc = STM32L4X5_SOC_GET_CLASS(dev_soc);
     MemoryRegion *system_memory = get_system_memory();
-    DeviceState *armv7m;
+    DeviceState *dev, *armv7m;
+    SysBusDevice *busdev;
+    int i;
 
     /*
      * We use s->refclk internally and only define it with qdev_init_clock_in()
@@ -122,6 +165,25 @@ static void stm32l4x5_soc_realize(DeviceState *dev_soc, Error **errp)
         return;
     }
 
+    dev = DEVICE(&s->exti);
+    if (!sysbus_realize(SYS_BUS_DEVICE(&s->exti), errp)) {
+        return;
+    }
+    busdev = SYS_BUS_DEVICE(dev);
+    sysbus_mmio_map(busdev, 0, exti_addr);
+    for (i = 0; i < NUM_EXTI_IRQ; i++) {
+        /* IRQ seems not to be connected ? */
+        sysbus_connect_irq(busdev, i, qdev_get_gpio_in(armv7m, exti_irq[i]));
+    }
+
+    /*
+     * Uncomment when Syscfg is implemented
+     * for (i = 0; i < 16; i++) {
+     *     qdev_connect_gpio_out(DEVICE(&s->syscfg), i,
+     *                           qdev_get_gpio_in(dev, i));
+     * }
+     */
+
     /* APB1 BUS */
     create_unimplemented_device("TIM2",      0x40000000, 0x400);
     create_unimplemented_device("TIM3",      0x40000400, 0x400);
@@ -162,7 +224,6 @@ static void stm32l4x5_soc_realize(DeviceState *dev_soc, Error **errp)
     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/misc/Kconfig b/hw/misc/Kconfig
index cc8a8c1418..3efe3dc2cc 100644
--- a/hw/misc/Kconfig
+++ b/hw/misc/Kconfig
@@ -87,6 +87,9 @@ config STM32F4XX_SYSCFG
 config STM32F4XX_EXTI
     bool
 
+config STM32L4X5_EXTI
+    bool
+
 config MIPS_ITU
     bool
 
diff --git a/hw/misc/meson.build b/hw/misc/meson.build
index 36c20d5637..16db6e228d 100644
--- a/hw/misc/meson.build
+++ b/hw/misc/meson.build
@@ -110,6 +110,7 @@ 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_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 0000000000..f0a535672c
--- /dev/null
+++ b/hw/misc/stm32l4x5_exti.c
@@ -0,0 +1,329 @@
+/*
+ * STM32L4x5 SoC family EXTI
+ *
+ * Copyright (c) 2014 Alistair Francis <alistair@alistair23.me>
+ * Copyright (c) 2023 Arnaud Minier <arnaud.minier@telecom-paris.fr>
+ * Copyright (c) 2023 Inès Varhol <ines.varhol@telecom-paris.fr>
+ *
+ * SPDX-License-Identifier: MIT
+ *
+ * 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.
+ */
+
+/* stm32l4x5_exti implementation is derived from stm32f4xx_exti */
+
+#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
+
+/* 0b11111111_10000010_00000000_00000000 */
+#define DIRECT_LINE_MASK1 0xFF820000
+/* 0b00000000_00000000_00000000_10000111 */
+#define DIRECT_LINE_MASK2 0x00000087
+/* 0b11111111_11111111_11111111_00000000 */
+#define RESERVED_BITS_MASK_EXTI_xMR2 0xFFFFFF00
+
+/* 0b00000000_00000000_00000000_01111000 */
+#define ACTIVABLE_xR2 (~DIRECT_LINE_MASK2 & ~RESERVED_BITS_MASK_EXTI_xMR2)
+
+static void stm32l4x5_exti_reset(DeviceState *dev)
+{
+    Stm32l4x5ExtiState *s = STM32L4X5_EXTI(dev);
+
+    s->exti_imr1 = DIRECT_LINE_MASK1;
+    s->exti_emr1 = 0x00000000;
+    s->exti_rtsr1 = 0x00000000;
+    s->exti_ftsr1 = 0x00000000;
+    s->exti_swier1 = 0x00000000;
+    s->exti_pr1 = 0x00000000;
+
+    s->exti_imr2 = DIRECT_LINE_MASK2;
+    s->exti_emr2 = 0x00000000;
+    s->exti_rtsr2 = 0x00000000;
+    s->exti_ftsr2 = 0x00000000;
+    s->exti_swier2 = 0x00000000;
+    s->exti_pr2 = 0x00000000;
+}
+
+static void stm32l4x5_exti_set_irq(void *opaque, int irq, int level)
+{
+    Stm32l4x5ExtiState *s = opaque;
+
+    trace_stm32l4x5_exti_set_irq(irq, level);
+
+    if (irq >= NUM_INTERRUPT_OUT_LINES) {
+        return;
+    }
+
+    if (irq < 32) {
+        if (((1 << irq) & s->exti_rtsr1) && level) {
+            /* Rising Edge */
+            s->exti_pr1 |= 1 << irq;
+        }
+
+        if (((1 << irq) & s->exti_ftsr1) && !level) {
+            /* Falling Edge */
+            s->exti_pr1 |= 1 << irq;
+        }
+
+        if (!((1 << irq) & s->exti_imr1)) {
+            /* Interrupt is masked */
+            return;
+        }
+    } else {
+        /* Shift the value to enable access in x2 registers*/
+        int irq_x2 = irq - 32;
+        if (((1 << irq_x2) & s->exti_rtsr2) && level) {
+            /* Rising Edge */
+            s->exti_pr2 |= 1 << irq_x2;
+        }
+
+        if (((1 << irq_x2) & s->exti_ftsr2) && !level) {
+            /* Falling Edge */
+            s->exti_pr2 |= 1 << irq_x2;
+        }
+
+        if (!((1 << irq_x2) & s->exti_imr2)) {
+            /* Interrupt is masked */
+            return;
+        }
+    }
+    qemu_irq_pulse(s->irq[irq]);
+}
+
+static uint64_t stm32l4x5_exti_read(void *opaque, hwaddr addr,
+                                    unsigned int size)
+{
+    Stm32l4x5ExtiState *s = opaque;
+    uint32_t r = 0;
+
+    switch (addr) {
+    case EXTI_IMR1:
+        r = s->exti_imr1;
+        break;
+    case EXTI_EMR1:
+        r = s->exti_emr1;
+        break;
+    case EXTI_RTSR1:
+        r = s->exti_rtsr1;
+        break;
+    case EXTI_FTSR1:
+        r = s->exti_ftsr1;
+        break;
+    case EXTI_SWIER1:
+        r = s->exti_swier1;
+        break;
+    case EXTI_PR1:
+        r = s->exti_pr1;
+        break;
+    case EXTI_IMR2:
+        r = s->exti_imr2;
+        break;
+    case EXTI_EMR2:
+        r = s->exti_emr2;
+        break;
+    case EXTI_RTSR2:
+        r = s->exti_rtsr2;
+        break;
+    case EXTI_FTSR2:
+        r = s->exti_ftsr2;
+        break;
+    case EXTI_SWIER2:
+        r = s->exti_swier2;
+        break;
+    case EXTI_PR2:
+        r = s->exti_pr2;
+        break;
+
+    default:
+        qemu_log_mask(LOG_GUEST_ERROR,
+                      "STM32L4X5_exti_read: Bad offset %x\n", (int)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;
+    uint32_t value = (uint32_t)val64;
+
+    trace_stm32l4x5_exti_write(addr, value);
+
+    switch (addr) {
+    case EXTI_IMR1:
+        s->exti_imr1 = value;
+        return;
+    case EXTI_EMR1:
+        s->exti_emr1 = value;
+        return;
+    case EXTI_RTSR1:
+        s->exti_rtsr1 = value & ~DIRECT_LINE_MASK1;
+        return;
+    case EXTI_FTSR1:
+        s->exti_ftsr1 = value & ~DIRECT_LINE_MASK1;
+        return;
+    case EXTI_SWIER1:
+        s->exti_swier1 = value & ~DIRECT_LINE_MASK1;
+        for (int i = 0; i < 32; i++) {
+            const uint32_t mask = 1 << i;
+            if (s->exti_swier1 & s->exti_imr1 & mask) {
+                s->exti_pr1 |= mask;
+                qemu_irq_pulse(s->irq[i]);
+            }
+        }
+        return;
+    case EXTI_PR1:
+        /* This bit is cleared by writing a 1 to it */
+        s->exti_pr1 &= ~(value & ~DIRECT_LINE_MASK1);
+        /* Don't forget to clean software interrupts */
+        s->exti_swier1 &= ~(value & ~DIRECT_LINE_MASK1);
+        for (int i = 0; i < 32; i++) {
+            const uint32_t mask = 1 << i;
+            if (!(s->exti_pr1 & mask)) {
+                qemu_irq_lower(s->irq[i]);
+            }
+        }
+        return;
+    case EXTI_IMR2:
+        s->exti_imr2 = value & ~RESERVED_BITS_MASK_EXTI_xMR2;
+        return;
+    case EXTI_EMR2:
+        s->exti_emr2 = value & ~RESERVED_BITS_MASK_EXTI_xMR2;
+        return;
+    case EXTI_RTSR2:
+        s->exti_rtsr2 = value & ACTIVABLE_xR2;
+        return;
+    case EXTI_FTSR2:
+        s->exti_ftsr2 = value & ACTIVABLE_xR2;
+        return;
+    case EXTI_SWIER2:
+        s->exti_swier2 = value & ACTIVABLE_xR2;
+        for (int i = 0; i < 8; i++) {
+            const uint32_t mask = 1 << i;
+            if (s->exti_swier2 & s->exti_imr2 & mask) {
+                s->exti_pr2 |= mask;
+                qemu_irq_raise(s->irq[32 + i]);
+            }
+        }
+        return;
+    case EXTI_PR2:
+        /* This bit is cleared by writing a 1 to it */
+        s->exti_pr2 &= ~value | ~ACTIVABLE_xR2;
+        /* Don't forget to clean software interrupts */
+        s->exti_swier2 &= ~value | ~ACTIVABLE_xR2;
+        for (int i = 0; i < 8; i++) {
+            const uint32_t mask = 1 << i;
+            if (!(s->exti_pr2 & mask)) {
+                qemu_irq_lower(s->irq[32 + i]);
+            }
+        }
+        return;
+    default:
+        qemu_log_mask(LOG_GUEST_ERROR,
+                      "STM32L4X5_exti_write: Bad offset %x\n", (int)addr);
+    }
+}
+
+static const MemoryRegionOps stm32l4x5_exti_ops = {
+    .read = stm32l4x5_exti_read,
+    .write = stm32l4x5_exti_write,
+    .endianness = DEVICE_NATIVE_ENDIAN,
+};
+
+static void stm32l4x5_exti_init(Object *obj)
+{
+    Stm32l4x5ExtiState *s = STM32L4X5_EXTI(obj);
+    int i;
+
+    for (i = 0; i < 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,
+                      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(exti_imr1, Stm32l4x5ExtiState),
+        VMSTATE_UINT32(exti_emr1, Stm32l4x5ExtiState),
+        VMSTATE_UINT32(exti_rtsr1, Stm32l4x5ExtiState),
+        VMSTATE_UINT32(exti_ftsr1, Stm32l4x5ExtiState),
+        VMSTATE_UINT32(exti_swier1, Stm32l4x5ExtiState),
+        VMSTATE_UINT32(exti_pr1, Stm32l4x5ExtiState),
+        VMSTATE_UINT32(exti_imr2, Stm32l4x5ExtiState),
+        VMSTATE_UINT32(exti_emr2, Stm32l4x5ExtiState),
+        VMSTATE_UINT32(exti_rtsr2, Stm32l4x5ExtiState),
+        VMSTATE_UINT32(exti_ftsr2, Stm32l4x5ExtiState),
+        VMSTATE_UINT32(exti_swier2, Stm32l4x5ExtiState),
+        VMSTATE_UINT32(exti_pr2, Stm32l4x5ExtiState),
+        VMSTATE_END_OF_LIST()
+    }
+};
+
+static void stm32l4x5_exti_class_init(ObjectClass *klass, void *data)
+{
+    DeviceClass *dc = DEVICE_CLASS(klass);
+
+    dc->reset = stm32l4x5_exti_reset;
+    dc->vmsd = &vmstate_stm32l4x5_exti;
+}
+
+static const TypeInfo stm32l4x5_exti_info = {
+    .name          = TYPE_STM32L4X5_EXTI,
+    .parent        = TYPE_SYS_BUS_DEVICE,
+    .instance_size = sizeof(Stm32l4x5ExtiState),
+    .instance_init = stm32l4x5_exti_init,
+    .class_init    = stm32l4x5_exti_class_init,
+};
+
+static void stm32l4x5_exti_register_types(void)
+{
+    type_register_static(&stm32l4x5_exti_info);
+}
+
+type_init(stm32l4x5_exti_register_types)
diff --git a/hw/misc/trace-events b/hw/misc/trace-events
index 05ff692441..2f01c62c0e 100644
--- a/hw/misc/trace-events
+++ b/hw/misc/trace-events
@@ -163,6 +163,11 @@ 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_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/include/hw/arm/stm32l4x5_soc.h b/include/hw/arm/stm32l4x5_soc.h
index ab61c4b8a1..940941598e 100644
--- a/include/hw/arm/stm32l4x5_soc.h
+++ b/include/hw/arm/stm32l4x5_soc.h
@@ -32,6 +32,7 @@
 #include "qemu/units.h"
 #include "hw/qdev-core.h"
 #include "hw/arm/armv7m.h"
+#include "hw/misc/stm32l4x5_exti.h"
 #include "qom/object.h"
 
 #define TYPE_STM32L4X5_SOC "stm32l4x5-soc"
@@ -45,6 +46,8 @@ struct Stm32l4x5SocState {
 
     ARMv7MState armv7m;
 
+    Stm32l4x5ExtiState exti;
+
     MemoryRegion sram1;
     MemoryRegion sram2;
     MemoryRegion flash;
diff --git a/include/hw/misc/stm32l4x5_exti.h b/include/hw/misc/stm32l4x5_exti.h
new file mode 100644
index 0000000000..4305e7fcbb
--- /dev/null
+++ b/include/hw/misc/stm32l4x5_exti.h
@@ -0,0 +1,64 @@
+/*
+ * STM32L4x5 SoC family EXTI
+ *
+ * Copyright (c) 2014 Alistair Francis <alistair@alistair23.me>
+ * Copyright (c) 2023 Arnaud Minier <arnaud.minier@telecom-paris.fr>
+ * Copyright (c) 2023 Inès Varhol <ines.varhol@telecom-paris.fr>
+ *
+ * SPDX-License-Identifier: MIT
+ *
+ * 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.
+ */
+
+/* stm32l4x5_exti implementation is derived from stm32f4xx_exti */
+
+#ifndef HW_STM32L4X5_EXTI_H
+#define HW_STM32L4X5_EXTI_H
+
+#include "hw/sysbus.h"
+#include "qom/object.h"
+
+#define TYPE_STM32L4X5_EXTI "stm32l4x5-exti"
+OBJECT_DECLARE_SIMPLE_TYPE(Stm32l4x5ExtiState, STM32L4X5_EXTI)
+
+#define NUM_GPIO_EVENT_IN_LINES 16
+#define NUM_INTERRUPT_OUT_LINES 40
+
+struct Stm32l4x5ExtiState {
+    SysBusDevice parent_obj;
+
+    MemoryRegion mmio;
+
+    uint32_t exti_imr1;
+    uint32_t exti_emr1;
+    uint32_t exti_rtsr1;
+    uint32_t exti_ftsr1;
+    uint32_t exti_swier1;
+    uint32_t exti_pr1;
+    uint32_t exti_imr2;
+    uint32_t exti_emr2;
+    uint32_t exti_rtsr2;
+    uint32_t exti_ftsr2;
+    uint32_t exti_swier2;
+    uint32_t exti_pr2;
+
+    qemu_irq irq[NUM_INTERRUPT_OUT_LINES];
+};
+
+#endif
diff --git a/tests/qtest/meson.build b/tests/qtest/meson.build
index c9945e69b1..bc62f201a8 100644
--- a/tests/qtest/meson.build
+++ b/tests/qtest/meson.build
@@ -194,6 +194,10 @@ qtests_aspeed = \
   ['aspeed_hace-test',
    'aspeed_smc-test',
    'aspeed_gpio-test']
+
+qtests_stm32l4x5 = \
+  ['stm32l4x5_exti-test']
+
 qtests_arm = \
   (config_all_devices.has_key('CONFIG_MPS2') ? ['sse-timer-test'] : []) + \
   (config_all_devices.has_key('CONFIG_CMSDK_APB_DUALTIMER') ? ['cmsdk-apb-dualtimer-test'] : []) + \
@@ -207,6 +211,7 @@ qtests_arm = \
   (config_all_devices.has_key('CONFIG_TPM_TIS_I2C') ? ['tpm-tis-i2c-test'] : []) + \
   (config_all_devices.has_key('CONFIG_VEXPRESS') ? ['test-arm-mptimer'] : []) + \
   (config_all_devices.has_key('CONFIG_MICROBIT') ? ['microbit-test'] : []) + \
+  (config_all_devices.has_key('CONFIG_STM32L4X5_SOC') ? qtests_stm32l4x5 : []) + \
   ['arm-cpu-features',
    'boot-serial-test']
 
diff --git a/tests/qtest/stm32l4x5_exti-test.c b/tests/qtest/stm32l4x5_exti-test.c
new file mode 100644
index 0000000000..a6cf04f7ba
--- /dev/null
+++ b/tests/qtest/stm32l4x5_exti-test.c
@@ -0,0 +1,102 @@
+/*
+ * QTest testcase for STML4XX_EXTI
+ *
+ * Copyright (c) 2023 Arnaud Minier <arnaud.minier@telecom-paris.fr>
+ * Copyright (c) 2023 Inès Varhol <ines.varhol@telecom-paris.fr>
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2 or later.
+ * See the COPYING file in the top-level directory.
+ */
+
+#include "qemu/osdep.h"
+#include "libqtest-single.h"
+
+#define EXTI_BASE_ADDR 0x40010400
+#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 GPIO_0_IRQ 6
+
+static void exti_writel(unsigned int offset, uint32_t value)
+{
+    writel(EXTI_BASE_ADDR + offset, value);
+}
+
+static uint32_t exti_readl(unsigned int offset)
+{
+    return readl(EXTI_BASE_ADDR + offset);
+}
+
+static void test_write_read(void)
+{
+    /* Test that we can write and retrieve a value from the device */
+    exti_writel(EXTI_IMR1, 0xFFFFFFFF);
+    const uint32_t imr1 = exti_readl(EXTI_IMR1);
+    g_assert_cmpuint(imr1, ==, 0xFFFFFFFF);
+
+    /* Test that reserved address are not written to */
+    exti_writel(EXTI_IMR2, 0xFFFFFFFF);
+    const uint32_t imr2 = exti_readl(EXTI_IMR2);
+    g_assert_cmpuint(imr2, ==, 0x000001FF);
+}
+
+static void test_direct_lines_write(void)
+{
+    /* Test that Direct Lines are not written to */
+    exti_writel(EXTI_RTSR2, 0xFFFFFFFF);
+    const uint32_t rtsr2 = exti_readl(EXTI_RTSR2);
+    g_assert_cmpuint(rtsr2, ==, 0x00000078);
+}
+
+static void test_software_interrupt(void)
+{
+    /* Test that we can launch irq using software in*/
+
+    g_assert_false(get_irq(GPIO_0_IRQ));
+    /* Bit 0 corresponds to GPIO Px_0 */
+    exti_writel(EXTI_IMR1, 0x00000001);
+    exti_writel(EXTI_SWIER1, 0x00000001);
+    uint32_t swier1 = exti_readl(EXTI_SWIER1);
+    uint32_t pr1 = exti_readl(EXTI_PR1);
+
+    g_assert_cmpuint(swier1, ==, 0x00000001);
+    g_assert_cmpuint(pr1, ==, 0x00000001);
+
+    g_assert_true(get_irq(GPIO_0_IRQ));
+
+    /* Reset the irq and check the registers state */
+    exti_writel(EXTI_PR1, 0x00000001);
+    swier1 = exti_readl(EXTI_SWIER1);
+    pr1 = exti_readl(EXTI_PR1);
+    g_assert_cmpuint(swier1, ==, 0x00000000);
+    g_assert_cmpuint(pr1, ==, 0x00000000);
+}
+
+int main(int argc, char **argv)
+{
+    int ret;
+
+    g_test_init(&argc, &argv, NULL);
+    g_test_set_nonfatal_assertions();
+
+    qtest_add_func("stm32l4x5/exti/write_read", test_write_read);
+    qtest_add_func("stm32l4x5/exti/direct_lines_write", test_direct_lines_write);
+    /* Fails for now, not implemented */
+    qtest_add_func("stm32l4x5/exti/software_interrupt", test_software_interrupt);
+
+    qtest_start("-machine b-l475e-iot01a");
+    ret = g_test_run();
+    qtest_end();
+
+    return ret;
+}
-- 
2.38.5


^ permalink raw reply related	[flat|nested] 4+ messages in thread

* [PATCH qemu 0/1] Implement the STM32L4x5 EXTI device
@ 2023-11-21 14:56 ~aminier
  2023-11-11 14:33 ` [PATCH qemu 1/1] Implement STM32L4x5 EXTI ~aminier
  2023-11-21 15:19 ` [PATCH qemu 0/1] Implement the STM32L4x5 EXTI device Philippe Mathieu-Daudé
  0 siblings, 2 replies; 4+ messages in thread
From: ~aminier @ 2023-11-21 14:56 UTC (permalink / raw)
  To: qemu-devel
  Cc: qemu-arm, alistair, philmd, peter.maydell, ines.varhol, arnaud.minier

This patch allows to emulate the STM32L4x5 EXTI device.
It implements register access and software interruptions.

This is RFC since we had troubles to make all tests pass.
More precisely, the line `g_assert_true(get_irq(GPIO_0_IRQ));` fails in
the software interrupts test. No irq seems to be fired.
(We also tested the stm32f4_exti (on which this implementation is based
upon) on but it also fails so don't really know if the test or the
implementation is incorrect)

Based-on: <170049810484.22920.612074576971878323-0@git.sr.ht>
([RFC v3 2/2] hw/arm: Add minimal support for the B-L475E-IOT01A board)

(We got the message id from
https://patchew.org/QEMU/170047309499.17129.4986209009679789101-0@git.sr.ht/
but don't really the standard procedure to get it. Sorry if this is
incorrect.)

Arnaud Minier (1):
  Implement STM32L4x5 EXTI

 hw/arm/Kconfig                    |   1 +
 hw/arm/stm32l4x5_soc.c            |  65 +++++-
 hw/misc/Kconfig                   |   3 +
 hw/misc/meson.build               |   1 +
 hw/misc/stm32l4x5_exti.c          | 329 ++++++++++++++++++++++++++++++
 hw/misc/trace-events              |   5 +
 include/hw/arm/stm32l4x5_soc.h    |   3 +
 include/hw/misc/stm32l4x5_exti.h  |  64 ++++++
 tests/qtest/meson.build           |   5 +
 tests/qtest/stm32l4x5_exti-test.c | 102 +++++++++
 10 files changed, 576 insertions(+), 2 deletions(-)
 create mode 100644 hw/misc/stm32l4x5_exti.c
 create mode 100644 include/hw/misc/stm32l4x5_exti.h
 create mode 100644 tests/qtest/stm32l4x5_exti-test.c

-- 
2.38.5


^ permalink raw reply	[flat|nested] 4+ messages in thread

* Re: [PATCH qemu 0/1] Implement the STM32L4x5 EXTI device
  2023-11-21 14:56 [PATCH qemu 0/1] Implement the STM32L4x5 EXTI device ~aminier
  2023-11-11 14:33 ` [PATCH qemu 1/1] Implement STM32L4x5 EXTI ~aminier
@ 2023-11-21 15:19 ` Philippe Mathieu-Daudé
  1 sibling, 0 replies; 4+ messages in thread
From: Philippe Mathieu-Daudé @ 2023-11-21 15:19 UTC (permalink / raw)
  To: ~aminier, qemu-devel; +Cc: qemu-arm, alistair, peter.maydell, ines.varhol

Hi Arnaud,

On 21/11/23 15:56, ~aminier wrote:
> This patch allows to emulate the STM32L4x5 EXTI device.
> It implements register access and software interruptions.


> Based-on: <170049810484.22920.612074576971878323-0@git.sr.ht>
> ([RFC v3 2/2] hw/arm: Add minimal support for the B-L475E-IOT01A board)
> 
> (We got the message id from
> https://patchew.org/QEMU/170047309499.17129.4986209009679789101-0@git.sr.ht/
> but don't really the standard procedure to get it. Sorry if this is
> incorrect.)

This is the correct tag and msg-id :)

> Arnaud Minier (1):
>    Implement STM32L4x5 EXTI



^ permalink raw reply	[flat|nested] 4+ messages in thread

* Re: [PATCH qemu 1/1] Implement STM32L4x5 EXTI
  2023-11-11 14:33 ` [PATCH qemu 1/1] Implement STM32L4x5 EXTI ~aminier
@ 2023-11-21 21:07   ` Philippe Mathieu-Daudé
  0 siblings, 0 replies; 4+ messages in thread
From: Philippe Mathieu-Daudé @ 2023-11-21 21:07 UTC (permalink / raw)
  To: ~aminier, qemu-devel; +Cc: qemu-arm, alistair, peter.maydell, ines.varhol

Hi Arnaud,

On 11/11/23 15:33, ~aminier wrote:
> From: Arnaud Minier <arnaud.minier@telecom-paris.fr>
> 
> Signed-off-by: Arnaud Minier <arnaud.minier@telecom-paris.fr>
> Signed-off-by: Inès Varhol <ines.varhol@telecom-paris.fr>
> ---
>   hw/arm/Kconfig                    |   1 +
>   hw/arm/stm32l4x5_soc.c            |  65 +++++-
>   hw/misc/Kconfig                   |   3 +
>   hw/misc/meson.build               |   1 +
>   hw/misc/stm32l4x5_exti.c          | 329 ++++++++++++++++++++++++++++++
>   hw/misc/trace-events              |   5 +
>   include/hw/arm/stm32l4x5_soc.h    |   3 +
>   include/hw/misc/stm32l4x5_exti.h  |  64 ++++++
>   tests/qtest/meson.build           |   5 +
>   tests/qtest/stm32l4x5_exti-test.c | 102 +++++++++
>   10 files changed, 576 insertions(+), 2 deletions(-)
>   create mode 100644 hw/misc/stm32l4x5_exti.c
>   create mode 100644 include/hw/misc/stm32l4x5_exti.h
>   create mode 100644 tests/qtest/stm32l4x5_exti-test.c


> diff --git a/hw/arm/stm32l4x5_soc.c b/hw/arm/stm32l4x5_soc.c
> index 198d3f6d3e..6f2a1b34b3 100644
> --- a/hw/arm/stm32l4x5_soc.c
> +++ b/hw/arm/stm32l4x5_soc.c
> @@ -43,10 +43,51 @@
>   #define SRAM2_BASE_ADDRESS 0x10000000
>   #define SRAM2_SIZE (32 * KiB)
>   
> +static const hwaddr exti_addr = 0x40010400;

Why not a #define?

> +#define NUM_EXTI_IRQ 40


> diff --git a/hw/misc/stm32l4x5_exti.c b/hw/misc/stm32l4x5_exti.c


> +static void stm32l4x5_exti_set_irq(void *opaque, int irq, int level)
> +{
> +    Stm32l4x5ExtiState *s = opaque;
> +
> +    trace_stm32l4x5_exti_set_irq(irq, level);
> +
> +    if (irq >= NUM_INTERRUPT_OUT_LINES) {
> +        return;

This can not happen. If you are unsure, this would be a programming
error, thus aborting is better, but nothing needed IMO.

> +    }
> +
> +    if (irq < 32) {
> +        if (((1 << irq) & s->exti_rtsr1) && level) {
> +            /* Rising Edge */
> +            s->exti_pr1 |= 1 << irq;
> +        }
> +
> +        if (((1 << irq) & s->exti_ftsr1) && !level) {
> +            /* Falling Edge */
> +            s->exti_pr1 |= 1 << irq;
> +        }
> +
> +        if (!((1 << irq) & s->exti_imr1)) {
> +            /* Interrupt is masked */
> +            return;
> +        }
> +    } else {
> +        /* Shift the value to enable access in x2 registers*/
> +        int irq_x2 = irq - 32;
> +        if (((1 << irq_x2) & s->exti_rtsr2) && level) {
> +            /* Rising Edge */
> +            s->exti_pr2 |= 1 << irq_x2;
> +        }
> +
> +        if (((1 << irq_x2) & s->exti_ftsr2) && !level) {
> +            /* Falling Edge */
> +            s->exti_pr2 |= 1 << irq_x2;
> +        }
> +
> +        if (!((1 << irq_x2) & s->exti_imr2)) {
> +            /* Interrupt is masked */
> +            return;
> +        }
> +    }
> +    qemu_irq_pulse(s->irq[irq]);
> +}

Could be simpler avoiding duplication, as:

---
static void stm32l4x5_exti_set_irq(void *opaque, int irq, int level)
{
     Stm32l4x5ExtiState *s = opaque;
     int oirq = irq;
     uint32_t *rtsr;
     uint32_t *ftsr;
     uint32_t *pr;
     uint32_t *imr;

     trace_stm32l4x5_exti_set_irq(irq, level);

     if (irq < 32) {
         rtsr = &s->exti_rtsr1;
         ftsr = &s->exti_ftsr1;
         pr = &s->exti_pr1;
         imr = &s->exti_imr1;
     } else {
         rtsr = &s->exti_rtsr2;
         ftsr = &s->exti_ftsr2;
         pr = &s->exti_pr2;
         imr = &s->exti_imr2;
         /* Shift the value to enable access in x2 registers. */
         irq -= 32;
     }

     if (((1 << irq) & *rtsr) && level) {
         /* Rising Edge */
         *pr |= 1 << irq;
     }

     if (((1 << irq) & *ftsr) && !level) {
         /* Falling Edge */
         *pr |= 1 << irq;
     }

     if (!((1 << irq) & *imr)) {
         /* Interrupt is masked */
         return;
     }

     qemu_irq_pulse(s->irq[oirq]);
}
---

But changing Stm32l4x5ExtiState as:

---
struct Stm32l4x5ExtiState {
     SysBusDevice parent_obj;

     MemoryRegion mmio;

     uint32_t imr[2];
     uint32_t emr[2];
     uint32_t rtsr[2];
     uint32_t ftsr[2];
     uint32_t swier[2];
     uint32_t pr[2];

     qemu_irq irq[NUM_INTERRUPT_OUT_LINES];
};
---

We get even simpler:

---
static void stm32l4x5_exti_set_irq(void *opaque, int irq, int level)
{
     Stm32l4x5ExtiState *s = opaque;
     unsigned n = irq >= 32;
     int oirq = irq;

     trace_stm32l4x5_exti_set_irq(irq, level);

     if (irq >= 32) {
         /* Shift the value to enable access in x2 registers. */
         irq -= 32;
     }

     if (((1 << irq) & s->rtsr[n]) && level) {
         /* Rising Edge */
         s->pr[n] |= 1 << irq;
     }

     if (((1 << irq) & s->ftsr[n]) && !level) {
         /* Falling Edge */
         s->pr[n] |= 1 << irq;
     }

     if (!((1 << irq) & s->imr[n])) {
         /* Interrupt is masked */
         return;
     }

     qemu_irq_pulse(s->irq[oirq]);
}
---

(code untested).

> +
> +static uint64_t stm32l4x5_exti_read(void *opaque, hwaddr addr,
> +                                    unsigned int size)
> +{
> +    Stm32l4x5ExtiState *s = opaque;
> +    uint32_t r = 0;

        unsigned n = addr >= EXTI_IMR2;
> +
> +    switch (addr) {
> +    case EXTI_IMR1:
> +        r = s->exti_imr1;
> +        break;

This becomes:

        case EXTI_IMR1:
        case EXTI_IMR2:
            r = s->exti_imr[n];
            break;
        ...

> +    default:
> +        qemu_log_mask(LOG_GUEST_ERROR,
> +                      "STM32L4X5_exti_read: Bad offset %x\n", (int)addr);

Please use '0x' prefix for hexadecimal.

> +        break;
> +    }
> +
> +    trace_stm32l4x5_exti_read(addr, r);
> +
> +    return r;
> +}


> +static const MemoryRegionOps stm32l4x5_exti_ops = {
> +    .read = stm32l4x5_exti_read,
> +    .write = stm32l4x5_exti_write,
> +    .endianness = DEVICE_NATIVE_ENDIAN,

Your implementation is 32-bit wide (all your registers are),
so:

        .impl.min_access_size = 4,
        .impl.max_access_size = 4,

What are the allowed accesses? any 8/16/32/64 bits?
(This is what happens when .valid fields aren't set).

> +};


> +static void stm32l4x5_exti_class_init(ObjectClass *klass, void *data)
> +{
> +    DeviceClass *dc = DEVICE_CLASS(klass);
> +
> +    dc->reset = stm32l4x5_exti_reset;

Better set a ResettableClass handler. DeviceClass::reset will soon
be deprecated.

> +    dc->vmsd = &vmstate_stm32l4x5_exti;
> +}


> +static void stm32l4x5_exti_register_types(void)
> +{
> +    type_register_static(&stm32l4x5_exti_info);
> +}

Preferably use the DEFINE_TYPES() macro.

> +type_init(stm32l4x5_exti_register_types)


> diff --git a/include/hw/misc/stm32l4x5_exti.h b/include/hw/misc/stm32l4x5_exti.h
> new file mode 100644
> index 0000000000..4305e7fcbb
> --- /dev/null
> +++ b/include/hw/misc/stm32l4x5_exti.h
> @@ -0,0 +1,64 @@
> +/*
> + * STM32L4x5 SoC family EXTI


"STM32L4x5 EXTI (Extended interrupts and events controller)"


> +#define NUM_GPIO_EVENT_IN_LINES 16

Since not used externally, NUM_GPIO_EVENT_IN_LINES could be
restricted to the source.

> +#define NUM_INTERRUPT_OUT_LINES 40
> +
> +struct Stm32l4x5ExtiState {
> +    SysBusDevice parent_obj;
> +
> +    MemoryRegion mmio;
> +
> +    uint32_t exti_imr1;
> +    uint32_t exti_emr1;
> +    uint32_t exti_rtsr1;
> +    uint32_t exti_ftsr1;
> +    uint32_t exti_swier1;
> +    uint32_t exti_pr1;
> +    uint32_t exti_imr2;
> +    uint32_t exti_emr2;
> +    uint32_t exti_rtsr2;
> +    uint32_t exti_ftsr2;
> +    uint32_t exti_swier2;
> +    uint32_t exti_pr2;

See previous suggestion for Stm32l4x5ExtiState. Besides, no
need to use the cumbersome 'exti_' prefix.

> +    qemu_irq irq[NUM_INTERRUPT_OUT_LINES];
> +};
> +
> +#endif

Good patch quality, looking forward for v2!

Regards,

Phil.


^ permalink raw reply	[flat|nested] 4+ messages in thread

end of thread, other threads:[~2023-11-21 21:09 UTC | newest]

Thread overview: 4+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2023-11-21 14:56 [PATCH qemu 0/1] Implement the STM32L4x5 EXTI device ~aminier
2023-11-11 14:33 ` [PATCH qemu 1/1] Implement STM32L4x5 EXTI ~aminier
2023-11-21 21:07   ` Philippe Mathieu-Daudé
2023-11-21 15:19 ` [PATCH qemu 0/1] Implement the STM32L4x5 EXTI device Philippe Mathieu-Daudé

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.