From: Yihao Fan <fanyi...@rt-thread.org> This patch adds support for the STM32F407 USART controllers device model.
Signed-off-by: Yihao Fan <fanyi...@rt-thread.org> --- MAINTAINERS | 2 + hw/arm/Kconfig | 1 + hw/arm/stm32f407_soc.c | 25 +++ hw/char/Kconfig | 3 + hw/char/meson.build | 1 + hw/char/stm32f4xx_usart.c | 236 ++++++++++++++++++++++++++++++ include/hw/arm/stm32f407_soc.h | 8 + include/hw/char/stm32f4xx_usart.h | 60 ++++++++ 8 files changed, 335 insertions(+) create mode 100644 hw/char/stm32f4xx_usart.c create mode 100644 include/hw/char/stm32f4xx_usart.h diff --git a/MAINTAINERS b/MAINTAINERS index 0dc7c7bf60..2054aba27e 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -1150,6 +1150,8 @@ M: Yihao Fan <fanyi...@rt-thread.org> L: qemu-...@nongnu.org S: Maintained F: hw/arm/stm32f407_soc.c +F: hw/char/stm32f4xx_usart.c +F: include/hw/char/stm32f4xx_usart.h Netduino 2 M: Alistair Francis <alist...@alistair23.me> diff --git a/hw/arm/Kconfig b/hw/arm/Kconfig index 3706a65286..c6a4919266 100644 --- a/hw/arm/Kconfig +++ b/hw/arm/Kconfig @@ -403,6 +403,7 @@ config STM32F407_SOC select ARM_V7M select STM32F4XX_SYSCFG select STM32F4XX_EXTI + select STM32F4XX_USART config B_L475E_IOT01A bool diff --git a/hw/arm/stm32f407_soc.c b/hw/arm/stm32f407_soc.c index 0a91d4bb10..8a929674af 100644 --- a/hw/arm/stm32f407_soc.c +++ b/hw/arm/stm32f407_soc.c @@ -20,6 +20,13 @@ static const int exti_irq[] = { 40, 40, 40, 40, 40 }; +static const uint32_t usart_addr[STM_NUM_USARTS] = { + STM32F407_USART1, STM32F407_USART2, STM32F407_USART3, + STM32F407_USART6 +}; +static const int usart_irq[STM_NUM_USARTS] = { + 37, 38, 39, 71 +}; static void stm32f407_soc_initfn(Object *obj) { @@ -32,6 +40,12 @@ static void stm32f407_soc_initfn(Object *obj) object_initialize_child(obj, "syscfg", &s->syscfg, TYPE_STM32F4XX_SYSCFG); object_initialize_child(obj, "exti", &s->exti, TYPE_STM32F4XX_EXTI); + for (i = 0; i < STM_NUM_USARTS; i++) { + object_initialize_child(obj, "usart[*]", &s->usart[i], + TYPE_STM32F4XX_USART); + } + + s->sysclk = qdev_init_clock_in(DEVICE(s), "sysclk", NULL, NULL, 0); s->refclk = qdev_init_clock_in(DEVICE(s), "refclk", NULL, NULL, 0); } @@ -105,6 +117,18 @@ static void stm32f407_soc_realize(DeviceState *dev_soc, Error **errp) qdev_connect_gpio_out(DEVICE(&s->syscfg), i, qdev_get_gpio_in(dev, i)); } + /* USART controllers */ + for (i = 0; i < STM_NUM_USARTS; i) { + dev = DEVICE(&(s->usart[i])); + qdev_prop_set_chr(dev, "chardev", serial_hd(i)); + if (!sysbus_realize(SYS_BUS_DEVICE(&s->usart[i]), errp)) { + return; + } + busdev = SYS_BUS_DEVICE(dev); + sysbus_mmio_map(busdev, 0, usart_addr[i]); + sysbus_connect_irq(busdev, 0, qdev_get_gpio_in(armv7m, usart_irq[i])); + } + } static void stm32f407_soc_class_init(ObjectClass *klass, void *data) diff --git a/hw/char/Kconfig b/hw/char/Kconfig index 9d517f3e28..25a0483fb3 100644 --- a/hw/char/Kconfig +++ b/hw/char/Kconfig @@ -51,6 +51,9 @@ config VIRTIO_SERIAL config STM32F2XX_USART bool +config STM32F4XX_USART + bool + config STM32L4X5_USART bool diff --git a/hw/char/meson.build b/hw/char/meson.build index 4e439da8b9..3372e77bbc 100644 --- a/hw/char/meson.build +++ b/hw/char/meson.build @@ -32,6 +32,7 @@ system_ss.add(when: 'CONFIG_RENESAS_SCI', if_true: files('renesas_sci.c')) system_ss.add(when: 'CONFIG_SIFIVE_UART', if_true: files('sifive_uart.c')) system_ss.add(when: 'CONFIG_SH_SCI', if_true: files('sh_serial.c')) system_ss.add(when: 'CONFIG_STM32F2XX_USART', if_true: files('stm32f2xx_usart.c')) +system_ss.add(when: 'CONFIG_STM32F4XX_USART', if_true: files('stm32f4xx_usart.c')) system_ss.add(when: 'CONFIG_STM32L4X5_USART', if_true: files('stm32l4x5_usart.c')) system_ss.add(when: 'CONFIG_MCHP_PFSOC_MMUART', if_true: files('mchp_pfsoc_mmuart.c')) system_ss.add(when: 'CONFIG_HTIF', if_true: files('riscv_htif.c')) diff --git a/hw/char/stm32f4xx_usart.c b/hw/char/stm32f4xx_usart.c new file mode 100644 index 0000000000..c3d2690275 --- /dev/null +++ b/hw/char/stm32f4xx_usart.c @@ -0,0 +1,236 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +#include "qemu/osdep.h" +#include "hw/char/stm32f4xx_usart.h" +#include "qemu/log.h" +#include "hw/irq.h" +#include "hw/qdev-properties.h" +#include "hw/qdev-properties-system.h" +#include "qemu/module.h" + +#ifndef STM_USART_ERR_DEBUG +#define STM_USART_ERR_DEBUG 0 +#endif + +#define DB_PRINT_L(lvl, fmt, args...) do { \ + if (STM_USART_ERR_DEBUG >= lvl) { \ + qemu_log("%s: " fmt, __func__, ## args); \ + } \ +} while (0) + +#define DB_PRINT(fmt, args...) DB_PRINT_L(1, fmt, ## args) + +static int stm32f4xx_usart_can_receive(void *opaque) +{ + STM32F4XXUsartState *s = opaque; + + if (!(s->usart_sr & USART_SR_RXNE)) { + return 1; + } + + return 0; +} + +static void stm32f4xx_usart_receive(void *opaque, const uint8_t *buf, int size) +{ + STM32F4XXUsartState *s = opaque; + + s->usart_dr = *buf; + + if (!(s->usart_cr1 & USART_CR1_UE && s->usart_cr1 & USART_CR1_RE)) { + /* USART not enabled - drop the chars */ + DB_PRINT("Dropping the chars\n"); + return; + } + + s->usart_sr |= USART_SR_RXNE; + + if (s->usart_cr1 & USART_CR1_RXNEIE) { + qemu_set_irq(s->irq, 1); + } + + DB_PRINT("Receiving: %c\n", s->usart_dr); +} + +static void stm32f4xx_usart_reset(DeviceState *dev) +{ + STM32F4XXUsartState *s = STM32F4XX_USART(dev); + + s->usart_sr = USART_SR_RESET; + s->usart_dr = 0x00000000; + s->usart_brr = 0x00000000; + s->usart_cr1 = 0x00000000; + s->usart_cr2 = 0x00000000; + s->usart_cr3 = 0x00000000; + s->usart_gtpr = 0x00000000; + + qemu_set_irq(s->irq, 0); +} + +static uint64_t stm32f4xx_usart_read(void *opaque, hwaddr addr, + unsigned int size) +{ + STM32F4XXUsartState *s = opaque; + uint64_t retvalue; + + DB_PRINT("Read 0x%"HWADDR_PRIx"\n", addr); + + switch (addr) { + case USART_SR: + retvalue = s->usart_sr; + qemu_chr_fe_accept_input(&s->chr); + return retvalue; + case USART_DR: + DB_PRINT("Value: 0x%" PRIx32 ", %c\n", s->usart_dr, (char) s->usart_dr); + s->usart_sr |= USART_SR_TXE; + s->usart_sr &= ~USART_SR_RXNE; + qemu_chr_fe_accept_input(&s->chr); + qemu_set_irq(s->irq, 0); + if (s->usart_cr1 & USART_CR1_M) { + return s->usart_dr & 0x1FF; + } else { + return s->usart_dr & 0xFF; + } + case USART_BRR: + return s->usart_brr; + case USART_CR1: + return s->usart_cr1; + case USART_CR2: + return s->usart_cr2; + case USART_CR3: + return s->usart_cr3; + case USART_GTPR: + return s->usart_gtpr; + default: + qemu_log_mask(LOG_GUEST_ERROR, + "%s: Bad offset 0x%"HWADDR_PRIx"\n", __func__, addr); + return 0; + } + + return 0; +} + +static void stm32f4xx_usart_write(void *opaque, hwaddr addr, + uint64_t val64, unsigned int size) +{ + STM32F4XXUsartState *s = opaque; + uint32_t value = val64; + unsigned char ch; + + DB_PRINT("Write 0x%" PRIx32 ", 0x%"HWADDR_PRIx"\n", value, addr); + + switch (addr) { + case USART_SR: + if (value <= 0x3FF) { + s->usart_sr |= value; + } else { + s->usart_sr &= value; + } + if (!(s->usart_sr & USART_SR_RXNE)) { + qemu_set_irq(s->irq, 0); + } + return; + case USART_DR: + if (s->usart_cr1 & USART_CR1_M) { + ch = value & 0x1FF; + } else { + ch = value & 0xFF; + } + if (!(s->usart_cr1 & USART_CR1_TE)) { + return; + } + if ((s->usart_sr & USART_SR_TC)) { + s->usart_sr &= ~USART_SR_TC; + } + ch = value; + qemu_chr_fe_write_all(&s->chr, &ch, 1); + s->usart_sr |= USART_SR_TXE; + if (s->usart_cr1 & USART_CR1_TXEIE) { + qemu_set_irq(s->irq, 0); + } + s->usart_sr |= USART_SR_TC; + if (s->usart_cr1 & USART_CR1_TCIE) { + qemu_set_irq(s->irq, 0); + } + break; + case USART_BRR: + s->usart_brr |= value & 0xFFFF; + break; + case USART_CR1: + if (!(s->usart_cr1 & USART_CR1_TE) && (value & USART_CR1_TE)) { + s->usart_dr = 0xFF; + } + s->usart_cr1 |= value & 0xFFFF; + if (s->usart_cr1 & USART_CR1_RXNEIE && + s->usart_sr & USART_SR_RXNE) { + qemu_set_irq(s->irq, 0); + } + break; + case USART_CR2: + s->usart_cr2 |= value & 0xFFFF; + break; + case USART_CR3: + s->usart_cr3 |= value; + break; + case USART_GTPR: + s->usart_gtpr |= value & 0xFFFF; + break; + default: + qemu_log_mask(LOG_GUEST_ERROR, + "%s: Bad offset 0x%"HWADDR_PRIx"\n", __func__, addr); + break; + } +} + +static const MemoryRegionOps stm32f4xx_usart_ops = { + .read = stm32f4xx_usart_read, + .write = stm32f4xx_usart_write, + .endianness = DEVICE_NATIVE_ENDIAN, +}; + +static Property stm32f4xx_usart_properties[] = { + DEFINE_PROP_CHR("chardev", STM32F4XXUsartState, chr), +}; + +static void stm32f4xx_usart_init(Object *obj) +{ + STM32F4XXUsartState *s = STM32F4XX_USART(obj); + + sysbus_init_irq(SYS_BUS_DEVICE(obj), &s->irq); + + memory_region_init_io(&s->mmio, obj, &stm32f4xx_usart_ops, s, + TYPE_STM32F4XX_USART, 0x400); + sysbus_init_mmio(SYS_BUS_DEVICE(obj), &s->mmio); +} + +static void stm32f4xx_usart_realize(DeviceState *dev, Error **errp) +{ + STM32F4XXUsartState *s = STM32F4XX_USART(dev); + + qemu_chr_fe_set_handlers(&s->chr, stm32f4xx_usart_can_receive, + stm32f4xx_usart_receive, NULL, NULL, + s, NULL, true); +} + +static void stm32f4xx_usart_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + + device_class_set_legacy_reset(dc, stm32f4xx_usart_reset); + device_class_set_props(dc, stm32f4xx_usart_properties); + dc->realize = stm32f4xx_usart_realize; +} + +static const TypeInfo stm32f4xx_usart_info = { + .name = TYPE_STM32F4XX_USART, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(STM32F4XXUsartState), + .instance_init = stm32f4xx_usart_init, + .class_init = stm32f4xx_usart_class_init, +}; + +static void stm32f4xx_usart_register_types(void) +{ + type_register_static(&stm32f4xx_usart_info); +} + +type_init(stm32f4xx_usart_register_types) diff --git a/include/hw/arm/stm32f407_soc.h b/include/hw/arm/stm32f407_soc.h index 19191dc44e..6599e8aa48 100644 --- a/include/hw/arm/stm32f407_soc.h +++ b/include/hw/arm/stm32f407_soc.h @@ -6,6 +6,7 @@ #include "hw/arm/armv7m.h" #include "hw/misc/stm32f4xx_syscfg.h" #include "hw/misc/stm32f4xx_exti.h" +#include "hw/char/stm32f4xx_usart.h" #include "qom/object.h" @@ -20,6 +21,12 @@ OBJECT_DECLARE_SIMPLE_TYPE(STM32F407State, STM32F407_SOC) #define SRAM_BASE_ADDRESS 0x20000000 #define SRAM_SIZE (192 * 1024) +#define STM_NUM_USARTS 4 +#define STM32F407_USART1 0x40011000 +#define STM32F407_USART2 0x40004400 +#define STM32F407_USART3 0x40004800 +#define STM32F407_USART6 0x40011400 + struct STM32F407State { /*< private >*/ @@ -31,6 +38,7 @@ struct STM32F407State { STM32F4xxSyscfgState syscfg; STM32F4xxExtiState exti; + STM32F4XXUsartState usart[STM_NUM_USARTS]; Clock *sysclk; Clock *refclk; diff --git a/include/hw/char/stm32f4xx_usart.h b/include/hw/char/stm32f4xx_usart.h new file mode 100644 index 0000000000..611906bd83 --- /dev/null +++ b/include/hw/char/stm32f4xx_usart.h @@ -0,0 +1,60 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +#ifndef HW_STM32F4XX_USART_H +#define HW_STM32F4XX_USART_H + +#include "hw/sysbus.h" +#include "chardev/char-fe.h" + +#define USART_SR 0x00 +#define USART_DR 0x04 +#define USART_BRR 0x08 +#define USART_CR1 0x0C +#define USART_CR2 0x10 +#define USART_CR3 0x14 +#define USART_GTPR 0x18 + +#define USART_SR_RESET 0x00C0 + +#define USART_SR_TXE (1 << 7) +#define USART_SR_TC (1 << 6) +#define USART_SR_RXNE (1 << 5) + +#define USART_CR1_UE (1 << 13) +#define USART_CR1_RXNEIE (1 << 5) +#define USART_CR1_TE (1 << 3) +#define USART_CR1_RE (1 << 2) +#define USART_CR1_M (1 << 12) +#define USART_CR1_TXEIE (1 << 7) +#define USART_CR1_TCIE (1 << 6) + +#define USART_CR2_CLKEN (1 << 11) +#define USART_CR2_LINEN (1 << 14) + +#define USART_CR3_SCEN (1 << 5) +#define USART_CR3_HDSEL (1 << 3) +#define USART_CR3_IREN (1 << 1) + +#define TYPE_STM32F4XX_USART "stm32f4xx-usart" +#define STM32F4XX_USART(obj) \ + OBJECT_CHECK(STM32F4XXUsartState, (obj), TYPE_STM32F4XX_USART) + +typedef struct { + /* <private> */ + SysBusDevice parent_obj; + + /* <public> */ + MemoryRegion mmio; + + uint32_t usart_sr; + uint32_t usart_dr; + uint32_t usart_brr; + uint32_t usart_cr1; + uint32_t usart_cr2; + uint32_t usart_cr3; + uint32_t usart_gtpr; + + CharBackend chr; + qemu_irq irq; +} STM32F4XXUsartState; + +#endif -- 2.43.0