hw/char: implement ESP32-S3 UART
S3 UART overrides ESP32's UART
This commit is contained in:
committed by
Ivan Grokhotkov
parent
76f9e1f154
commit
dced9356d8
@@ -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)
|
||||
@@ -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'))
|
||||
|
||||
|
||||
@@ -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)
|
||||
Reference in New Issue
Block a user