[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 <mcb30@ipxe.org>
This commit is contained in:
Michael Brown
2025-06-23 22:40:04 +01:00
parent 0ed1dea7f4
commit 9ada09c919
4 changed files with 19 additions and 4 deletions

View File

@@ -37,6 +37,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
#define ISA_UART( NAME, BASE ) \ #define ISA_UART( NAME, BASE ) \
static struct ns16550_uart ns16550_ ## NAME = { \ static struct ns16550_uart ns16550_ ## NAME = { \
.base = ( ( void * ) (BASE) ), \ .base = ( ( void * ) (BASE) ), \
.clock = NS16550_CLK_DEFAULT, \
}; \ }; \
struct uart NAME = { \ struct uart NAME = { \
.refcnt = REF_INIT ( ref_no_free ), \ .refcnt = REF_INIT ( ref_no_free ), \

View File

@@ -46,6 +46,7 @@ static int dwuart_probe ( struct dt_device *dt, unsigned int offset ) {
struct ns16550_uart *ns16550; struct ns16550_uart *ns16550;
struct uart *uart; struct uart *uart;
uint32_t shift; uint32_t shift;
uint32_t clock;
int rc; int rc;
/* Allocate and initialise UART */ /* Allocate and initialise UART */
@@ -71,6 +72,13 @@ static int dwuart_probe ( struct dt_device *dt, unsigned int offset ) {
shift = 0; shift = 0;
ns16550->shift = shift; 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 */ /* Register UART */
if ( ( rc = uart_register ( uart ) ) != 0 ) if ( ( rc = uart_register ( uart ) ) != 0 )
goto err_register; goto err_register;

View File

@@ -138,7 +138,8 @@ static int ns16550_init ( struct uart *uart, unsigned int baud ) {
ns16550_write ( ns16550, NS16550_LCR, ns16550_write ( ns16550, NS16550_LCR,
( NS16550_LCR_8N1 | NS16550_LCR_DLAB ) ); ( NS16550_LCR_8N1 | NS16550_LCR_DLAB ) );
if ( baud ) { if ( baud ) {
ns16550->divisor = ( NS16550_MAX_BAUD / baud ); ns16550->divisor = ( ( ns16550->clock / baud ) /
NS16550_CLK_BIT );
dlm = ( ( ns16550->divisor >> 8 ) & 0xff ); dlm = ( ( ns16550->divisor >> 8 ) & 0xff );
dll = ( ( ns16550->divisor >> 0 ) & 0xff ); dll = ( ( ns16550->divisor >> 0 ) & 0xff );
ns16550_write ( ns16550, NS16550_DLM, dlm ); ns16550_write ( ns16550, NS16550_DLM, dlm );

View File

@@ -73,19 +73,24 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
/** Divisor latch (most significant byte) */ /** Divisor latch (most significant byte) */
#define NS16550_DLM 0x01 #define NS16550_DLM 0x01
/** Maximum baud rate */
#define NS16550_MAX_BAUD 115200
/** A 16550-compatible UART */ /** A 16550-compatible UART */
struct ns16550_uart { struct ns16550_uart {
/** Register base address */ /** Register base address */
void *base; void *base;
/** Register shift */ /** Register shift */
unsigned int shift; unsigned int shift;
/** Input clock frequency */
unsigned int clock;
/** Baud rate divisor */ /** Baud rate divisor */
uint16_t 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 <bits/ns16550.h> #include <bits/ns16550.h>
/** Dummy COM1 UART for non-x86 platforms /** Dummy COM1 UART for non-x86 platforms