hw/char: implement ESP32-S3 UART

S3 UART overrides ESP32's UART
This commit is contained in:
Dmitry Yakovlev
2024-04-23 17:42:52 +03:00
committed by Ivan Grokhotkov
parent 76f9e1f154
commit dced9356d8
3 changed files with 224 additions and 0 deletions
+138
View File
@@ -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)
+1
View File
@@ -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'))
+85
View File
@@ -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)