[U-Boot] Implement AUART for i.MX28

Marek Vasut
2013-07-09T22:46:31Z

Hi Andreas,

> I have tried to implement an AUART driver for i.MX28.
> However for it to work I must print 1 character to the
> debug UART via the serial_pl01x driver. If I do this
> the AUART will start working. If I don't nothing will
> be printed to the AUART. Anybody can see any obvious errors?
>
> Signed-off-by: Andreas Wass <andreas.wass@dalelven.com>

I have to wonder, is the AUART no standard UART IP ? Probably not as Linux also
has a separate driver for this ...

Lots of rambling follows below, but please dont be put off by it.

Make sure to CC me and Fabio on the next submission. I can help you debugging
the driver if you clean it up a bit.

> drivers/serial/Makefile | 1 +
> drivers/serial/mxs-regs-uartapp.h | 307
> +++++++++++++++++++++++++++++++++++++ drivers/serial/mxs_auart.c |
> 161 +++++++++++++++++++
> drivers/serial/serial.c | 11 +-
> drivers/serial/serial_pl01x.c | 19 ++-
> 5 files changed, 492 insertions(+), 7 deletions(-)
> create mode 100644 drivers/serial/mxs-regs-uartapp.h
> create mode 100644 drivers/serial/mxs_auart.c
>
> diff --git a/drivers/serial/Makefile b/drivers/serial/Makefile
> index fbc4e97..f4e0d45 100644
> --- a/drivers/serial/Makefile
> +++ b/drivers/serial/Makefile
> @@ -52,6 +52,7 @@ COBJS-$(CONFIG_XILINX_UARTLITE) += serial_xuartlite.o
> COBJS-$(CONFIG_SANDBOX_SERIAL) += sandbox.o
> COBJS-$(CONFIG_SCIF_CONSOLE) += serial_sh.o
> COBJS-$(CONFIG_ZYNQ_SERIAL) += serial_zynq.o
> +COBJS-$(CONFIG_MXS_AUART) += mxs_auart.o
>
> ifndef CONFIG_SPL_BUILD
> COBJS-$(CONFIG_USB_TTY) += usbtty.o
> diff --git a/drivers/serial/mxs-regs-uartapp.h
> b/drivers/serial/mxs-regs-uartapp.h new file mode 100644
> index 0000000..aad9a78
> --- /dev/null
> +++ b/drivers/serial/mxs-regs-uartapp.h
[...]

Please follow the pattern of arch/arm/include/asm/arch-mxs/regs-*.h with regards
to struct-based access and using (1 << n) to declare bits.

> diff --git a/drivers/serial/mxs_auart.c b/drivers/serial/mxs_auart.c
> new file mode 100644
> index 0000000..b9a4e82

[...]

> +#include <common.h>
> +#include <serial.h>
> +#include <linux/compiler.h>
> +#include "mxs-regs-uartapp.h"
> +
> +DECLARE_GLOBAL_DATA_PTR;
> +
> +#define REGS_UARTAPP3_BASE (0x80070000)

Use arch/arm/include/asm/arch-mxs/regs-base.h , but since there are multiple,
you can add a config option to select which AUART to use.

> +#define REGS_UARTAPP_BASE REGS_UARTAPP3_BASE
> +#define REG_RD(base, reg) \
> + (*(volatile unsigned int *)((base) + (reg)))
> +#define REG_WR(base, reg, value) \
> + ((*(volatile unsigned int *)((base) + (reg))) = (value))

Use readl() / writel() / clrsetbits_le32() etc.

> +/* HACK should be removed when issue is fixed! */
> +void dbg_puts(const char *s);
> +
> +static void mxs_auart_reset(void)

mxs_reset_block();

[...]

> +/*
> + * Set baud rate. The settings are always 8n1
> + */
> +void mxs_auart_setbrg(void)
> +{
> + u32 div;
> + u32 linectrl = 0;
> +
> + div = (CONFIG_MXS_AUART_CLK * 32) / CONFIG_BAUDRATE;

Does this not integrate with the MXS clock stuff in
arch/arm/cpu/arm926ejs/mxs/clock.c ? You might want to implement a function
there to govern the clock speed somehow.

> + linectrl |= BF_UARTAPP_LINECTRL_BAUD_DIVFRAC(div & 0x3F);
> + linectrl |= BF_UARTAPP_LINECTRL_BAUD_DIVINT(div >> 6);
> + linectrl |= BF_UARTAPP_LINECTRL_WLEN(3);
> + linectrl |= BM_UARTAPP_LINECTRL_FEN;
> +
> + REG_WR(REGS_UARTAPP_BASE, HW_UARTAPP_LINECTRL, linectrl);
> +}
> +
> +void mxs_auart_init(void)
> +{
> + mxs_auart_reset();

mxs_reset_block() and drop the above ;-)

> + REG_WR(REGS_UARTAPP_BASE, HW_UARTAPP_INTR, 0);
> + serial_setbrg();
> +
> + REG_WR(REGS_UARTAPP_BASE, HW_UARTAPP_CTRL2_CLR,
> + BM_UARTAPP_CTRL2_RTSEN | BM_UARTAPP_CTRL2_CTSEN |
> + BM_UARTAPP_CTRL2_USE_LCR2);
> + REG_WR(REGS_UARTAPP_BASE, HW_UARTAPP_CTRL2_SET,
> + BM_UARTAPP_CTRL2_RXE | BM_UARTAPP_CTRL2_TXE |
> + BM_UARTAPP_CTRL2_UARTEN);
> +
> + /* HACK, the driver will not work without this.
> + * Don't know how to fix
> + */

See http://www.denx.de/wiki/U-Boot/CodingStyle about the comments.

> + dbg_puts("\n");

Is your AUART set as default uart ? Or how do you operate it ?

> + return 0;
> +}
> +
> +void mxs_auart_putc(const char c)
> +{
> + while (REG_RD(REGS_UARTAPP_BASE, HW_UARTAPP_STAT) &
> + BM_UARTAPP_STAT_TXFF)
> + ;

Careful about endless loop here.

> + REG_WR(REGS_UARTAPP_BASE, HW_UARTAPP_DATA, c);
> + if (c == '\n')
> + mxs_auart_putc('\r');
> +
> +}
> +
> +void mxs_auart_puts(const char *s)
> +{
> + while (*s)
> + mxs_auart_putc(*s++);

Use default_serial_puts()

> +}
> +
> +int mxs_auart_tstc(void)
> +{
> + return !(REG_RD(REGS_UARTAPP_BASE, HW_UARTAPP_STAT) &
> + BM_UARTAPP_STAT_RXFE);
> +}
> +
> +int mxs_auart_getc(void)
> +{
> + while (!mxs_auart_tstc())
> + ;
> +
> + return REG_RD(REGS_UARTAPP_BASE, HW_UARTAPP_DATA) & 0xff;
> +}
> +
> +static struct serial_device mxs_auart_drv = {
> + .name = "mxs_auart_serial",
> + .start = mxs_auart_init,
> + .stop = NULL,
> + .setbrg = mxs_auart_setbrg,
> + .putc = mxs_auart_putc,
> + .puts = mxs_auart_puts,
> + .getc = mxs_auart_getc,
> + .tstc = mxs_auart_tstc,
> +};
> +
> +void mxs_auart_initialize(void)
> +{
> + serial_register(&mxs_auart_drv);
> +}
> +
> +__weak struct serial_device *default_serial_console(void)
> +{
> + return &mxs_auart_drv;
> +} …