mirror of
https://github.com/AsahiLinux/m1n1
synced 2024-12-20 10:43:08 +00:00
2724d69f4c
Signed-off-by: Janne Grunau <j@jannau.net>
125 lines
3 KiB
C
125 lines
3 KiB
C
/* 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;
|
|
}
|