]> git.ipfire.org Git - thirdparty/ipxe.git/commitdiff
[dwuart] Read input clock frequency from the device tree
authorMichael Brown <mcb30@ipxe.org>
Mon, 23 Jun 2025 21:40:04 +0000 (22:40 +0100)
committerMichael Brown <mcb30@ipxe.org>
Mon, 23 Jun 2025 21:56:38 +0000 (22:56 +0100)
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>
src/arch/x86/core/x86_uart.c
src/drivers/uart/dwuart.c
src/drivers/uart/ns16550.c
src/include/ipxe/ns16550.h

index a1d643a58f752893a1006584a521246762b87bd3..03809ff9b3190783720d3e88c10da3d45b4ae870 100644 (file)
@@ -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 ),                     \
index 540cde057d3fa938e3f91a442459b8f046bed92e..ce08e8ebf9de48f9aab51a5ca1ffe7dde7f81ba3 100644 (file)
@@ -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;
index 5455c68fb9cc8fcc91b563217e179c21fe85494e..428771ab508a269437b13a9e5058041b9d10d264 100644 (file)
@@ -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 );
index 6699205e2a1fcbffbef4a12d157ea7c357cf2f6b..693094866a73ce6b6411c0e97a5e1826efa05ea3 100644 (file)
@@ -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 <bits/ns16550.h>
 
 /** Dummy COM1 UART for non-x86 platforms