From 6c8fb4b89d49c40339fe61b7ec549d90f1ce9480 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Tue, 17 Jun 2025 14:28:18 +0100 Subject: [uart] Allow for the existence of non-16550 UARTs Remove the assumption that all platforms use a fixed number of 16550 UARTs identifiable by a simple numeric index. Create an abstraction allowing for dynamic instantiation and registration of any number of arbitrary UART models. The common case of the serial console on x86 uses a single fixed UART specified at compile time. Avoid unnecessarily dragging in the dynamic instantiation code in this use case by allowing COMCONSOLE to refer to a single static UART object representing the relevant port. When selecting a UART by command-line argument (as used in the "gdbstub serial " command), allow the UART to be specified as either a numeric index (to retain backwards compatiblity) or a case-insensitive port name such as "COM2". Signed-off-by: Michael Brown --- src/drivers/uart/ns16550.c | 179 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 179 insertions(+) create mode 100644 src/drivers/uart/ns16550.c (limited to 'src/drivers/uart') diff --git a/src/drivers/uart/ns16550.c b/src/drivers/uart/ns16550.c new file mode 100644 index 000000000..58a71261b --- /dev/null +++ b/src/drivers/uart/ns16550.c @@ -0,0 +1,179 @@ +/* + * Copyright (C) 2025 Michael Brown . + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. + */ + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +/** @file + * + * 16550-compatible UART + * + */ + +#include +#include +#include +#include + +/** Timeout for transmit holding register to become empty */ +#define NS16550_THRE_TIMEOUT_MS 100 + +/** Timeout for transmitter to become empty */ +#define NS16550_TEMT_TIMEOUT_MS 1000 + +/** + * Transmit data + * + * @v uart UART + * @v data Data + */ +static void ns16550_transmit ( struct uart *uart, uint8_t data ) { + struct ns16550_uart *ns16550 = + container_of ( uart, struct ns16550_uart, uart ); + unsigned int i; + uint8_t lsr; + + /* Wait for transmitter holding register to become empty */ + for ( i = 0 ; i < NS16550_THRE_TIMEOUT_MS ; i++ ) { + lsr = ns16550_read ( ns16550, NS16550_LSR ); + if ( lsr & NS16550_LSR_THRE ) + break; + mdelay ( 1 ); + } + + /* Transmit data (even if we timed out) */ + ns16550_write ( ns16550, NS16550_THR, data ); +} + +/** + * Check if data is ready + * + * @v uart UART + * @ret ready Data is ready + */ +static int ns16550_data_ready ( struct uart *uart ) { + struct ns16550_uart *ns16550 = + container_of ( uart, struct ns16550_uart, uart ); + uint8_t lsr; + + /* Check for receive data ready */ + lsr = ns16550_read ( ns16550, NS16550_LSR ); + return ( lsr & NS16550_LSR_DR ); +} + +/** + * Receive data + * + * @v uart UART + * @ret data Data + */ +static uint8_t ns16550_receive ( struct uart *uart ) { + struct ns16550_uart *ns16550 = + container_of ( uart, struct ns16550_uart, uart ); + uint8_t rbr; + + /* Receive byte */ + rbr = ns16550_read ( ns16550, NS16550_RBR ); + return rbr; +} + +/** + * Flush transmitted data + * + * @v uart UART + */ +static void ns16550_flush ( struct uart *uart ) { + struct ns16550_uart *ns16550 = + container_of ( uart, struct ns16550_uart, uart ); + unsigned int i; + uint8_t lsr; + + /* Wait for transmitter to become empty */ + for ( i = 0 ; i < NS16550_TEMT_TIMEOUT_MS ; i++ ) { + lsr = ns16550_read ( ns16550, NS16550_LSR ); + if ( lsr & NS16550_LSR_TEMT ) + break; + } +} + +/** + * Initialise UART + * + * @v uart UART + * @v baud Baud rate, or zero to leave unchanged + * @ret rc Return status code + */ +static int ns16550_init ( struct uart *uart, unsigned int baud ) { + struct ns16550_uart *ns16550 = + container_of ( uart, struct ns16550_uart, uart ); + uint8_t dlm; + uint8_t dll; + + /* Fail if UART scratch register seems not to be present */ + ns16550_write ( ns16550, NS16550_SCR, 0x18 ); + if ( ns16550_read ( ns16550, NS16550_SCR ) != 0x18 ) + return -ENODEV; + ns16550_write ( ns16550, NS16550_SCR, 0xae ); + if ( ns16550_read ( ns16550, NS16550_SCR ) != 0xae ) + return -ENODEV; + + /* Configure divisor and line control register, if applicable */ + ns16550_write ( ns16550, NS16550_LCR, + ( NS16550_LCR_8N1 | NS16550_LCR_DLAB ) ); + if ( baud ) { + ns16550->divisor = ( NS16550_MAX_BAUD / baud ); + dlm = ( ( ns16550->divisor >> 8 ) & 0xff ); + dll = ( ( ns16550->divisor >> 0 ) & 0xff ); + ns16550_write ( ns16550, NS16550_DLM, dlm ); + ns16550_write ( ns16550, NS16550_DLL, dll ); + } else { + dlm = ns16550_read ( ns16550, NS16550_DLM ); + dll = ns16550_read ( ns16550, NS16550_DLL ); + ns16550->divisor = ( ( dlm << 8 ) | dll ); + } + ns16550_write ( ns16550, NS16550_LCR, NS16550_LCR_8N1 ); + + /* Disable interrupts */ + ns16550_write ( ns16550, NS16550_IER, 0 ); + + /* Enable FIFOs */ + ns16550_write ( ns16550, NS16550_FCR, NS16550_FCR_FE ); + + /* Assert DTR and RTS */ + ns16550_write ( ns16550, NS16550_MCR, + ( NS16550_MCR_DTR | NS16550_MCR_RTS ) ); + + /* Flush any stale received data */ + while ( ns16550_data_ready ( uart ) ) + ns16550_receive ( uart ); + + return 0; +} + +/** 16550 UART operations */ +struct uart_operations ns16550_operations = { + .transmit = ns16550_transmit, + .data_ready = ns16550_data_ready, + .receive = ns16550_receive, + .init = ns16550_init, + .flush = ns16550_flush, +}; -- cgit v1.2.3-55-g7522 From cca1cfd49ec3ac0ada90197d11118a99d16aed5b Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Sat, 21 Jun 2025 23:11:56 +0100 Subject: [uart] Allow for dynamically registered 16550 UARTs Use the generic UART driver-private data pointer, rather than embedding the generic UART within the 16550 UART structure. Signed-off-by: Michael Brown --- src/arch/x86/core/x86_uart.c | 13 +++++++------ src/arch/x86/include/bits/ns16550.h | 16 ++++++++-------- src/arch/x86/interface/syslinux/comboot_call.c | 4 +--- src/drivers/uart/ns16550.c | 15 +++++---------- src/include/ipxe/ns16550.h | 2 -- 5 files changed, 21 insertions(+), 29 deletions(-) (limited to 'src/drivers/uart') diff --git a/src/arch/x86/core/x86_uart.c b/src/arch/x86/core/x86_uart.c index 2580b931b..a1d643a58 100644 --- a/src/arch/x86/core/x86_uart.c +++ b/src/arch/x86/core/x86_uart.c @@ -35,13 +35,14 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); /** Define a fixed ISA UART */ #define ISA_UART( NAME, BASE ) \ - struct ns16550_uart NAME = { \ - .uart = { \ - .refcnt = REF_INIT ( ref_no_free ), \ - .name = #NAME, \ - .op = &ns16550_operations, \ - }, \ + static struct ns16550_uart ns16550_ ## NAME = { \ .base = ( ( void * ) (BASE) ), \ + }; \ + struct uart NAME = { \ + .refcnt = REF_INIT ( ref_no_free ), \ + .name = #NAME, \ + .op = &ns16550_operations, \ + .priv = &ns16550_ ## NAME, \ } /* Fixed ISA UARTs */ diff --git a/src/arch/x86/include/bits/ns16550.h b/src/arch/x86/include/bits/ns16550.h index 9f7d741ae..cc2bd84c1 100644 --- a/src/arch/x86/include/bits/ns16550.h +++ b/src/arch/x86/include/bits/ns16550.h @@ -46,15 +46,15 @@ ns16550_read ( struct ns16550_uart *ns16550, unsigned int address ) { #define COM4_BASE 0x2e8 /* Fixed ISA serial ports */ -extern struct ns16550_uart com1; -extern struct ns16550_uart com2; -extern struct ns16550_uart com3; -extern struct ns16550_uart com4; +extern struct uart com1; +extern struct uart com2; +extern struct uart com3; +extern struct uart com4; /* Fixed ISA serial port names */ -#define COM1 &com1.uart -#define COM2 &com2.uart -#define COM3 &com3.uart -#define COM4 &com4.uart +#define COM1 &com1 +#define COM2 &com2 +#define COM3 &com3 +#define COM4 &com4 #endif /* _BITS_NS16550_H */ diff --git a/src/arch/x86/interface/syslinux/comboot_call.c b/src/arch/x86/interface/syslinux/comboot_call.c index 97bdaeae3..c3e921075 100644 --- a/src/arch/x86/interface/syslinux/comboot_call.c +++ b/src/arch/x86/interface/syslinux/comboot_call.c @@ -447,9 +447,7 @@ static __asmcall __used void int22 ( struct i386_all_regs *ix86 ) { case 0x000B: /* Get Serial Console Configuration */ if ( serial_console ) { - struct ns16550_uart *comport = - container_of ( serial_console, - struct ns16550_uart, uart ); + struct ns16550_uart *comport = serial_console->priv; ix86->regs.dx = ( ( intptr_t ) comport->base ); ix86->regs.cx = comport->divisor; diff --git a/src/drivers/uart/ns16550.c b/src/drivers/uart/ns16550.c index 58a71261b..cf8c744c6 100644 --- a/src/drivers/uart/ns16550.c +++ b/src/drivers/uart/ns16550.c @@ -47,8 +47,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); * @v data Data */ static void ns16550_transmit ( struct uart *uart, uint8_t data ) { - struct ns16550_uart *ns16550 = - container_of ( uart, struct ns16550_uart, uart ); + struct ns16550_uart *ns16550 = uart->priv; unsigned int i; uint8_t lsr; @@ -71,8 +70,7 @@ static void ns16550_transmit ( struct uart *uart, uint8_t data ) { * @ret ready Data is ready */ static int ns16550_data_ready ( struct uart *uart ) { - struct ns16550_uart *ns16550 = - container_of ( uart, struct ns16550_uart, uart ); + struct ns16550_uart *ns16550 = uart->priv; uint8_t lsr; /* Check for receive data ready */ @@ -87,8 +85,7 @@ static int ns16550_data_ready ( struct uart *uart ) { * @ret data Data */ static uint8_t ns16550_receive ( struct uart *uart ) { - struct ns16550_uart *ns16550 = - container_of ( uart, struct ns16550_uart, uart ); + struct ns16550_uart *ns16550 = uart->priv; uint8_t rbr; /* Receive byte */ @@ -102,8 +99,7 @@ static uint8_t ns16550_receive ( struct uart *uart ) { * @v uart UART */ static void ns16550_flush ( struct uart *uart ) { - struct ns16550_uart *ns16550 = - container_of ( uart, struct ns16550_uart, uart ); + struct ns16550_uart *ns16550 = uart->priv; unsigned int i; uint8_t lsr; @@ -123,8 +119,7 @@ static void ns16550_flush ( struct uart *uart ) { * @ret rc Return status code */ static int ns16550_init ( struct uart *uart, unsigned int baud ) { - struct ns16550_uart *ns16550 = - container_of ( uart, struct ns16550_uart, uart ); + struct ns16550_uart *ns16550 = uart->priv; uint8_t dlm; uint8_t dll; diff --git a/src/include/ipxe/ns16550.h b/src/include/ipxe/ns16550.h index 3aaab6891..6699205e2 100644 --- a/src/include/ipxe/ns16550.h +++ b/src/include/ipxe/ns16550.h @@ -78,8 +78,6 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); /** A 16550-compatible UART */ struct ns16550_uart { - /** Generic UART */ - struct uart uart; /** Register base address */ void *base; /** Register shift */ -- cgit v1.2.3-55-g7522 From 53a3befb692a34ef84047a0fe3320a0ec04d8d9d Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Sat, 21 Jun 2025 23:13:58 +0100 Subject: [dwuart] Add a basic driver for the Synopsys DesignWare UART Signed-off-by: Michael Brown --- src/drivers/uart/dwuart.c | 120 +++++++++++++++++++++++++++++++++++++++++++++ src/include/ipxe/errfile.h | 1 + 2 files changed, 121 insertions(+) create mode 100644 src/drivers/uart/dwuart.c (limited to 'src/drivers/uart') diff --git a/src/drivers/uart/dwuart.c b/src/drivers/uart/dwuart.c new file mode 100644 index 000000000..bf83afd80 --- /dev/null +++ b/src/drivers/uart/dwuart.c @@ -0,0 +1,120 @@ +/* + * Copyright (C) 2025 Michael Brown . + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. + */ + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +/** @file + * + * DesignWare UART + * + */ + +#include +#include +#include +#include +#include + +/** + * Probe devicetree device + * + * @v dt Devicetree device + * @v offset Starting node offset + * @ret rc Return status code + */ +static int dwuart_probe ( struct dt_device *dt, unsigned int offset ) { + struct ns16550_uart *ns16550; + struct uart *uart; + uint32_t shift; + int rc; + + /* Allocate and initialise UART */ + uart = alloc_uart ( sizeof ( *ns16550 ) ); + if ( ! uart ) { + rc = -ENOMEM; + goto err_alloc; + } + uart->name = dt->name; + uart->op = &ns16550_operations; + ns16550 = uart->priv; + dt_set_drvdata ( dt, uart ); + + /* Map registers */ + ns16550->base = dt_ioremap ( dt, offset, 0, 0 ); + if ( ! ns16550->base ) { + rc = -ENODEV; + goto err_ioremap; + } + + /* Get register shift */ + if ( ( rc = fdt_u32 ( &sysfdt, offset, "reg-shift", &shift ) ) != 0 ) + shift = 0; + ns16550->shift = shift; + + /* Register UART */ + if ( ( rc = uart_register ( uart ) ) != 0 ) + goto err_register; + + return 0; + + uart_unregister ( uart ); + err_register: + iounmap ( ns16550->base ); + err_ioremap: + uart_nullify ( uart ); + uart_put ( uart ); + err_alloc: + return rc; +} + +/** + * Remove devicetree device + * + * @v dt Devicetree device + */ +static void dwuart_remove ( struct dt_device *dt ) { + struct uart *uart = dt_get_drvdata ( dt ); + struct ns16550_uart *ns16550 = uart->priv; + + /* Unregister UART */ + uart_unregister ( uart ); + + /* Free UART */ + iounmap ( ns16550->base ); + uart_nullify ( uart ); + uart_put ( uart ); +} + +/** DesignWare UART compatible model identifiers */ +static const char * dwuart_ids[] = { + "snps,dw-apb-uart", +}; + +/** DesignWare UART devicetree driver */ +struct dt_driver dwuart_driver __dt_driver = { + .name = "dwuart", + .ids = dwuart_ids, + .id_count = ( sizeof ( dwuart_ids ) / sizeof ( dwuart_ids[0] ) ), + .probe = dwuart_probe, + .remove = dwuart_remove, +}; diff --git a/src/include/ipxe/errfile.h b/src/include/ipxe/errfile.h index 1a39af43f..9eaa784fc 100644 --- a/src/include/ipxe/errfile.h +++ b/src/include/ipxe/errfile.h @@ -110,6 +110,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #define ERRFILE_nvsvpd ( ERRFILE_DRIVER | 0x00140000 ) #define ERRFILE_uart ( ERRFILE_DRIVER | 0x00150000 ) #define ERRFILE_ns16550 ( ERRFILE_DRIVER | 0x00160000 ) +#define ERRFILE_dwuart ( ERRFILE_DRIVER | 0x00170000 ) #define ERRFILE_3c509 ( ERRFILE_DRIVER | 0x00200000 ) #define ERRFILE_bnx2 ( ERRFILE_DRIVER | 0x00210000 ) -- cgit v1.2.3-55-g7522 From 5d9f20bbd69408ad349e7ead0e72979f90c84e2d Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Mon, 23 Jun 2025 15:10:27 +0100 Subject: [dwuart] Add "ns16550a" compatible device ID Signed-off-by: Michael Brown --- src/drivers/uart/dwuart.c | 1 + 1 file changed, 1 insertion(+) (limited to 'src/drivers/uart') diff --git a/src/drivers/uart/dwuart.c b/src/drivers/uart/dwuart.c index bf83afd80..540cde057 100644 --- a/src/drivers/uart/dwuart.c +++ b/src/drivers/uart/dwuart.c @@ -108,6 +108,7 @@ static void dwuart_remove ( struct dt_device *dt ) { /** DesignWare UART compatible model identifiers */ static const char * dwuart_ids[] = { "snps,dw-apb-uart", + "ns16550a", }; /** DesignWare UART devicetree driver */ -- cgit v1.2.3-55-g7522 From 0ed1dea7f4f5c5b2b7cd0130eb49c2c5c0b0ea42 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Mon, 23 Jun 2025 22:37:32 +0100 Subject: [uart] Wait for 16550 UART to become idle before modifying LCR Some implementations of 16550-compatible UARTs (e.g. the DesignWare UART) are known to ignore writes to the line control register while the transmitter is active. Wait for the transmitter to become empty before attempting to write to the line control register. Signed-off-by: Michael Brown --- src/drivers/uart/ns16550.c | 3 +++ 1 file changed, 3 insertions(+) (limited to 'src/drivers/uart') diff --git a/src/drivers/uart/ns16550.c b/src/drivers/uart/ns16550.c index cf8c744c6..5455c68fb 100644 --- a/src/drivers/uart/ns16550.c +++ b/src/drivers/uart/ns16550.c @@ -131,6 +131,9 @@ static int ns16550_init ( struct uart *uart, unsigned int baud ) { if ( ns16550_read ( ns16550, NS16550_SCR ) != 0xae ) return -ENODEV; + /* Wait for UART to become idle before modifying LCR */ + ns16550_flush ( uart ); + /* Configure divisor and line control register, if applicable */ ns16550_write ( ns16550, NS16550_LCR, ( NS16550_LCR_8N1 | NS16550_LCR_DLAB ) ); -- cgit v1.2.3-55-g7522 From 9ada09c91966c42b49393307b326ff9b078ed448 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Mon, 23 Jun 2025 22:40:04 +0100 Subject: [dwuart] Read input clock frequency from the device tree The 16550 design includes a programmable 16-bit clock divider for an arbitrary input clock, requiring knowledge of the input clock frequency in order to calculate the divider value for a given baud rate. The 16550 UARTs in an x86 PC will always have a 1.8432 MHz input clock. Non-x86 systems may have other input clock frequencies. Define the input clock frequency as a property of a 16550 UART, and read the value from the device tree "clock-frequency" property. Signed-off-by: Michael Brown --- src/arch/x86/core/x86_uart.c | 1 + src/drivers/uart/dwuart.c | 8 ++++++++ src/drivers/uart/ns16550.c | 3 ++- src/include/ipxe/ns16550.h | 11 ++++++++--- 4 files changed, 19 insertions(+), 4 deletions(-) (limited to 'src/drivers/uart') diff --git a/src/arch/x86/core/x86_uart.c b/src/arch/x86/core/x86_uart.c index a1d643a58..03809ff9b 100644 --- a/src/arch/x86/core/x86_uart.c +++ b/src/arch/x86/core/x86_uart.c @@ -37,6 +37,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #define ISA_UART( NAME, BASE ) \ static struct ns16550_uart ns16550_ ## NAME = { \ .base = ( ( void * ) (BASE) ), \ + .clock = NS16550_CLK_DEFAULT, \ }; \ struct uart NAME = { \ .refcnt = REF_INIT ( ref_no_free ), \ diff --git a/src/drivers/uart/dwuart.c b/src/drivers/uart/dwuart.c index 540cde057..ce08e8ebf 100644 --- a/src/drivers/uart/dwuart.c +++ b/src/drivers/uart/dwuart.c @@ -46,6 +46,7 @@ static int dwuart_probe ( struct dt_device *dt, unsigned int offset ) { struct ns16550_uart *ns16550; struct uart *uart; uint32_t shift; + uint32_t clock; int rc; /* Allocate and initialise UART */ @@ -71,6 +72,13 @@ static int dwuart_probe ( struct dt_device *dt, unsigned int offset ) { shift = 0; ns16550->shift = shift; + /* Get clock rate */ + if ( ( rc = fdt_u32 ( &sysfdt, offset, "clock-frequency", + &clock ) ) != 0 ) { + clock = NS16550_CLK_DEFAULT; + } + ns16550->clock = clock; + /* Register UART */ if ( ( rc = uart_register ( uart ) ) != 0 ) goto err_register; diff --git a/src/drivers/uart/ns16550.c b/src/drivers/uart/ns16550.c index 5455c68fb..428771ab5 100644 --- a/src/drivers/uart/ns16550.c +++ b/src/drivers/uart/ns16550.c @@ -138,7 +138,8 @@ static int ns16550_init ( struct uart *uart, unsigned int baud ) { ns16550_write ( ns16550, NS16550_LCR, ( NS16550_LCR_8N1 | NS16550_LCR_DLAB ) ); if ( baud ) { - ns16550->divisor = ( NS16550_MAX_BAUD / baud ); + ns16550->divisor = ( ( ns16550->clock / baud ) / + NS16550_CLK_BIT ); dlm = ( ( ns16550->divisor >> 8 ) & 0xff ); dll = ( ( ns16550->divisor >> 0 ) & 0xff ); ns16550_write ( ns16550, NS16550_DLM, dlm ); diff --git a/src/include/ipxe/ns16550.h b/src/include/ipxe/ns16550.h index 6699205e2..693094866 100644 --- a/src/include/ipxe/ns16550.h +++ b/src/include/ipxe/ns16550.h @@ -73,19 +73,24 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); /** Divisor latch (most significant byte) */ #define NS16550_DLM 0x01 -/** Maximum baud rate */ -#define NS16550_MAX_BAUD 115200 - /** A 16550-compatible UART */ struct ns16550_uart { /** Register base address */ void *base; /** Register shift */ unsigned int shift; + /** Input clock frequency */ + unsigned int clock; /** Baud rate divisor */ uint16_t divisor; }; +/** Post-division clock cycles per data bit */ +#define NS16550_CLK_BIT 16 + +/** Default input clock rate (1.8432 MHz) */ +#define NS16550_CLK_DEFAULT 1843200 + #include /** Dummy COM1 UART for non-x86 platforms -- cgit v1.2.3-55-g7522 From 08d4d7fe9db04c8f44ac063d4b722fb3e8f3fffe Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Wed, 5 Nov 2025 12:16:22 +0000 Subject: [uart] Make baud rate a property of the UART Make the current baud rate (if specified) a property of the UART, to allow the default_serial_console() function to specify the default baud rate as well as the default UART device. Signed-off-by: Michael Brown --- src/core/gdbserial.c | 4 +++- src/core/serial.c | 14 ++++++++++---- src/core/uart.c | 3 +-- src/drivers/uart/ns16550.c | 7 +++---- src/include/ipxe/uart.h | 11 ++++++----- 5 files changed, 23 insertions(+), 16 deletions(-) (limited to 'src/drivers/uart') diff --git a/src/core/gdbserial.c b/src/core/gdbserial.c index 0f74e5fe0..61add5d47 100644 --- a/src/core/gdbserial.c +++ b/src/core/gdbserial.c @@ -95,7 +95,9 @@ struct gdb_transport * gdbserial_configure ( const char *name, return NULL; uart_get ( gdbserial_uart ); - if ( ( rc = uart_init ( gdbserial_uart, baud ) ) != 0 ) + gdbserial_uart->baud = baud; + + if ( ( rc = uart_init ( gdbserial_uart ) ) != 0 ) return NULL; return &serial_gdb_transport; diff --git a/src/core/serial.c b/src/core/serial.c index f3b33cd90..34ae4a17b 100644 --- a/src/core/serial.c +++ b/src/core/serial.c @@ -74,8 +74,14 @@ struct uart *serial_console = NULL; * @ret uart Serial console UART, or NULL */ static struct uart * serial_comconsole ( void ) { + struct uart *uart = COMCONSOLE; + unsigned int baud = COMSPEED; - return COMCONSOLE; + /* Set default baud rate, if applicable */ + if ( uart && baud ) + uart->baud = baud; + + return uart; } /** @@ -154,9 +160,9 @@ static void serial_init ( void ) { return; /* Initialise UART */ - if ( ( rc = uart_init ( uart, COMSPEED ) ) != 0 ) { - DBGC ( uart, "SERIAL could not initialise %s baud %d: %s\n", - uart->name, COMSPEED, strerror ( rc ) ); + if ( ( rc = uart_init ( uart ) ) != 0 ) { + DBGC ( uart, "SERIAL could not initialise %s: %s\n", + uart->name, strerror ( rc ) ); return; } diff --git a/src/core/uart.c b/src/core/uart.c index a645bd398..5bff9fdd5 100644 --- a/src/core/uart.c +++ b/src/core/uart.c @@ -49,8 +49,7 @@ static uint8_t null_uart_receive ( struct uart *uart __unused ) { return 0; } -static int null_uart_init ( struct uart *uart __unused, - unsigned int baud __unused ) { +static int null_uart_init ( struct uart *uart __unused ) { return 0; } diff --git a/src/drivers/uart/ns16550.c b/src/drivers/uart/ns16550.c index 428771ab5..f00e834b4 100644 --- a/src/drivers/uart/ns16550.c +++ b/src/drivers/uart/ns16550.c @@ -115,10 +115,9 @@ static void ns16550_flush ( struct uart *uart ) { * Initialise UART * * @v uart UART - * @v baud Baud rate, or zero to leave unchanged * @ret rc Return status code */ -static int ns16550_init ( struct uart *uart, unsigned int baud ) { +static int ns16550_init ( struct uart *uart ) { struct ns16550_uart *ns16550 = uart->priv; uint8_t dlm; uint8_t dll; @@ -137,8 +136,8 @@ static int ns16550_init ( struct uart *uart, unsigned int baud ) { /* Configure divisor and line control register, if applicable */ ns16550_write ( ns16550, NS16550_LCR, ( NS16550_LCR_8N1 | NS16550_LCR_DLAB ) ); - if ( baud ) { - ns16550->divisor = ( ( ns16550->clock / baud ) / + if ( uart->baud ) { + ns16550->divisor = ( ( ns16550->clock / uart->baud ) / NS16550_CLK_BIT ); dlm = ( ( ns16550->divisor >> 8 ) & 0xff ); dll = ( ( ns16550->divisor >> 0 ) & 0xff ); diff --git a/src/include/ipxe/uart.h b/src/include/ipxe/uart.h index 15adfa932..f2ecf3ce9 100644 --- a/src/include/ipxe/uart.h +++ b/src/include/ipxe/uart.h @@ -22,6 +22,9 @@ struct uart { /** List of registered UARTs */ struct list_head list; + /** Baud rate (if specified) */ + unsigned int baud; + /** UART operations */ struct uart_operations *op; /** Driver-private data */ @@ -56,10 +59,9 @@ struct uart_operations { * Initialise UART * * @v uart UART - * @v baud Baud rate, or zero to leave unchanged * @ret rc Return status code */ - int ( * init ) ( struct uart *uart, unsigned int baud ); + int ( * init ) ( struct uart *uart ); /** * Flush transmitted data * @@ -109,13 +111,12 @@ uart_receive ( struct uart *uart ) { * Initialise UART * * @v uart UART - * @v baud Baud rate, or zero to leave unchanged * @ret rc Return status code */ static inline __attribute__ (( always_inline )) int -uart_init ( struct uart *uart, unsigned int baud ) { +uart_init ( struct uart *uart ) { - return uart->op->init ( uart, baud ); + return uart->op->init ( uart ); } /** -- cgit v1.2.3-55-g7522