diff --git a/hw/char/esp32s3_uart.c b/hw/char/esp32s3_uart.c new file mode 100644 index 0000000000..c231f2c35c --- /dev/null +++ b/hw/char/esp32s3_uart.c @@ -0,0 +1,138 @@ +/* + * ESP32S3 UART emulation + * + * Copyright (c) 2023 Espressif Systems (Shanghai) Co. Ltd. + * + * This implementation overrides the ESP32 UARt one, check it out first. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 or + * (at your option) any later version. + */ + +#include "qemu/osdep.h" +#include "qemu/module.h" +#include "qapi/error.h" +#include "sysemu/sysemu.h" +#include "hw/sysbus.h" +#include "hw/irq.h" +#include "hw/char/esp32s3_uart.h" +#include "hw/misc/esp32s3_rtc_cntl.h" + +#define ESP32_UART_AUTOBAUD A_UART_AUTOBAUD +#define ESP32_UART_CONF1 A_UART_CONF1 + +static uint64_t esp32s3_uart_read(void *opaque, hwaddr addr, unsigned int size) +{ + ESP32S3UARTClass *class = ESP32S3_UART_GET_CLASS(opaque); + ESP32S3UARTState *s = ESP32S3_UART(opaque); + uint32_t r = 0; + + switch (addr) + { + case A_ESP32S3_UART_CONF1: + /* Return the mirrored conf1 */ + r = s->conf1; + break; + + default: + r = class->parent_uart_read(opaque, addr, size); + break; + } + + return r; +} + +static void esp32s3_uart_write(void *opaque, hwaddr addr, + uint64_t value, unsigned int size) +{ + ESP32S3UARTClass *class = ESP32S3_UART_GET_CLASS(opaque); + ESP32S3UARTState *s = ESP32S3_UART(opaque); + uint32_t autobaud = 0; + + /* UART_RXD_CNT_REG register is not at the same address on the ESP32 and ESP32-S3, so make the + * the translation here. */ + switch (addr) { + + case A_ESP32S3_UART_CONF0: + /* On the S3, AUTOBAUD is part of CONF0 register, but on the ESP32, it has its own + * register, poke that register instead */ + autobaud = FIELD_EX32(value, ESP32S3_UART_CONF0, AUTOBAUD_EN) ? 1 : 0; + class->parent_uart_write(opaque, ESP32_UART_AUTOBAUD, autobaud, sizeof(uint32_t)); + class->parent_uart_write(opaque, addr, value, size); + break; + + case A_ESP32S3_UART_MEM_CONF: + s->parent.rx_tout_thres = FIELD_EX32(value, ESP32S3_UART_MEM_CONF, RX_TOUT_THRHD); + break; + + case A_ESP32S3_UART_CONF1: + /* Store the value in our own mirror as the application may read it back */ + s->conf1 = value; + + /* Write the protected members of the parent's structure */ + s->parent.rx_tout_ena = FIELD_EX32(value, ESP32S3_UART_CONF1, RX_TOUT_EN); + s->parent.tx_empty_threshold = FIELD_EX32(value, ESP32S3_UART_CONF1, TXFIFO_EMPTY_THRHD); + s->parent.rx_full_threshold = FIELD_EX32(value, ESP32S3_UART_CONF1, RXFIFO_FULL_THRHD); + + esp32_uart_set_rx_timeout(&s->parent); + esp32_uart_update_irq(&s->parent); + break; + + default: + class->parent_uart_write(opaque, addr, value, size); + break; + } +} + +static void esp32s3_uart_realize(DeviceState *dev, Error **errp) +{ + // ESP32S3UARTState* esp32s3 = ESP32S3_UART(dev); + ESP32S3UARTClass* esp32s3_class = ESP32S3_UART_GET_CLASS(dev); + + /* Call the realize function of the parent class: ESP32UARTClass */ + esp32s3_class->parent_realize(dev, errp); +} + + +static void esp32s3_uart_init(Object *obj) +{ + /* No need to call parent's class function, this is done automatically by the QOM, even before + * calling the current function. */ +} + + +static void esp32s3_uart_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + ESP32S3UARTClass* esp32s3 = ESP32S3_UART_CLASS(klass); + ESP32UARTClass* esp32 = ESP32_UART_CLASS(klass); + + /* Set our class' parent_realize field to the current realize function, set by the + * parent class initializer. + * After doing this, it will be necessary for our function, esp32s3_uart_realize, + * to manually call the parent's one. */ + device_class_set_parent_realize(dc, esp32s3_uart_realize, &esp32s3->parent_realize); + + /* Override the UART operations functions */ + esp32s3->parent_uart_write = esp32->uart_write; + esp32s3->parent_uart_read = esp32->uart_read; + esp32->uart_write = esp32s3_uart_write; + esp32->uart_read = esp32s3_uart_read; +} + +static const TypeInfo esp32s3_uart_info = { + .name = TYPE_ESP32S3_UART, + .parent = TYPE_ESP32_UART, + .instance_size = sizeof(ESP32S3UARTState), + .instance_init = esp32s3_uart_init, + .class_init = esp32s3_uart_class_init, + .class_size = sizeof(ESP32S3UARTClass) +}; + +static void esp32s3_uart_register_types(void) +{ + type_register_static(&esp32s3_uart_info); +} + +type_init(esp32s3_uart_register_types) diff --git a/hw/char/meson.build b/hw/char/meson.build index 2d28bfcda9..cceb3c5315 100644 --- a/hw/char/meson.build +++ b/hw/char/meson.build @@ -35,6 +35,7 @@ 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_XTENSA_ESP32', if_true: files('esp32_uart.c')) system_ss.add(when: 'CONFIG_RISCV_ESP32C3', if_true: files('esp32_uart.c', 'esp32c3_uart.c')) +system_ss.add(when: 'CONFIG_XTENSA_ESP32S3', if_true: files('esp32_uart.c', 'esp32s3_uart.c')) system_ss.add(when: 'CONFIG_HTIF', if_true: files('riscv_htif.c')) system_ss.add(when: 'CONFIG_GOLDFISH_TTY', if_true: files('goldfish_tty.c')) diff --git a/include/hw/char/esp32s3_uart.h b/include/hw/char/esp32s3_uart.h new file mode 100644 index 0000000000..de409fa29b --- /dev/null +++ b/include/hw/char/esp32s3_uart.h @@ -0,0 +1,85 @@ +#pragma once + +#include "esp32_uart.h" + + +#define TYPE_ESP32S3_UART "esp32s3_soc.uart" +/* This macro can be used to "convert" a generic object into a more specific object, here ESP32S3UARTState. + * For example, it can convert a DeviceState to a ESP32S3UARTState */ +#define ESP32S3_UART(obj) OBJECT_CHECK(ESP32S3UARTState, (obj), TYPE_ESP32S3_UART) +/* This one can be used to get the class of a given object. + * For example, it can give the ESP32UARTClass type structure out of a DeviceState or a + * ESP32S3UARTState structure. */ +#define ESP32S3_UART_GET_CLASS(obj) OBJECT_GET_CLASS(ESP32S3UARTClass, obj, TYPE_ESP32S3_UART) +/* Finally, this macro is used to convert a generic class to ESP32S3UARTClass. + * For example, it can be used to convert an ObjectClass to a ESP32S3UARTClass*/ +#define ESP32S3_UART_CLASS(klass) OBJECT_CLASS_CHECK(ESP32S3UARTClass, klass, TYPE_ESP32S3_UART) + +typedef struct ESP32S3UARTState { + ESP32UARTState parent; + + uint32_t conf1; +} ESP32S3UARTState; + +typedef struct ESP32S3UARTClass { + ESP32UARTClass parent_class; + + /* These function pointers will be during class init, they will be populated + * by device_class_set_parent_* functions. They can then be called in the + * respective class methods: realize. */ + DeviceRealize parent_realize; + + /* Virtual attributes/methods overriden */ + void (*parent_uart_write)(void *opaque, hwaddr addr, uint64_t value, unsigned int size); + uint64_t (*parent_uart_read)(void *opaque, hwaddr addr, unsigned int size); +} ESP32S3UARTClass; + + +/** + * Define the ESP32-S3 specific registers, or the ones that saw their address or fields + * changed from the ESP32 UART + */ +REG32(ESP32S3_UART_CONF0, 0x20) + FIELD(ESP32S3_UART_CONF0, MEM_CLK_EN, 28, 1) + FIELD(ESP32S3_UART_CONF0, AUTOBAUD_EN, 27, 1) + FIELD(ESP32S3_UART_CONF0, ERR_WR_MASK, 26, 1) + FIELD(ESP32S3_UART_CONF0, CLK_EN, 25, 1) + FIELD(ESP32S3_UART_CONF0, DTR_INV, 24, 1) + FIELD(ESP32S3_UART_CONF0, RTS_INV, 23, 1) + FIELD(ESP32S3_UART_CONF0, TXD_INV, 22, 1) + FIELD(ESP32S3_UART_CONF0, DSR_INV, 21, 1) + FIELD(ESP32S3_UART_CONF0, CTS_INV, 20, 1) + FIELD(ESP32S3_UART_CONF0, RXD_INV, 19, 1) + FIELD(ESP32S3_UART_CONF0, TXFIFO_RST, 18, 1) + FIELD(ESP32S3_UART_CONF0, RXFIFO_RST, 17, 1) + FIELD(ESP32S3_UART_CONF0, IRDA_EN, 16, 1) + FIELD(ESP32S3_UART_CONF0, TX_FLOW_EN, 15, 1) + FIELD(ESP32S3_UART_CONF0, LOOPBACK, 14, 1) + FIELD(ESP32S3_UART_CONF0, IRDA_RX_INV, 13, 1) + FIELD(ESP32S3_UART_CONF0, IRDA_TX_INV, 12, 1) + FIELD(ESP32S3_UART_CONF0, IRDA_WCTL, 11, 1) + FIELD(ESP32S3_UART_CONF0, IRDA_TX_EN, 10, 1) + FIELD(ESP32S3_UART_CONF0, IRDA_DPLX, 9, 1) + FIELD(ESP32S3_UART_CONF0, TXD_BRK, 8, 1) + FIELD(ESP32S3_UART_CONF0, SW_DTR, 7, 1) + FIELD(ESP32S3_UART_CONF0, SW_RTS, 6, 1) + FIELD(ESP32S3_UART_CONF0, STOP_BIT_NUM, 4, 2) + FIELD(ESP32S3_UART_CONF0, BIT_NUM, 2, 2) + FIELD(ESP32S3_UART_CONF0, PARITY_EN, 1, 1) + FIELD(ESP32S3_UART_CONF0, PARITY, 0, 1) + +REG32(ESP32S3_UART_CONF1, 0x24) + FIELD(ESP32S3_UART_CONF1, RX_TOUT_EN, 23, 1) + FIELD(ESP32S3_UART_CONF1, RX_FLOW_EN, 22, 1) + FIELD(ESP32S3_UART_CONF1, RX_TOUT_FLOW_DIS, 21, 1) + FIELD(ESP32S3_UART_CONF1, DIS_RX_DAT_OVF, 20, 1) + FIELD(ESP32S3_UART_CONF1, TXFIFO_EMPTY_THRHD, 10, 10) + FIELD(ESP32S3_UART_CONF1, RXFIFO_FULL_THRHD, 0, 10) + +REG32(ESP32S3_UART_MEM_CONF, 0x60) + FIELD(ESP32S3_UART_MEM_CONF, MEM_FORCE_PU, 28, 1) + FIELD(ESP32S3_UART_MEM_CONF, MEM_FORCE_PD, 27, 1) + FIELD(ESP32S3_UART_MEM_CONF, RX_TOUT_THRHD, 17, 10) + FIELD(ESP32S3_UART_MEM_CONF, RX_FLOW_THRHD, 7, 9) + FIELD(ESP32S3_UART_MEM_CONF, TX_SIZE, 4, 3) + FIELD(ESP32S3_UART_MEM_CONF, RX_SIZE, 1, 3)