aboutsummaryrefslogtreecommitdiff
path: root/sys/dev/uart
diff options
context:
space:
mode:
Diffstat (limited to 'sys/dev/uart')
-rw-r--r--sys/dev/uart/uart.h2
-rw-r--r--sys/dev/uart/uart_bus.h1
-rw-r--r--sys/dev/uart/uart_bus_fdt.c3
-rw-r--r--sys/dev/uart/uart_bus_pci.c14
-rw-r--r--sys/dev/uart/uart_core.c19
-rw-r--r--sys/dev/uart/uart_cpu_acpi.c151
-rw-r--r--sys/dev/uart/uart_cpu_fdt.c2
-rw-r--r--sys/dev/uart/uart_dev_ns8250.c86
-rw-r--r--sys/dev/uart/uart_dev_ns8250.h1
-rw-r--r--sys/dev/uart/uart_dev_pl011.c48
-rw-r--r--sys/dev/uart/uart_dev_quicc.c4
-rw-r--r--sys/dev/uart/uart_dev_z8530.c4
-rw-r--r--sys/dev/uart/uart_subr.c17
-rw-r--r--sys/dev/uart/uart_tty.c2
14 files changed, 280 insertions, 74 deletions
diff --git a/sys/dev/uart/uart.h b/sys/dev/uart/uart.h
index 4cdec00c9829..b9401996e655 100644
--- a/sys/dev/uart/uart.h
+++ b/sys/dev/uart/uart.h
@@ -40,11 +40,13 @@
struct uart_bas {
bus_space_tag_t bst;
bus_space_handle_t bsh;
+ void *driver1;
u_int chan;
u_int rclk;
u_int regshft;
u_int regiowidth;
u_int busy_detect;
+ u_int rclk_guess;/* if rclk == 0, use baud + divisor to compute rclk */
};
#define uart_regofs(bas, reg) ((reg) << (bas)->regshft)
diff --git a/sys/dev/uart/uart_bus.h b/sys/dev/uart/uart_bus.h
index ccf8ad06a8ec..a605e3d20be7 100644
--- a/sys/dev/uart/uart_bus.h
+++ b/sys/dev/uart/uart_bus.h
@@ -56,7 +56,6 @@
/* UART quirk flags */
#define UART_F_BUSY_DETECT 0x1
-#define UART_F_IGNORE_SPCR_REGSHFT 0x2
/*
* UART class & instance (=softc)
diff --git a/sys/dev/uart/uart_bus_fdt.c b/sys/dev/uart/uart_bus_fdt.c
index 7725d09e212b..431f2962adb2 100644
--- a/sys/dev/uart/uart_bus_fdt.c
+++ b/sys/dev/uart/uart_bus_fdt.c
@@ -234,7 +234,8 @@ uart_cpu_fdt_probe(struct uart_class **classp, bus_space_tag_t *bst,
(struct uart_class *)uart_fdt_find_by_node(node, 1);
if (class == NULL)
return (ENXIO);
- clk = 0;
+ if (uart_fdt_get_clock(node, &clk) != 0)
+ clk = 0;
}
/*
diff --git a/sys/dev/uart/uart_bus_pci.c b/sys/dev/uart/uart_bus_pci.c
index 5f82ef9307d0..14ac213066b8 100644
--- a/sys/dev/uart/uart_bus_pci.c
+++ b/sys/dev/uart/uart_bus_pci.c
@@ -106,6 +106,20 @@ static const struct pci_id pci_ns8250_ids[] = {
{ 0x131f, 0x2000, 0xffff, 0, "Siig CyberSerial (1-port) 16550", 0x10 },
{ 0x131f, 0x2001, 0xffff, 0, "Siig CyberSerial (1-port) 16650", 0x10 },
{ 0x131f, 0x2002, 0xffff, 0, "Siig CyberSerial (1-port) 16850", 0x10 },
+{ 0x135a, 0x0a61, 0xffff, 0, "Brainboxes UC-324", 0x18 },
+{ 0x135a, 0x0aa1, 0xffff, 0, "Brainboxes UC-246", 0x18 },
+{ 0x135a, 0x0aa2, 0xffff, 0, "Brainboxes UC-246", 0x18 },
+{ 0x135a, 0x0d60, 0xffff, 0, "Intashield IS-100", 0x18 },
+{ 0x135a, 0x0da0, 0xffff, 0, "Intashield IS-300", 0x18 },
+{ 0x135a, 0x4000, 0xffff, 0, "Brainboxes PX-420", 0x10 },
+{ 0x135a, 0x4001, 0xffff, 0, "Brainboxes PX-431", 0x10 },
+{ 0x135a, 0x4002, 0xffff, 0, "Brainboxes PX-820", 0x10 },
+{ 0x135a, 0x4003, 0xffff, 0, "Brainboxes PX-831", 0x10 },
+{ 0x135a, 0x4004, 0xffff, 0, "Brainboxes PX-246", 0x10 },
+{ 0x135a, 0x4005, 0xffff, 0, "Brainboxes PX-101", 0x10 },
+{ 0x135a, 0x4006, 0xffff, 0, "Brainboxes PX-257", 0x10 },
+{ 0x135a, 0x4008, 0xffff, 0, "Brainboxes PX-846", 0x10 },
+{ 0x135a, 0x4009, 0xffff, 0, "Brainboxes PX-857", 0x10 },
{ 0x135c, 0x0190, 0xffff, 0, "Quatech SSCLP-100", 0x18 },
{ 0x135c, 0x01c0, 0xffff, 0, "Quatech SSCLP-200/300", 0x18 },
{ 0x135e, 0x7101, 0xffff, 0, "Sealevel Systems Single Port RS-232/422/485/530",
diff --git a/sys/dev/uart/uart_core.c b/sys/dev/uart/uart_core.c
index c2bc818a6fc2..0ee43f6d2d94 100644
--- a/sys/dev/uart/uart_core.c
+++ b/sys/dev/uart/uart_core.c
@@ -38,11 +38,11 @@
#include <sys/malloc.h>
#include <sys/queue.h>
#include <sys/reboot.h>
+#include <sys/stdarg.h>
#include <sys/sysctl.h>
#include <machine/bus.h>
#include <sys/rman.h>
#include <machine/resource.h>
-#include <machine/stdarg.h>
#include <dev/uart/uart.h>
#include <dev/uart/uart_bus.h>
@@ -559,13 +559,19 @@ uart_bus_probe(device_t dev, int regshft, int regiowidth, int rclk, int rid, int
uart_cpu_eqres(&sc->sc_bas, &sysdev->bas)) {
/* XXX check if ops matches class. */
sc->sc_sysdev = sysdev;
- sysdev->bas.rclk = sc->sc_bas.rclk;
- }
+ if (sysdev->bas.rclk != 0) {
+ /* Let the boot sequence control */
+ sc->sc_bas.rclk = sysdev->bas.rclk;
+ } else {
+ /* Boot didn't set it, use use class */
+ sysdev->bas.rclk = sc->sc_bas.rclk;
+ }
+ }
}
error = UART_PROBE(sc);
bus_release_resource(dev, sc->sc_rtype, sc->sc_rrid, sc->sc_rres);
- return ((error) ? error : BUS_PROBE_DEFAULT);
+ return ((error) ? error : 0);
}
int
@@ -746,6 +752,11 @@ uart_bus_attach(device_t dev)
"rx_overruns", CTLFLAG_RD, &sc->sc_rxoverruns, 0,
"Receive overruns");
+ SYSCTL_ADD_INT(device_get_sysctl_ctx(dev),
+ SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO,
+ "rclk", CTLFLAG_RD, &sc->sc_bas.rclk, 0,
+ "Baud clock for device");
+
return (0);
fail:
diff --git a/sys/dev/uart/uart_cpu_acpi.c b/sys/dev/uart/uart_cpu_acpi.c
index 9c9ffc1e3194..7382c47a8db6 100644
--- a/sys/dev/uart/uart_cpu_acpi.c
+++ b/sys/dev/uart/uart_cpu_acpi.c
@@ -167,40 +167,60 @@ uart_cpu_acpi_spcr(int devtype, struct uart_devinfo *di)
if (error != 0)
goto out;
- switch (spcr->BaudRate) {
- case 0:
- /* Special value; means "keep current value unchanged". */
- di->baudrate = 0;
- break;
- case 3:
- di->baudrate = 9600;
- break;
- case 4:
- di->baudrate = 19200;
- break;
- case 6:
- di->baudrate = 57600;
- break;
- case 7:
- di->baudrate = 115200;
- break;
- default:
- printf("SPCR has reserved BaudRate value: %d!\n",
- (int)spcr->BaudRate);
- goto out;
+ /*
+ * SPCR Rev 4 and newer allow a precise baudrate to be passed in for
+ * things like 1.5M or 2.0M. If we have that, then use that value,
+ * otherwise try to decode the older enumeration.
+ */
+ if (spcr->Header.Revision >= 4 && spcr->PreciseBaudrate != 0) {
+ di->baudrate = spcr->PreciseBaudrate;
+ } else {
+ switch (spcr->BaudRate) {
+ case 0:
+ /* Special value; means "keep current value unchanged". */
+ di->baudrate = 0;
+ break;
+ case 3:
+ di->baudrate = 9600;
+ break;
+ case 4:
+ di->baudrate = 19200;
+ break;
+ case 6:
+ di->baudrate = 57600;
+ break;
+ case 7:
+ di->baudrate = 115200;
+ break;
+ default:
+ printf("SPCR has reserved BaudRate value: %d!\n",
+ (int)spcr->BaudRate);
+ goto out;
+ }
}
+
+ /*
+ * Rev 3 and newer can specify a rclk, use it if it's there. It's
+ * defined to be 0 when it's not known, and we've initialized rclk to 0
+ * in uart_cpu_acpi_init_devinfo, so we don't have to test for it.
+ */
+ if (spcr->Header.Revision >= 3)
+ di->bas.rclk = spcr->UartClkFreq;
+
+ /*
+ * If no rclk is set, then we will assume the BIOS has configured the
+ * hardware at the stated baudrate, so we can use it to guess the rclk
+ * relatively accurately, so make a note for later.
+ */
+ if (di->bas.rclk == 0)
+ di->bas.rclk_guess = 1;
+
if (spcr->PciVendorId != PCIV_INVALID &&
spcr->PciDeviceId != PCIV_INVALID) {
di->pci_info.vendor = spcr->PciVendorId;
di->pci_info.device = spcr->PciDeviceId;
}
- /* Apply device tweaks. */
- if ((cd->cd_quirks & UART_F_IGNORE_SPCR_REGSHFT) ==
- UART_F_IGNORE_SPCR_REGSHFT) {
- di->bas.regshft = cd->cd_regshft;
- }
-
/* Create a bus space handle. */
error = bus_space_map(di->bas.bst, spcr->SerialPort.Address,
uart_getrange(class), 0, &di->bas.bsh);
@@ -210,12 +230,89 @@ out:
return (error);
}
+static int
+uart_cpu_acpi_dbg2(struct uart_devinfo *di)
+{
+ vm_paddr_t dbg2_physaddr;
+ ACPI_TABLE_DBG2 *dbg2;
+ ACPI_DBG2_DEVICE *dbg2_dev;
+ ACPI_GENERIC_ADDRESS *base_address;
+ struct acpi_uart_compat_data *cd;
+ struct uart_class *class;
+ int error;
+ bool found;
+
+ /* Look for the DBG2 table. */
+ dbg2_physaddr = acpi_find_table(ACPI_SIG_DBG2);
+ if (dbg2_physaddr == 0)
+ return (ENXIO);
+
+ dbg2 = acpi_map_table(dbg2_physaddr, ACPI_SIG_DBG2);
+ if (dbg2 == NULL) {
+ printf("Unable to map the DBG2 table!\n");
+ return (ENXIO);
+ }
+
+ error = ENXIO;
+
+ dbg2_dev = (ACPI_DBG2_DEVICE *)((uintptr_t)dbg2 + dbg2->InfoOffset);
+ found = false;
+ while ((uintptr_t)dbg2_dev + dbg2_dev->Length <=
+ (uintptr_t)dbg2 + dbg2->Header.Length) {
+ if (dbg2_dev->PortType != ACPI_DBG2_SERIAL_PORT)
+ goto next;
+
+ /* XXX: Too restrictive? */
+ if (dbg2_dev->RegisterCount != 1)
+ goto next;
+
+ cd = uart_cpu_acpi_scan(dbg2_dev->PortSubtype);
+ if (cd == NULL)
+ goto next;
+
+ class = cd->cd_class;
+ base_address = (ACPI_GENERIC_ADDRESS *)
+ ((uintptr_t)dbg2_dev + dbg2_dev->BaseAddressOffset);
+
+ error = uart_cpu_acpi_init_devinfo(di, class, base_address);
+ if (error == 0) {
+ found = true;
+ break;
+ }
+
+next:
+ dbg2_dev = (ACPI_DBG2_DEVICE *)
+ ((uintptr_t)dbg2_dev + dbg2_dev->Length);
+ }
+ if (!found)
+ goto out;
+
+ /* XXX: Find the correct value */
+ di->baudrate = 115200;
+
+ /* Create a bus space handle. */
+ error = bus_space_map(di->bas.bst, base_address->Address,
+ uart_getrange(class), 0, &di->bas.bsh);
+
+out:
+ acpi_unmap_table(dbg2);
+ return (error);
+}
+
int
uart_cpu_acpi_setup(int devtype, struct uart_devinfo *di)
{
+ char *cp;
+
switch(devtype) {
case UART_DEV_CONSOLE:
return (uart_cpu_acpi_spcr(devtype, di));
+ case UART_DEV_DBGPORT:
+ /* Use the Debug Port Table 2 (DBG2) to find a debug uart */
+ cp = kern_getenv("hw.acpi.enable_dbg2");
+ if (cp != NULL && strcasecmp(cp, "yes") == 0)
+ return (uart_cpu_acpi_dbg2(di));
+ break;
}
return (ENXIO);
}
diff --git a/sys/dev/uart/uart_cpu_fdt.c b/sys/dev/uart/uart_cpu_fdt.c
index 1cc1c795f29f..fd1647cd78aa 100644
--- a/sys/dev/uart/uart_cpu_fdt.c
+++ b/sys/dev/uart/uart_cpu_fdt.c
@@ -81,7 +81,7 @@ uart_cpu_getdev(int devtype, struct uart_devinfo *di)
/* Allow overriding the FDT using the environment. */
class = &uart_ns8250_class;
err = uart_getenv(devtype, di, class);
- if (!err)
+ if (err == 0)
return (0);
err = uart_cpu_fdt_probe(&class, &bst, &bsh, &br, &rclk,
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;
diff --git a/sys/dev/uart/uart_dev_ns8250.h b/sys/dev/uart/uart_dev_ns8250.h
index 324ff72f6e5d..e8a17e96c268 100644
--- a/sys/dev/uart/uart_dev_ns8250.h
+++ b/sys/dev/uart/uart_dev_ns8250.h
@@ -41,6 +41,7 @@ struct ns8250_softc {
uint8_t ier_mask;
uint8_t ier_rxbits;
uint8_t busy_detect;
+ int noise_count;
};
extern struct uart_ops uart_ns8250_ops;
diff --git a/sys/dev/uart/uart_dev_pl011.c b/sys/dev/uart/uart_dev_pl011.c
index daba9d19704c..a0d5a5b1c7e2 100644
--- a/sys/dev/uart/uart_dev_pl011.c
+++ b/sys/dev/uart/uart_dev_pl011.c
@@ -172,6 +172,27 @@ static int
uart_pl011_probe(struct uart_bas *bas)
{
+ /*
+ * Versions of QEMU before 41f7b58b634e (8.3) reported bogus values for
+ * this tabel. The PL011 IP is always 32-bits wide and should be shifted
+ * 2 to match the 4-byte size of the data. QEMU reported these values
+ * incorrectly before that.
+ * https://github.com/qemu/qemu/commit/41f7b58b634ec3b60ae874375d2bbb61d790971e
+ *
+ * In additon, other hardware vendors also reported this value
+ * incorrectly. It's not tied to what the ACPI device node is, but was a
+ * misunderstanding coupled with a Linux driver that didn't need the
+ * right values. Quirks used to be used to ignore the bad values, now we
+ * detect the historic mistake and override (to allow for a future where
+ * we may need to override these values).
+ *
+ * PL011 Docs: https://developer.arm.com/documentation/ddi0183/latest/
+ */
+ if (bas->regshft == 0 || bas->regiowidth == 1) {
+ bas->regshft = 2;
+ bas->regiowidth = 4;
+ }
+
return (0);
}
@@ -231,6 +252,24 @@ uart_pl011_param(struct uart_bas *bas, int baudrate, int databits, int stopbits,
__uart_setreg(bas, UART_IFLS, FIFO_IFLS_BITS);
__uart_setreg(bas, UART_CR, ctrl);
+
+ /*
+ * Loader tells us to infer the rclk when it sets xo to 0 in
+ * hw.uart.console. The APCI SPCR code does likewise. 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. This method should be
+ * good to 5% or better because the error in baud rates needs to be
+ * below this for devices to communicate.
+ */
+ if (bas->rclk == 0 && baudrate > 0 && bas->rclk_guess) {
+ uint32_t div;
+
+ div = ((__uart_getreg(bas, UART_IBRD) & IBRD_BDIVINT) << 6) |
+ (__uart_getreg(bas, UART_FBRD) & FBRD_BDIVFRAC);
+ bas->rclk = (div * baudrate) / 4;
+ }
+
}
static void
@@ -338,7 +377,8 @@ static struct uart_class uart_pl011_class = {
.uc_ops = &uart_pl011_ops,
.uc_range = 0x48,
.uc_rclk = 0,
- .uc_rshift = 2
+ .uc_rshift = 2,
+ .uc_riowidth = 4,
};
UART_CLASS(uart_pl011_class);
@@ -352,9 +392,9 @@ UART_FDT_CLASS_AND_DEVICE(fdt_compat_data);
#ifdef DEV_ACPI
static struct acpi_uart_compat_data acpi_compat_data[] = {
- {"ARMH0011", &uart_pl011_class, ACPI_DBG2_ARM_PL011, 2, 0, 0, UART_F_IGNORE_SPCR_REGSHFT, "uart pl011"},
- {"ARMHB000", &uart_pl011_class, ACPI_DBG2_ARM_SBSA_GENERIC, 2, 0, 0, UART_F_IGNORE_SPCR_REGSHFT, "uart pl011"},
- {"ARMHB000", &uart_pl011_class, ACPI_DBG2_ARM_SBSA_32BIT, 2, 0, 0, UART_F_IGNORE_SPCR_REGSHFT, "uart pl011"},
+ {"ARMH0011", &uart_pl011_class, ACPI_DBG2_ARM_PL011, 2, 0, 0, 0, "uart pl011"},
+ {"ARMHB000", &uart_pl011_class, ACPI_DBG2_ARM_SBSA_GENERIC, 2, 0, 0, 0, "uart pl011"},
+ {"ARMHB000", &uart_pl011_class, ACPI_DBG2_ARM_SBSA_32BIT, 2, 0, 0, 0, "uart pl011"},
{NULL, NULL, 0, 0, 0, 0, 0, NULL},
};
UART_ACPI_CLASS_AND_DEVICE(acpi_compat_data);
diff --git a/sys/dev/uart/uart_dev_quicc.c b/sys/dev/uart/uart_dev_quicc.c
index bd735f2da6f4..d6a8846b874e 100644
--- a/sys/dev/uart/uart_dev_quicc.c
+++ b/sys/dev/uart/uart_dev_quicc.c
@@ -412,7 +412,6 @@ quicc_bus_param(struct uart_softc *sc, int baudrate, int databits,
static int
quicc_bus_probe(struct uart_softc *sc)
{
- char buf[80];
int error;
error = quicc_probe(&sc->sc_bas);
@@ -422,8 +421,7 @@ quicc_bus_probe(struct uart_softc *sc)
sc->sc_rxfifosz = 1;
sc->sc_txfifosz = 1;
- snprintf(buf, sizeof(buf), "quicc, channel %d", sc->sc_bas.chan);
- device_set_desc_copy(sc->sc_dev, buf);
+ device_set_descf(sc->sc_dev, "quicc, channel %d", sc->sc_bas.chan);
return (0);
}
diff --git a/sys/dev/uart/uart_dev_z8530.c b/sys/dev/uart/uart_dev_z8530.c
index 2ca480a5690d..45bf63f20bb2 100644
--- a/sys/dev/uart/uart_dev_z8530.c
+++ b/sys/dev/uart/uart_dev_z8530.c
@@ -509,7 +509,6 @@ z8530_bus_param(struct uart_softc *sc, int baudrate, int databits,
static int
z8530_bus_probe(struct uart_softc *sc)
{
- char buf[80];
int error;
char ch;
@@ -522,8 +521,7 @@ z8530_bus_probe(struct uart_softc *sc)
ch = sc->sc_bas.chan - 1 + 'A';
- snprintf(buf, sizeof(buf), "z8530, channel %c", ch);
- device_set_desc_copy(sc->sc_dev, buf);
+ device_set_descf(sc->sc_dev, "z8530, channel %c", ch);
return (0);
}
diff --git a/sys/dev/uart/uart_subr.c b/sys/dev/uart/uart_subr.c
index 03c7fd8caea9..ca127b3a956e 100644
--- a/sys/dev/uart/uart_subr.c
+++ b/sys/dev/uart/uart_subr.c
@@ -185,6 +185,7 @@ out:
* mm = Memory mapped I/O address
* pa = Parity
* rs = Register shift
+ * rw = Register width
* sb = Stopbits
* xo = Device clock (xtal oscillator)
*
@@ -200,13 +201,6 @@ uart_getenv(int devtype, struct uart_devinfo *di, struct uart_class *class)
int error;
/*
- * All uart_class references are weak. Make sure the default
- * device class has been compiled-in.
- */
- if (class == NULL)
- return (ENXIO);
-
- /*
* Check the environment variables "hw.uart.console" and
* "hw.uart.dbgport". These variables, when present, specify
* which UART port is to be used as serial console or debug
@@ -278,6 +272,8 @@ uart_getenv(int devtype, struct uart_devinfo *di, struct uart_class *class)
break;
case UART_TAG_XO:
di->bas.rclk = uart_parse_long(&spec);
+ if (di->bas.rclk == 0)
+ di->bas.rclk_guess = 1;
break;
default:
goto inval;
@@ -298,6 +294,13 @@ uart_getenv(int devtype, struct uart_devinfo *di, struct uart_class *class)
freeenv(cp);
/*
+ * The default uart_class reference is weak. Make sure the default
+ * device class has been compiled-in or we've set one with dt=.
+ */
+ if (class == NULL)
+ return (ENXIO);
+
+ /*
* Accept only the well-known baudrates. Any invalid baudrate
* is silently replaced with a 0-valued baudrate. The 0 baudrate
* has special meaning. It means that we're not supposed to
diff --git a/sys/dev/uart/uart_tty.c b/sys/dev/uart/uart_tty.c
index faae077916f3..d15d1d0c6ac2 100644
--- a/sys/dev/uart/uart_tty.c
+++ b/sys/dev/uart/uart_tty.c
@@ -36,11 +36,11 @@
#include <sys/kernel.h>
#include <sys/malloc.h>
#include <sys/reboot.h>
+#include <sys/stdarg.h>
#include <machine/bus.h>
#include <sys/rman.h>
#include <sys/tty.h>
#include <machine/resource.h>
-#include <machine/stdarg.h>
#include <dev/uart/uart.h>
#include <dev/uart/uart_bus.h>