From mboxrd@z Thu Jan 1 00:00:00 1970 Received: from list by lists.gnu.org with archive (Exim 4.90_1) id 1lN0p2-0005t4-Py for mharc-grub-devel@gnu.org; Thu, 18 Mar 2021 18:08:52 -0400 Received: from eggs.gnu.org ([2001:470:142:3::10]:43674) by lists.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1lN0oy-0005ot-5R for grub-devel@gnu.org; Thu, 18 Mar 2021 18:08:48 -0400 Received: from gate.crashing.org ([63.228.1.57]:56739) by eggs.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1lN0ow-0007Ks-38 for grub-devel@gnu.org; Thu, 18 Mar 2021 18:08:47 -0400 Received: from u11bb3cc8ece954.lan (localhost.localdomain [127.0.0.1]) by gate.crashing.org (8.14.1/8.14.1) with ESMTP id 12IM7UMs005587; Thu, 18 Mar 2021 17:07:38 -0500 From: Benjamin Herrenschmidt To: grub-devel@gnu.org Cc: Matthias Lange , Benjamin Herrenschmidt Subject: [PATCH 3/5] ns8250: Add base support for MMIO UARTs Date: Fri, 19 Mar 2021 09:07:26 +1100 Message-Id: <20210318220728.495970-4-benh@kernel.crashing.org> X-Mailer: git-send-email 2.25.1 In-Reply-To: <20210318220728.495970-1-benh@kernel.crashing.org> References: <20210318220728.495970-1-benh@kernel.crashing.org> MIME-Version: 1.0 Content-Transfer-Encoding: 8bit Received-SPF: permerror client-ip=63.228.1.57; envelope-from=benh@kernel.crashing.org; helo=gate.crashing.org X-Spam_score_int: -18 X-Spam_score: -1.9 X-Spam_bar: - X-Spam_report: (-1.9 / 5.0 requ) BAYES_00=-1.9, T_SPF_HELO_PERMERROR=0.01, T_SPF_PERMERROR=0.01 autolearn=ham autolearn_force=no X-Spam_action: no action X-BeenThere: grub-devel@gnu.org X-Mailman-Version: 2.1.23 Precedence: list List-Id: The development of GNU GRUB List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Thu, 18 Mar 2021 22:08:48 -0000 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. Signed-off-by: Benjamin Herrenschmidt --- docs/grub.texi | 3 +- grub-core/term/ns8250.c | 78 ++++++++++++++++++++++++++++++++--------- grub-core/term/serial.c | 22 ++++++++++-- include/grub/serial.h | 10 +++++- 4 files changed, 92 insertions(+), 21 deletions(-) diff --git a/docs/grub.texi b/docs/grub.texi index eeb3118eb..4a3287119 100644 --- a/docs/grub.texi +++ b/docs/grub.texi @@ -3878,7 +3878,8 @@ Commands usable anywhere in the menu and in the command-line. Initialize a serial device. @var{unit} is a number in the range 0-3 specifying which serial port to use; default is 0, which corresponds to the port often called COM1. @var{port} is the I/O port where the UART -is to be found; if specified it takes precedence over @var{unit}. +is to be found; if specified it takes precedence over @var{unit}. It can +also be ``mmio'' followed by the MMIO address of the port in hexadecimal. @var{speed} is the transmission speed; default is 9600. @var{word} and @var{stop} are the number of data bits and stop bits. Data bits must be in the range 5-8 and stop bits must be 1 or 2. Default is 8 data diff --git a/grub-core/term/ns8250.c b/grub-core/term/ns8250.c index 39809d042..183e14b3b 100644 --- a/grub-core/term/ns8250.c +++ b/grub-core/term/ns8250.c @@ -44,6 +44,24 @@ static int dead_ports = 0; #define DEFAULT_BASE_CLOCK 115200 #endif +static inline unsigned char +ns8250_reg_read (struct grub_serial_port *port, grub_addr_t reg) +{ + asm volatile("" : : : "memory"); + if (port->mmio) + return *((volatile unsigned char *)(port->mmio_base + reg)); + return grub_inb (port->port + reg); +} + +static inline void +ns8250_reg_write (struct grub_serial_port *port, unsigned char value, grub_addr_t reg) +{ + asm volatile("" : : : "memory"); + if (port->mmio) + *((volatile char *)(port->mmio_base + reg)) = value; + else + grub_outb (value, port->port + reg); +} /* Convert speed to divisor. */ static unsigned short @@ -94,43 +112,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; @@ -146,8 +163,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; } @@ -167,7 +184,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) { @@ -180,7 +197,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. @@ -260,6 +277,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 = 0; err = grub_serial_config_defaults (&com_ports[i]); if (err) grub_print_error (); @@ -311,8 +329,36 @@ grub_serial_ns8250_add_port (grub_port_t port) } p->driver = &grub_ns8250_driver; grub_serial_config_defaults (p); + p->mmio = 0; 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) + return NULL; + p->name = grub_xasprintf ("mmio%llx", (unsigned long long) addr); + if (!p->name) + { + grub_free (p); + return NULL; + } + p->driver = &grub_ns8250_driver; + grub_serial_config_defaults (p); + p->mmio = 1; + 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 f9271b092..7d4dbb2de 100644 --- a/grub-core/term/serial.c +++ b/grub-core/term/serial.c @@ -160,6 +160,18 @@ grub_serial_find (const char *name) if (!name) return NULL; + FOR_SERIAL_PORTS (port) + if (grub_strcmp (port->name, name) == 0) + break; + } + if (!port && grub_memcmp (name, "mmio", sizeof ("mmio") - 1) == 0 + && grub_isxdigit (name [sizeof ("mmio") - 1])) + { + name = grub_serial_ns8250_add_mmio (grub_strtoul (&name[sizeof ("mmio") - 1], + 0, 16), NULL); + if (!name) + return NULL; + FOR_SERIAL_PORTS (port) if (grub_strcmp (port->name, name) == 0) break; @@ -195,14 +207,18 @@ grub_cmd_serial (grub_extcmd_context_t ctxt, int argc, char **args) if (state[OPTION_UNIT].set) { grub_snprintf (pname, sizeof (pname), "com%ld", - grub_strtoul (state[0].arg, 0, 0)); + grub_strtoul (state[OPTION_UNIT].arg, 0, 0)); name = pname; } 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[OPTION_PORT].arg); + else + grub_snprintf (pname, sizeof (pname), "port%lx", + grub_strtoul (state[OPTION_PORT].arg, 0, 0)); + name = pname; } diff --git a/include/grub/serial.h b/include/grub/serial.h index 67379de45..a5756cd25 100644 --- a/include/grub/serial.h +++ b/include/grub/serial.h @@ -86,9 +86,16 @@ struct grub_serial_port */ union { + struct + { + int 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 +185,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.25.1