summaryrefslogtreecommitdiffstats
path: root/sys/dev/uart
diff options
context:
space:
mode:
authorcperciva <cperciva@FreeBSD.org>2013-01-27 23:33:42 +0000
committercperciva <cperciva@FreeBSD.org>2013-01-27 23:33:42 +0000
commit3729189626665da7fd8e8a5d10dc5f58d564e362 (patch)
tree7fa46a1de3860102cdf1b12bc28488219d8a2459 /sys/dev/uart
parent4006779c7a56867016924fa95f9de9d42772115e (diff)
downloadFreeBSD-src-3729189626665da7fd8e8a5d10dc5f58d564e362.zip
FreeBSD-src-3729189626665da7fd8e8a5d10dc5f58d564e362.tar.gz
Add a loader tunable "hw.broken_txfifo" which enables a workaround for a
bug in old versions of QEMU (and Xen, and other places using QEMU code). On those buggy emulated UARTs, the "TX idle" interrupt gets lost; with this workaround, we spinwait for the TX to happen and then send ourselves the interrupt. It's ugly but it works, while minimizing the impact on the code for the !broken_txfifo case. MFC after: 2 weeks
Diffstat (limited to 'sys/dev/uart')
-rw-r--r--sys/dev/uart/uart_dev_ns8250.c14
1 files changed, 13 insertions, 1 deletions
diff --git a/sys/dev/uart/uart_dev_ns8250.c b/sys/dev/uart/uart_dev_ns8250.c
index 5d56a68..eb36da1 100644
--- a/sys/dev/uart/uart_dev_ns8250.c
+++ b/sys/dev/uart/uart_dev_ns8250.c
@@ -31,6 +31,8 @@ __FBSDID("$FreeBSD$");
#include <sys/systm.h>
#include <sys/bus.h>
#include <sys/conf.h>
+#include <sys/kernel.h>
+#include <sys/sysctl.h>
#include <machine/bus.h>
#include <dev/uart/uart.h>
@@ -845,6 +847,11 @@ ns8250_bus_setsig(struct uart_softc *sc, int sig)
return (0);
}
+static int broken_txfifo = 0;
+SYSCTL_INT(_hw, OID_AUTO, broken_txfifo, CTLFLAG_RW | CTLFLAG_TUN,
+ &broken_txfifo, 0, "UART FIFO has QEMU emulation bug");
+TUNABLE_INT("hw.broken_txfifo", &broken_txfifo);
+
static int
ns8250_bus_transmit(struct uart_softc *sc)
{
@@ -862,7 +869,12 @@ ns8250_bus_transmit(struct uart_softc *sc)
uart_setreg(bas, REG_DATA, sc->sc_txbuf[i]);
uart_barrier(bas);
}
- sc->sc_txbusy = 1;
+ if (broken_txfifo)
+ ns8250_drain(bas, UART_DRAIN_TRANSMITTER);
+ else
+ sc->sc_txbusy = 1;
uart_unlock(sc->sc_hwmtx);
+ if (broken_txfifo)
+ uart_sched_softih(sc, SER_INT_TXIDLE);
return (0);
}
OpenPOWER on IntegriCloud