m1n1/src/hv_vuart.c
Pip Cet 9a5707889a hv_vuart: Avoid reporting bytes in the RX buffer when it's empty.
Signed-off-by: Pip Cet <pipcet@gmail.com>
2021-08-24 21:07:43 +09:00

124 lines
2.9 KiB
C

/* SPDX-License-Identifier: MIT */
#include "hv.h"
#include "aic.h"
#include "uart.h"
#include "uart_regs.h"
static iodev_id_t vuart_iodev;
bool active = false;
u32 ucon = 0;
u32 utrstat = 0;
u32 ufstat = 0;
int vuart_irq = 0;
static void update_irq(void)
{
ssize_t rx_queued;
iodev_handle_events(vuart_iodev);
utrstat |= UTRSTAT_TXBE | UTRSTAT_TXE;
utrstat &= ~UTRSTAT_RXD;
ufstat = 0;
if ((rx_queued = iodev_can_read(vuart_iodev))) {
utrstat |= UTRSTAT_RXD;
if (rx_queued > 15)
ufstat = FIELD_PREP(UFSTAT_RXCNT, 15) | UFSTAT_RXFULL;
else
ufstat = FIELD_PREP(UFSTAT_RXCNT, rx_queued);
if (FIELD_GET(UCON_RXMODE, ucon) == UCON_MODE_IRQ && ucon & UCON_RXTO_ENA) {
utrstat |= UTRSTAT_RXTO;
}
}
if (FIELD_GET(UCON_TXMODE, ucon) == UCON_MODE_IRQ && ucon & UCON_TXTHRESH_ENA) {
utrstat |= UTRSTAT_TXTHRESH;
}
if (vuart_irq) {
uart_clear_irqs();
if (utrstat & (UTRSTAT_TXTHRESH | UTRSTAT_RXTHRESH | UTRSTAT_RXTO)) {
aic_set_sw(vuart_irq, true);
} else {
aic_set_sw(vuart_irq, false);
}
}
// printf("HV: vuart UTRSTAT=0x%x UFSTAT=0x%x UCON=0x%x\n", utrstat, ufstat, ucon);
}
static bool handle_vuart(u64 addr, u64 *val, bool write, int width)
{
UNUSED(width);
addr &= 0xfff;
update_irq();
if (write) {
// printf("HV: vuart W 0x%lx <- 0x%lx (%d)\n", addr, *val, width);
switch (addr) {
case UCON:
ucon = *val;
break;
case UTXH: {
uint8_t b = *val;
if (iodev_can_write(vuart_iodev))
iodev_write(vuart_iodev, &b, 1);
break;
}
case UTRSTAT:
utrstat &= ~(*val & (UTRSTAT_TXTHRESH | UTRSTAT_RXTHRESH | UTRSTAT_RXTO));
break;
}
} else {
switch (addr) {
case UCON:
*val = ucon;
break;
case URXH:
if (iodev_can_read(vuart_iodev)) {
uint8_t c;
iodev_read(vuart_iodev, &c, 1);
*val = c;
} else {
*val = 0;
}
break;
case UTRSTAT:
*val = utrstat;
break;
case UFSTAT:
*val = ufstat;
break;
default:
*val = 0;
break;
}
// printf("HV: vuart R 0x%lx -> 0x%lx (%d)\n", addr, *val, width);
}
return true;
}
void hv_vuart_poll(void)
{
if (!active)
return;
update_irq();
}
void hv_map_vuart(u64 base, int irq, iodev_id_t iodev)
{
hv_map_hook(base, handle_vuart, 0x1000);
vuart_iodev = iodev;
vuart_irq = irq;
active = true;
}