From 139d081384c2f613ac74b372cb7afe2386d556e4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pali=20Roh=C3=A1r?= Date: Tue, 25 May 2021 19:42:38 +0200 Subject: [PATCH 1/6] serial: a37xx: Fix parent clock rate value and divider calculation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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 Reviewed-by: Marek Behún Reviewed-by: Stefan Roese --- 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 #include #include +#include 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, From 3d9c1d5ddac8f040730c228086a083214e992419 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marek=20Beh=C3=BAn?= Date: Tue, 25 May 2021 19:42:39 +0200 Subject: [PATCH 2/6] clk: armada-37xx: Set DM_FLAG_PRE_RELOC MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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 Reviewed-by: Stefan Roese --- 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, }; From 5f41bab86c57bb5f0e1ee335ae402c7559937416 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pali=20Roh=C3=A1r?= Date: Tue, 25 May 2021 19:42:40 +0200 Subject: [PATCH 3/6] serial: a37xx: Use TBG as parent clock MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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 Signed-off-by: Marek Behún Reviewed-by: Stefan Roese --- 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 +#include #include #include #include @@ -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, From 8214728e4fe6f4ed495a5f85d0fc921cef77e0e9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pali=20Roh=C3=A1r?= Date: Tue, 25 May 2021 19:42:41 +0200 Subject: [PATCH 4/6] serial: a37xx: Switch to XTAL clock when booting Linux kernel MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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 Reviewed-by: Marek Behún Reviewed-by: Stefan Roese --- 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, }; From 7d9e9f582790d2572f6aff366043306a7e5c7767 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pali=20Roh=C3=A1r?= Date: Tue, 25 May 2021 19:42:42 +0200 Subject: [PATCH 5/6] arm: mvebu: a37xx: Enable more baudrates MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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 Reviewed-by: Marek Behún Reviewed-by: Stefan Roese --- 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 9c021a1ef9..6712839823 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 From 800433814af22708ec11f8042089bcf2c8bb1fa6 Mon Sep 17 00:00:00 2001 From: Tim Harvey Date: Thu, 17 Jun 2021 16:31:07 -0700 Subject: [PATCH 6/6] octeontx: do not require cavium BDK node to be present The cavium,bdk node is a non-standard dt node used by the BDK and therefore it is removed from the dt before booting Linux. Do not require this node to exist as it won't for standard dt's. Signed-off-by: Tim Harvey Reviewed-by: Stefan Roese --- board/Marvell/octeontx/board-fdt.c | 20 ++++++++------------ 1 file changed, 8 insertions(+), 12 deletions(-) diff --git a/board/Marvell/octeontx/board-fdt.c b/board/Marvell/octeontx/board-fdt.c index 0b05ef11e9..1db2a4a267 100644 --- a/board/Marvell/octeontx/board-fdt.c +++ b/board/Marvell/octeontx/board-fdt.c @@ -281,20 +281,16 @@ int ft_board_setup(void *blob, struct bd_info *bd) } if (blob) { + /* delete cavium,bdk node if it exists */ offset = fdt_path_offset(blob, "/cavium,bdk"); - if (offset < 0) { - printf("ERROR: FDT BDK node not found\n"); - return offset; + if (offset >= 0) { + ret = fdt_del_node(blob, offset); + if (ret < 0) { + printf("WARNING : could not remove bdk node\n"); + return ret; + } + debug("%s deleted bdk node\n", __func__); } - - /* delete node */ - ret = fdt_del_node(blob, offset); - if (ret < 0) { - printf("WARNING : could not remove bdk node\n"); - return ret; - } - - debug("%s deleted bdk node\n", __func__); } return 0;