[PATCH u-boot-marvell 0/5] Support higher baudrates on Armada 3720 UART

This series adds support for more baudrates on Armada 3720 UART (up to 6 MBaud).
Marek Behún (1): clk: armada-37xx: Set DM_FLAG_PRE_RELOC
Pali Rohár (4): serial: a37xx: Fix parent clock rate value and divider calculation serial: a37xx: Use TBG as parent clock serial: a37xx: Switch to XTAL clock when booting Linux kernel arm: mvebu: a37xx: Enable more baudrates
drivers/clk/mvebu/armada-37xx-periph.c | 1 + drivers/clk/mvebu/armada-37xx-tbg.c | 1 + drivers/serial/serial_mvebu_a3700.c | 179 ++++++++++++++++++++++++- include/configs/mvebu_armada-37xx.h | 9 +- include/configs/turris_mox.h | 9 +- 5 files changed, 190 insertions(+), 9 deletions(-)

From: Pali Rohár pali@kernel.org
UART parent clock is by default the platform's xtal clock, which is 25 MHz.
The value defined in the driver, though, is 25.8048 MHz. This is a hack for the suboptimal divisor calculation Divisor = UART clock / (16 * baudrate) which does not use rounding division, resulting in a suboptimal value for divisor if the correct parent clock rate was used.
Change the code for divisor calculation to round to closest value, i.e. Divisor = Round(UART clock / (16 * baudrate)) and change the parent clock rate value to that returned by get_ref_clk().
This makes A3720 UART stable at standard UART baudrates between 1800 and 230400.
Signed-off-by: Pali Rohár pali@kernel.org Reviewed-by: Marek Behún marek.behun@nic.cz --- drivers/serial/serial_mvebu_a3700.c | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-)
diff --git a/drivers/serial/serial_mvebu_a3700.c b/drivers/serial/serial_mvebu_a3700.c index 8f404879a5..9e7e479f80 100644 --- a/drivers/serial/serial_mvebu_a3700.c +++ b/drivers/serial/serial_mvebu_a3700.c @@ -7,6 +7,7 @@ #include <dm.h> #include <serial.h> #include <asm/io.h> +#include <asm/arch/cpu.h>
struct mvebu_plat { void __iomem *base; @@ -29,8 +30,6 @@ struct mvebu_plat { #define UART_CTRL_RXFIFO_RESET 0x4000 #define UART_CTRL_TXFIFO_RESET 0x8000
-#define CONFIG_UART_BASE_CLOCK 25804800 - static int mvebu_serial_putc(struct udevice *dev, const char ch) { struct mvebu_plat *plat = dev_get_plat(dev); @@ -75,12 +74,15 @@ static int mvebu_serial_setbrg(struct udevice *dev, int baudrate) { struct mvebu_plat *plat = dev_get_plat(dev); void __iomem *base = plat->base; + u32 parent_rate, divider;
/* * Calculate divider * baudrate = clock / 16 / divider */ - writel(CONFIG_UART_BASE_CLOCK / baudrate / 16, base + UART_BAUD_REG); + parent_rate = get_ref_clk() * 1000000; + divider = DIV_ROUND_CLOSEST(parent_rate, baudrate * 16); + writel(divider, base + UART_BAUD_REG);
/* * Set Programmable Oversampling Stack to 0, @@ -144,6 +146,7 @@ U_BOOT_DRIVER(serial_mvebu) = { static inline void _debug_uart_init(void) { void __iomem *base = (void __iomem *)CONFIG_DEBUG_UART_BASE; + u32 baudrate, parent_rate, divider;
/* reset FIFOs */ writel(UART_CTRL_RXFIFO_RESET | UART_CTRL_TXFIFO_RESET, @@ -156,7 +159,10 @@ static inline void _debug_uart_init(void) * Calculate divider * baudrate = clock / 16 / divider */ - writel(CONFIG_UART_BASE_CLOCK / 115200 / 16, base + UART_BAUD_REG); + baudrate = 115200; + parent_rate = get_ref_clk() * 1000000; + divider = DIV_ROUND_CLOSEST(parent_rate, baudrate * 16); + writel(divider, base + UART_BAUD_REG);
/* * Set Programmable Oversampling Stack to 0,

On 25.05.21 19:42, Marek Behún wrote:
From: Pali Rohár pali@kernel.org
UART parent clock is by default the platform's xtal clock, which is 25 MHz.
The value defined in the driver, though, is 25.8048 MHz. This is a hack for the suboptimal divisor calculation Divisor = UART clock / (16 * baudrate) which does not use rounding division, resulting in a suboptimal value for divisor if the correct parent clock rate was used.
Change the code for divisor calculation to round to closest value, i.e. Divisor = Round(UART clock / (16 * baudrate)) and change the parent clock rate value to that returned by get_ref_clk().
This makes A3720 UART stable at standard UART baudrates between 1800 and 230400.
Signed-off-by: Pali Rohár pali@kernel.org Reviewed-by: Marek Behún marek.behun@nic.cz
Reviewed-by: Stefan Roese sr@denx.de
Thanks, Stefan
drivers/serial/serial_mvebu_a3700.c | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-)
diff --git a/drivers/serial/serial_mvebu_a3700.c b/drivers/serial/serial_mvebu_a3700.c index 8f404879a5..9e7e479f80 100644 --- a/drivers/serial/serial_mvebu_a3700.c +++ b/drivers/serial/serial_mvebu_a3700.c @@ -7,6 +7,7 @@ #include <dm.h> #include <serial.h> #include <asm/io.h> +#include <asm/arch/cpu.h>
struct mvebu_plat { void __iomem *base; @@ -29,8 +30,6 @@ struct mvebu_plat { #define UART_CTRL_RXFIFO_RESET 0x4000 #define UART_CTRL_TXFIFO_RESET 0x8000
-#define CONFIG_UART_BASE_CLOCK 25804800
- static int mvebu_serial_putc(struct udevice *dev, const char ch) { struct mvebu_plat *plat = dev_get_plat(dev);
@@ -75,12 +74,15 @@ static int mvebu_serial_setbrg(struct udevice *dev, int baudrate) { struct mvebu_plat *plat = dev_get_plat(dev); void __iomem *base = plat->base;
u32 parent_rate, divider;
/*
- Calculate divider
- baudrate = clock / 16 / divider
*/
- writel(CONFIG_UART_BASE_CLOCK / baudrate / 16, base + UART_BAUD_REG);
parent_rate = get_ref_clk() * 1000000;
divider = DIV_ROUND_CLOSEST(parent_rate, baudrate * 16);
writel(divider, base + UART_BAUD_REG);
/*
- Set Programmable Oversampling Stack to 0,
@@ -144,6 +146,7 @@ U_BOOT_DRIVER(serial_mvebu) = { static inline void _debug_uart_init(void) { void __iomem *base = (void __iomem *)CONFIG_DEBUG_UART_BASE;
u32 baudrate, parent_rate, divider;
/* reset FIFOs */ writel(UART_CTRL_RXFIFO_RESET | UART_CTRL_TXFIFO_RESET,
@@ -156,7 +159,10 @@ static inline void _debug_uart_init(void) * Calculate divider * baudrate = clock / 16 / divider */
- writel(CONFIG_UART_BASE_CLOCK / 115200 / 16, base + UART_BAUD_REG);
baudrate = 115200;
parent_rate = get_ref_clk() * 1000000;
divider = DIV_ROUND_CLOSEST(parent_rate, baudrate * 16);
writel(divider, base + UART_BAUD_REG);
/*
- Set Programmable Oversampling Stack to 0,
Viele Grüße, Stefan

Setting DM_FLAG_PRE_RELOC for Armada 3720 clock drivers (TBG and peripheral clocks) makes it possible for serial driver to retrieve clock rates via clk API.
Signed-off-by: Marek Behún marek.behun@nic.cz --- drivers/clk/mvebu/armada-37xx-periph.c | 1 + drivers/clk/mvebu/armada-37xx-tbg.c | 1 + 2 files changed, 2 insertions(+)
diff --git a/drivers/clk/mvebu/armada-37xx-periph.c b/drivers/clk/mvebu/armada-37xx-periph.c index b0f47c33b3..3b767d7060 100644 --- a/drivers/clk/mvebu/armada-37xx-periph.c +++ b/drivers/clk/mvebu/armada-37xx-periph.c @@ -626,4 +626,5 @@ U_BOOT_DRIVER(armada_37xx_periph_clk) = { .ops = &armada_37xx_periph_clk_ops, .priv_auto = sizeof(struct a37xx_periphclk), .probe = armada_37xx_periph_clk_probe, + .flags = DM_FLAG_PRE_RELOC, }; diff --git a/drivers/clk/mvebu/armada-37xx-tbg.c b/drivers/clk/mvebu/armada-37xx-tbg.c index b1c0852e89..054aff5e6a 100644 --- a/drivers/clk/mvebu/armada-37xx-tbg.c +++ b/drivers/clk/mvebu/armada-37xx-tbg.c @@ -152,4 +152,5 @@ U_BOOT_DRIVER(armada_37xx_tbg_clk) = { .ops = &armada_37xx_tbg_clk_ops, .priv_auto = sizeof(struct a37xx_tbgclk), .probe = armada_37xx_tbg_clk_probe, + .flags = DM_FLAG_PRE_RELOC, };

On 25.05.21 19:42, Marek Behún wrote:
Setting DM_FLAG_PRE_RELOC for Armada 3720 clock drivers (TBG and peripheral clocks) makes it possible for serial driver to retrieve clock rates via clk API.
Signed-off-by: Marek Behún marek.behun@nic.cz
Reviewed-by: Stefan Roese sr@denx.de
Thanks, Stefan
drivers/clk/mvebu/armada-37xx-periph.c | 1 + drivers/clk/mvebu/armada-37xx-tbg.c | 1 + 2 files changed, 2 insertions(+)
diff --git a/drivers/clk/mvebu/armada-37xx-periph.c b/drivers/clk/mvebu/armada-37xx-periph.c index b0f47c33b3..3b767d7060 100644 --- a/drivers/clk/mvebu/armada-37xx-periph.c +++ b/drivers/clk/mvebu/armada-37xx-periph.c @@ -626,4 +626,5 @@ U_BOOT_DRIVER(armada_37xx_periph_clk) = { .ops = &armada_37xx_periph_clk_ops, .priv_auto = sizeof(struct a37xx_periphclk), .probe = armada_37xx_periph_clk_probe,
- .flags = DM_FLAG_PRE_RELOC, };
diff --git a/drivers/clk/mvebu/armada-37xx-tbg.c b/drivers/clk/mvebu/armada-37xx-tbg.c index b1c0852e89..054aff5e6a 100644 --- a/drivers/clk/mvebu/armada-37xx-tbg.c +++ b/drivers/clk/mvebu/armada-37xx-tbg.c @@ -152,4 +152,5 @@ U_BOOT_DRIVER(armada_37xx_tbg_clk) = { .ops = &armada_37xx_tbg_clk_ops, .priv_auto = sizeof(struct a37xx_tbgclk), .probe = armada_37xx_tbg_clk_probe,
- .flags = DM_FLAG_PRE_RELOC, };
Viele Grüße, Stefan

From: Pali Rohár pali@kernel.org
Using TBG clock as parent clock for UART allows us using higher baudrates than 230400.
Turris MOX with external FT232RL USB-UART works fine up to 3 MBaud (which is maximum for this USB-UART controller), while EspressoBIN with integrated pl2303 USB-UART also works fine up to 6 MBaud.
Slower baudrates with TBG as a parent clock can be achieved by increasing TBG dividers and oversampling divider. When using the slowest TBG clock, minimal working baudrate is 300.
Signed-off-by: Pali Rohár pali@kernel.org Signed-off-by: Marek Behún marek.behun@nic.cz --- drivers/serial/serial_mvebu_a3700.c | 106 ++++++++++++++++++++++++++-- 1 file changed, 101 insertions(+), 5 deletions(-)
diff --git a/drivers/serial/serial_mvebu_a3700.c b/drivers/serial/serial_mvebu_a3700.c index 9e7e479f80..ba2ac5917f 100644 --- a/drivers/serial/serial_mvebu_a3700.c +++ b/drivers/serial/serial_mvebu_a3700.c @@ -4,6 +4,7 @@ */
#include <common.h> +#include <clk.h> #include <dm.h> #include <serial.h> #include <asm/io.h> @@ -11,6 +12,8 @@
struct mvebu_plat { void __iomem *base; + ulong tbg_rate; + u8 tbg_idx; };
/* @@ -74,21 +77,70 @@ static int mvebu_serial_setbrg(struct udevice *dev, int baudrate) { struct mvebu_plat *plat = dev_get_plat(dev); void __iomem *base = plat->base; - u32 parent_rate, divider; + u32 divider, d1, d2; + u32 oversampling;
/* * Calculate divider * baudrate = clock / 16 / divider */ - parent_rate = get_ref_clk() * 1000000; - divider = DIV_ROUND_CLOSEST(parent_rate, baudrate * 16); - writel(divider, base + UART_BAUD_REG); + d1 = d2 = 1; + divider = DIV_ROUND_CLOSEST(plat->tbg_rate, baudrate * 16 * d1 * d2);
/* * Set Programmable Oversampling Stack to 0, * UART defaults to 16x scheme */ - writel(0, base + UART_POSSR_REG); + oversampling = 0; + + if (divider < 1) + divider = 1; + else if (divider > 1023) { + /* + * If divider is too high for selected baudrate then set + * divider d1 to the maximal value 6. + */ + d1 = 6; + divider = DIV_ROUND_CLOSEST(plat->tbg_rate, + baudrate * 16 * d1 * d2); + if (divider < 1) + divider = 1; + else if (divider > 1023) { + /* + * If divider is still too high then set also divider + * d2 to the maximal value 6. + */ + d2 = 6; + divider = DIV_ROUND_CLOSEST(plat->tbg_rate, + baudrate * 16 * d1 * d2); + if (divider < 1) + divider = 1; + else if (divider > 1023) { + /* + * And if divider is still to high then + * use oversampling with maximal factor 63. + */ + oversampling = (63 << 0) | (63 << 8) | + (63 << 16) | (63 << 24); + divider = DIV_ROUND_CLOSEST(plat->tbg_rate, + baudrate * 63 * d1 * d2); + if (divider < 1) + divider = 1; + else if (divider > 1023) + divider = 1023; + } + } + } + + divider |= BIT(19); /* Do not use XTAL as a base clock */ + divider |= d1 << 15; /* Set d1 divider */ + divider |= d2 << 12; /* Set d2 divider */ + divider |= plat->tbg_idx << 10; /* Use selected TBG as a base clock */ + + while (!(readl(base + UART_STATUS_REG) & UART_STATUS_TX_EMPTY)) + ; + writel(divider, base + UART_BAUD_REG); + writel(oversampling, base + UART_POSSR_REG);
return 0; } @@ -97,6 +149,50 @@ static int mvebu_serial_probe(struct udevice *dev) { struct mvebu_plat *plat = dev_get_plat(dev); void __iomem *base = plat->base; + struct udevice *nb_clk; + ofnode nb_clk_node; + int i, res; + + nb_clk_node = ofnode_by_compatible(ofnode_null(), + "marvell,armada-3700-periph-clock-nb"); + if (!ofnode_valid(nb_clk_node)) { + printf("%s: NB periph clock node not available\n", __func__); + return -ENODEV; + } + + res = device_get_global_by_ofnode(nb_clk_node, &nb_clk); + if (res) { + printf("%s: Cannot get NB periph clock\n", __func__); + return res; + } + + /* + * Choose the TBG clock with lowest frequency which allows to configure + * UART also at lower baudrates. + */ + for (i = 0; i < 4; i++) { + struct clk clk; + ulong rate; + + res = clk_get_by_index_nodev(nb_clk_node, i, &clk); + if (res) { + printf("%s: Cannot get TBG clock %i: %i\n", __func__, + i, res); + return -ENODEV; + } + + rate = clk_get_rate(&clk); + if (!rate || IS_ERR_VALUE(rate)) { + printf("%s: Cannot get rate for TBG clock %i\n", + __func__, i); + return -EINVAL; + } + + if (!i || plat->tbg_rate > rate) { + plat->tbg_rate = rate; + plat->tbg_idx = i; + } + }
/* reset FIFOs */ writel(UART_CTRL_RXFIFO_RESET | UART_CTRL_TXFIFO_RESET,

On 25.05.21 19:42, Marek Behún wrote:
From: Pali Rohár pali@kernel.org
Using TBG clock as parent clock for UART allows us using higher baudrates than 230400.
Turris MOX with external FT232RL USB-UART works fine up to 3 MBaud (which is maximum for this USB-UART controller), while EspressoBIN with integrated pl2303 USB-UART also works fine up to 6 MBaud.
Slower baudrates with TBG as a parent clock can be achieved by increasing TBG dividers and oversampling divider. When using the slowest TBG clock, minimal working baudrate is 300.
Signed-off-by: Pali Rohár pali@kernel.org Signed-off-by: Marek Behún marek.behun@nic.cz
Reviewed-by: Stefan Roese sr@denx.de
Thanks, Stefan
drivers/serial/serial_mvebu_a3700.c | 106 ++++++++++++++++++++++++++-- 1 file changed, 101 insertions(+), 5 deletions(-)
diff --git a/drivers/serial/serial_mvebu_a3700.c b/drivers/serial/serial_mvebu_a3700.c index 9e7e479f80..ba2ac5917f 100644 --- a/drivers/serial/serial_mvebu_a3700.c +++ b/drivers/serial/serial_mvebu_a3700.c @@ -4,6 +4,7 @@ */
#include <common.h> +#include <clk.h> #include <dm.h> #include <serial.h> #include <asm/io.h> @@ -11,6 +12,8 @@
struct mvebu_plat { void __iomem *base;
ulong tbg_rate;
u8 tbg_idx; };
/*
@@ -74,21 +77,70 @@ static int mvebu_serial_setbrg(struct udevice *dev, int baudrate) { struct mvebu_plat *plat = dev_get_plat(dev); void __iomem *base = plat->base;
- u32 parent_rate, divider;
u32 divider, d1, d2;
u32 oversampling;
/*
- Calculate divider
- baudrate = clock / 16 / divider
*/
- parent_rate = get_ref_clk() * 1000000;
- divider = DIV_ROUND_CLOSEST(parent_rate, baudrate * 16);
- writel(divider, base + UART_BAUD_REG);
d1 = d2 = 1;
divider = DIV_ROUND_CLOSEST(plat->tbg_rate, baudrate * 16 * d1 * d2);
/*
- Set Programmable Oversampling Stack to 0,
- UART defaults to 16x scheme
*/
- writel(0, base + UART_POSSR_REG);
oversampling = 0;
if (divider < 1)
divider = 1;
else if (divider > 1023) {
/*
* If divider is too high for selected baudrate then set
* divider d1 to the maximal value 6.
*/
d1 = 6;
divider = DIV_ROUND_CLOSEST(plat->tbg_rate,
baudrate * 16 * d1 * d2);
if (divider < 1)
divider = 1;
else if (divider > 1023) {
/*
* If divider is still too high then set also divider
* d2 to the maximal value 6.
*/
d2 = 6;
divider = DIV_ROUND_CLOSEST(plat->tbg_rate,
baudrate * 16 * d1 * d2);
if (divider < 1)
divider = 1;
else if (divider > 1023) {
/*
* And if divider is still to high then
* use oversampling with maximal factor 63.
*/
oversampling = (63 << 0) | (63 << 8) |
(63 << 16) | (63 << 24);
divider = DIV_ROUND_CLOSEST(plat->tbg_rate,
baudrate * 63 * d1 * d2);
if (divider < 1)
divider = 1;
else if (divider > 1023)
divider = 1023;
}
}
}
divider |= BIT(19); /* Do not use XTAL as a base clock */
divider |= d1 << 15; /* Set d1 divider */
divider |= d2 << 12; /* Set d2 divider */
divider |= plat->tbg_idx << 10; /* Use selected TBG as a base clock */
while (!(readl(base + UART_STATUS_REG) & UART_STATUS_TX_EMPTY))
;
writel(divider, base + UART_BAUD_REG);
writel(oversampling, base + UART_POSSR_REG);
return 0; }
@@ -97,6 +149,50 @@ static int mvebu_serial_probe(struct udevice *dev) { struct mvebu_plat *plat = dev_get_plat(dev); void __iomem *base = plat->base;
struct udevice *nb_clk;
ofnode nb_clk_node;
int i, res;
nb_clk_node = ofnode_by_compatible(ofnode_null(),
"marvell,armada-3700-periph-clock-nb");
if (!ofnode_valid(nb_clk_node)) {
printf("%s: NB periph clock node not available\n", __func__);
return -ENODEV;
}
res = device_get_global_by_ofnode(nb_clk_node, &nb_clk);
if (res) {
printf("%s: Cannot get NB periph clock\n", __func__);
return res;
}
/*
* Choose the TBG clock with lowest frequency which allows to configure
* UART also at lower baudrates.
*/
for (i = 0; i < 4; i++) {
struct clk clk;
ulong rate;
res = clk_get_by_index_nodev(nb_clk_node, i, &clk);
if (res) {
printf("%s: Cannot get TBG clock %i: %i\n", __func__,
i, res);
return -ENODEV;
}
rate = clk_get_rate(&clk);
if (!rate || IS_ERR_VALUE(rate)) {
printf("%s: Cannot get rate for TBG clock %i\n",
__func__, i);
return -EINVAL;
}
if (!i || plat->tbg_rate > rate) {
plat->tbg_rate = rate;
plat->tbg_idx = i;
}
}
/* reset FIFOs */ writel(UART_CTRL_RXFIFO_RESET | UART_CTRL_TXFIFO_RESET,
Viele Grüße, Stefan

From: Pali Rohár pali@kernel.org
Unfortunately the UART driver in current Linux for Armada 3700 expects UART's parent clock to be XTAL and calculats baudrate divisor according to XTAL clock. Therefore we must switch back to XTAL clock before booting kernel.
Implement .remove method for this driver with DM_FLAG_OS_PREPARE flag set.
If current baudrate is unsuitable for XTAL clock then we do not change anything. This can only happen if the user either configured unsupported settings or knows what they are doing and has kernel patches which allow usage of non-XTAL parent clock.
Signed-off-by: Pali Rohár pali@kernel.org Reviewed-by: Marek Behún marek.behun@nic.cz --- drivers/serial/serial_mvebu_a3700.c | 67 +++++++++++++++++++++++++++++ 1 file changed, 67 insertions(+)
diff --git a/drivers/serial/serial_mvebu_a3700.c b/drivers/serial/serial_mvebu_a3700.c index ba2ac5917f..c7e66fef87 100644 --- a/drivers/serial/serial_mvebu_a3700.c +++ b/drivers/serial/serial_mvebu_a3700.c @@ -204,6 +204,71 @@ static int mvebu_serial_probe(struct udevice *dev) return 0; }
+static int mvebu_serial_remove(struct udevice *dev) +{ + struct mvebu_plat *plat = dev_get_plat(dev); + void __iomem *base = plat->base; + ulong new_parent_rate, parent_rate; + u32 new_divider, divider; + u32 new_oversampling; + u32 oversampling; + u32 d1, d2; + + /* + * Switch UART base clock back to XTAL because older Linux kernel + * expects it. Otherwise it does not calculate UART divisor correctly + * and therefore UART does not work in kernel. + */ + divider = readl(base + UART_BAUD_REG); + if (!(divider & BIT(19))) /* UART already uses XTAL */ + return 0; + + /* Read current divisors settings */ + d1 = (divider >> 15) & 7; + d2 = (divider >> 12) & 7; + parent_rate = plat->tbg_rate; + divider &= 1023; + oversampling = readl(base + UART_POSSR_REG) & 63; + if (!oversampling) + oversampling = 16; + + /* Calculate new divisor against XTAL clock without changing baudrate */ + new_oversampling = 0; + new_parent_rate = get_ref_clk() * 1000000; + new_divider = DIV_ROUND_CLOSEST(new_parent_rate * divider * d1 * d2 * + oversampling, parent_rate * 16); + + /* + * UART does not work reliably when XTAL divisor is smaller than 4. + * In this case we do not switch UART parent to XTAL. User either + * configured unsupported settings or has newer kernel with patches + * which allow usage of non-XTAL clock as a parent clock. + */ + if (new_divider < 4) + return 0; + + /* + * If new divisor is larger than maximal supported, try to switch + * from default x16 scheme to oversampling with maximal factor 63. + */ + if (new_divider > 1023) { + new_oversampling = 63; + new_divider = DIV_ROUND_CLOSEST(new_parent_rate * divider * d1 * + d2 * oversampling, + parent_rate * new_oversampling); + if (new_divider < 4 || new_divider > 1023) + return 0; + } + + while (!(readl(base + UART_STATUS_REG) & UART_STATUS_TX_EMPTY)) + ; + + writel(new_divider, base + UART_BAUD_REG); + writel(new_oversampling, base + UART_POSSR_REG); + + return 0; +} + static int mvebu_serial_of_to_plat(struct udevice *dev) { struct mvebu_plat *plat = dev_get_plat(dev); @@ -232,6 +297,8 @@ U_BOOT_DRIVER(serial_mvebu) = { .of_to_plat = mvebu_serial_of_to_plat, .plat_auto = sizeof(struct mvebu_plat), .probe = mvebu_serial_probe, + .remove = mvebu_serial_remove, + .flags = DM_FLAG_OS_PREPARE, .ops = &mvebu_serial_ops, };

On 25.05.21 19:42, Marek Behún wrote:
From: Pali Rohár pali@kernel.org
Unfortunately the UART driver in current Linux for Armada 3700 expects UART's parent clock to be XTAL and calculats baudrate divisor according to XTAL clock. Therefore we must switch back to XTAL clock before booting kernel.
Do you plan to enhance the Linux driver as well to support TBG as clock in input at some time?
Implement .remove method for this driver with DM_FLAG_OS_PREPARE flag set.
If current baudrate is unsuitable for XTAL clock then we do not change anything. This can only happen if the user either configured unsupported settings or knows what they are doing and has kernel patches which allow usage of non-XTAL parent clock.
Signed-off-by: Pali Rohár pali@kernel.org Reviewed-by: Marek Behún marek.behun@nic.cz
Reviewed-by: Stefan Roese sr@denx.de
Thanks, Stefan
drivers/serial/serial_mvebu_a3700.c | 67 +++++++++++++++++++++++++++++ 1 file changed, 67 insertions(+)
diff --git a/drivers/serial/serial_mvebu_a3700.c b/drivers/serial/serial_mvebu_a3700.c index ba2ac5917f..c7e66fef87 100644 --- a/drivers/serial/serial_mvebu_a3700.c +++ b/drivers/serial/serial_mvebu_a3700.c @@ -204,6 +204,71 @@ static int mvebu_serial_probe(struct udevice *dev) return 0; }
+static int mvebu_serial_remove(struct udevice *dev) +{
- struct mvebu_plat *plat = dev_get_plat(dev);
- void __iomem *base = plat->base;
- ulong new_parent_rate, parent_rate;
- u32 new_divider, divider;
- u32 new_oversampling;
- u32 oversampling;
- u32 d1, d2;
- /*
* Switch UART base clock back to XTAL because older Linux kernel
* expects it. Otherwise it does not calculate UART divisor correctly
* and therefore UART does not work in kernel.
*/
- divider = readl(base + UART_BAUD_REG);
- if (!(divider & BIT(19))) /* UART already uses XTAL */
return 0;
- /* Read current divisors settings */
- d1 = (divider >> 15) & 7;
- d2 = (divider >> 12) & 7;
- parent_rate = plat->tbg_rate;
- divider &= 1023;
- oversampling = readl(base + UART_POSSR_REG) & 63;
- if (!oversampling)
oversampling = 16;
- /* Calculate new divisor against XTAL clock without changing baudrate */
- new_oversampling = 0;
- new_parent_rate = get_ref_clk() * 1000000;
- new_divider = DIV_ROUND_CLOSEST(new_parent_rate * divider * d1 * d2 *
oversampling, parent_rate * 16);
- /*
* UART does not work reliably when XTAL divisor is smaller than 4.
* In this case we do not switch UART parent to XTAL. User either
* configured unsupported settings or has newer kernel with patches
* which allow usage of non-XTAL clock as a parent clock.
*/
- if (new_divider < 4)
return 0;
- /*
* If new divisor is larger than maximal supported, try to switch
* from default x16 scheme to oversampling with maximal factor 63.
*/
- if (new_divider > 1023) {
new_oversampling = 63;
new_divider = DIV_ROUND_CLOSEST(new_parent_rate * divider * d1 *
d2 * oversampling,
parent_rate * new_oversampling);
if (new_divider < 4 || new_divider > 1023)
return 0;
- }
- while (!(readl(base + UART_STATUS_REG) & UART_STATUS_TX_EMPTY))
;
- writel(new_divider, base + UART_BAUD_REG);
- writel(new_oversampling, base + UART_POSSR_REG);
- return 0;
+}
- static int mvebu_serial_of_to_plat(struct udevice *dev) { struct mvebu_plat *plat = dev_get_plat(dev);
@@ -232,6 +297,8 @@ U_BOOT_DRIVER(serial_mvebu) = { .of_to_plat = mvebu_serial_of_to_plat, .plat_auto = sizeof(struct mvebu_plat), .probe = mvebu_serial_probe,
- .remove = mvebu_serial_remove,
- .flags = DM_FLAG_OS_PREPARE, .ops = &mvebu_serial_ops, };
Viele Grüße, Stefan

On Thursday 27 May 2021 08:17:41 Stefan Roese wrote:
On 25.05.21 19:42, Marek Behún wrote:
From: Pali Rohár pali@kernel.org
Unfortunately the UART driver in current Linux for Armada 3700 expects UART's parent clock to be XTAL and calculats baudrate divisor according to XTAL clock. Therefore we must switch back to XTAL clock before booting kernel.
Do you plan to enhance the Linux driver as well to support TBG as clock in input at some time?
Yes! I have already written patches but they need cleanup and more tests.
Implement .remove method for this driver with DM_FLAG_OS_PREPARE flag set.
If current baudrate is unsuitable for XTAL clock then we do not change anything. This can only happen if the user either configured unsupported settings or knows what they are doing and has kernel patches which allow usage of non-XTAL parent clock.
Signed-off-by: Pali Rohár pali@kernel.org Reviewed-by: Marek Behún marek.behun@nic.cz
Reviewed-by: Stefan Roese sr@denx.de
Thanks, Stefan
drivers/serial/serial_mvebu_a3700.c | 67 +++++++++++++++++++++++++++++ 1 file changed, 67 insertions(+)
diff --git a/drivers/serial/serial_mvebu_a3700.c b/drivers/serial/serial_mvebu_a3700.c index ba2ac5917f..c7e66fef87 100644 --- a/drivers/serial/serial_mvebu_a3700.c +++ b/drivers/serial/serial_mvebu_a3700.c @@ -204,6 +204,71 @@ static int mvebu_serial_probe(struct udevice *dev) return 0; } +static int mvebu_serial_remove(struct udevice *dev) +{
- struct mvebu_plat *plat = dev_get_plat(dev);
- void __iomem *base = plat->base;
- ulong new_parent_rate, parent_rate;
- u32 new_divider, divider;
- u32 new_oversampling;
- u32 oversampling;
- u32 d1, d2;
- /*
* Switch UART base clock back to XTAL because older Linux kernel
* expects it. Otherwise it does not calculate UART divisor correctly
* and therefore UART does not work in kernel.
*/
- divider = readl(base + UART_BAUD_REG);
- if (!(divider & BIT(19))) /* UART already uses XTAL */
return 0;
- /* Read current divisors settings */
- d1 = (divider >> 15) & 7;
- d2 = (divider >> 12) & 7;
- parent_rate = plat->tbg_rate;
- divider &= 1023;
- oversampling = readl(base + UART_POSSR_REG) & 63;
- if (!oversampling)
oversampling = 16;
- /* Calculate new divisor against XTAL clock without changing baudrate */
- new_oversampling = 0;
- new_parent_rate = get_ref_clk() * 1000000;
- new_divider = DIV_ROUND_CLOSEST(new_parent_rate * divider * d1 * d2 *
oversampling, parent_rate * 16);
- /*
* UART does not work reliably when XTAL divisor is smaller than 4.
* In this case we do not switch UART parent to XTAL. User either
* configured unsupported settings or has newer kernel with patches
* which allow usage of non-XTAL clock as a parent clock.
*/
- if (new_divider < 4)
return 0;
- /*
* If new divisor is larger than maximal supported, try to switch
* from default x16 scheme to oversampling with maximal factor 63.
*/
- if (new_divider > 1023) {
new_oversampling = 63;
new_divider = DIV_ROUND_CLOSEST(new_parent_rate * divider * d1 *
d2 * oversampling,
parent_rate * new_oversampling);
if (new_divider < 4 || new_divider > 1023)
return 0;
- }
- while (!(readl(base + UART_STATUS_REG) & UART_STATUS_TX_EMPTY))
;
- writel(new_divider, base + UART_BAUD_REG);
- writel(new_oversampling, base + UART_POSSR_REG);
- return 0;
+}
- static int mvebu_serial_of_to_plat(struct udevice *dev) { struct mvebu_plat *plat = dev_get_plat(dev);
@@ -232,6 +297,8 @@ U_BOOT_DRIVER(serial_mvebu) = { .of_to_plat = mvebu_serial_of_to_plat, .plat_auto = sizeof(struct mvebu_plat), .probe = mvebu_serial_probe,
- .remove = mvebu_serial_remove,
- .flags = DM_FLAG_OS_PREPARE, .ops = &mvebu_serial_ops, };
Viele Grüße, Stefan
-- DENX Software Engineering GmbH, Managing Director: Wolfgang Denk HRB 165235 Munich, Office: Kirchenstr.5, D-82194 Groebenzell, Germany Phone: (+49)-8142-66989-51 Fax: (+49)-8142-66989-80 Email: sr@denx.de

On Thursday 27 May 2021 10:17:33 Pali Rohár wrote:
On Thursday 27 May 2021 08:17:41 Stefan Roese wrote:
On 25.05.21 19:42, Marek Behún wrote:
From: Pali Rohár pali@kernel.org
Unfortunately the UART driver in current Linux for Armada 3700 expects UART's parent clock to be XTAL and calculats baudrate divisor according to XTAL clock. Therefore we must switch back to XTAL clock before booting kernel.
Do you plan to enhance the Linux driver as well to support TBG as clock in input at some time?
Yes! I have already written patches but they need cleanup and more tests.
Patches for Linux kernel are here: https://lore.kernel.org/linux-serial/20210624224909.6350-1-pali@kernel.org/

From: Pali Rohár pali@kernel.org
Extend CONFIG_SYS_BAUDRATE_TABLE and include all standard baudrates and also nonstandard up to the 6 MBaud. U-Boot's A3720 UART driver can use baudrates from 300 Baud to 6 MBaud.
This changes all A3720 boards, since all of them include either mvebu_armada-37xx.h or turris_mox.h config file.
Signed-off-by: Pali Rohár pali@kernel.org Reviewed-by: Marek Behún marek.behun@nic.cz --- include/configs/mvebu_armada-37xx.h | 9 +++++++-- include/configs/turris_mox.h | 9 +++++++-- 2 files changed, 14 insertions(+), 4 deletions(-)
diff --git a/include/configs/mvebu_armada-37xx.h b/include/configs/mvebu_armada-37xx.h index 2ad4325eaf..a2bea2947d 100644 --- a/include/configs/mvebu_armada-37xx.h +++ b/include/configs/mvebu_armada-37xx.h @@ -17,8 +17,13 @@
#define CONFIG_SYS_BOOTM_LEN SZ_64M /* Increase max gunzip size */
-#define CONFIG_SYS_BAUDRATE_TABLE { 9600, 19200, 38400, 57600, \ - 115200, 230400, 460800, 921600 } +#define CONFIG_SYS_BAUDRATE_TABLE { 300, 600, 1200, 1800, 2400, 4800, \ + 9600, 19200, 38400, 57600, 115200, \ + 230400, 460800, 500000, 576000, \ + 921600, 1000000, 1152000, 1500000, \ + 2000000, 2500000, 3000000, 3500000, \ + 4000000, 4500000, 5000000, 5500000, \ + 6000000 }
/* * For booting Linux, the board info and command line data diff --git a/include/configs/turris_mox.h b/include/configs/turris_mox.h index 51445ec60a..df312f2019 100644 --- a/include/configs/turris_mox.h +++ b/include/configs/turris_mox.h @@ -22,8 +22,13 @@
/* auto boot */
-#define CONFIG_SYS_BAUDRATE_TABLE { 9600, 19200, 38400, 57600, \ - 115200, 230400, 460800, 921600 } +#define CONFIG_SYS_BAUDRATE_TABLE { 300, 600, 1200, 1800, 2400, 4800, \ + 9600, 19200, 38400, 57600, 115200, \ + 230400, 460800, 500000, 576000, \ + 921600, 1000000, 1152000, 1500000, \ + 2000000, 2500000, 3000000, 3500000, \ + 4000000, 4500000, 5000000, 5500000, \ + 6000000 }
/* * For booting Linux, the board info and command line data

On 25.05.21 19:42, Marek Behún wrote:
From: Pali Rohár pali@kernel.org
Extend CONFIG_SYS_BAUDRATE_TABLE and include all standard baudrates and also nonstandard up to the 6 MBaud. U-Boot's A3720 UART driver can use baudrates from 300 Baud to 6 MBaud.
This changes all A3720 boards, since all of them include either mvebu_armada-37xx.h or turris_mox.h config file.
Signed-off-by: Pali Rohár pali@kernel.org Reviewed-by: Marek Behún marek.behun@nic.cz
Reviewed-by: Stefan Roese sr@denx.de
Thanks, Stefan
include/configs/mvebu_armada-37xx.h | 9 +++++++-- include/configs/turris_mox.h | 9 +++++++-- 2 files changed, 14 insertions(+), 4 deletions(-)
diff --git a/include/configs/mvebu_armada-37xx.h b/include/configs/mvebu_armada-37xx.h index 2ad4325eaf..a2bea2947d 100644 --- a/include/configs/mvebu_armada-37xx.h +++ b/include/configs/mvebu_armada-37xx.h @@ -17,8 +17,13 @@
#define CONFIG_SYS_BOOTM_LEN SZ_64M /* Increase max gunzip size */
-#define CONFIG_SYS_BAUDRATE_TABLE { 9600, 19200, 38400, 57600, \
115200, 230400, 460800, 921600 }
+#define CONFIG_SYS_BAUDRATE_TABLE { 300, 600, 1200, 1800, 2400, 4800, \
9600, 19200, 38400, 57600, 115200, \
230400, 460800, 500000, 576000, \
921600, 1000000, 1152000, 1500000, \
2000000, 2500000, 3000000, 3500000, \
4000000, 4500000, 5000000, 5500000, \
6000000 }
/*
- For booting Linux, the board info and command line data
diff --git a/include/configs/turris_mox.h b/include/configs/turris_mox.h index 51445ec60a..df312f2019 100644 --- a/include/configs/turris_mox.h +++ b/include/configs/turris_mox.h @@ -22,8 +22,13 @@
/* auto boot */
-#define CONFIG_SYS_BAUDRATE_TABLE { 9600, 19200, 38400, 57600, \
115200, 230400, 460800, 921600 }
+#define CONFIG_SYS_BAUDRATE_TABLE { 300, 600, 1200, 1800, 2400, 4800, \
9600, 19200, 38400, 57600, 115200, \
230400, 460800, 500000, 576000, \
921600, 1000000, 1152000, 1500000, \
2000000, 2500000, 3000000, 3500000, \
4000000, 4500000, 5000000, 5500000, \
6000000 }
/*
- For booting Linux, the board info and command line data
Viele Grüße, Stefan

On 25.05.21 19:42, Marek Behún wrote:
This series adds support for more baudrates on Armada 3720 UART (up to 6 MBaud).
Marek Behún (1): clk: armada-37xx: Set DM_FLAG_PRE_RELOC
Pali Rohár (4): serial: a37xx: Fix parent clock rate value and divider calculation serial: a37xx: Use TBG as parent clock serial: a37xx: Switch to XTAL clock when booting Linux kernel arm: mvebu: a37xx: Enable more baudrates
drivers/clk/mvebu/armada-37xx-periph.c | 1 + drivers/clk/mvebu/armada-37xx-tbg.c | 1 + drivers/serial/serial_mvebu_a3700.c | 179 ++++++++++++++++++++++++- include/configs/mvebu_armada-37xx.h | 9 +- include/configs/turris_mox.h | 9 +- 5 files changed, 190 insertions(+), 9 deletions(-)
Applied to u-boot-marvell/master
Thanks, Stefan
participants (3)
-
Marek Behún
-
Pali Rohár
-
Stefan Roese