summaryrefslogtreecommitdiff
path: root/tools/src/hv_vuart.c
diff options
context:
space:
mode:
Diffstat (limited to 'tools/src/hv_vuart.c')
-rw-r--r--tools/src/hv_vuart.c125
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;
+}