408 lines
12 KiB
C
408 lines
12 KiB
C
/*
|
|
* ESP32 UART emulation
|
|
*
|
|
* Copyright (c) 2019 Espressif Systems (Shanghai) Co. Ltd.
|
|
*
|
|
* The QEMU model of nRF51 UART by Julia Suvorova was used as a template.
|
|
*
|
|
* 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/log.h"
|
|
#include "qemu/module.h"
|
|
#include "qapi/error.h"
|
|
#include "qemu/error-report.h"
|
|
#include "sysemu/sysemu.h"
|
|
#include "chardev/char-fe.h"
|
|
#include "hw/registerfields.h"
|
|
#include "hw/sysbus.h"
|
|
#include "hw/irq.h"
|
|
#include "hw/qdev-properties.h"
|
|
#include "hw/qdev-properties-system.h"
|
|
#include "hw/char/esp32_uart.h"
|
|
#include "trace.h"
|
|
|
|
|
|
static gboolean uart_transmit(void *do_not_use, GIOCondition cond, void *opaque);
|
|
static void uart_receive(void *opaque, const uint8_t *buf, int size);
|
|
|
|
|
|
void esp32_uart_update_irq(ESP32UARTState *s)
|
|
{
|
|
bool irq = false;
|
|
|
|
uint32_t tx_empty_raw = (fifo8_num_used(&s->tx_fifo) <= s->tx_empty_threshold);
|
|
uint32_t rx_full_raw = (fifo8_num_used(&s->rx_fifo) >= s->rx_full_threshold);
|
|
uint32_t tx_done_raw = (fifo8_num_used(&s->tx_fifo) == 0);
|
|
uint32_t rxfifo_tout_raw = (s->rxfifo_tout) ? 1 : 0;
|
|
|
|
uint32_t int_raw = s->reg[R_UART_INT_RAW];
|
|
int_raw = FIELD_DP32(int_raw, UART_INT_RAW, RXFIFO_FULL, rx_full_raw);
|
|
int_raw = FIELD_DP32(int_raw, UART_INT_RAW, TXFIFO_EMPTY, tx_empty_raw);
|
|
int_raw = FIELD_DP32(int_raw, UART_INT_RAW, TX_DONE, tx_done_raw);
|
|
int_raw = FIELD_DP32(int_raw, UART_INT_RAW, RXFIFO_TOUT, rxfifo_tout_raw);
|
|
s->reg[R_UART_INT_RAW] = int_raw;
|
|
|
|
uint32_t int_st = s->reg[R_UART_INT_RAW] & s->reg[R_UART_INT_ENA];
|
|
irq = int_st != 0;
|
|
s->reg[R_UART_INT_ST] = int_st;
|
|
|
|
qemu_set_irq(s->irq, irq);
|
|
}
|
|
|
|
|
|
void esp32_uart_set_rx_timeout(ESP32UARTState *s)
|
|
{
|
|
if (s->rx_tout_ena) {
|
|
int64_t now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
|
|
int64_t rx_timeout_ns = now + s->rx_tout_thres * NANOSECONDS_PER_SECOND / s->baud_rate;
|
|
/* If throttling is done, make sure timeout doesn't happen before more data
|
|
* is allowed to come. Offset it by 1ms.
|
|
*/
|
|
if (rx_timeout_ns <= s->throttle_timer.expire_time) {
|
|
rx_timeout_ns = s->throttle_timer.expire_time + 10000000;
|
|
}
|
|
timer_mod_ns(&s->rx_timeout_timer, rx_timeout_ns);
|
|
} else {
|
|
timer_del(&s->rx_timeout_timer);
|
|
s->rxfifo_tout = false;
|
|
}
|
|
}
|
|
|
|
|
|
static uint64_t uart_read(void *opaque, hwaddr addr, unsigned int size)
|
|
{
|
|
ESP32UARTState *s = ESP32_UART(opaque);
|
|
uint64_t r = 0;
|
|
|
|
switch (addr) {
|
|
case A_UART_FIFO:
|
|
if (fifo8_num_used(&s->rx_fifo) == 0) {
|
|
r = 0xEE;
|
|
error_report("esp_uart: read UART FIFO while it is empty");
|
|
} else {
|
|
r = fifo8_pop(&s->rx_fifo);
|
|
esp32_uart_update_irq(s);
|
|
qemu_chr_fe_accept_input(&s->chr);
|
|
}
|
|
break;
|
|
|
|
case A_UART_STATUS:
|
|
r = FIELD_DP32(r, UART_STATUS, RXFIFO_CNT, fifo8_num_used(&s->rx_fifo));
|
|
r = FIELD_DP32(r, UART_STATUS, TXFIFO_CNT, fifo8_num_used(&s->tx_fifo));
|
|
break;
|
|
|
|
case A_UART_LOWPULSE:
|
|
case A_UART_HIGHPULSE:
|
|
r = 337; /* FIXME: this should depend on the APB frequency */
|
|
break;
|
|
case A_UART_MEM_CONF:
|
|
r = FIELD_DP32(r, UART_MEM_CONF, RX_SIZE, (unsigned char)(UART_FIFO_LENGTH/128));
|
|
r = FIELD_DP32(r, UART_MEM_CONF, TX_SIZE, (unsigned char)(UART_FIFO_LENGTH/128));
|
|
break;
|
|
case A_UART_MEM_RX_STATUS: {
|
|
uint32_t fifo_size = fifo8_num_used(&s->rx_fifo);
|
|
/* The software only cares about the differene between WR_ADDR and RD_ADDR;
|
|
* to keep things simpler, set RD_ADDR to 0 and WR_ADDR to the number of bytes
|
|
* in the FIFO. 128 is a special case — write and read pointers should be
|
|
* the same in this case.
|
|
*/
|
|
r = FIELD_DP32(0, UART_MEM_RX_STATUS, WR_ADDR, (fifo_size == 128) ? 0 : fifo_size);
|
|
}
|
|
break;
|
|
case A_UART_DATE:
|
|
r = 0x15122500;
|
|
break;
|
|
default:
|
|
r = s->reg[addr / 4];
|
|
break;
|
|
}
|
|
|
|
return r;
|
|
}
|
|
|
|
|
|
static void uart_write(void *opaque, hwaddr addr,
|
|
uint64_t value, unsigned int size)
|
|
{
|
|
ESP32UARTState *s = ESP32_UART(opaque);
|
|
|
|
switch (addr) {
|
|
case A_UART_FIFO:
|
|
if (fifo8_num_free(&s->tx_fifo) == 0) {
|
|
error_report("esp_uart: write to UART FIFO while it is full");
|
|
} else {
|
|
fifo8_push(&s->tx_fifo, (uint8_t) (value & 0xff));
|
|
uart_transmit(NULL, G_IO_OUT, s);
|
|
}
|
|
break;
|
|
|
|
case A_UART_INT_CLR:
|
|
s->reg[R_UART_INT_ST] &= ~((uint32_t) value);
|
|
s->reg[addr / 4] = value;
|
|
if (value & R_UART_INT_CLR_RXFIFO_TOUT_MASK) {
|
|
s->rxfifo_tout = false;
|
|
}
|
|
break;
|
|
|
|
case A_UART_INT_ENA:
|
|
s->reg[addr / 4] = value;
|
|
break;
|
|
|
|
case A_UART_CLKDIV: {
|
|
s->reg[addr / 4] = value;
|
|
unsigned clkdiv = (FIELD_EX32(s->reg[R_UART_CLKDIV], UART_CLKDIV, CLKDIV) << 4) +
|
|
FIELD_EX32(s->reg[R_UART_CLKDIV], UART_CLKDIV, CLKDIV_FRAG);
|
|
unsigned baud_rate = 115200;
|
|
if (clkdiv != 0) {
|
|
/* FIXME: this should depend on the APB frequency */
|
|
baud_rate = (unsigned) ((40000000ULL << 4) / clkdiv);
|
|
}
|
|
s->baud_rate = baud_rate;
|
|
break;
|
|
}
|
|
|
|
case A_UART_AUTOBAUD:
|
|
/* If autobaud is enabled, pretend that sufficient number of edges on the RXD line
|
|
* have been received instantly. Autobaud is only used in the ROM bootloader,
|
|
* and it doesn't care if the result is ready immediately.
|
|
*/
|
|
if (FIELD_EX32(value, UART_AUTOBAUD, EN)) {
|
|
s->reg[R_UART_RXD_CNT] = 0x3FF;
|
|
} else {
|
|
s->reg[R_UART_RXD_CNT] = 0;
|
|
}
|
|
s->reg[addr / 4] = value;
|
|
break;
|
|
|
|
case A_UART_INT_RAW:
|
|
case A_UART_INT_ST:
|
|
case A_UART_STATUS:
|
|
/* no-op */
|
|
break;
|
|
|
|
case A_UART_CONF1:
|
|
s->reg[addr / 4] = value;
|
|
s->tx_empty_threshold = FIELD_EX32(s->reg[R_UART_CONF1], UART_CONF1, TXFIFO_EMPTY_THRD);
|
|
s->rx_full_threshold = FIELD_EX32(s->reg[R_UART_CONF1], UART_CONF1, RXFIFO_FULL_THRD);
|
|
/* On the ESP32, rx_tout_thres is in units of (bit_time * 8).
|
|
* Note this is different on later chips.
|
|
*/
|
|
s->rx_tout_thres = 8 * FIELD_EX32(s->reg[R_UART_CONF1], UART_CONF1, TOUT_THRD);
|
|
s->rx_tout_ena = FIELD_EX32(s->reg[R_UART_CONF1], UART_CONF1, TOUT_EN) != 0;
|
|
esp32_uart_set_rx_timeout(s);
|
|
esp32_uart_update_irq(s);
|
|
break;
|
|
|
|
default:
|
|
if (addr > sizeof(s->reg)) {
|
|
error_report("esp_uart: write to addr=0x%x out of bounds\n", (uint32_t) addr);
|
|
} else {
|
|
s->reg[addr / 4] = value;
|
|
}
|
|
break;
|
|
|
|
}
|
|
esp32_uart_update_irq(s);
|
|
}
|
|
|
|
|
|
static gboolean uart_transmit(void *do_not_use, GIOCondition cond, void *opaque)
|
|
{
|
|
ESP32UARTState *s = ESP32_UART(opaque);
|
|
|
|
s->tx_watch_handle = 0;
|
|
|
|
/* drain the fifo instantly, if the char device backend is not connected */
|
|
if (!qemu_chr_fe_backend_open(&s->chr)) {
|
|
fifo8_reset(&s->tx_fifo);
|
|
return FALSE;
|
|
}
|
|
|
|
while (fifo8_num_used(&s->tx_fifo) > 0) {
|
|
uint8_t b = fifo8_peek(&s->tx_fifo);
|
|
int r = qemu_chr_fe_write(&s->chr, &b, 1);
|
|
if (r == 1) {
|
|
fifo8_pop(&s->tx_fifo);
|
|
} else {
|
|
s->tx_watch_handle = qemu_chr_fe_add_watch(&s->chr, G_IO_OUT | G_IO_HUP,
|
|
uart_transmit, s);
|
|
break;
|
|
}
|
|
}
|
|
|
|
esp32_uart_update_irq(s);
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
static void uart_receive(void *opaque, const uint8_t *buf, int size)
|
|
{
|
|
ESP32UARTState *s = ESP32_UART(opaque);
|
|
|
|
if (size == 0) {
|
|
return;
|
|
}
|
|
|
|
/* If we can receive anything: cancel any pending RX timeout timer,
|
|
* and clear the receive timeout flag.
|
|
*/
|
|
if (fifo8_num_free(&s->rx_fifo) > 0) {
|
|
timer_del(&s->rx_timeout_timer);
|
|
s->rxfifo_tout = false;
|
|
}
|
|
|
|
/* Move the data into the FIFO */
|
|
for (int i = 0; i < size && fifo8_num_free(&s->rx_fifo) > 0; i++) {
|
|
fifo8_push(&s->rx_fifo, buf[i]);
|
|
}
|
|
|
|
/* Receive throttling: some applications (in particular the ESP32 ROM bootloader)
|
|
* may work incorrectly if the data comes in much faster than what UART baud rate
|
|
* would allow. This code adds a delay every UART_FIFO_LENGTH bytes, to make the
|
|
* average data rate match the configured baud rate.
|
|
* This doesn't need to be very precise, so only add the delay if the FIFO is full
|
|
* (which most likely means that more data will come).
|
|
*/
|
|
if (fifo8_is_full(&s->rx_fifo)) {
|
|
s->throttle_rx = true;
|
|
const int bits_per_symbol = 10;
|
|
int64_t throttle_time_ns = (int64_t) UART_FIFO_LENGTH * bits_per_symbol * NANOSECONDS_PER_SECOND / s->baud_rate;
|
|
timer_mod_ns(&s->throttle_timer,
|
|
qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) +
|
|
throttle_time_ns);
|
|
}
|
|
|
|
esp32_uart_set_rx_timeout(s);
|
|
esp32_uart_update_irq(s);
|
|
}
|
|
|
|
static int uart_can_receive(void *opaque)
|
|
{
|
|
ESP32UARTState *s = ESP32_UART(opaque);
|
|
if (s->throttle_rx) {
|
|
return 0;
|
|
}
|
|
return fifo8_num_free(&s->rx_fifo);
|
|
}
|
|
|
|
static void uart_event(void *opaque, QEMUChrEvent event)
|
|
{
|
|
/* TODO: handle UART break */
|
|
}
|
|
|
|
|
|
static void uart_throttle_timer_cb(void* opaque)
|
|
{
|
|
ESP32UARTState *s = ESP32_UART(opaque);
|
|
s->throttle_rx = false;
|
|
qemu_chr_fe_accept_input(&s->chr);
|
|
}
|
|
|
|
static void uart_rx_timeout_timer_cb(void* opaque)
|
|
{
|
|
ESP32UARTState *s = ESP32_UART(opaque);
|
|
s->rxfifo_tout = true;
|
|
esp32_uart_update_irq(s);
|
|
}
|
|
|
|
static void esp32_uart_reset_hold(Object *obj, ResetType type)
|
|
{
|
|
ESP32UARTState *s = ESP32_UART(obj);
|
|
|
|
memset(s->reg, 0, sizeof(s->reg));
|
|
s->reg[R_UART_RXD_CNT] = 0;
|
|
s->reg[R_UART_INT_ST] = 0;
|
|
s->reg[R_UART_INT_RAW] = 0;
|
|
s->reg[R_UART_INT_ENA] = 0;
|
|
s->reg[R_UART_AUTOBAUD] = 0;
|
|
/* Default baud rate divider after reset */
|
|
s->reg[R_UART_CLKDIV] = FIELD_DP32(0, UART_CLKDIV, CLKDIV, 0x2B6);
|
|
s->baud_rate = 115200;
|
|
fifo8_reset(&s->tx_fifo);
|
|
fifo8_reset(&s->rx_fifo);
|
|
if (s->tx_watch_handle) {
|
|
g_source_remove(s->tx_watch_handle);
|
|
s->tx_watch_handle = 0;
|
|
}
|
|
timer_del(&s->throttle_timer);
|
|
s->throttle_rx = false;
|
|
s->rx_tout_ena = false;
|
|
s->tx_empty_threshold = 0;
|
|
s->rx_full_threshold = 0;
|
|
s->rx_tout_thres = 0;
|
|
qemu_irq_lower(s->irq);
|
|
}
|
|
|
|
|
|
static void esp32_uart_realize(DeviceState *dev, Error **errp)
|
|
{
|
|
ESP32UARTState *s = ESP32_UART(dev);
|
|
|
|
qemu_chr_fe_set_handlers(&s->chr, uart_can_receive, uart_receive,
|
|
uart_event, NULL, s, NULL, true);
|
|
}
|
|
|
|
|
|
static void esp32_uart_init(Object *obj)
|
|
{
|
|
ESP32UARTState *s = ESP32_UART(obj);
|
|
SysBusDevice *sbd = SYS_BUS_DEVICE(obj);
|
|
ESP32UARTClass *class = ESP32_UART_GET_CLASS(obj);
|
|
|
|
s->uart_ops = (MemoryRegionOps) {
|
|
.read = class->uart_read,
|
|
.write = class->uart_write,
|
|
.endianness = DEVICE_LITTLE_ENDIAN,
|
|
};
|
|
|
|
memory_region_init_io(&s->iomem, obj, &s->uart_ops, s,
|
|
TYPE_ESP32_UART, UART_REG_CNT * sizeof(uint32_t));
|
|
sysbus_init_mmio(sbd, &s->iomem);
|
|
sysbus_init_irq(sbd, &s->irq);
|
|
fifo8_create(&s->tx_fifo, UART_FIFO_LENGTH);
|
|
fifo8_create(&s->rx_fifo, UART_FIFO_LENGTH);
|
|
timer_init_ns(&s->throttle_timer, QEMU_CLOCK_VIRTUAL, uart_throttle_timer_cb, s);
|
|
timer_init_ns(&s->rx_timeout_timer, QEMU_CLOCK_VIRTUAL, uart_rx_timeout_timer_cb, s);
|
|
}
|
|
|
|
|
|
static Property esp32_uart_properties[] = {
|
|
DEFINE_PROP_CHR("chardev", ESP32UARTState, chr),
|
|
DEFINE_PROP_END_OF_LIST(),
|
|
};
|
|
|
|
static void esp32_uart_class_init(ObjectClass *klass, void *data)
|
|
{
|
|
DeviceClass *dc = DEVICE_CLASS(klass);
|
|
ESP32UARTClass *class = ESP32_UART_CLASS(klass);
|
|
ResettableClass *rc = RESETTABLE_CLASS(klass);
|
|
|
|
/* Populate the virtual attributes and methods here (if any) */
|
|
class->uart_write = uart_write;
|
|
class->uart_read = uart_read;
|
|
|
|
rc->phases.hold = esp32_uart_reset_hold;
|
|
dc->realize = esp32_uart_realize;
|
|
device_class_set_props(dc, esp32_uart_properties);
|
|
}
|
|
|
|
static const TypeInfo esp32_uart_info = {
|
|
.name = TYPE_ESP32_UART,
|
|
.parent = TYPE_SYS_BUS_DEVICE,
|
|
.instance_size = sizeof(ESP32UARTState),
|
|
.instance_init = esp32_uart_init,
|
|
.class_init = esp32_uart_class_init,
|
|
.class_size = sizeof(ESP32UARTClass)
|
|
};
|
|
|
|
static void esp32_uart_register_types(void)
|
|
{
|
|
type_register_static(&esp32_uart_info);
|
|
}
|
|
|
|
type_init(esp32_uart_register_types)
|