@samitouri / QOSamiQemu / commits / 7997b782f4

hw/char: sifive_uart: Sync txwm interrupt pending status after TX FIFO enqueue

Currently, the txwm interrupt pending status is only updated when the asynchronous transmit handler runs. This can cause the txwm interrupt state to become unsynchronized between the SiFive UART and the interrupt controller. For example, when a txwm interrupt is raised, the corresponding APLIC pending bit is also set. However, if software later enqueues additional characters into the TX FIFO exceeding the transmit watermark, the APLIC pending bit may remain set because the txwm interrupt pending status is not updated at enqueue time. This issue has been observed on resource-constrained machines, where Linux reports spurious IRQ errors. In these cases, the asynchronous transmit handler is unable to drain the TX FIFO quickly enough to update the txwm pending status before software reads the ip register, which derives the txwm pending state directly from the actual number of characters in the TX FIFO. This commit fixes the issue by updating the txwm interrupt pending status immediately after enqueuing data into the TX FIFO, ensuring that the interrupt pending status between the SiFive UART and the interrupt controller remains synchronized. Signed-off-by: Frank Chang <frank.chang@sifive.com> Reviewed-by: Alistair Francis <alistair.francis@wdc.com> Message-ID: <20260312033201.1619554-3-frank.chang@sifive.com> Signed-off-by: Alistair Francis <alistair.francis@wdc.com>

Frank Chang committed Mar 12, 2026 at 11:31 UTC 7997b782f454f455d6d2aa6a49253b315ce3a8bc
1 file changed +16
hw/char/sifive_uart.c
+16
@@ -124,12 +124,20 @@ static void sifive_uart_trigger_tx_fifo(SiFiveUARTState *s)
124 static void sifive_uart_write_tx_fifo(SiFiveUARTState *s, const uint8_t *buf,
125 int size)
126 {
127 + uint32_t txcnt = SIFIVE_UART_GET_TXCNT(s->txctrl);
128 + bool update_irq = false;
129 +
130 if (size > fifo8_num_free(&s->tx_fifo)) {
131 size = fifo8_num_free(&s->tx_fifo);
132 qemu_log_mask(LOG_GUEST_ERROR, "sifive_uart: TX FIFO overflow.\n");
133 }
134
135 if (size > 0) {
136 + if (fifo8_num_used(&s->tx_fifo) < txcnt &&
137 + (fifo8_num_used(&s->tx_fifo) + size) >= txcnt) {
138 + update_irq = true;
139 + }
140 +
141 fifo8_push_all(&s->tx_fifo, buf, size);
142 }
143
@@ -137,6 +145,14 @@ static void sifive_uart_write_tx_fifo(SiFiveUARTState *s, const uint8_t *buf,
145 s->txfifo |= SIFIVE_UART_TXFIFO_FULL;
146 }
147
148 + /*
149 + * Update txwm interrupt pending status when the number of entries
150 + * in the transmit FIFO crosses or reaches the watermark.
151 + */
152 + if (update_irq) {
153 + sifive_uart_update_irq(s);
154 + }
155 +
156 sifive_uart_trigger_tx_fifo(s);
157 }
158