diff options
Diffstat (limited to 'sys/dev/uart/uart_dev_ns8250.c')
-rw-r--r-- | sys/dev/uart/uart_dev_ns8250.c | 86 |
1 files changed, 64 insertions, 22 deletions
diff --git a/sys/dev/uart/uart_dev_ns8250.c b/sys/dev/uart/uart_dev_ns8250.c index 16c3cb2fc5a9..0f19ede6d9df 100644 --- a/sys/dev/uart/uart_dev_ns8250.c +++ b/sys/dev/uart/uart_dev_ns8250.c @@ -77,6 +77,11 @@ static int broken_txfifo = 0; SYSCTL_INT(_hw, OID_AUTO, broken_txfifo, CTLFLAG_RWTUN, &broken_txfifo, 0, "UART FIFO has QEMU emulation bug"); +static int uart_noise_threshold = 0; +SYSCTL_INT(_hw, OID_AUTO, uart_noise_threshold, CTLFLAG_RWTUN, + &uart_noise_threshold, 0, + "Number of UART RX interrupts where TX is not ready, before data is discarded"); + /* * To use early printf on x86, add the following to your kernel config: * @@ -126,11 +131,11 @@ ns8250_clrint(struct uart_bas *bas) } } -static int -ns8250_delay(struct uart_bas *bas) +static uint32_t +ns8250_get_divisor(struct uart_bas *bas) { - int divisor; - u_char lcr; + uint32_t divisor; + uint8_t lcr; lcr = uart_getreg(bas, REG_LCR); uart_setreg(bas, REG_LCR, lcr | LCR_DLAB); @@ -140,6 +145,16 @@ ns8250_delay(struct uart_bas *bas) uart_setreg(bas, REG_LCR, lcr); uart_barrier(bas); + return (divisor); +} + +static int +ns8250_delay(struct uart_bas *bas) +{ + int divisor; + + divisor = ns8250_get_divisor(bas); + /* 1/10th the time to transmit 1 character (estimate). */ if (divisor <= 134) return (16000000 * divisor / bas->rclk); @@ -187,7 +202,7 @@ ns8250_drain(struct uart_bas *bas, int what) while ((uart_getreg(bas, REG_LSR) & LSR_TEMT) == 0 && --limit) DELAY(delay); if (limit == 0) { - /* printf("ns8250: transmitter appears stuck... "); */ + /* printf("uart: ns8250: transmitter appears stuck... "); */ return (EIO); } } @@ -215,7 +230,7 @@ ns8250_drain(struct uart_bas *bas, int what) DELAY(delay << 2); } if (limit == 0) { - /* printf("ns8250: receiver appears broken... "); */ + /* printf("uart: ns8250: receiver appears broken... "); */ return (EIO); } } @@ -250,12 +265,12 @@ ns8250_flush(struct uart_bas *bas, int what) * https://github.com/rust-vmm/vm-superio/issues/83 */ lsr = uart_getreg(bas, REG_LSR); - if (((lsr & LSR_TEMT) == 0) && (what & UART_FLUSH_TRANSMITTER)) + if (((lsr & LSR_THRE) == 0) && (what & UART_FLUSH_TRANSMITTER)) drain |= UART_DRAIN_TRANSMITTER; if ((lsr & LSR_RXRDY) && (what & UART_FLUSH_RECEIVER)) drain |= UART_DRAIN_RECEIVER; if (drain != 0) { - printf("ns8250: UART FCR is broken\n"); + printf("uart: ns8250: UART FCR is broken (%#x)\n", drain); ns8250_drain(bas, drain); } } @@ -284,8 +299,8 @@ ns8250_param(struct uart_bas *bas, int baudrate, int databits, int stopbits, lcr |= LCR_STOPB; lcr |= parity << 3; - /* Set baudrate. */ - if (baudrate > 0) { + /* Set baudrate if we know a rclk and both are not 0. */ + if (baudrate > 0 && bas->rclk > 0) { divisor = ns8250_divisor(bas->rclk, baudrate); if (divisor == 0) return (EINVAL); @@ -349,10 +364,6 @@ ns8250_init(struct uart_bas *bas, int baudrate, int databits, int stopbits, { u_char ier; - if (bas->rclk == 0) - bas->rclk = DEFAULT_RCLK; - ns8250_param(bas, baudrate, databits, stopbits, parity); - /* Disable all interrupt sources. */ /* * We use 0xe0 instead of 0xf0 as the mask because the XScale PXA @@ -363,6 +374,30 @@ ns8250_init(struct uart_bas *bas, int baudrate, int databits, int stopbits, uart_setreg(bas, REG_IER, ier); uart_barrier(bas); + /* + * Loader tells us to infer the rclk when it sets xo to 0 in + * hw.uart.console. We know the baudrate was set by the firmware, so + * calculate rclk from baudrate and the divisor register. If 'div' is + * actually 0, the resulting 0 value will have us fall back to other + * rclk methods. + */ + if (bas->rclk_guess && bas->rclk == 0 && baudrate != 0) { + uint32_t div; + + div = ns8250_get_divisor(bas); + bas->rclk = baudrate * div * 16; + } + + /* + * Pick a default because we just don't know. This likely needs future + * refinement, but that's hard outside of consoles to know what to use. + * But defer as long as possible if there's no defined baud rate. + */ + if (bas->rclk == 0 && baudrate != 0) + bas->rclk = DEFAULT_RCLK; + + ns8250_param(bas, baudrate, databits, stopbits, parity); + /* Disable the FIFO (if present). */ uart_setreg(bas, REG_FCR, 0); uart_barrier(bas); @@ -460,9 +495,11 @@ UART_CLASS(uart_ns8250_class); static struct acpi_uart_compat_data acpi_compat_data[] = { {"AMD0020", &uart_ns8250_class, 0, 2, 0, 48000000, UART_F_BUSY_DETECT, "AMD / Synopsys Designware UART"}, {"AMDI0020", &uart_ns8250_class, 0, 2, 0, 48000000, UART_F_BUSY_DETECT, "AMD / Synopsys Designware UART"}, + {"APMC0D08", &uart_ns8250_class, ACPI_DBG2_16550_COMPATIBLE, 2, 4, 0, 0, "APM compatible UART"}, {"MRVL0001", &uart_ns8250_class, ACPI_DBG2_16550_SUBSET, 2, 0, 200000000, UART_F_BUSY_DETECT, "Marvell / Synopsys Designware UART"}, {"SCX0006", &uart_ns8250_class, 0, 2, 0, 62500000, UART_F_BUSY_DETECT, "SynQuacer / Synopsys Designware UART"}, {"HISI0031", &uart_ns8250_class, 0, 2, 0, 200000000, UART_F_BUSY_DETECT, "HiSilicon / Synopsys Designware UART"}, + {"INTC1006", &uart_ns8250_class, 0, 2, 0, 25000000, 0, "Intel ARM64 UART"}, {"NXP0018", &uart_ns8250_class, 0, 0, 0, 350000000, UART_F_BUSY_DETECT, "NXP / Synopsys Designware UART"}, {"PNP0500", &uart_ns8250_class, 0, 0, 0, 0, 0, "Standard PC COM port"}, {"PNP0501", &uart_ns8250_class, 0, 0, 0, 0, 0, "16550A-compatible COM port"}, @@ -725,14 +762,7 @@ ns8250_bus_ioctl(struct uart_softc *sc, int request, intptr_t data) uart_barrier(bas); break; case UART_IOCTL_BAUD: - lcr = uart_getreg(bas, REG_LCR); - uart_setreg(bas, REG_LCR, lcr | LCR_DLAB); - uart_barrier(bas); - divisor = uart_getreg(bas, REG_DLL) | - (uart_getreg(bas, REG_DLH) << 8); - uart_barrier(bas); - uart_setreg(bas, REG_LCR, lcr); - uart_barrier(bas); + divisor = ns8250_get_divisor(bas); baudrate = (divisor > 0) ? bas->rclk / divisor / 16 : 0; if (baudrate > 0) *(int*)data = baudrate; @@ -987,6 +1017,7 @@ int ns8250_bus_receive(struct uart_softc *sc) { struct uart_bas *bas; + struct ns8250_softc *ns8250 = (struct ns8250_softc *)sc; int xc; uint8_t lsr; @@ -998,6 +1029,17 @@ ns8250_bus_receive(struct uart_softc *sc) sc->sc_rxbuf[sc->sc_rxput] = UART_STAT_OVERRUN; break; } + /* Filter out possible noise on the line. + * Expect that the device should be able to transmit as well as + * receive, so if we receive too many characters before transmit + * is ready, it's probably noise. + */ + if ((lsr & (LSR_TXRDY | LSR_TEMT)) == 0 && + uart_noise_threshold > 0) { + if (++ns8250->noise_count >= uart_noise_threshold) + break; + } else + ns8250->noise_count = 0; xc = uart_getreg(bas, REG_DATA); if (lsr & LSR_FE) xc |= UART_STAT_FRAMERR; |