On 10/08/2026 15:01, Ferass El Hafidi wrote:
On Thu, 06 Aug 2026 10:18, "Lucas Tanure via groups.io" <[email protected]> wrote:
The S4-generation UART (S4, T7, ...) derives its baud rate from the
24 MHz crystal divided by 2, selected via the XTAL_DIV2 bit in the
baud rate register, while older SoCs divide the crystal by 3. Add the
"amlogic,meson-s4-uart" compatible with driver data selecting the
div-by-2 scheme, matching the Linux driver (meson_s4_uart_data) and
the configuration the vendor BL2 programs on T7 hardware.

Older SoCs keep the existing div-by-3 behaviour: Linux also uses
div-by-2 on G12A, but both modes work there and switching would risk
regressing boards that have shipped with div-by-3 for years.

Also drain the transmitter before changing the baud rate so in-flight
characters are not garbled, as the Linux driver does.

Assisted-by: Claude:claude-fable-5

As I understand it, LLM-assisted patches are not welcome in U-Boot right
now: https://lore.kernel.org/u-boot/20260515220758.GM1858239@bill-the-cat/

AFAIK there isn't an official AI policy yet, but it seems the concensus
on that matter is currently "please don't". Sorry.

Ok, I will drop this line.


Signed-off-by: Lucas Tanure <[email protected]>
---
drivers/serial/serial_meson.c | 51 ++++++++++++++++++++++++++---------
1 file changed, 38 insertions(+), 13 deletions(-)

diff --git a/drivers/serial/serial_meson.c b/drivers/serial/ serial_meson.c
index cc71381f87e..d909fedcfc4 100644
--- a/drivers/serial/serial_meson.c
+++ b/drivers/serial/serial_meson.c
@@ -62,26 +62,49 @@ struct meson_serial_plat {
#define AML_UART_REG5_USE_NEW_BAUD    BIT(23) /* default 1 (use new baud rate register) */
#define AML_UART_REG5_BAUD_MASK        0x7fffff

+/* Driver data flags */
+#define MESON_UART_XTAL_DIV2    BIT(0)
+
#if CONFIG_IS_ENABLED(DM_SERIAL)
-static u32 meson_calc_baud_divisor(ulong src_rate, u32 baud)
+static u32 meson_uart_xtal_div(struct udevice *dev)
+{
+    /*
+     * S4-generation UARTs (S4, T7, ...) derive the baud rate from the
+     * crystal divided by 2, older ones divide by 3.
+     */
+    return (dev_get_driver_data(dev) & MESON_UART_XTAL_DIV2) ? 2 : 3;
+}
+
+static u32 meson_calc_baud_divisor(struct udevice *dev, ulong src_rate, u32 baud)
{
    /*
     * Usually src_rate is 24 MHz (from crystal) as clock source for serial -     * device. Since 8 Mb/s is the maximum supported baud rate, use div by 3 -     * to derive baud rate. This choice is used also in meson_serial_setbrg.
+     * device. Since 8 Mb/s is the maximum supported baud rate, use a
+     * divided crystal to derive the baud rate. This choice is used also in
+     * meson_serial_setbrg.
     */
-    return DIV_ROUND_CLOSEST(src_rate / 3, baud) - 1;
+    return DIV_ROUND_CLOSEST(src_rate / meson_uart_xtal_div(dev), baud) - 1;
}

-static void meson_serial_set_baud(struct meson_uart *uart, ulong src_rate, u32 baud) +static void meson_serial_set_baud(struct udevice *dev, struct meson_uart *uart,
+                  ulong src_rate, u32 baud)
{
    /*
-     * Set crystal divided by 3 (regardless of device tree clock property) +     * Set the divided crystal (regardless of device tree clock property)
     * as clock source and the corresponding divisor to approximate baud
     */
-    u32 divisor = meson_calc_baud_divisor(src_rate, baud);
+    u32 divisor = meson_calc_baud_divisor(dev, src_rate, baud);
    u32 val = AML_UART_REG5_USE_XTAL_CLK | AML_UART_REG5_USE_NEW_BAUD |
        (divisor & AML_UART_REG5_BAUD_MASK);
+
+    if (meson_uart_xtal_div(dev) == 2)
+        val |= AML_UART_REG5_XTAL_DIV2;
+
+    /* Drain the transmitter before changing the baud rate */
+    while ((readl(&uart->status) & (AML_UART_TX_EMPTY | AML_UART_XMIT_BUSY))
+           != AML_UART_TX_EMPTY)
+        ;
+
    writel(val, &uart->reg5);
}

@@ -109,7 +132,7 @@ static int meson_serial_probe(struct udevice *dev)
        return ret;
    ulong rate = clk_get_rate(&per_clk);

-    meson_serial_set_baud(uart, rate, CONFIG_BAUDRATE);
+    meson_serial_set_baud(dev, uart, rate, CONFIG_BAUDRATE);
    meson_serial_init(uart);

    return 0;
@@ -165,8 +188,9 @@ static int meson_serial_setbrg(struct udevice *dev, const int baud)
{
    /*
     * Change device baud rate if baud is reasonable (considering a 23 bit
-     * counter with an 8 MHz clock input) and the actual baud
-     * rate is within 2% of the requested value (2% is arbitrary).
+     * counter with an 8 MHz, or 12 MHz for XTAL_DIV2 devices, clock input) +     * and the actual baud rate is within 2% of the requested value (2% is
+     * arbitrary).
     */
    if (baud < 1 || baud > 8000000)
        return -EINVAL;
@@ -179,14 +203,14 @@ static int meson_serial_setbrg(struct udevice *dev, const int baud)
    if (ret)
        return ret;
    ulong rate = clk_get_rate(&per_clk);
-    u32 divisor = meson_calc_baud_divisor(rate, baud);
-    u32 calc_baud = (rate / 3) / (divisor + 1);
+    u32 divisor = meson_calc_baud_divisor(dev, rate, baud);
+    u32 calc_baud = (rate / meson_uart_xtal_div(dev)) / (divisor + 1);
    u32 calc_err = baud > calc_baud ? baud - calc_baud : calc_baud - baud;

    if (((calc_err * 100) / baud) > 2)
        return -EINVAL;

-    meson_serial_set_baud(uart, rate, baud);
+    meson_serial_set_baud(dev, uart, rate, baud);

    return 0;
}
@@ -244,6 +268,7 @@ static const struct udevice_id meson_serial_ids[] = {
    { .compatible = "amlogic,meson-uart" },
    { .compatible = "amlogic,meson-gx-uart" },
    { .compatible = "amlogic,meson-a1-uart" },
+    { .compatible = "amlogic,meson-s4-uart", .data = MESON_UART_XTAL_DIV2 },
    { }
};

--
2.55.0

Best regards,
Ferass

Reply via email to