[PATCH u-boot-marvell 4/5] serial: a37xx: Switch to XTAL clock when booting Linux kernel

Pali Rohár pali at kernel.org
Thu May 27 10:17:33 CEST 2021


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 at 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 at kernel.org>
> > Reviewed-by: Marek Behún <marek.behun at nic.cz>
> 
> Reviewed-by: Stefan Roese <sr at 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 at denx.de


More information about the U-Boot mailing list