[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[PATCH v2 3/8] ns8250: Add base support for MMIO UARTs
From: |
Benjamin Herrenschmidt |
Subject: |
[PATCH v2 3/8] ns8250: Add base support for MMIO UARTs |
Date: |
Thu, 22 Dec 2022 17:12:52 +1100 |
From: Benjamin Herrenschmidt <benh@amazon.com>
This adds the ability for the driver to access UARTs via MMIO instead
of PIO selectively at runtime, and exposes a new function to add an
MMIO port.
In an ideal world, MMIO accessors would be generic and have architecture
specific memory barriers. However, existing drivers don't have them and
most of those "bare metal" drivers tend to be for x86 which doesn't need
them. If necessary, those can be added later.
Signed-off-by: Benjamin Herrenschmidt <benh@kernel.crashing.org>
---
grub-core/term/ns8250.c | 80 ++++++++++++++++++++++++++++++++---------
grub-core/term/serial.c | 8 +++--
include/grub/serial.h | 11 +++++-
3 files changed, 80 insertions(+), 19 deletions(-)
diff --git a/grub-core/term/ns8250.c b/grub-core/term/ns8250.c
index 83b25990c..43b9b3719 100644
--- a/grub-core/term/ns8250.c
+++ b/grub-core/term/ns8250.c
@@ -43,6 +43,24 @@ static int dead_ports = 0;
#define DEFAULT_BASE_CLOCK 115200
#endif
+static grub_uint8_t
+ns8250_reg_read (struct grub_serial_port *port, grub_addr_t reg)
+{
+ asm volatile("" : : : "memory");
+ if (port->mmio)
+ return *((volatile grub_uint8_t *) (port->mmio_base + reg));
+ return grub_inb (port->port + reg);
+}
+
+static void
+ns8250_reg_write (struct grub_serial_port *port, grub_uint8_t, grub_addr_t reg)
+{
+ asm volatile("" : : : "memory");
+ if (port->mmio)
+ *((volatile grub_uint8_t *) (port->mmio_base + reg)) = value;
+ else
+ grub_outb (value, port->port + reg);
+}
/* Convert speed to divisor. */
static unsigned short
@@ -93,43 +111,42 @@ do_real_config (struct grub_serial_port *port)
divisor = serial_get_divisor (port, &port->config);
/* Turn off the interrupt. */
- grub_outb (0, port->port + UART_IER);
+ ns8250_reg_write (port, 0, UART_IER);
/* Set DLAB. */
- grub_outb (UART_DLAB, port->port + UART_LCR);
+ ns8250_reg_write (port, UART_DLAB, UART_LCR);
- /* Set the baud rate. */
- grub_outb (divisor & 0xFF, port->port + UART_DLL);
- grub_outb (divisor >> 8, port->port + UART_DLH);
+ ns8250_reg_write (port, divisor & 0xFF, UART_DLL);
+ ns8250_reg_write (port, divisor >> 8, UART_DLH);
/* Set the line status. */
status |= (parities[port->config.parity]
| (port->config.word_len - 5)
| stop_bits[port->config.stop_bits]);
- grub_outb (status, port->port + UART_LCR);
+ ns8250_reg_write (port, status, UART_LCR);
if (port->config.rtscts)
{
/* Enable the FIFO. */
- grub_outb (UART_ENABLE_FIFO_TRIGGER1, port->port + UART_FCR);
+ ns8250_reg_write (port, UART_ENABLE_FIFO_TRIGGER1, UART_FCR);
/* Turn on DTR and RTS. */
- grub_outb (UART_ENABLE_DTRRTS, port->port + UART_MCR);
+ ns8250_reg_write (port, UART_ENABLE_DTRRTS, UART_MCR);
}
else
{
/* Enable the FIFO. */
- grub_outb (UART_ENABLE_FIFO_TRIGGER14, port->port + UART_FCR);
+ ns8250_reg_write (port, UART_ENABLE_FIFO_TRIGGER14, UART_FCR);
/* Turn on DTR, RTS, and OUT2. */
- grub_outb (UART_ENABLE_DTRRTS | UART_ENABLE_OUT2, port->port + UART_MCR);
+ ns8250_reg_write (port, UART_ENABLE_DTRRTS | UART_ENABLE_OUT2, UART_MCR);
}
/* Drain the input buffer. */
endtime = grub_get_time_ms () + 1000;
- while (grub_inb (port->port + UART_LSR) & UART_DATA_READY)
+ while (ns8250_reg_read (port, UART_LSR) & UART_DATA_READY)
{
- grub_inb (port->port + UART_RX);
+ ns8250_reg_read (port, UART_RX);
if (grub_get_time_ms () > endtime)
{
port->broken = 1;
@@ -145,8 +162,8 @@ static int
serial_hw_fetch (struct grub_serial_port *port)
{
do_real_config (port);
- if (grub_inb (port->port + UART_LSR) & UART_DATA_READY)
- return grub_inb (port->port + UART_RX);
+ if (ns8250_reg_read (port, UART_LSR) & UART_DATA_READY)
+ return ns8250_reg_read (port, UART_RX);
return -1;
}
@@ -166,7 +183,7 @@ serial_hw_put (struct grub_serial_port *port, const int c)
else
endtime = grub_get_time_ms () + 200;
/* Wait until the transmitter holding register is empty. */
- while ((grub_inb (port->port + UART_LSR) & UART_EMPTY_TRANSMITTER) == 0)
+ while ((ns8250_reg_read (port, UART_LSR) & UART_EMPTY_TRANSMITTER) == 0)
{
if (grub_get_time_ms () > endtime)
{
@@ -179,7 +196,7 @@ serial_hw_put (struct grub_serial_port *port, const int c)
if (port->broken)
port->broken--;
- grub_outb (c, port->port + UART_TX);
+ ns8250_reg_write (port, c, UART_TX);
}
/* Initialize a serial device. PORT is the port number for a serial device.
@@ -262,6 +279,7 @@ grub_ns8250_init (void)
com_ports[i].name = com_names[i];
com_ports[i].driver = &grub_ns8250_driver;
com_ports[i].port = serial_hw_io_addr[i];
+ com_ports[i].mmio = false;
err = grub_serial_config_defaults (&com_ports[i]);
if (err)
grub_print_error ();
@@ -289,6 +307,7 @@ grub_serial_ns8250_add_port (grub_port_t port)
{
struct grub_serial_port *p;
unsigned i;
+
for (i = 0; i < GRUB_SERIAL_PORT_NUM; i++)
if (com_ports[i].port == port)
{
@@ -316,8 +335,37 @@ grub_serial_ns8250_add_port (grub_port_t port)
}
p->driver = &grub_ns8250_driver;
grub_serial_config_defaults (p);
+ p->mmio = false;
p->port = port;
grub_serial_register (p);
return p->name;
}
+
+char *
+grub_serial_ns8250_add_mmio (grub_addr_t addr)
+{
+ struct grub_serial_port *p;
+ unsigned i;
+
+ for (i = 0; i < GRUB_SERIAL_PORT_NUM; i++)
+ if (com_ports[i].mmio && com_ports[i].mmio_base == addr)
+ return com_names[i];
+
+ p = grub_malloc (sizeof (*p));
+ if (p == NULL)
+ return NULL;
+ p->name = grub_xasprintf ("mmio,%llx", (unsigned long long) addr);
+ if (p->name == NULL)
+ {
+ grub_free (p);
+ return NULL;
+ }
+ p->driver = &grub_ns8250_driver;
+ grub_serial_config_defaults (p);
+ p->mmio = true;
+ p->mmio_base = addr;
+ grub_serial_register (p);
+
+ return p->name;
+}
diff --git a/grub-core/term/serial.c b/grub-core/term/serial.c
index 842322b1c..c8f5f02db 100644
--- a/grub-core/term/serial.c
+++ b/grub-core/term/serial.c
@@ -201,8 +201,12 @@ grub_cmd_serial (grub_extcmd_context_t ctxt, int argc,
char **args)
if (state[OPTION_PORT].set)
{
- grub_snprintf (pname, sizeof (pname), "port%lx",
- grub_strtoul (state[1].arg, 0, 0));
+ if (grub_memcmp (state[OPTION_PORT].arg, "mmio", 4) == 0)
+ grub_snprintf(pname, sizeof (pname), "%s", state[1].arg);
+ else
+ grub_snprintf (pname, sizeof (pname), "port%lx",
+ grub_strtoul (state[1].arg, 0, 0));
+
name = pname;
}
diff --git a/include/grub/serial.h b/include/grub/serial.h
index 67379de45..5e008f0f5 100644
--- a/include/grub/serial.h
+++ b/include/grub/serial.h
@@ -86,9 +86,17 @@ struct grub_serial_port
*/
union
{
+ struct
+ {
+ bool mmio;
+ union
+ {
#if defined(__mips__) || defined (__i386__) || defined (__x86_64__)
- grub_port_t port;
+ grub_port_t port;
#endif
+ grub_addr_t mmio_base;
+ };
+ };
struct
{
grub_usb_device_t usbdev;
@@ -178,6 +186,7 @@ grub_serial_config_defaults (struct grub_serial_port *port)
#if defined(__mips__) || defined (__i386__) || defined (__x86_64__)
void grub_ns8250_init (void);
char *grub_serial_ns8250_add_port (grub_port_t port);
+char *grub_serial_ns8250_add_mmio (grub_addr_t addr);
#endif
#ifdef GRUB_MACHINE_IEEE1275
void grub_ofserial_init (void);
--
2.34.1
- [PATCH v2 0/8] serial: Add MMIO & SPCR support, Benjamin Herrenschmidt, 2022/12/22
- [PATCH v2 1/8] acpi: Export a generic grub_acpi_find_table, Benjamin Herrenschmidt, 2022/12/22
- [PATCH v2 2/8] acpi: Add SPCR and generic address definitions, Benjamin Herrenschmidt, 2022/12/22
- [PATCH v2 3/8] ns8250: Add base support for MMIO UARTs,
Benjamin Herrenschmidt <=
- [PATCH v2 5/8] ns8250: Add configuration parameter when adding ports, Benjamin Herrenschmidt, 2022/12/22
- [PATCH v2 4/8] ns8250: Move base clock definition to a header, Benjamin Herrenschmidt, 2022/12/22
- [PATCH v2 8/8] serial: Add ability to specify MMIO ports via 'serial', Benjamin Herrenschmidt, 2022/12/22
- [PATCH v2 6/8] ns8250: Use ACPI SPCR table when available to configure serial, Benjamin Herrenschmidt, 2022/12/22
- [PATCH v2 7/8] ns8250: Support more MMIO access sizes, Benjamin Herrenschmidt, 2022/12/22