* [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