diff options
Diffstat (limited to 'tools/src/hv_vuart.c')
| -rw-r--r-- | tools/src/hv_vuart.c | 125 |
1 files changed, 125 insertions, 0 deletions
diff --git a/tools/src/hv_vuart.c b/tools/src/hv_vuart.c new file mode 100644 index 0000000..595c031 --- /dev/null +++ b/tools/src/hv_vuart.c @@ -0,0 +1,125 @@ +/* SPDX-License-Identifier: MIT */ + +#include "hv.h" +#include "aic.h" +#include "iodev.h" +#include "uart.h" +#include "uart_regs.h" +#include "usb.h" + +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(IODEV_USB_VUART); + + utrstat |= UTRSTAT_TXBE | UTRSTAT_TXE; + utrstat &= ~UTRSTAT_RXD; + + ufstat = 0; + if ((rx_queued = iodev_can_read(IODEV_USB_VUART))) { + 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(struct exc_info *ctx, u64 addr, u64 *val, bool write, int width) +{ + UNUSED(ctx); + 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(IODEV_USB_VUART)) + iodev_write(IODEV_USB_VUART, &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(IODEV_USB_VUART)) { + uint8_t c; + iodev_read(IODEV_USB_VUART, &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); + usb_iodev_vuart_setup(iodev); + vuart_irq = irq; + active = true; +} |
