hw/char: sifive_uart: Raise IRQ according to the Tx/Rx watermark thresholds
Currently, the SiFive UART raises an IRQ whenever:
1. ie.txwm is enabled.
2. ie.rxwm is enabled and the Rx FIFO is not empty.
It does not check the watermark thresholds set by software. However,
since commit [1] changed the SiFive UART character printing from
synchronous to asynchronous, Tx overflows may occur, causing characters
to be dropped when running Linux because:
1. The Linux SiFive UART driver sets the transmit watermark level to 1
[2], meaning a transmit watermark interrupt is raised whenever a
character is enqueued into the Tx FIFO.
2. Upon receiving a transmit watermark interrupt, the Linux driver
transfers up to a full Tx FIFO's worth of characters from the Linux
serial transmit buffer [3], without checking the txdata.full flag
before transferring multiple characters [4].
To fix this issue, we must honor the Tx/Rx watermark thresholds and
raise interrupts only when the Tx threshold is exceeded or the Rx
threshold is undercut.
[1] 53c1557b23
[2] https://github.com/torvalds/linux/blob/master/drivers/tty/serial/sifive.c#L1039
[3] https://github.com/torvalds/linux/blob/master/drivers/tty/serial/sifive.c#L538
[4] https://github.com/torvalds/linux/blob/master/drivers/tty/serial/sifive.c#L291
Signed-off-by: Frank Chang <frank.chang@sifive.com>
Signed-off-by: Emmanuel Blot <emmanuel.blot@sifive.com>
Reviewed-by: Alistair Francis <alistair.francis@wdc.com>
Message-ID: <20250911160647.5710-2-frank.chang@sifive.com>
Signed-off-by: Alistair Francis <alistair.francis@wdc.com>
This commit is contained in:
parent
7a9202eaeb
commit
191df34617
1 changed files with 12 additions and 12 deletions
|
|
@ -35,16 +35,17 @@
|
|||
*/
|
||||
|
||||
/* Returns the state of the IP (interrupt pending) register */
|
||||
static uint64_t sifive_uart_ip(SiFiveUARTState *s)
|
||||
static uint32_t sifive_uart_ip(SiFiveUARTState *s)
|
||||
{
|
||||
uint64_t ret = 0;
|
||||
uint32_t ret = 0;
|
||||
|
||||
uint64_t txcnt = SIFIVE_UART_GET_TXCNT(s->txctrl);
|
||||
uint64_t rxcnt = SIFIVE_UART_GET_RXCNT(s->rxctrl);
|
||||
uint32_t txcnt = SIFIVE_UART_GET_TXCNT(s->txctrl);
|
||||
uint32_t rxcnt = SIFIVE_UART_GET_RXCNT(s->rxctrl);
|
||||
|
||||
if (txcnt != 0) {
|
||||
if (fifo8_num_used(&s->tx_fifo) < txcnt) {
|
||||
ret |= SIFIVE_UART_IP_TXWM;
|
||||
}
|
||||
|
||||
if (s->rx_fifo_len > rxcnt) {
|
||||
ret |= SIFIVE_UART_IP_RXWM;
|
||||
}
|
||||
|
|
@ -55,15 +56,14 @@ static uint64_t sifive_uart_ip(SiFiveUARTState *s)
|
|||
static void sifive_uart_update_irq(SiFiveUARTState *s)
|
||||
{
|
||||
int cond = 0;
|
||||
if ((s->ie & SIFIVE_UART_IE_TXWM) ||
|
||||
((s->ie & SIFIVE_UART_IE_RXWM) && s->rx_fifo_len)) {
|
||||
uint32_t ip = sifive_uart_ip(s);
|
||||
|
||||
if (((ip & SIFIVE_UART_IP_TXWM) && (s->ie & SIFIVE_UART_IE_TXWM)) ||
|
||||
((ip & SIFIVE_UART_IP_RXWM) && (s->ie & SIFIVE_UART_IE_RXWM))) {
|
||||
cond = 1;
|
||||
}
|
||||
if (cond) {
|
||||
qemu_irq_raise(s->irq);
|
||||
} else {
|
||||
qemu_irq_lower(s->irq);
|
||||
}
|
||||
|
||||
qemu_set_irq(s->irq, cond);
|
||||
}
|
||||
|
||||
static gboolean sifive_uart_xmit(void *do_not_use, GIOCondition cond,
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue