term/ns8250: Add base support for MMIO UARTs

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>
Reviewed-by: Daniel Kiper <daniel.kiper@oracle.com>
This commit is contained in:
Benjamin Herrenschmidt authored and Daniel Kiper committed 2023-01-19 17:36:25 +01:00
1 parent 55604aaad2
commit 9fb22d0478
2 files changed
+74 -17

No files matched your search

+64 -16
View File
@@ -43,6 +43,24 @@ static int dead_ports = 0;
#define DEFAULT_BASE_CLOCK 115200 #define DEFAULT_BASE_CLOCK 115200
#endif #endif
static grub_uint8_t
ns8250_reg_read (struct grub_serial_port *port, grub_addr_t reg)
{
asm volatile("" : : : "memory");
if (port->mmio == true)
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 value, grub_addr_t reg)
{
asm volatile("" : : : "memory");
if (port->mmio == true)
*((volatile grub_uint8_t *) (port->mmio_base + reg)) = value;
else
grub_outb (value, port->port + reg);
}
/* Convert speed to divisor. */ /* Convert speed to divisor. */
static unsigned short static unsigned short
@@ -93,43 +111,42 @@ do_real_config (struct grub_serial_port *port)
divisor = serial_get_divisor (port, &port->config); divisor = serial_get_divisor (port, &port->config);
/* Turn off the interrupt. */ /* Turn off the interrupt. */
grub_outb (0, port->port + UART_IER); ns8250_reg_write (port, 0, UART_IER);
/* Set DLAB. */ /* Set DLAB. */
grub_outb (UART_DLAB, port->port + UART_LCR); ns8250_reg_write (port, UART_DLAB, UART_LCR);
/* Set the baud rate. */ ns8250_reg_write (port, divisor & 0xFF, UART_DLL);
grub_outb (divisor & 0xFF, port->port + UART_DLL); ns8250_reg_write (port, divisor >> 8, UART_DLH);
grub_outb (divisor >> 8, port->port + UART_DLH);
/* Set the line status. */ /* Set the line status. */
status |= (parities[port->config.parity] status |= (parities[port->config.parity]
| (port->config.word_len - 5) | (port->config.word_len - 5)
| stop_bits[port->config.stop_bits]); | stop_bits[port->config.stop_bits]);
grub_outb (status, port->port + UART_LCR); ns8250_reg_write (port, status, UART_LCR);
if (port->config.rtscts) if (port->config.rtscts)
{ {
/* Enable the FIFO. */ /* 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. */ /* Turn on DTR and RTS. */
grub_outb (UART_ENABLE_DTRRTS, port->port + UART_MCR); ns8250_reg_write (port, UART_ENABLE_DTRRTS, UART_MCR);
} }
else else
{ {
/* Enable the FIFO. */ /* 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. */ /* 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. */ /* Drain the input buffer. */
endtime = grub_get_time_ms () + 1000; 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) if (grub_get_time_ms () > endtime)
{ {
port->broken = 1; port->broken = 1;
@@ -145,8 +162,8 @@ static int
serial_hw_fetch (struct grub_serial_port *port) serial_hw_fetch (struct grub_serial_port *port)
{ {
do_real_config (port); do_real_config (port);
if (grub_inb (port->port + UART_LSR) & UART_DATA_READY) if (ns8250_reg_read (port, UART_LSR) & UART_DATA_READY)
return grub_inb (port->port + UART_RX); return ns8250_reg_read (port, UART_RX);
return -1; return -1;
} }
@@ -166,7 +183,7 @@ serial_hw_put (struct grub_serial_port *port, const int c)
else else
endtime = grub_get_time_ms () + 200; endtime = grub_get_time_ms () + 200;
/* Wait until the transmitter holding register is empty. */ /* 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) if (grub_get_time_ms () > endtime)
{ {
@@ -179,7 +196,7 @@ serial_hw_put (struct grub_serial_port *port, const int c)
if (port->broken) if (port->broken)
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. /* 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].name = com_names[i];
com_ports[i].driver = &grub_ns8250_driver; com_ports[i].driver = &grub_ns8250_driver;
com_ports[i].port = serial_hw_io_addr[i]; com_ports[i].port = serial_hw_io_addr[i];
com_ports[i].mmio = false;
err = grub_serial_config_defaults (&com_ports[i]); err = grub_serial_config_defaults (&com_ports[i]);
if (err) if (err)
grub_print_error (); grub_print_error ();
@@ -289,6 +307,7 @@ grub_serial_ns8250_add_port (grub_port_t port)
{ {
struct grub_serial_port *p; struct grub_serial_port *p;
unsigned i; unsigned i;
for (i = 0; i < GRUB_SERIAL_PORT_NUM; i++) for (i = 0; i < GRUB_SERIAL_PORT_NUM; i++)
if (com_ports[i].port == port) if (com_ports[i].port == port)
{ {
@@ -316,8 +335,37 @@ grub_serial_ns8250_add_port (grub_port_t port)
} }
p->driver = &grub_ns8250_driver; p->driver = &grub_ns8250_driver;
grub_serial_config_defaults (p); grub_serial_config_defaults (p);
p->mmio = false;
p->port = port; p->port = port;
grub_serial_register (p); grub_serial_register (p);
return p->name; 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 == true && 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;
}
+10 -1
View File
@@ -86,9 +86,17 @@ struct grub_serial_port
*/ */
union union
{ {
struct
{
bool mmio;
union
{
#if defined(__mips__) || defined (__i386__) || defined (__x86_64__) #if defined(__mips__) || defined (__i386__) || defined (__x86_64__)
grub_port_t port; grub_port_t port;
#endif #endif
grub_addr_t mmio_base;
};
};
struct struct
{ {
grub_usb_device_t usbdev; 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__) #if defined(__mips__) || defined (__i386__) || defined (__x86_64__)
void grub_ns8250_init (void); void grub_ns8250_init (void);
char *grub_serial_ns8250_add_port (grub_port_t port); char *grub_serial_ns8250_add_port (grub_port_t port);
char *grub_serial_ns8250_add_mmio (grub_addr_t addr);
#endif #endif
#ifdef GRUB_MACHINE_IEEE1275 #ifdef GRUB_MACHINE_IEEE1275
void grub_ofserial_init (void); void grub_ofserial_init (void);