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