qemu-devel.nongnu.org archive mirror
 help / color / mirror / Atom feed
* [PATCH 1/4] STM32F4: new RCC device
@ 2024-09-27 15:07 Román Cárdenas Rodríguez
  2024-09-27 15:07 ` [PATCH 2/4] Add RCC device to stm32f405 SoC Román Cárdenas Rodríguez
                   ` (3 more replies)
  0 siblings, 4 replies; 9+ messages in thread
From: Román Cárdenas Rodríguez @ 2024-09-27 15:07 UTC (permalink / raw)
  To: qemu-arm
  Cc: alistair, peter.maydell, qemu-devel,
	Román Cárdenas Rodríguez

Generic RCC class for STM32 devices. It can be used for most of STM32 chips.
Note that it only implements enable and reset capabilities.

Signed-off-by: Roman Cardenas Rodriguez <rcardenas.rod@gmail.com>
---
 hw/misc/Kconfig             |   3 +
 hw/misc/meson.build         |   1 +
 hw/misc/stm32_rcc.c         | 162 ++++++++++++++++++++++++++++++++++++
 hw/misc/trace-events        |   6 ++
 include/hw/misc/stm32_rcc.h |  91 ++++++++++++++++++++
 5 files changed, 263 insertions(+)
 create mode 100644 hw/misc/stm32_rcc.c
 create mode 100644 include/hw/misc/stm32_rcc.h

diff --git a/hw/misc/Kconfig b/hw/misc/Kconfig
index 1e08785b83..6bdc77cbe5 100644
--- a/hw/misc/Kconfig
+++ b/hw/misc/Kconfig
@@ -82,6 +82,9 @@ config IMX
     select SSI
     select USB_EHCI_SYSBUS
 
+config STM32_RCC
+    bool
+
 config STM32F2XX_SYSCFG
     bool
 
diff --git a/hw/misc/meson.build b/hw/misc/meson.build
index 2ca8717be2..e3cc4c34e3 100644
--- a/hw/misc/meson.build
+++ b/hw/misc/meson.build
@@ -106,6 +106,7 @@ system_ss.add(when: 'CONFIG_XLNX_VERSAL', if_true: files(
 system_ss.add(when: 'CONFIG_XLNX_VERSAL_TRNG', if_true: files(
   'xlnx-versal-trng.c',
 ))
+system_ss.add(when: 'CONFIG_STM32_RCC', if_true: files('stm32_rcc.c'))
 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'))
diff --git a/hw/misc/stm32_rcc.c b/hw/misc/stm32_rcc.c
new file mode 100644
index 0000000000..26672b5b24
--- /dev/null
+++ b/hw/misc/stm32_rcc.c
@@ -0,0 +1,162 @@
+/*
+ * STM32 RCC (only reset and enable registers are implemented)
+ *
+ * Copyright (c) 2024 Román Cárdenas <rcardenas.rod@gmail.com>
+ *
+ * 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.
+ */
+
+#include "qemu/osdep.h"
+#include "qemu/log.h"
+#include "trace.h"
+#include "hw/irq.h"
+#include "migration/vmstate.h"
+#include "hw/misc/stm32_rcc.h"
+
+static void stm32_rcc_reset(DeviceState *dev)
+{
+    STM32RccState *s = STM32_RCC(dev);
+
+    for (int i = 0; i < STM32_RCC_NREGS; i++) {
+        s->regs[i] = 0;
+    }
+}
+
+static uint64_t stm32_rcc_read(void *opaque, hwaddr addr, unsigned int size)
+{
+    STM32RccState *s = STM32_RCC(opaque);
+
+    uint32_t value = 0;
+    if (addr > STM32_RCC_DCKCFGR2) {
+        qemu_log_mask(LOG_GUEST_ERROR, "%s: Bad offset 0x%"HWADDR_PRIx"\n",
+                      __func__, addr);
+    } else {
+        value = s->regs[addr >> 2];
+    }
+    trace_stm32_rcc_read(addr, value);
+    return value;
+}
+
+static void stm32_rcc_write(void *opaque, hwaddr addr,
+                            uint64_t val64, unsigned int size)
+{
+    STM32RccState *s = STM32_RCC(opaque);
+    uint32_t value = val64;
+    uint32_t prev_value, new_value, irq_offset;
+
+    trace_stm32_rcc_write(value, addr);
+
+    if (addr > STM32_RCC_DCKCFGR2) {
+        qemu_log_mask(LOG_GUEST_ERROR, "%s: Bad offset 0x%"HWADDR_PRIx"\n",
+                      __func__, addr);
+        return;
+    }
+
+    switch (addr) {
+    case STM32_RCC_AHB1_RSTR...STM32_RCC_APB2_RSTR:
+        prev_value = s->regs[addr / 4];
+        s->regs[addr / 4] = value;
+
+        irq_offset = ((addr - STM32_RCC_AHB1_RSTR) / 4) * 32;
+        for (int i = 0; i < 32; i++) {
+            new_value = extract32(value, i, 1);
+            if (extract32(prev_value, i, 1) && !new_value) {
+                trace_stm32_rcc_pulse_reset(irq_offset + i, new_value);
+                qemu_set_irq(s->reset_irq[irq_offset + i], new_value);
+            }
+        }
+        return;
+    case STM32_RCC_AHB1_ENR...STM32_RCC_APB2_ENR:
+        prev_value = s->regs[addr / 4];
+        s->regs[addr / 4] = value;
+
+        irq_offset = ((addr - STM32_RCC_AHB1_ENR) / 4) * 32;
+        for (int i = 0; i < 32; i++) {
+            new_value = extract32(value, i, 1);
+            if (!extract32(prev_value, i, 1) && new_value) {
+                trace_stm32_rcc_pulse_enable(irq_offset + i, new_value);
+                qemu_set_irq(s->enable_irq[irq_offset + i], new_value);
+            }
+        }
+        return;
+    default:
+        qemu_log_mask(
+            LOG_UNIMP,
+            "%s: The RCC peripheral only supports enable and reset in QEMU\n",
+            __func__
+        );
+        s->regs[addr >> 2] = value;
+    }
+}
+
+static const MemoryRegionOps stm32_rcc_ops = {
+    .read = stm32_rcc_read,
+    .write = stm32_rcc_write,
+    .endianness = DEVICE_NATIVE_ENDIAN,
+};
+
+static void stm32_rcc_init(Object *obj)
+{
+    STM32RccState *s = STM32_RCC(obj);
+
+    memory_region_init_io(&s->mmio, obj, &stm32_rcc_ops, s,
+                          TYPE_STM32_RCC, STM32_RCC_PERIPHERAL_SIZE);
+    sysbus_init_mmio(SYS_BUS_DEVICE(obj), &s->mmio);
+
+    qdev_init_gpio_out(DEVICE(obj), s->reset_irq, STM32_RCC_NIRQS);
+    qdev_init_gpio_out(DEVICE(obj), s->enable_irq, STM32_RCC_NIRQS);
+
+    for (int i = 0; i < STM32_RCC_NIRQS; i++) {
+        sysbus_init_irq(SYS_BUS_DEVICE(obj), &s->reset_irq[i]);
+        sysbus_init_irq(SYS_BUS_DEVICE(obj), &s->enable_irq[i]);
+    }
+}
+
+static const VMStateDescription vmstate_stm32_rcc = {
+    .name = TYPE_STM32_RCC,
+    .version_id = 1,
+    .minimum_version_id = 1,
+    .fields = (const VMStateField[]) {
+        VMSTATE_UINT32_ARRAY(regs, STM32RccState, STM32_RCC_NREGS),
+        VMSTATE_END_OF_LIST()
+    }
+};
+
+static void stm32_rcc_class_init(ObjectClass *klass, void *data)
+{
+    DeviceClass *dc = DEVICE_CLASS(klass);
+
+    dc->vmsd = &vmstate_stm32_rcc;
+    device_class_set_legacy_reset(dc, stm32_rcc_reset);
+}
+
+static const TypeInfo stm32_rcc_info = {
+    .name          = TYPE_STM32_RCC,
+    .parent        = TYPE_SYS_BUS_DEVICE,
+    .instance_size = sizeof(STM32RccState),
+    .instance_init = stm32_rcc_init,
+    .class_init    = stm32_rcc_class_init,
+};
+
+static void stm32_rcc_register_types(void)
+{
+    type_register_static(&stm32_rcc_info);
+}
+
+type_init(stm32_rcc_register_types)
diff --git a/hw/misc/trace-events b/hw/misc/trace-events
index 1be0717c0c..b9fbcb0924 100644
--- a/hw/misc/trace-events
+++ b/hw/misc/trace-events
@@ -156,6 +156,12 @@ npcm7xx_pwm_write(const char *id, uint64_t offset, uint32_t value) "%s offset: 0
 npcm7xx_pwm_update_freq(const char *id, uint8_t index, uint32_t old_value, uint32_t new_value) "%s pwm[%u] Update Freq: old_freq: %u, new_freq: %u"
 npcm7xx_pwm_update_duty(const char *id, uint8_t index, uint32_t old_value, uint32_t new_value) "%s pwm[%u] Update Duty: old_duty: %u, new_duty: %u"
 
+# stm32_rcc.c
+stm32_rcc_read(uint64_t addr, uint64_t data) "reg read: addr: 0x%" PRIx64 " val: 0x%" PRIx64 ""
+stm32_rcc_write(uint64_t addr, uint64_t data) "reg write: addr: 0x%" PRIx64 " val: 0x%" PRIx64 ""
+stm32_rcc_pulse_enable(int line, int level) "Enable: %d to %d"
+stm32_rcc_pulse_reset(int line, int level) "Reset: %d to %d"
+
 # stm32f4xx_syscfg.c
 stm32f4xx_syscfg_set_irq(int gpio, int line, int level) "Interrupt: GPIO: %d, Line: %d; Level: %d"
 stm32f4xx_pulse_exti(int irq) "Pulse EXTI: %d"
diff --git a/include/hw/misc/stm32_rcc.h b/include/hw/misc/stm32_rcc.h
new file mode 100644
index 0000000000..ffbdf202ea
--- /dev/null
+++ b/include/hw/misc/stm32_rcc.h
@@ -0,0 +1,91 @@
+/*
+ * STM32 RCC (only reset and enable registers are implemented)
+ *
+ * Copyright (c) 2024 Román Cárdenas <rcardenas.rod@gmail.com>
+ *
+ * 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 HW_STM32_RCC_H
+#define HW_STM32_RCC_H
+
+#include "hw/sysbus.h"
+#include "qom/object.h"
+
+#define STM32_RCC_CR 0x00
+#define STM32_RCC_PLL_CFGR 0x04
+#define STM32_RCC_CFGR 0x08
+#define STM32_RCC_CIR 0x0C
+#define STM32_RCC_AHB1_RSTR 0x10
+#define STM32_RCC_AHB2_RSTR 0x14
+#define STM32_RCC_AHB3_RSTR 0x18
+
+#define STM32_RCC_APB1_RSTR 0x20
+#define STM32_RCC_APB2_RSTR 0x24
+
+#define STM32_RCC_AHB1_ENR 0x30
+#define STM32_RCC_AHB2_ENR 0x34
+#define STM32_RCC_AHB3_ENR 0x38
+
+#define STM32_RCC_APB1_ENR 0x40
+#define STM32_RCC_APB2_ENR 0x44
+
+#define STM32_RCC_AHB1_LPENR 0x50
+#define STM32_RCC_AHB2_LPENR 0x54
+#define STM32_RCC_AHB3_LPENR 0x58
+
+#define STM32_RCC_APB1_LPENR 0x60
+#define STM32_RCC_APB2_LPENR 0x64
+
+#define STM32_RCC_BDCR 0x70
+#define STM32_RCC_CSR 0x74
+
+#define STM32_RCC_SSCGR 0x80
+#define STM32_RCC_PLLI2SCFGR 0x84
+#define STM32_RCC_PLLSAI_CFGR 0x88
+#define STM32_RCC_DCKCFGR 0x8C
+#define STM32_RCC_CKGATENR 0x90
+#define STM32_RCC_DCKCFGR2 0x94
+
+#define STM32_RCC_NREGS ((STM32_RCC_DCKCFGR2 >> 2) + 1)
+#define STM32_RCC_PERIPHERAL_SIZE 0x400
+#define STM32_RCC_NIRQS (32 * 5) /* 32 bits per reg, 5 en/rst regs */
+
+#define STM32_RCC_GPIO_IRQ_OFFSET 0
+
+#define TYPE_STM32_RCC "stm32.rcc"
+
+typedef struct STM32RccState STM32RccState;
+
+DECLARE_INSTANCE_CHECKER(STM32RccState, STM32_RCC, TYPE_STM32_RCC)
+
+#define NUM_GPIO_EVENT_IN_LINES 16
+
+struct STM32RccState {
+    SysBusDevice parent_obj;
+
+    MemoryRegion mmio;
+
+    uint32_t regs[STM32_RCC_NREGS];
+
+    qemu_irq enable_irq[STM32_RCC_NIRQS];
+    qemu_irq reset_irq[STM32_RCC_NIRQS];
+};
+
+#endif /* HW_STM32_RCC_H */
-- 
2.39.3 (Apple Git-146)



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

* [PATCH 2/4] Add RCC device to stm32f405 SoC
  2024-09-27 15:07 [PATCH 1/4] STM32F4: new RCC device Román Cárdenas Rodríguez
@ 2024-09-27 15:07 ` Román Cárdenas Rodríguez
  2024-09-27 15:07 ` [PATCH 3/4] STM32: new GPIO device Román Cárdenas Rodríguez
                   ` (2 subsequent siblings)
  3 siblings, 0 replies; 9+ messages in thread
From: Román Cárdenas Rodríguez @ 2024-09-27 15:07 UTC (permalink / raw)
  To: qemu-arm
  Cc: alistair, peter.maydell, qemu-devel,
	Román Cárdenas Rodríguez

Signed-off-by: Roman Cardenas Rodriguez <rcardenas.rod@gmail.com>
---
 docs/system/arm/stm32.rst      |  3 ++-
 hw/arm/Kconfig                 |  1 +
 hw/arm/stm32f405_soc.c         | 12 +++++++++++-
 include/hw/arm/stm32f405_soc.h |  2 ++
 4 files changed, 16 insertions(+), 2 deletions(-)

diff --git a/docs/system/arm/stm32.rst b/docs/system/arm/stm32.rst
index 3b640f3ee0..ca7a55841b 100644
--- a/docs/system/arm/stm32.rst
+++ b/docs/system/arm/stm32.rst
@@ -36,6 +36,7 @@ Supported devices
  * SPI controller
  * System configuration (SYSCFG)
  * Timer controller (TIMER)
+ * Reset and Clock Controller (RCC) (STM32F4 only, reset and enable only)
 
 Missing devices
 ---------------
@@ -53,7 +54,7 @@ Missing devices
  * Power supply configuration (PWR)
  * Random Number Generator (RNG)
  * Real-Time Clock (RTC) controller
- * Reset and Clock Controller (RCC)
+ * Reset and Clock Controller (RCC) (other features than reset and enable)
  * Secure Digital Input/Output (SDIO) interface
  * USB OTG
  * Watchdog controller (IWDG, WWDG)
diff --git a/hw/arm/Kconfig b/hw/arm/Kconfig
index 1ad60da7aa..0629f47cb3 100644
--- a/hw/arm/Kconfig
+++ b/hw/arm/Kconfig
@@ -466,6 +466,7 @@ config STM32F405_SOC
     bool
     select ARM_V7M
     select OR_IRQ
+    select STM32_RCC
     select STM32F4XX_SYSCFG
     select STM32F4XX_EXTI
 
diff --git a/hw/arm/stm32f405_soc.c b/hw/arm/stm32f405_soc.c
index 2ad5b79a06..72ae62156f 100644
--- a/hw/arm/stm32f405_soc.c
+++ b/hw/arm/stm32f405_soc.c
@@ -30,6 +30,7 @@
 #include "hw/qdev-clock.h"
 #include "hw/misc/unimp.h"
 
+#define RCC_ADDR                       0x40023800
 #define SYSCFG_ADD                     0x40013800
 static const uint32_t usart_addr[] = { 0x40011000, 0x40004400, 0x40004800,
                                        0x40004C00, 0x40005000, 0x40011400,
@@ -59,6 +60,8 @@ static void stm32f405_soc_initfn(Object *obj)
 
     object_initialize_child(obj, "armv7m", &s->armv7m, TYPE_ARMV7M);
 
+    object_initialize_child(obj, "rcc", &s->rcc, TYPE_STM32_RCC);
+
     object_initialize_child(obj, "syscfg", &s->syscfg, TYPE_STM32F4XX_SYSCFG);
 
     for (i = 0; i < STM_NUM_USARTS; i++) {
@@ -160,6 +163,14 @@ static void stm32f405_soc_realize(DeviceState *dev_soc, Error **errp)
         return;
     }
 
+    /* Reset and clock controller */
+    dev = DEVICE(&s->rcc);
+    if (!sysbus_realize(SYS_BUS_DEVICE(&s->rcc), errp)) {
+        return;
+    }
+    busdev = SYS_BUS_DEVICE(dev);
+    sysbus_mmio_map(busdev, 0, RCC_ADDR);
+
     /* System configuration controller */
     dev = DEVICE(&s->syscfg);
     if (!sysbus_realize(SYS_BUS_DEVICE(&s->syscfg), errp)) {
@@ -276,7 +287,6 @@ static void stm32f405_soc_realize(DeviceState *dev_soc, Error **errp)
     create_unimplemented_device("GPIOH",       0x40021C00, 0x400);
     create_unimplemented_device("GPIOI",       0x40022000, 0x400);
     create_unimplemented_device("CRC",         0x40023000, 0x400);
-    create_unimplemented_device("RCC",         0x40023800, 0x400);
     create_unimplemented_device("Flash Int",   0x40023C00, 0x400);
     create_unimplemented_device("BKPSRAM",     0x40024000, 0x400);
     create_unimplemented_device("DMA1",        0x40026000, 0x400);
diff --git a/include/hw/arm/stm32f405_soc.h b/include/hw/arm/stm32f405_soc.h
index d15c03c4b5..2eeada64de 100644
--- a/include/hw/arm/stm32f405_soc.h
+++ b/include/hw/arm/stm32f405_soc.h
@@ -25,6 +25,7 @@
 #ifndef HW_ARM_STM32F405_SOC_H
 #define HW_ARM_STM32F405_SOC_H
 
+#include "hw/misc/stm32_rcc.h"
 #include "hw/misc/stm32f4xx_syscfg.h"
 #include "hw/timer/stm32f2xx_timer.h"
 #include "hw/char/stm32f2xx_usart.h"
@@ -55,6 +56,7 @@ struct STM32F405State {
 
     ARMv7MState armv7m;
 
+    STM32RccState rcc;
     STM32F4xxSyscfgState syscfg;
     STM32F4xxExtiState exti;
     STM32F2XXUsartState usart[STM_NUM_USARTS];
-- 
2.39.3 (Apple Git-146)



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

* [PATCH 3/4] STM32: new GPIO device
  2024-09-27 15:07 [PATCH 1/4] STM32F4: new RCC device Román Cárdenas Rodríguez
  2024-09-27 15:07 ` [PATCH 2/4] Add RCC device to stm32f405 SoC Román Cárdenas Rodríguez
@ 2024-09-27 15:07 ` Román Cárdenas Rodríguez
  2024-10-04 18:08   ` Peter Maydell
  2024-10-04 18:23   ` Peter Maydell
  2024-09-27 15:07 ` [PATCH 4/4] Add GPIO device to stm32f405 SoC Román Cárdenas Rodríguez
  2024-10-04 18:11 ` [PATCH 1/4] STM32F4: new RCC device Peter Maydell
  3 siblings, 2 replies; 9+ messages in thread
From: Román Cárdenas Rodríguez @ 2024-09-27 15:07 UTC (permalink / raw)
  To: qemu-arm
  Cc: alistair, peter.maydell, qemu-devel,
	Román Cárdenas Rodríguez

Generic GPIO class for STM32 devices. It can be used for most of STM32 chips.
Note that it does not implement configuration locking mechanisms.

Signed-off-by: Roman Cardenas Rodriguez <rcardenas.rod@gmail.com>
---
 hw/gpio/Kconfig              |   3 +
 hw/gpio/meson.build          |   1 +
 hw/gpio/stm32_gpio.c         | 386 +++++++++++++++++++++++++++++++++++
 hw/gpio/trace-events         |   8 +
 include/hw/arm/stm32.h       |  41 ++++
 include/hw/gpio/stm32_gpio.h | 109 ++++++++++
 6 files changed, 548 insertions(+)
 create mode 100644 hw/gpio/stm32_gpio.c
 create mode 100644 include/hw/arm/stm32.h
 create mode 100644 include/hw/gpio/stm32_gpio.h

diff --git a/hw/gpio/Kconfig b/hw/gpio/Kconfig
index 19c97cc823..9601b7d1bf 100644
--- a/hw/gpio/Kconfig
+++ b/hw/gpio/Kconfig
@@ -17,6 +17,9 @@ config GPIO_PWR
 config SIFIVE_GPIO
     bool
 
+config STM32_GPIO
+    bool
+
 config STM32L4X5_GPIO
     bool
 
diff --git a/hw/gpio/meson.build b/hw/gpio/meson.build
index a7495d196a..81f2a5458e 100644
--- a/hw/gpio/meson.build
+++ b/hw/gpio/meson.build
@@ -15,6 +15,7 @@ system_ss.add(when: 'CONFIG_RASPI', if_true: files(
     'bcm2835_gpio.c',
     'bcm2838_gpio.c'
 ))
+system_ss.add(when: 'CONFIG_STM32_GPIO', if_true: files('stm32_gpio.c'))
 system_ss.add(when: 'CONFIG_STM32L4X5_SOC', if_true: files('stm32l4x5_gpio.c'))
 system_ss.add(when: 'CONFIG_ASPEED_SOC', if_true: files('aspeed_gpio.c'))
 system_ss.add(when: 'CONFIG_SIFIVE_GPIO', if_true: files('sifive_gpio.c'))
diff --git a/hw/gpio/stm32_gpio.c b/hw/gpio/stm32_gpio.c
new file mode 100644
index 0000000000..825607b56a
--- /dev/null
+++ b/hw/gpio/stm32_gpio.c
@@ -0,0 +1,386 @@
+/*
+ * STM32 System-on-Chip general purpose input/output register definition
+ *
+ * Copyright 2024 Román Cárdenas <rcardenas.rod@gmail.com>
+ *
+ * Based on sifive_gpio.c:
+ *
+ * Copyright 2019 AdaCore
+ *
+ * This code is licensed under the GPL version 2 or later.  See
+ * the COPYING file in the top-level directory.
+ */
+
+#include "qemu/osdep.h"
+#include "qemu/log.h"
+#include "hw/irq.h"
+#include "hw/qdev-properties.h"
+#include "hw/arm/stm32.h"
+#include "hw/gpio/stm32_gpio.h"
+#include "migration/vmstate.h"
+#include "trace.h"
+
+static void stm32_gpio_update_state(STM32GPIOState *s)
+{
+    bool prev_id, new_id, od, in, in_mask;
+    uint8_t mode, pupd;
+
+    for (size_t i = 0; i < s->ngpio; i++) {
+        prev_id = extract32(s->idr, i, 1);
+        od = extract32(s->odr, i, 1);
+        in = extract32(s->in, i, 1);
+        in_mask = extract32(s->in_mask, i, 1);
+
+        mode = extract32(s->moder, i * 2, 2);
+        pupd = extract32(s->pupdr, i * 2, 2);
+
+        /* Pin both driven externally and internally */
+        if (mode == STM32_GPIO_MODE_OUTPUT && in_mask) {
+            qemu_log_mask(LOG_GUEST_ERROR, "GPIO pin %zu short circuited\n", i);
+        }
+
+        if (in_mask) {
+            /* The pin is driven by external device */
+            new_id = in;
+        } else if (mode == STM32_GPIO_MODE_OUTPUT) {
+            /* The pin is driven by internal circuit */
+            new_id = od;
+        } else {
+            /* Floating? Apply pull-up resistor */
+            new_id = pupd == STM32_GPIO_PULL_UP;
+        }
+
+        /* Update IDR */
+        s->idr = deposit32(s->idr, i, 1, new_id);
+
+        /* If pin is in input mode and IDR has changed, trigger an IRQ */
+        if (new_id != prev_id) {
+            if (mode == STM32_GPIO_MODE_INPUT) {
+                qemu_set_irq(s->input_irq[i], new_id);
+            }
+        }
+    }
+    /* Notify that GPIO has changed its state */
+    qemu_irq_pulse(s->state_irq);
+}
+
+static void stm32_gpio_reset(DeviceState *dev)
+{
+    STM32GPIOState *s = STM32_GPIO(dev);
+
+    /*
+     * Enabled is not affected by reset. It is ruled by RCC IDR is not
+     * directly reset. It is updated at the end by update_state
+     */
+
+    /* By default, we set all the registers to 0 */
+    s->moder = 0;
+    s->otyper = 0;
+    s->ospeedr = 0;
+    s->pupdr = 0;
+    s->odr = 0;
+    s->lckr = 0;
+    s->aflr = 0;
+    s->afhr = 0;
+
+    /* Next, we check model particularities */
+    if (s->family == STM32_F4) {
+        if (s->port == STM32_GPIO_PORT_A) {
+            s->moder     = 0xA8000000;
+            s->pupdr   = 0x64000000;
+        } else if (s->port == STM32_GPIO_PORT_B) {
+            s->moder     = 0x00000280;
+            s->ospeedr = 0x000000C0;
+            s->pupdr   = 0x00000100;
+        }
+    }
+
+    stm32_gpio_update_state(s);
+}
+
+static void stm32_gpio_irq_reset(void *opaque, int line, int value)
+{
+    STM32GPIOState *s = STM32_GPIO(opaque);
+
+    trace_stm32_gpio_irq_reset(line, value);
+
+    bool prev_reset = s->reset;
+    s->reset = value != 0;
+    if (prev_reset != s->reset) {
+        if (s->reset) {
+            stm32_gpio_reset(DEVICE(s));
+        } else {
+            stm32_gpio_update_state(s);
+        }
+    }
+}
+
+static void stm32_gpio_irq_enable(void *opaque, int line, int value)
+{
+    STM32GPIOState *s = STM32_GPIO(opaque);
+
+    trace_stm32_gpio_irq_enable(line, value);
+
+    bool prev_enable = s->enable;
+    s->enable = value != 0;
+    if (prev_enable != s->enable) {
+        stm32_gpio_update_state(s);
+    }
+}
+
+static void stm32_gpio_irq_set(void *opaque, int line, int value)
+{
+    STM32GPIOState *s = STM32_GPIO(opaque);
+
+    trace_stm32_gpio_irq_set(line, value);
+
+    assert(line >= 0 && line < s->ngpio);
+
+    s->in_mask = deposit32(s->in_mask, line, 1, value >= 0);
+
+    /*
+     * If value < 0, the pin is connected to a load.
+     * If value == 0, the pin is low.
+     * If value > 0, the pin is high.
+     */
+    if (value >= 0) {
+        s->in = deposit32(s->in, line, 1, value != 0);
+    }
+
+    stm32_gpio_update_state(s);
+}
+
+
+static uint64_t stm32_gpio_read(void *opaque, hwaddr offset, unsigned int size)
+{
+    STM32GPIOState *s = STM32_GPIO(opaque);
+    uint64_t r = 0;
+
+    if (!s->enable) {
+        qemu_log_mask(
+            LOG_GUEST_ERROR, "%s: GPIO peripheral is disabled\n", __func__
+        );
+        return 0;
+    }
+
+    switch (offset) {
+    case STM32_GPIO_REG_MODER:
+        r = s->moder;
+        break;
+
+    case STM32_GPIO_REG_OTYPER:
+        r = s->otyper;
+        break;
+
+    case STM32_GPIO_REG_OSPEEDR:
+        r = s->ospeedr;
+        break;
+
+    case STM32_GPIO_REG_PUPDR:
+        r = s->pupdr;
+        break;
+
+    case STM32_GPIO_REG_IDR:
+        r = s->idr;
+        break;
+
+    case STM32_GPIO_REG_ODR:
+        r = s->odr;
+        break;
+
+    case STM32_GPIO_REG_BSRR:
+        break; /* BSRR is write-only */
+
+    case STM32_GPIO_REG_LCKR:
+        r = s->lckr;
+        break;
+
+    case STM32_GPIO_REG_AFRL:
+        r = s->aflr;
+        break;
+
+    case STM32_GPIO_REG_AFRH:
+        r = s->afhr;
+        break;
+
+    case STM32_GPIO_REG_BRR:
+        if (s->family != STM32_F4) {
+            break; /* BRR is write-only */
+        }
+        /* STM32F4xx SoCs do not have this register */
+        qemu_log_mask(
+            LOG_GUEST_ERROR,
+            "%s: bad read offset 0x%" HWADDR_PRIx "\n",  __func__, offset
+        );
+        break;
+
+    default:
+        qemu_log_mask(
+            LOG_GUEST_ERROR,
+            "%s: bad read offset 0x%" HWADDR_PRIx "\n",  __func__, offset
+        );
+    }
+
+    trace_stm32_gpio_read(offset, r);
+
+    return r;
+}
+
+static void stm32_gpio_write(void *opaque, hwaddr offset,
+                             uint64_t value, unsigned int size)
+{
+    STM32GPIOState *s = STM32_GPIO(opaque);
+
+    trace_stm32_gpio_write(offset, value);
+
+    if (!s->enable) {
+        qemu_log_mask(
+            LOG_GUEST_ERROR, "%s: GPIO peripheral is disabled\n", __func__
+        );
+        return;
+    }
+
+    switch (offset) {
+
+    case STM32_GPIO_REG_MODER:
+        s->moder = value;
+        break;
+
+    case STM32_GPIO_REG_OTYPER:
+        s->otyper = value;
+        break;
+
+    case STM32_GPIO_REG_OSPEEDR:
+        s->ospeedr = value;
+        break;
+
+    case STM32_GPIO_REG_PUPDR:
+        s->pupdr = value;
+        break;
+
+    case STM32_GPIO_REG_IDR:
+        break; /* IDR is read-only */
+
+    case STM32_GPIO_REG_ODR:
+        s->odr = value; /* IDR is updated in update_state */
+        break;
+
+    case STM32_GPIO_REG_BSRR:
+        s->odr &= ~((value >> 16) & 0xFFFF);
+        /* set bits have higher priority than reset bits */
+        s->odr |= value & 0xFFFF;
+        break;
+
+    case STM32_GPIO_REG_LCKR:
+        s->lckr = value;
+        break;
+
+    case STM32_GPIO_REG_AFRL:
+        s->aflr = value;
+        break;
+
+    case STM32_GPIO_REG_AFRH:
+        s->afhr = value;
+        break;
+
+    case STM32_GPIO_REG_BRR:
+        if (s->family != STM32_F4) {
+            s->odr &= ~(value & 0xFFFF);
+            break;
+        }
+        /* STM32F4xx SoCs do not have this register */
+        qemu_log_mask(
+            LOG_GUEST_ERROR, "%s: bad write offset 0x%" HWADDR_PRIx "\n",
+            __func__, offset
+        );
+        break;
+
+    default:
+        qemu_log_mask(
+            LOG_GUEST_ERROR, "%s: bad write offset 0x%" HWADDR_PRIx "\n",
+            __func__, offset
+        );
+    }
+
+    stm32_gpio_update_state(s);
+}
+
+static const MemoryRegionOps stm32_gpio_ops = {
+    .read =  stm32_gpio_read,
+    .write = stm32_gpio_write,
+    .endianness = DEVICE_NATIVE_ENDIAN,
+    .impl.min_access_size = 4,
+    .impl.max_access_size = 4,
+};
+
+static const VMStateDescription vmstate_stm32_gpio = {
+    .name = TYPE_STM32_GPIO,
+    .version_id = 1,
+    .minimum_version_id = 1,
+    .fields = (const VMStateField[]) {
+        VMSTATE_UINT32(moder, STM32GPIOState),
+        VMSTATE_UINT32(otyper, STM32GPIOState),
+        VMSTATE_UINT32(ospeedr, STM32GPIOState),
+        VMSTATE_UINT32(pupdr, STM32GPIOState),
+        VMSTATE_UINT32(idr, STM32GPIOState),
+        VMSTATE_UINT32(odr, STM32GPIOState),
+        VMSTATE_UINT32(lckr, STM32GPIOState),
+        VMSTATE_UINT32(aflr, STM32GPIOState),
+        VMSTATE_UINT32(afhr, STM32GPIOState),
+        VMSTATE_BOOL(reset, STM32GPIOState),
+        VMSTATE_BOOL(enable, STM32GPIOState),
+        VMSTATE_UINT32(in, STM32GPIOState),
+        VMSTATE_UINT32(in_mask, STM32GPIOState),
+        VMSTATE_END_OF_LIST()
+    }
+};
+
+static Property stm32_gpio_properties[] = {
+    DEFINE_PROP_UINT32("family", STM32GPIOState, family, STM32_F2),
+    DEFINE_PROP_UINT32("port", STM32GPIOState, port, STM32_GPIO_PORT_A),
+    DEFINE_PROP_UINT32("ngpio", STM32GPIOState, ngpio, STM32_GPIO_NPINS),
+    DEFINE_PROP_END_OF_LIST(),
+};
+
+static void stm32_gpio_realize(DeviceState *dev, Error **errp)
+{
+    STM32GPIOState *s = STM32_GPIO(dev);
+
+    memory_region_init_io(&s->mmio, OBJECT(dev), &stm32_gpio_ops, s,
+                          TYPE_STM32_GPIO, STM32_GPIO_PERIPHERAL_SIZE);
+    sysbus_init_mmio(SYS_BUS_DEVICE(dev), &s->mmio);
+
+    qdev_init_gpio_in_named(DEVICE(s), stm32_gpio_irq_reset, "reset-in", 1);
+    qdev_init_gpio_in_named(DEVICE(s), stm32_gpio_irq_enable, "enable-in", 1);
+    qdev_init_gpio_in_named(DEVICE(s), stm32_gpio_irq_set,
+                            "input-in", STM32_GPIO_NPINS);
+
+    qdev_init_gpio_out_named(DEVICE(s), &s->state_irq, "state-out", 1);
+    qdev_init_gpio_out_named(DEVICE(s), s->input_irq,
+                             "input-out", STM32_GPIO_NPINS);
+}
+
+static void stm32_gpio_class_init(ObjectClass *klass, void *data)
+{
+    DeviceClass *dc = DEVICE_CLASS(klass);
+
+    device_class_set_props(dc, stm32_gpio_properties);
+    dc->vmsd = &vmstate_stm32_gpio;
+    dc->realize = stm32_gpio_realize;
+    device_class_set_legacy_reset(dc, stm32_gpio_reset);
+    dc->desc = "STM32 GPIO";
+}
+
+static const TypeInfo stm32_gpio_info = {
+    .name = TYPE_STM32_GPIO,
+    .parent = TYPE_SYS_BUS_DEVICE,
+    .instance_size = sizeof(STM32GPIOState),
+    .class_init = stm32_gpio_class_init
+};
+
+static void stm32_gpio_register_types(void)
+{
+    type_register_static(&stm32_gpio_info);
+}
+
+type_init(stm32_gpio_register_types)
diff --git a/hw/gpio/trace-events b/hw/gpio/trace-events
index b91cc7e9a4..b469c5dec1 100644
--- a/hw/gpio/trace-events
+++ b/hw/gpio/trace-events
@@ -36,6 +36,14 @@ sifive_gpio_update_output_irq(int64_t line, int64_t value) "line %" PRIi64 " val
 aspeed_gpio_read(uint64_t offset, uint64_t value) "offset: 0x%" PRIx64 " value 0x%" PRIx64
 aspeed_gpio_write(uint64_t offset, uint64_t value) "offset: 0x%" PRIx64 " value 0x%" PRIx64
 
+# stm32_gpio.c
+stm32_gpio_read(uint64_t offset, uint64_t r) "offset 0x%" PRIx64 " value 0x%" PRIx64
+stm32_gpio_write(uint64_t offset, uint64_t value) "offset 0x%" PRIx64 " value 0x%" PRIx64
+stm32_gpio_irq_enable(int64_t line, int64_t value) "line %" PRIi64 " value %" PRIi64
+stm32_gpio_irq_reset(int64_t line, int64_t value) "line %" PRIi64 " value %" PRIi64
+stm32_gpio_irq_set(int64_t line, int64_t value) "line %" PRIi64 " value %" PRIi64
+stm32_gpio_update_output_irq(int64_t line, int64_t value) "line %" PRIi64 " value %" PRIi64
+
 # stm32l4x5_gpio.c
 stm32l4x5_gpio_read(char *gpio, uint64_t addr) "GPIO%s addr: 0x%" PRIx64 " "
 stm32l4x5_gpio_write(char *gpio, uint64_t addr, uint64_t data) "GPIO%s addr: 0x%" PRIx64 " val: 0x%" PRIx64 ""
diff --git a/include/hw/arm/stm32.h b/include/hw/arm/stm32.h
new file mode 100644
index 0000000000..7e8b9a5524
--- /dev/null
+++ b/include/hw/arm/stm32.h
@@ -0,0 +1,41 @@
+/*
+ * STM32 chip configuration parameters.
+ * These enums are used to configure STM32 chips, as well as their peripherals.
+ *
+ * Copyright 2024 Román Cárdenas <rcardenas.rod@gmail.com>
+ *
+ * This code is licensed under the GPL version 2 or later.  See
+ * the COPYING file in the top-level directory.
+ */
+
+#ifndef STM32_H
+#define STM32_H
+
+enum {
+    /* High Performance */
+    STM32_F2,
+    STM32_F4,
+    STM32_H5,
+    STM32_F7,
+    STM32_H7,
+    /* Mainstream */
+    STM32_C0,
+    STM32_F0,
+    STM32_G0,
+    STM32_F1,
+    STM32_F3,
+    STM32_G4,
+    /* Ultra Low Power */
+    STM32_L0,
+    STM32_L4,
+    STM32_L4P,
+    STM32_L5,
+    STM32_U5,
+    /* Wireless */
+    STM32_WL,
+    STM32_WB0,
+    STM32_WB,
+    STM32_WBA,
+};
+
+#endif /* STM32_H */
diff --git a/include/hw/gpio/stm32_gpio.h b/include/hw/gpio/stm32_gpio.h
new file mode 100644
index 0000000000..373c2fa842
--- /dev/null
+++ b/include/hw/gpio/stm32_gpio.h
@@ -0,0 +1,109 @@
+/*
+ * STM32 System-on-Chip general purpose input/output register definition.
+ * While this implementation should work for most of STM32 SoCs, there are
+ * a few chips with different GPIO peripheral. For example, STM32F1 series.
+ *
+ * Copyright 2024 Román Cárdenas <rcardenas.rod@gmail.com>
+ *
+ * Based on sifive_gpio.c:
+ *
+ * Copyright 2019 AdaCore
+ *
+ * This code is licensed under the GPL version 2 or later.  See
+ * the COPYING file in the top-level directory.
+ */
+
+#ifndef STM32_GPIO_H
+#define STM32_GPIO_H
+
+#include "hw/sysbus.h"
+#include "qom/object.h"
+
+#define TYPE_STM32_GPIO "stm32.gpio"
+
+typedef struct STM32GPIOState STM32GPIOState;
+
+DECLARE_INSTANCE_CHECKER(STM32GPIOState, STM32_GPIO, TYPE_STM32_GPIO)
+
+#define STM32_GPIO_REG_MODER       0x000
+#define STM32_GPIO_REG_OTYPER      0x004
+#define STM32_GPIO_REG_OSPEEDR     0x008
+#define STM32_GPIO_REG_PUPDR       0x00C
+#define STM32_GPIO_REG_IDR         0x010
+#define STM32_GPIO_REG_ODR         0x014
+#define STM32_GPIO_REG_BSRR        0x018
+#define STM32_GPIO_REG_LCKR        0x01C
+#define STM32_GPIO_REG_AFRL        0x020
+#define STM32_GPIO_REG_AFRH        0x024
+#define STM32_GPIO_REG_BRR         0x028
+
+#define STM32_GPIO_NPINS           16
+#define STM32_GPIO_PERIPHERAL_SIZE 0x400
+
+struct STM32GPIOState {
+    SysBusDevice parent_obj;
+
+    MemoryRegion mmio;
+
+    /* GPIO registers */
+    uint32_t moder;
+    uint32_t otyper;
+    uint32_t ospeedr;
+    uint32_t pupdr;
+    uint32_t idr;      /* Actual value of the pin */
+    uint32_t odr;      /* Pin value requested by the user */
+    uint32_t lckr;     /* TODO implement locking sequence */
+    uint32_t aflr;
+    uint32_t afhr;
+
+    /* state flags from RCC */
+    bool reset;
+    bool enable;
+
+    /* External input */
+    uint32_t in;
+    /*
+     * If in_mask == 0, the pin is disconnected/connected to a load.
+     * If value == 1, the pin is connected to value in in.
+     */
+    uint32_t in_mask;
+
+    /* IRQ to notify that the GPIO has updated its state */
+    qemu_irq state_irq;
+    /* IRQs to relay each input pin changes to other STM32 peripherals */
+    qemu_irq input_irq[STM32_GPIO_NPINS];
+
+    /* config */
+    uint32_t family; /* e.g. STM32_F4 */
+    uint32_t port;   /* e.g. STM32_GPIO_PORT_A */
+    uint32_t ngpio;  /* e.g. 16 */
+};
+
+enum STM32GPIOPort {
+    STM32_GPIO_PORT_A = 0,
+    STM32_GPIO_PORT_B = 1,
+    STM32_GPIO_PORT_C = 2,
+    STM32_GPIO_PORT_D = 3,
+    STM32_GPIO_PORT_E = 4,
+    STM32_GPIO_PORT_F = 5,
+    STM32_GPIO_PORT_G = 6,
+    STM32_GPIO_PORT_H = 7,
+    STM32_GPIO_PORT_I = 8,
+    STM32_GPIO_PORT_J = 9,
+    STM32_GPIO_PORT_K = 10,
+};
+
+enum STM32GPIOMode {
+    STM32_GPIO_MODE_INPUT  = 0,
+    STM32_GPIO_MODE_OUTPUT = 1,
+    STM32_GPIO_MODE_AF     = 2,
+    STM32_GPIO_MODE_ANALOG = 3,
+};
+
+enum STM32GPIOPull {
+    STM32_GPIO_PULL_NONE = 0,
+    STM32_GPIO_PULL_UP   = 1,
+    STM32_GPIO_PULL_DOWN = 2,
+};
+
+#endif /* STM32_GPIO_H */
-- 
2.39.3 (Apple Git-146)



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

* [PATCH 4/4] Add GPIO device to stm32f405 SoC
  2024-09-27 15:07 [PATCH 1/4] STM32F4: new RCC device Román Cárdenas Rodríguez
  2024-09-27 15:07 ` [PATCH 2/4] Add RCC device to stm32f405 SoC Román Cárdenas Rodríguez
  2024-09-27 15:07 ` [PATCH 3/4] STM32: new GPIO device Román Cárdenas Rodríguez
@ 2024-09-27 15:07 ` Román Cárdenas Rodríguez
  2024-10-04 18:11 ` [PATCH 1/4] STM32F4: new RCC device Peter Maydell
  3 siblings, 0 replies; 9+ messages in thread
From: Román Cárdenas Rodríguez @ 2024-09-27 15:07 UTC (permalink / raw)
  To: qemu-arm
  Cc: alistair, peter.maydell, qemu-devel,
	Román Cárdenas Rodríguez

Signed-off-by: Roman Cardenas Rodriguez <rcardenas.rod@gmail.com>
---
 hw/arm/Kconfig                 |  1 +
 hw/arm/stm32f405_soc.c         | 40 ++++++++++++++++++++++++++--------
 hw/misc/stm32f4xx_syscfg.c     | 24 ++++++++++++--------
 include/hw/arm/stm32f405_soc.h |  3 +++
 4 files changed, 50 insertions(+), 18 deletions(-)

diff --git a/hw/arm/Kconfig b/hw/arm/Kconfig
index 0629f47cb3..031fbe0934 100644
--- a/hw/arm/Kconfig
+++ b/hw/arm/Kconfig
@@ -467,6 +467,7 @@ config STM32F405_SOC
     select ARM_V7M
     select OR_IRQ
     select STM32_RCC
+    select STM32_GPIO
     select STM32F4XX_SYSCFG
     select STM32F4XX_EXTI
 
diff --git a/hw/arm/stm32f405_soc.c b/hw/arm/stm32f405_soc.c
index 72ae62156f..1ea5c8314d 100644
--- a/hw/arm/stm32f405_soc.c
+++ b/hw/arm/stm32f405_soc.c
@@ -26,6 +26,7 @@
 #include "qapi/error.h"
 #include "exec/address-spaces.h"
 #include "sysemu/sysemu.h"
+#include "hw/arm/stm32.h"
 #include "hw/arm/stm32f405_soc.h"
 #include "hw/qdev-clock.h"
 #include "hw/misc/unimp.h"
@@ -43,6 +44,7 @@ static const uint32_t adc_addr[] = { 0x40012000, 0x40012100, 0x40012200,
 static const uint32_t spi_addr[] =   { 0x40013000, 0x40003800, 0x40003C00,
                                        0x40013400, 0x40015000, 0x40015400 };
 #define EXTI_ADDR                      0x40013C00
+#define GPIO_ADDR                      0x40020000
 
 #define SYSCFG_IRQ               71
 static const int usart_irq[] = { 37, 38, 39, 52, 53, 71, 82, 83 };
@@ -82,6 +84,10 @@ static void stm32f405_soc_initfn(Object *obj)
         object_initialize_child(obj, "spi[*]", &s->spi[i], TYPE_STM32F2XX_SPI);
     }
 
+    for (i = STM32_GPIO_PORT_A; i <= STM32_GPIO_PORT_I; i++) {
+        object_initialize_child(obj, "gpio[*]", &s->gpio[i], TYPE_STM32_GPIO);
+    }
+
     object_initialize_child(obj, "exti", &s->exti, TYPE_STM32F4XX_EXTI);
 
     s->sysclk = qdev_init_clock_in(DEVICE(s), "sysclk", NULL, NULL, 0);
@@ -240,6 +246,31 @@ static void stm32f405_soc_realize(DeviceState *dev_soc, Error **errp)
         sysbus_connect_irq(busdev, 0, qdev_get_gpio_in(armv7m, spi_irq[i]));
     }
 
+    /* GPIO devices */
+    for (i = STM32_GPIO_PORT_A; i <= STM32_GPIO_PORT_I; i++) {
+        dev = DEVICE(&(s->gpio[i]));
+        qdev_prop_set_uint32(dev, "family", STM32_F4);
+        qdev_prop_set_uint32(dev, "port", i);
+        qdev_prop_set_uint32(dev, "ngpio", STM32_GPIO_NPINS);
+        if (!sysbus_realize(SYS_BUS_DEVICE(&s->gpio[i]), errp)) {
+            return;
+        }
+        busdev = SYS_BUS_DEVICE(dev);
+        sysbus_mmio_map(busdev, 0,
+                        GPIO_ADDR + (i * STM32_GPIO_PERIPHERAL_SIZE));
+
+        qdev_connect_gpio_out(DEVICE(&s->rcc), STM32_RCC_GPIO_IRQ_OFFSET + i,
+                              qdev_get_gpio_in_named(dev, "reset-in", 0));
+        qdev_connect_gpio_out(DEVICE(&s->rcc),
+                              STM32_RCC_GPIO_IRQ_OFFSET + i + STM32_RCC_NIRQS,
+                              qdev_get_gpio_in_named(dev, "enable-in", 0));
+        for (int j = 0; j < STM32_GPIO_NPINS; j++) {
+            qdev_connect_gpio_out_named(dev, "input-out", j,
+                                        qdev_get_gpio_in(DEVICE(&s->syscfg),
+                                        i * STM32_GPIO_NPINS + j));
+        }
+    }
+
     /* EXTI device */
     dev = DEVICE(&s->exti);
     if (!sysbus_realize(SYS_BUS_DEVICE(&s->exti), errp)) {
@@ -277,15 +308,6 @@ static void stm32f405_soc_realize(DeviceState *dev_soc, Error **errp)
     create_unimplemented_device("timer[9]",    0x40014000, 0x400);
     create_unimplemented_device("timer[10]",   0x40014400, 0x400);
     create_unimplemented_device("timer[11]",   0x40014800, 0x400);
-    create_unimplemented_device("GPIOA",       0x40020000, 0x400);
-    create_unimplemented_device("GPIOB",       0x40020400, 0x400);
-    create_unimplemented_device("GPIOC",       0x40020800, 0x400);
-    create_unimplemented_device("GPIOD",       0x40020C00, 0x400);
-    create_unimplemented_device("GPIOE",       0x40021000, 0x400);
-    create_unimplemented_device("GPIOF",       0x40021400, 0x400);
-    create_unimplemented_device("GPIOG",       0x40021800, 0x400);
-    create_unimplemented_device("GPIOH",       0x40021C00, 0x400);
-    create_unimplemented_device("GPIOI",       0x40022000, 0x400);
     create_unimplemented_device("CRC",         0x40023000, 0x400);
     create_unimplemented_device("Flash Int",   0x40023C00, 0x400);
     create_unimplemented_device("BKPSRAM",     0x40024000, 0x400);
diff --git a/hw/misc/stm32f4xx_syscfg.c b/hw/misc/stm32f4xx_syscfg.c
index 7d0f3eb5f5..d42e6821db 100644
--- a/hw/misc/stm32f4xx_syscfg.c
+++ b/hw/misc/stm32f4xx_syscfg.c
@@ -27,6 +27,7 @@
 #include "trace.h"
 #include "hw/irq.h"
 #include "migration/vmstate.h"
+#include "hw/gpio/stm32_gpio.h"
 #include "hw/misc/stm32f4xx_syscfg.h"
 
 static void stm32f4xx_syscfg_reset(DeviceState *dev)
@@ -45,17 +46,21 @@ static void stm32f4xx_syscfg_reset(DeviceState *dev)
 static void stm32f4xx_syscfg_set_irq(void *opaque, int irq, int level)
 {
     STM32F4xxSyscfgState *s = opaque;
-    int icrreg = irq / 4;
-    int startbit = (irq & 3) * 4;
-    uint8_t config = irq / 16;
+    uint8_t pin = irq & 0xF; /* first 4 bits encode the pin number */
+    uint8_t port = irq >> 4; /* the rest encode the port number */
 
-    trace_stm32f4xx_syscfg_set_irq(irq / 16, irq % 16, level);
+    g_assert(port <= STM32_GPIO_PORT_I); /* stm32f4 only has ports A-I */
+
+    int icrreg = pin / 4;
+    int startbit = (pin % 4) * 4;
+
+    trace_stm32f4xx_syscfg_set_irq(port, pin, level);
 
     g_assert(icrreg < SYSCFG_NUM_EXTICR);
 
-    if (extract32(s->syscfg_exticr[icrreg], startbit, 4) == config) {
-        qemu_set_irq(s->gpio_out[irq], level);
-        trace_stm32f4xx_pulse_exti(irq);
+    if (extract32(s->syscfg_exticr[icrreg], startbit, 4) == port) {
+        qemu_set_irq(s->gpio_out[pin], level);
+        trace_stm32f4xx_pulse_exti(pin);
    }
 }
 
@@ -129,8 +134,9 @@ static void stm32f4xx_syscfg_init(Object *obj)
                           TYPE_STM32F4XX_SYSCFG, 0x400);
     sysbus_init_mmio(SYS_BUS_DEVICE(obj), &s->mmio);
 
-    qdev_init_gpio_in(DEVICE(obj), stm32f4xx_syscfg_set_irq, 16 * 9);
-    qdev_init_gpio_out(DEVICE(obj), s->gpio_out, 16);
+    qdev_init_gpio_in(DEVICE(obj), stm32f4xx_syscfg_set_irq,
+                      STM32_GPIO_NPINS * (STM32_GPIO_PORT_I + 1));
+    qdev_init_gpio_out(DEVICE(obj), s->gpio_out, STM32_GPIO_NPINS);
 }
 
 static const VMStateDescription vmstate_stm32f4xx_syscfg = {
diff --git a/include/hw/arm/stm32f405_soc.h b/include/hw/arm/stm32f405_soc.h
index 2eeada64de..62072815a7 100644
--- a/include/hw/arm/stm32f405_soc.h
+++ b/include/hw/arm/stm32f405_soc.h
@@ -26,6 +26,7 @@
 #define HW_ARM_STM32F405_SOC_H
 
 #include "hw/misc/stm32_rcc.h"
+#include "hw/gpio/stm32_gpio.h"
 #include "hw/misc/stm32f4xx_syscfg.h"
 #include "hw/timer/stm32f2xx_timer.h"
 #include "hw/char/stm32f2xx_usart.h"
@@ -43,6 +44,7 @@ OBJECT_DECLARE_SIMPLE_TYPE(STM32F405State, STM32F405_SOC)
 #define STM_NUM_TIMERS 4
 #define STM_NUM_ADCS 6
 #define STM_NUM_SPIS 6
+#define STM_NUM_GPIOS (STM32_GPIO_PORT_I - STM32_GPIO_PORT_A + 1)
 
 #define FLASH_BASE_ADDRESS 0x08000000
 #define FLASH_SIZE (1024 * 1024)
@@ -64,6 +66,7 @@ struct STM32F405State {
     OrIRQState adc_irqs;
     STM32F2XXADCState adc[STM_NUM_ADCS];
     STM32F2XXSPIState spi[STM_NUM_SPIS];
+    STM32GPIOState gpio[STM_NUM_GPIOS];
 
     MemoryRegion ccm;
     MemoryRegion sram;
-- 
2.39.3 (Apple Git-146)



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

* Re: [PATCH 3/4] STM32: new GPIO device
  2024-09-27 15:07 ` [PATCH 3/4] STM32: new GPIO device Román Cárdenas Rodríguez
@ 2024-10-04 18:08   ` Peter Maydell
  2024-10-09 14:32     ` Román Cárdenas Rodríguez
  2024-10-04 18:23   ` Peter Maydell
  1 sibling, 1 reply; 9+ messages in thread
From: Peter Maydell @ 2024-10-04 18:08 UTC (permalink / raw)
  To: Román Cárdenas Rodríguez; +Cc: qemu-arm, alistair, qemu-devel

On Fri, 27 Sept 2024 at 16:08, Román Cárdenas Rodríguez
<rcardenas.rod@gmail.com> wrote:
>
> Generic GPIO class for STM32 devices. It can be used for most of STM32 chips.
> Note that it does not implement configuration locking mechanisms.

So we already have an stm32l4x5 GPIO device. How different
is that one from these ones? Should we be sharing implementation,
or are they too different?

When adding a new file, could you please add lines to
the MAINTAINERS file putting it in an appropriate subsection?

> +static void stm32_gpio_irq_reset(void *opaque, int line, int value)
> +{
> +    STM32GPIOState *s = STM32_GPIO(opaque);
> +
> +    trace_stm32_gpio_irq_reset(line, value);
> +
> +    bool prev_reset = s->reset;

QEMU's coding style says that variable declarations
should always be at the start of a code block, never
in the middle. (There are some other cases of this here
and in patch 4, I think.)

> +    s->reset = value != 0;
> +    if (prev_reset != s->reset) {
> +        if (s->reset) {
> +            stm32_gpio_reset(DEVICE(s));
> +        } else {
> +            stm32_gpio_update_state(s);
> +        }
> +    }
> +}

> +static Property stm32_gpio_properties[] = {
> +    DEFINE_PROP_UINT32("family", STM32GPIOState, family, STM32_F2),

For this sort of situation where we have a set of devices that
are very similar but have some slight model-to-model variation,
rather than using a device property to tell the device which
variation it is, we generally use a parent class with the
implementation and a set of child classes which tweak fields
controlling the parent class behaviour. For an example, look at
hw/gpio/aspeed_gpio.c -- TYPE_ASPEED_GPIO is marked ".abstract = true",
and it has most of the implementation. Then the various subtypes
inherit from it, and their class init functions set fields in
the class struct to define the behaviour.
hw/char/stm32l4x5_usart.c is another example.

> +static void stm32_gpio_class_init(ObjectClass *klass, void *data)
> +{
> +    DeviceClass *dc = DEVICE_CLASS(klass);
> +
> +    device_class_set_props(dc, stm32_gpio_properties);
> +    dc->vmsd = &vmstate_stm32_gpio;
> +    dc->realize = stm32_gpio_realize;
> +    device_class_set_legacy_reset(dc, stm32_gpio_reset);

Could you use the non-legacy reset instead? It's not much of
a change, instead of calling this you set the ResettableClass
phases.hold, and the reset function has a slightly different
function signature. hw/char/stm32l4x5_usart.c has an example.

thanks
-- PMM


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

* Re: [PATCH 1/4] STM32F4: new RCC device
  2024-09-27 15:07 [PATCH 1/4] STM32F4: new RCC device Román Cárdenas Rodríguez
                   ` (2 preceding siblings ...)
  2024-09-27 15:07 ` [PATCH 4/4] Add GPIO device to stm32f405 SoC Román Cárdenas Rodríguez
@ 2024-10-04 18:11 ` Peter Maydell
  3 siblings, 0 replies; 9+ messages in thread
From: Peter Maydell @ 2024-10-04 18:11 UTC (permalink / raw)
  To: Román Cárdenas Rodríguez; +Cc: qemu-arm, alistair, qemu-devel

On Fri, 27 Sept 2024 at 16:08, Román Cárdenas Rodríguez
<rcardenas.rod@gmail.com> wrote:
>
> Generic RCC class for STM32 devices. It can be used for most of STM32 chips.
> Note that it only implements enable and reset capabilities.
>
> Signed-off-by: Roman Cardenas Rodriguez <rcardenas.rod@gmail.com>

Patches 1 and 2 looked OK to me, so I've applied them
to target-arm.next. Thanks!

PS: for future submissions, if you send a multi-patch series
could you send it with a cover letter, please (of the kind
that "git format-patch --cover-letter" produces)? Our automatic
tooling expects multi-patch threads to have a cover letter,
so having one makes the process a bit smoother.

-- PMM


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

* Re: [PATCH 3/4] STM32: new GPIO device
  2024-09-27 15:07 ` [PATCH 3/4] STM32: new GPIO device Román Cárdenas Rodríguez
  2024-10-04 18:08   ` Peter Maydell
@ 2024-10-04 18:23   ` Peter Maydell
  1 sibling, 0 replies; 9+ messages in thread
From: Peter Maydell @ 2024-10-04 18:23 UTC (permalink / raw)
  To: Román Cárdenas Rodríguez; +Cc: qemu-arm, alistair, qemu-devel

On Fri, 27 Sept 2024 at 16:08, Román Cárdenas Rodríguez
<rcardenas.rod@gmail.com> wrote:
>
> Generic GPIO class for STM32 devices. It can be used for most of STM32 chips.
> Note that it does not implement configuration locking mechanisms.
>
> Signed-off-by: Roman Cardenas Rodriguez <rcardenas.rod@gmail.com>
> ---
>  hw/gpio/Kconfig              |   3 +
>  hw/gpio/meson.build          |   1 +
>  hw/gpio/stm32_gpio.c         | 386 +++++++++++++++++++++++++++++++++++
>  hw/gpio/trace-events         |   8 +
>  include/hw/arm/stm32.h       |  41 ++++
>  include/hw/gpio/stm32_gpio.h | 109 ++++++++++
>  6 files changed, 548 insertions(+)
>  create mode 100644 hw/gpio/stm32_gpio.c
>  create mode 100644 include/hw/arm/stm32.h
>  create mode 100644 include/hw/gpio/stm32_gpio.h

Oh, I forgot -- could you consider adding some tests for the
new device? We have tests already for stm32l4x5_gpio in
tests/qtest/stm32l4x5_gpio-test.c which you can probably use
as a pattern. You can add the test cases as an extra patch on
the end of the series.

thanks
-- PMM


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

* Re: [PATCH 3/4] STM32: new GPIO device
  2024-10-04 18:08   ` Peter Maydell
@ 2024-10-09 14:32     ` Román Cárdenas Rodríguez
  2024-10-10 10:20       ` Peter Maydell
  0 siblings, 1 reply; 9+ messages in thread
From: Román Cárdenas Rodríguez @ 2024-10-09 14:32 UTC (permalink / raw)
  To: Peter Maydell; +Cc: qemu-arm, alistair, qemu-devel

Looks like the stm32l4x5 is quite similar to my implementation. It didn’t exist when I started with my implementation . I will take a closer look and work on improving/extending the stm32l4x5 GPIO with my proposal. Is it OK if I rename it to stm32_gpio? so it is clearer that the implementation is generic and can fit almost any STM32 target.

Sorry about the issues with the patches, I am not familiar with this methodology of working with git.
Could you guide me on how to send with you a revised version of patches 3 and 4?

> On 4 Oct 2024, at 20:08, Peter Maydell <peter.maydell@linaro.org> wrote:
> 
> On Fri, 27 Sept 2024 at 16:08, Román Cárdenas Rodríguez
> <rcardenas.rod@gmail.com> wrote:
>> 
>> Generic GPIO class for STM32 devices. It can be used for most of STM32 chips.
>> Note that it does not implement configuration locking mechanisms.
> 
> So we already have an stm32l4x5 GPIO device. How different
> is that one from these ones? Should we be sharing implementation,
> or are they too different?
> 
> When adding a new file, could you please add lines to
> the MAINTAINERS file putting it in an appropriate subsection?
> 
>> +static void stm32_gpio_irq_reset(void *opaque, int line, int value)
>> +{
>> +    STM32GPIOState *s = STM32_GPIO(opaque);
>> +
>> +    trace_stm32_gpio_irq_reset(line, value);
>> +
>> +    bool prev_reset = s->reset;
> 
> QEMU's coding style says that variable declarations
> should always be at the start of a code block, never
> in the middle. (There are some other cases of this here
> and in patch 4, I think.)
> 
>> +    s->reset = value != 0;
>> +    if (prev_reset != s->reset) {
>> +        if (s->reset) {
>> +            stm32_gpio_reset(DEVICE(s));
>> +        } else {
>> +            stm32_gpio_update_state(s);
>> +        }
>> +    }
>> +}
> 
>> +static Property stm32_gpio_properties[] = {
>> +    DEFINE_PROP_UINT32("family", STM32GPIOState, family, STM32_F2),
> 
> For this sort of situation where we have a set of devices that
> are very similar but have some slight model-to-model variation,
> rather than using a device property to tell the device which
> variation it is, we generally use a parent class with the
> implementation and a set of child classes which tweak fields
> controlling the parent class behaviour. For an example, look at
> hw/gpio/aspeed_gpio.c -- TYPE_ASPEED_GPIO is marked ".abstract = true",
> and it has most of the implementation. Then the various subtypes
> inherit from it, and their class init functions set fields in
> the class struct to define the behaviour.
> hw/char/stm32l4x5_usart.c is another example.
> 
>> +static void stm32_gpio_class_init(ObjectClass *klass, void *data)
>> +{
>> +    DeviceClass *dc = DEVICE_CLASS(klass);
>> +
>> +    device_class_set_props(dc, stm32_gpio_properties);
>> +    dc->vmsd = &vmstate_stm32_gpio;
>> +    dc->realize = stm32_gpio_realize;
>> +    device_class_set_legacy_reset(dc, stm32_gpio_reset);
> 
> Could you use the non-legacy reset instead? It's not much of
> a change, instead of calling this you set the ResettableClass
> phases.hold, and the reset function has a slightly different
> function signature. hw/char/stm32l4x5_usart.c has an example.
> 
> thanks
> -- PMM



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

* Re: [PATCH 3/4] STM32: new GPIO device
  2024-10-09 14:32     ` Román Cárdenas Rodríguez
@ 2024-10-10 10:20       ` Peter Maydell
  0 siblings, 0 replies; 9+ messages in thread
From: Peter Maydell @ 2024-10-10 10:20 UTC (permalink / raw)
  To: Román Cárdenas Rodríguez; +Cc: qemu-arm, alistair, qemu-devel

On Wed, 9 Oct 2024 at 15:32, Román Cárdenas Rodríguez
<rcardenas.rod@gmail.com> wrote:
>
> Looks like the stm32l4x5 is quite similar to my implementation. It didn’t exist when I started with my implementation . I will take a closer look and work on improving/extending the stm32l4x5 GPIO with my proposal. Is it OK if I rename it to stm32_gpio? so it is clearer that the implementation is generic and can fit almost any STM32 target.

Yes, renaming would be OK. (Do that in a patch of its own
that does the rename and nothing else.)

> Sorry about the issues with the patches, I am not familiar with this methodology of working with git.
> Could you guide me on how to send with you a revised version of patches 3 and 4?

So I've taken your patches 1 and 2 into my git tree; they
will appear upstream probably either end of this week
or beginning of next week. If that happens before you're
ready to send out a version 2 of this series, you can
rebase your patches on head-of-git and then send
out a new series with patches 3 and 4 in it. If you're
ready to send v2 before the other patches land in upstream
git, just send out v2 with the whole 4-patch set in it.

thanks
-- PMM


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

end of thread, other threads:[~2024-10-10 10:22 UTC | newest]

Thread overview: 9+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2024-09-27 15:07 [PATCH 1/4] STM32F4: new RCC device Román Cárdenas Rodríguez
2024-09-27 15:07 ` [PATCH 2/4] Add RCC device to stm32f405 SoC Román Cárdenas Rodríguez
2024-09-27 15:07 ` [PATCH 3/4] STM32: new GPIO device Román Cárdenas Rodríguez
2024-10-04 18:08   ` Peter Maydell
2024-10-09 14:32     ` Román Cárdenas Rodríguez
2024-10-10 10:20       ` Peter Maydell
2024-10-04 18:23   ` Peter Maydell
2024-09-27 15:07 ` [PATCH 4/4] Add GPIO device to stm32f405 SoC Román Cárdenas Rodríguez
2024-10-04 18:11 ` [PATCH 1/4] STM32F4: new RCC device Peter Maydell

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).