Signed-off-by: Evgeny Ermakov <evgeny.v.erma...@gmail.com>
---
 include/hw/char/stm32f7xx_usart.h |  30 +++
 hw/char/stm32f7xx_usart.c         | 361 ++++++++++++++++++++++++++++++
 hw/arm/Kconfig                    |   1 +
 hw/char/Kconfig                   |   3 +
 hw/char/meson.build               |   1 +
 hw/char/trace-events              |   4 +
 6 files changed, 400 insertions(+)
 create mode 100644 include/hw/char/stm32f7xx_usart.h
 create mode 100644 hw/char/stm32f7xx_usart.c

diff --git a/include/hw/char/stm32f7xx_usart.h 
b/include/hw/char/stm32f7xx_usart.h
new file mode 100644
index 0000000000..ec005be8d8
--- /dev/null
+++ b/include/hw/char/stm32f7xx_usart.h
@@ -0,0 +1,30 @@
+/*
+ * STM32F7XX Universal synchronous/asynchronous receiver transmitter (USART)
+ *
+ * Copyright (c) 2022 Evgeny Ermakov <evgeny.v.erma...@gmail.com>
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#ifndef HW_CHAR_STM32F7XX_USART_H
+#define HW_CHAR_STM32F7XX_USART_H
+
+#include "hw/arm/stm32f.h"
+#include "chardev/char-fe.h"
+
+#define TYPE_STM32F7XX_USART "stm32f7xx-usart"
+OBJECT_DECLARE_SIMPLE_TYPE(STM32F7XXUSARTState, STM32F7XX_USART)
+
+#define STM32F7XX_USART_R_MAX 11
+
+struct STM32F7XXUSARTState {
+    /*< private >*/
+    STM32FPeripheralState parent_obj;
+
+    uint32_t regs[STM32F7XX_USART_R_MAX];
+
+    CharBackend chr;
+    qemu_irq irq;
+};
+
+#endif /* HW_CHAR_STM32F7XX_USART_H */
diff --git a/hw/char/stm32f7xx_usart.c b/hw/char/stm32f7xx_usart.c
new file mode 100644
index 0000000000..122781705a
--- /dev/null
+++ b/hw/char/stm32f7xx_usart.c
@@ -0,0 +1,361 @@
+/*
+ * STM32F7XX Universal synchronous/asynchronous receiver transmitter (USART)
+ *
+ * Reference documents:
+ *   - Reference manual RM0385
+ *       "STM32F75xxx and stm32f74xxx advanced Arm(R)-based 32-bit MCUs"
+ *   - Reference manual RM0410
+ *       "STM32F76xxx and STM32F77xxx advanced Arm(R)-based 32-bit MCUs"
+ *
+ * Copyright (c) 2022 Evgeny Ermakov <evgeny.v.erma...@gmail.com>
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#include "qemu/osdep.h"
+#include "hw/char/stm32f7xx_usart.h"
+#include "hw/irq.h"
+#include "hw/qdev-properties-system.h"
+#include "hw/registerfields.h"
+#include "migration/vmstate.h"
+#include "qemu/log.h"
+#include "trace.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)
+
+REG32(CR1,         0x00)
+    /* reserved: 31:29, 1 */
+    FIELD(CR1,        M1,             28, 1)
+    FIELD(CR1,        EOBIE,          27, 1)
+    FIELD(CR1,        RTOIE,          26, 1)
+    FIELD(CR1,        DEAT,           21, 5)
+    FIELD(CR1,        DEDT,           16, 5)
+    FIELD(CR1,        OVER8,          15, 1)
+    FIELD(CR1,        CMIE,           14, 1)
+    FIELD(CR1,        MME,            13, 1)
+    FIELD(CR1,        M0,             12, 1)
+    FIELD(CR1,        WAKE,           11, 1)
+    FIELD(CR1,        PCE,            10, 1)
+    FIELD(CR1,        PS,              9, 1)
+    FIELD(CR1,        PEIE,            8, 1)
+    FIELD(CR1,        TXEIE,           7, 1)
+    FIELD(CR1,        TCIE,            6, 1)
+    FIELD(CR1,        RXNEIE,          5, 1)
+    FIELD(CR1,        IDLEIE,          4, 1)
+    FIELD(CR1,        TE,              3, 1)
+    FIELD(CR1,        RE,              2, 1)
+    FIELD(CR1,        UE,              0, 1)
+REG32(CR2,         0x04)
+    /* reserved: 7, 3:0 */
+    FIELD(CR2,        ADD,            24, 8)
+    FIELD(CR2,        RTOEN,          23, 1)
+    FIELD(CR2,        ABRMOD,         21, 2)
+    FIELD(CR2,        ABREN,          20, 1)
+    FIELD(CR2,        MSBFIRST,       19, 1)
+    FIELD(CR2,        DATAINV,        18, 1)
+    FIELD(CR2,        TXINV,          17, 1)
+    FIELD(CR2,        RXINV,          16, 1)
+    FIELD(CR2,        SWAP,           15, 1)
+    FIELD(CR2,        LINEN,          14, 1)
+    FIELD(CR2,        STOP,           12, 2)
+    FIELD(CR2,        CLKEN,          11, 1)
+    FIELD(CR2,        CPOL,           10, 1)
+    FIELD(CR2,        CPHA,            9, 1)
+    FIELD(CR2,        LBCL,            8, 1)
+    FIELD(CR2,        LBDIE,           6, 1)
+    FIELD(CR2,        LBDL,            5, 1)
+    FIELD(CR2,        ADDM7,           4, 1)
+REG32(CR3,         0x08)
+    /* reserved: 31:25, 16 */
+    FIELD(CR3,        TCBGTIE,        24, 1)
+    FIELD(CR3,        UCESM,          23, 1)
+    FIELD(CR3,        WUFIE,          22, 1)
+    FIELD(CR3,        WUS,            20, 2)
+    FIELD(CR3,        SCARCNT,        17, 3)
+    FIELD(CR3,        DEP,            15, 1)
+    FIELD(CR3,        DEM,            14, 1)
+    FIELD(CR3,        DDRE,           13, 1)
+    FIELD(CR3,        OVRDIS,         12, 1)
+    FIELD(CR3,        ONEBIT,         11, 1)
+    FIELD(CR3,        CTSIE,          10, 1)
+    FIELD(CR3,        CTSE,            9, 1)
+    FIELD(CR3,        RTSE,            8, 1)
+    FIELD(CR3,        DMAT,            7, 1)
+    FIELD(CR3,        DMAR,            6, 1)
+    FIELD(CR3,        SCEN,            5, 1)
+    FIELD(CR3,        NACK,            4, 1)
+    FIELD(CR3,        HDSEL,           3, 1)
+    FIELD(CR3,        IRLP,            2, 1)
+    FIELD(CR3,        IREN,            1, 1)
+    FIELD(CR3,        EIE,             0, 1)
+REG32(BRR,         0x0c)
+    /* reserved: 31:16 */
+    FIELD(BRR,        BRR,             0, 15)
+REG32(GTPR,        0x10)
+    /* reserved: 31:16 */
+    FIELD(GTPR,       GT,              8, 8)
+    FIELD(GTPR,       PSC,             0, 8)
+REG32(RTOR,        0x14)
+    FIELD(RTOR,       BLEN,           24, 8)
+    FIELD(RTOR,       RTO,             0, 24)
+REG32(RQR,         0x18)
+    /* reserved: 31:5 */
+    FIELD(RQR,        TXFRQ,           4, 1)
+    FIELD(RQR,        RXFRQ,           3, 1)
+    FIELD(RQR,        MMRQ,            2, 1)
+    FIELD(RQR,        SBKRQ,           1, 1)
+    FIELD(RQR,        ABRRQ,           0, 1)
+REG32(ISR,         0x1c)
+    /* reserved: 31:26, 24:23, 13 */
+    FIELD(ISR,        TCBGT,          25, 1)
+    FIELD(ISR,        REACK,          22, 1)
+    FIELD(ISR,        TEACK,          21, 1)
+    FIELD(ISR,        WUF,            20, 1)
+    FIELD(ISR,        RWU,            19, 1)
+    FIELD(ISR,        SBKF,           18, 1)
+    FIELD(ISR,        CMF,            17, 1)
+    FIELD(ISR,        BUSY,           16, 1)
+    FIELD(ISR,        ABRF,           15, 1)
+    FIELD(ISR,        ABRE,           14, 1)
+    FIELD(ISR,        EOBF,           12, 1)
+    FIELD(ISR,        RTOF,           11, 1)
+    FIELD(ISR,        CTS,            10, 1)
+    FIELD(ISR,        CTSIF,           9, 1)
+    FIELD(ISR,        LBDF,            8, 1)
+    FIELD(ISR,        TXE,             7, 1)
+    FIELD(ISR,        TC,              6, 1)
+    FIELD(ISR,        RXNE,            5, 1)
+    FIELD(ISR,        IDLE,            4, 1)
+    FIELD(ISR,        ORE,             3, 1)
+    FIELD(ISR,        NF,              2, 1)
+    FIELD(ISR,        FE,              1, 1)
+    FIELD(ISR,        PE,              0, 1)
+REG32(ICR,         0x20)
+    /* reserved: 31:21, 19:18, 16:13, 10, 5 */
+    FIELD(ICR,        WUCF,           20, 1)
+    FIELD(ICR,        CMCF,           17, 1)
+    FIELD(ICR,        EOBCF,          12, 1)
+    FIELD(ICR,        RTOCF,          11, 1)
+    FIELD(ICR,        CTSCF,           9, 1)
+    FIELD(ICR,        LBDCF,           8, 1)
+    FIELD(ICR,        TCBGTCF,         7, 1)
+    FIELD(ICR,        TCCF,            6, 1)
+    FIELD(ICR,        IDLECF,          4, 1)
+    FIELD(ICR,        ORECF,           3, 1)
+    FIELD(ICR,        NCF,             2, 1)
+    FIELD(ICR,        FECF,            1, 1)
+    FIELD(ICR,        PECF,            0, 1)
+REG32(RDR,         0x24)
+    /* reserved: 31:9 */
+    FIELD(RDR,        RDR,             0, 9)
+REG32(TDR,         0x28)
+    /* reserved: 31:9 */
+    FIELD(TDR,        TDR,             0, 9)
+
+
+static int stm32f7xx_usart_can_receive(void *opaque)
+{
+    /* STM32F7XXUSARTState *s = opaque; */
+
+    /* if (!(s->usart_sr & USART_SR_RXNE)) { */
+    /*     return 1; */
+    /* } */
+
+    return 0;
+}
+
+static void stm32f7xx_usart_receive(void *opaque, const uint8_t *buf, int size)
+{
+#if 0
+    STM32F7XXUSARTState *s = opaque;
+
+    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_dr = *buf;
+    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);
+#endif
+}
+
+static uint32_t stm32f7xx_usart_read(void *opaque, hwaddr addr,
+                                     unsigned int size)
+{
+    STM32F7XXUSARTState *s = opaque;
+
+    trace_stm32f7xx_usart_read(addr);
+
+    switch (addr) {
+    case A_CR1:
+        return s->regs[R_CR1];
+    case A_CR2:
+        return s->regs[R_CR2];
+    case A_CR3:
+        return s->regs[R_CR3];
+    case A_BRR:
+        return s->regs[R_BRR];
+    case A_GTPR:
+        return s->regs[R_GTPR];
+    case A_RTOR:
+        return s->regs[R_RTOR];
+    case A_RQR:
+        return s->regs[R_RQR];
+    case A_ISR:
+        return s->regs[R_ISR];
+    case A_ICR:
+        return s->regs[R_ICR];
+    case A_RDR:
+        return s->regs[R_RDR];
+    case A_TDR:
+        return s->regs[R_TDR];
+    default:
+        STM32F_LOG_BAD_OFFSET();
+        break;
+    }
+
+    return 0;
+}
+
+static void stm32f7xx_usart_write(void *opaque, hwaddr addr,
+                                  uint32_t value, unsigned int size)
+{
+    STM32F7XXUSARTState *s = opaque;
+    /* unsigned char ch; */
+
+    trace_stm32f7xx_usart_write(addr, value);
+
+    switch (addr) {
+    case A_CR1:
+        s->regs[R_CR1] = value;
+        break;
+    case A_CR2:
+        s->regs[R_CR2] = value;
+        break;
+    case A_CR3:
+        s->regs[R_CR3] = value;
+        break;
+    case A_BRR:
+        s->regs[R_BRR] = value;
+        break;
+    case A_GTPR:
+        s->regs[R_GTPR] = value;
+        break;
+    case A_RTOR:
+        s->regs[R_RTOR] = value;
+        break;
+    case A_RQR:
+        s->regs[R_RQR] = value;
+        break;
+    case A_ISR:
+        s->regs[R_ISR] = value;
+        break;
+    case A_ICR:
+        s->regs[R_ICR] = value;
+        break;
+    case A_RDR:
+        s->regs[R_RDR] = value;
+        break;
+    case A_TDR:
+        if (value < 0xf000) {
+            uint8_t ch = value;
+            /* XXX this blocks entire thread. Rewrite to use
+             * qemu_chr_fe_write and background I/O callbacks */
+            qemu_chr_fe_write_all(&s->chr, &ch, 1);
+            /* XXX I/O are currently synchronous, making it impossible for
+               software to observe transient states where TXE or TC aren't
+               set. Unlike TXE however, which is read-only, software may
+               clear TC by writing 0 to the SR register, so set it again
+               on each write. */
+            /* s->usart_sr |= USART_SR_TC; */
+        }
+        break;
+    default:
+        STM32F_LOG_BAD_OFFSET();
+        break;
+    }
+}
+
+static void stm32f7xx_usart_reset_enter(Object *obj, ResetType type)
+{
+    STM32F7XXUSARTState *s = STM32F7XX_USART(obj);
+
+    memset(s->regs, 0, sizeof(s->regs));
+    s->regs[R_ISR] = 0x020000c0;
+}
+
+static void stm32f7xx_usart_reset_exit(Object *obj)
+{
+    STM32F7XXUSARTState *s = STM32F7XX_USART(obj);
+
+    qemu_set_irq(s->irq, 0);
+}
+
+static void stm32f7xx_usart_init(Object *obj)
+{
+    STM32F7XXUSARTState *s = STM32F7XX_USART(obj);
+
+    sysbus_init_irq(SYS_BUS_DEVICE(obj), &s->irq);
+}
+
+static void stm32f7xx_usart_realize(DeviceState *dev, Error **errp)
+{
+    STM32F7XXUSARTState *s = STM32F7XX_USART(dev);
+
+    qemu_chr_fe_set_handlers(&s->chr, stm32f7xx_usart_can_receive,
+                             stm32f7xx_usart_receive, NULL, NULL,
+                             s, NULL, true);
+}
+
+static Property stm32f7xx_usart_properties[] = {
+    DEFINE_PROP_CHR("chardev", STM32F7XXUSARTState, chr),
+    DEFINE_PROP_END_OF_LIST()
+};
+
+static void stm32f7xx_usart_class_init(ObjectClass *klass, void *data)
+{
+    DeviceClass *dc = DEVICE_CLASS(klass);
+    STM32FPeripheralClass *pc = STM32F_PERIPHERAL_CLASS(klass);
+    ResettableClass *rc = RESETTABLE_CLASS(klass);
+
+    dc->realize = stm32f7xx_usart_realize;
+    device_class_set_props(dc, stm32f7xx_usart_properties);
+    rc->phases.enter = stm32f7xx_usart_reset_enter;
+    rc->phases.exit = stm32f7xx_usart_reset_exit;
+    pc->mmio_size = 0x400;
+    pc->mmio_read = stm32f7xx_usart_read;
+    pc->mmio_write = stm32f7xx_usart_write;
+}
+
+static const TypeInfo stm32f7xx_usart_info = {
+    .name          = TYPE_STM32F7XX_USART,
+    .parent        = TYPE_STM32F_PERIPHERAL,
+    .instance_size = sizeof(STM32F7XXUSARTState),
+    .instance_init = stm32f7xx_usart_init,
+    .class_init    = stm32f7xx_usart_class_init,
+};
+
+static void stm32f7xx_usart_register_types(void)
+{
+    type_register_static(&stm32f7xx_usart_info);
+}
+
+type_init(stm32f7xx_usart_register_types)
diff --git a/hw/arm/Kconfig b/hw/arm/Kconfig
index c2f6e748b0..02dfbcb99a 100644
--- a/hw/arm/Kconfig
+++ b/hw/arm/Kconfig
@@ -376,6 +376,7 @@ config STM32F405_SOC
 config STM32F7XX_SOC
     bool
     select STM32F
+    select STM32F7XX_USART
 
 config XLNX_ZYNQMP_ARM
     bool
diff --git a/hw/char/Kconfig b/hw/char/Kconfig
index 6b6cf2fc1d..22b1cf8062 100644
--- a/hw/char/Kconfig
+++ b/hw/char/Kconfig
@@ -41,6 +41,9 @@ config VIRTIO_SERIAL
 config STM32F2XX_USART
     bool
 
+config STM32F7XX_USART
+    bool
+
 config CMSDK_APB_UART
     bool
 
diff --git a/hw/char/meson.build b/hw/char/meson.build
index 7b594f51b8..6230750375 100644
--- a/hw/char/meson.build
+++ b/hw/char/meson.build
@@ -31,6 +31,7 @@ softmmu_ss.add(when: 'CONFIG_RENESAS_SCI', if_true: 
files('renesas_sci.c'))
 softmmu_ss.add(when: 'CONFIG_SIFIVE_UART', if_true: files('sifive_uart.c'))
 softmmu_ss.add(when: 'CONFIG_SH_SCI', if_true: files('sh_serial.c'))
 softmmu_ss.add(when: 'CONFIG_STM32F2XX_USART', if_true: 
files('stm32f2xx_usart.c'))
+softmmu_ss.add(when: 'CONFIG_STM32F7XX_USART', if_true: 
files('stm32f7xx_usart.c'))
 softmmu_ss.add(when: 'CONFIG_MCHP_PFSOC_MMUART', if_true: 
files('mchp_pfsoc_mmuart.c'))
 
 specific_ss.add(when: 'CONFIG_HTIF', if_true: files('riscv_htif.c'))
diff --git a/hw/char/trace-events b/hw/char/trace-events
index 2ecb36232e..41fa3c0b46 100644
--- a/hw/char/trace-events
+++ b/hw/char/trace-events
@@ -105,3 +105,7 @@ cadence_uart_baudrate(unsigned baudrate) "baudrate %u"
 # sh_serial.c
 sh_serial_read(char *id, unsigned size, uint64_t offs, uint64_t val) " %s size 
%d offs 0x%02" PRIx64 " -> 0x%02" PRIx64
 sh_serial_write(char *id, unsigned size, uint64_t offs, uint64_t val) "%s size 
%d offs 0x%02" PRIx64 " <- 0x%02" PRIx64
+
+# stm32f7xx_usart.c
+stm32f7xx_usart_read(uint64_t addr) " addr: 0x%02" PRIx64
+stm32f7xx_usart_write(uint64_t addr, uint64_t data) "addr: 0x%02" PRIx64 " 
val: 0x%" PRIx64
-- 
2.38.1


Reply via email to