serial: a37xx: Fix parent clock rate value and divider calculation

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>
This commit is contained in:
Pali Rohár 2021-05-25 19:42:38 +02:00 committed by Stefan Roese
parent fd075f77ca
commit 139d081384

View file

@ -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,