[PATCH] serial: a37xx: Reset whole UART when changing parent clock from TBG to XTAL

Stefan Roese sr at denx.de
Tue Nov 30 07:36:09 CET 2021


On 11/15/21 13:48, Pali Rohár wrote:
> Sometimes UART stops transmitting characters after UART clock is changed
> back to XTAL. In this state UART fifo is always full. Kernel during early
> boot wants to print output on UART and is waiting for non-empty UART fifo.
> Which leads to CPU hangup without any (debug) output on UART.
> 
> Marvell Armada 3700 Functional Specifications says that for programming
> fractional divisor registers it is required to disable UART, enable
> loopback mode, reset fifos, program registers, disable loopback mode,
> release reset of fifos and enable UART.
> 
> But these steps do not fix above mentioned issue that UART hangup. Also
> gating UART clock does not help. And even resetting UART state machines do
> not help.
> 
> Experiments showed that UART fifo is unblocked after board is being reset
> (during board reset UART HW transmit UART fifo even CPU is not executing
> kernel/bootloader anymore).
> 
> And another experiments showed that same workaround can be achieved also
> by external reset of UART HW (without need to reset board).
> 
> So do not implement any of "Marvell recommended" steps from Functional
> Specifications as they do not work. And rather prior changing parent clock
> back to XTAL, do external reset of UART HW. This operation also resets all
> UART registers, so basically it also sets UART clock to default, which is
> XTAL. It is unknown why UART hangups and enters such broken state.
> 
> Signed-off-by: Pali Rohár <pali at kernel.org>
> ---
>   drivers/serial/serial_mvebu_a3700.c | 13 ++++++++++++-
>   1 file changed, 12 insertions(+), 1 deletion(-)
> 
> diff --git a/drivers/serial/serial_mvebu_a3700.c b/drivers/serial/serial_mvebu_a3700.c
> index 6bca8e4b7e2d..93e4d38d34c7 100644
> --- a/drivers/serial/serial_mvebu_a3700.c
> +++ b/drivers/serial/serial_mvebu_a3700.c
> @@ -9,6 +9,7 @@
>   #include <serial.h>
>   #include <asm/io.h>
>   #include <asm/arch/cpu.h>
> +#include <mach/soc.h>
>   
>   struct mvebu_plat {
>   	void __iomem *base;
> @@ -213,6 +214,7 @@ static int mvebu_serial_remove(struct udevice *dev)
>   	u32 new_oversampling;
>   	u32 oversampling;
>   	u32 d1, d2;
> +	u32 nb_rst;
>   
>   	/*
>   	 * Switch UART base clock back to XTAL because older Linux kernel
> @@ -260,12 +262,22 @@ static int mvebu_serial_remove(struct udevice *dev)
>   			return 0;
>   	}
>   
> +	/* wait until TX empty */
>   	while (!(readl(base + UART_STATUS_REG) & UART_STATUS_TX_EMPTY))
>   		;
>   
> +	/* external reset of UART via North Bridge Peripheral */
> +	nb_rst = readl(MVEBU_REGISTER(0x12400));
> +	writel(nb_rst & ~BIT(3), MVEBU_REGISTER(0x12400));
> +	writel(nb_rst | BIT(3), MVEBU_REGISTER(0x12400));

I'm not that fond of this type of access to some "external" register in
this driver. But adding some reset driver might be a bit too much here.
So:

Reviewed-by: Stefan Roese <sr at denx.de>

Thanks,
Stefan


> +
> +	/* set baudrate and oversampling */
>   	writel(new_divider, base + UART_BAUD_REG);
>   	writel(new_oversampling, base + UART_POSSR_REG);
>   
> +	/* No Parity, 1 Stop */
> +	writel(0, base + UART_CTRL_REG);
> +
>   	return 0;
>   }
>   
> @@ -305,7 +317,6 @@ U_BOOT_DRIVER(serial_mvebu) = {
>   #ifdef CONFIG_DEBUG_MVEBU_A3700_UART
>   
>   #include <debug_uart.h>
> -#include <mach/soc.h>
>   
>   static inline void _debug_uart_init(void)
>   {
> 

Viele Grüße,
Stefan Roese

-- 
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 at denx.de


More information about the U-Boot mailing list