[U-Boot] [PATCH 4/8] serial: add serial driver for BCM6345
Álvaro Fernández Rojas
noltari at gmail.com
Sat Apr 15 17:17:24 UTC 2017
Hi Daniel,
El 14/04/2017 a las 20:19, Daniel Schwierzeck escribió:
>
>
> Am 13.04.2017 um 17:52 schrieb Álvaro Fernández Rojas:
>> It is based on linux/drivers/tty/serial/bcm63xx_uart.c
>>
>> Signed-off-by: Álvaro Fernández Rojas <noltari at gmail.com>
>> ---
>> drivers/serial/Kconfig | 14 ++
>> drivers/serial/Makefile | 1 +
>> drivers/serial/serial_bcm6345.c | 341 ++++++++++++++++++++++++++++++++++++++++
>> 3 files changed, 356 insertions(+)
>> create mode 100644 drivers/serial/serial_bcm6345.c
>>
>> diff --git a/drivers/serial/Kconfig b/drivers/serial/Kconfig
>> index c0ec2ec..ca776d8 100644
>> --- a/drivers/serial/Kconfig
>> +++ b/drivers/serial/Kconfig
>> @@ -134,6 +134,14 @@ config DEBUG_UART_ATMEL
>> will need to provide parameters to make this work. The driver will
>> be available until the real driver-model serial is running.
>>
>> +config DEBUG_UART_BCM6345
>> + bool "BCM6345 UART"
>> + depends on BCM6345_SERIAL
>> + help
>> + Select this to enable a debug UART on BCM6345 SoCs. You
>> + will need to provide parameters to make this work. The driver will
>> + be available until the real driver model serial is running.
>> +
>> config DEBUG_UART_NS16550
>> bool "ns16550"
>> help
>> @@ -339,6 +347,12 @@ config ATMEL_USART
>> configured in the device tree, and input clock frequency can
>> be got from the clk node.
>>
>> +config BCM6345_SERIAL
>> + bool "Support for BCM6345 UART"
>> + depends on DM_SERIAL
>> + help
>> + Select this to enable UART on BCM6345 SoCs.
>> +
>> config FSL_LPUART
>> bool "Freescale LPUART support"
>> help
>> diff --git a/drivers/serial/Makefile b/drivers/serial/Makefile
>> index 4382cf9..dca31b2 100644
>> --- a/drivers/serial/Makefile
>> +++ b/drivers/serial/Makefile
>> @@ -20,6 +20,7 @@ obj-$(CONFIG_ALTERA_JTAG_UART) += altera_jtag_uart.o
>> obj-$(CONFIG_AR933X_UART) += serial_ar933x.o
>> obj-$(CONFIG_ARM_DCC) += arm_dcc.o
>> obj-$(CONFIG_ATMEL_USART) += atmel_usart.o
>> +obj-$(CONFIG_BCM6345_SERIAL) += serial_bcm6345.o
>> obj-$(CONFIG_EFI_APP) += serial_efi.o
>> obj-$(CONFIG_LPC32XX_HSUART) += lpc32xx_hsuart.o
>> obj-$(CONFIG_MCFUART) += mcfuart.o
>> diff --git a/drivers/serial/serial_bcm6345.c b/drivers/serial/serial_bcm6345.c
>> new file mode 100644
>> index 0000000..8754e76
>> --- /dev/null
>> +++ b/drivers/serial/serial_bcm6345.c
>> @@ -0,0 +1,341 @@
>> +/*
>> + * Copyright (C) 2017 Álvaro Fernández Rojas <noltari at gmail.com>
>> + *
>> + * Derived from linux/drivers/tty/serial/bcm63xx_uart.c:
>> + * Copyright (C) 2008 Maxime Bizon <mbizon at freebox.fr>
>> + *
>> + * SPDX-License-Identifier: GPL-2.0+
>> + */
>> +
>> +#include <serial.h>
>> +#include <errno.h>
>> +#include <clk.h>
>> +#include <debug_uart.h>
>> +#include <asm/io.h>
>> +#include <asm/types.h>
>> +#include <dm/device.h>
>> +
>> +/* UART Control Register */
>> +#define UART_CTL_REG 0x0
>> +#define UART_CTL_RXTMOUTCNT_SHIFT 0
>> +#define UART_CTL_RXTMOUTCNT_MASK (0x1f << UART_CTL_RXTMOUTCNT_SHIFT)
>> +#define UART_CTL_RSTTXDN_SHIFT 5
>> +#define UART_CTL_RSTTXDN_MASK (1 << UART_CTL_RSTTXDN_SHIFT)
>> +#define UART_CTL_RSTRXFIFO_SHIFT 6
>> +#define UART_CTL_RSTRXFIFO_MASK (1 << UART_CTL_RSTRXFIFO_SHIFT)
>> +#define UART_CTL_RSTTXFIFO_SHIFT 7
>> +#define UART_CTL_RSTTXFIFO_MASK (1 << UART_CTL_RSTTXFIFO_SHIFT)
>> +#define UART_CTL_STOPBITS_SHIFT 8
>> +#define UART_CTL_STOPBITS_MASK (0xf << UART_CTL_STOPBITS_SHIFT)
>> +#define UART_CTL_STOPBITS_1 (0x7 << UART_CTL_STOPBITS_SHIFT)
>> +#define UART_CTL_STOPBITS_2 (0xf << UART_CTL_STOPBITS_SHIFT)
>> +#define UART_CTL_BITSPERSYM_SHIFT 12
>> +#define UART_CTL_BITSPERSYM_MASK (0x3 << UART_CTL_BITSPERSYM_SHIFT)
>> +#define UART_CTL_XMITBRK_SHIFT 14
>> +#define UART_CTL_XMITBRK_MASK (1 << UART_CTL_XMITBRK_SHIFT)
>> +#define UART_CTL_RSVD_SHIFT 15
>> +#define UART_CTL_RSVD_MASK (1 << UART_CTL_RSVD_SHIFT)
>> +#define UART_CTL_RXPAREVEN_SHIFT 16
>> +#define UART_CTL_RXPAREVEN_MASK (1 << UART_CTL_RXPAREVEN_SHIFT)
>> +#define UART_CTL_RXPAREN_SHIFT 17
>> +#define UART_CTL_RXPAREN_MASK (1 << UART_CTL_RXPAREN_SHIFT)
>> +#define UART_CTL_TXPAREVEN_SHIFT 18
>> +#define UART_CTL_TXPAREVEN_MASK (1 << UART_CTL_TXPAREVEN_SHIFT)
>> +#define UART_CTL_TXPAREN_SHIFT 18
>> +#define UART_CTL_TXPAREN_MASK (1 << UART_CTL_TXPAREN_SHIFT)
>> +#define UART_CTL_LOOPBACK_SHIFT 20
>> +#define UART_CTL_LOOPBACK_MASK (1 << UART_CTL_LOOPBACK_SHIFT)
>> +#define UART_CTL_RXEN_SHIFT 21
>> +#define UART_CTL_RXEN_MASK (1 << UART_CTL_RXEN_SHIFT)
>> +#define UART_CTL_TXEN_SHIFT 22
>> +#define UART_CTL_TXEN_MASK (1 << UART_CTL_TXEN_SHIFT)
>> +#define UART_CTL_BRGEN_SHIFT 23
>> +#define UART_CTL_BRGEN_MASK (1 << UART_CTL_BRGEN_SHIFT)
>> +
>> +/* UART Baudword register */
>> +#define UART_BAUD_REG 0x4
>> +
>> +/* UART Misc Control register */
>> +#define UART_MCTL_REG 0x8
>> +#define UART_MCTL_DTR_SHIFT 0
>> +#define UART_MCTL_DTR_MASK (1 << UART_MCTL_DTR_SHIFT)
>> +#define UART_MCTL_RTS_SHIFT 1
>> +#define UART_MCTL_RTS_MASK (1 << UART_MCTL_RTS_SHIFT)
>> +#define UART_MCTL_RXFIFOTHRESH_SHIFT 8
>> +#define UART_MCTL_RXFIFOTHRESH_MASK (0xf << UART_MCTL_RXFIFOTHRESH_SHIFT)
>> +#define UART_MCTL_TXFIFOTHRESH_SHIFT 12
>> +#define UART_MCTL_TXFIFOTHRESH_MASK (0xf << UART_MCTL_TXFIFOTHRESH_SHIFT)
>> +#define UART_MCTL_RXFIFOFILL_SHIFT 16
>> +#define UART_MCTL_RXFIFOFILL_MASK (0x1f << UART_MCTL_RXFIFOFILL_SHIFT)
>> +#define UART_MCTL_TXFIFOFILL_SHIFT 24
>> +#define UART_MCTL_TXFIFOFILL_MASK (0x1f << UART_MCTL_TXFIFOFILL_SHIFT)
>> +
>> +/* UART External Input Configuration register */
>> +#define UART_EXTINP_REG 0xc
>> +#define UART_EXTINP_RI_SHIFT 0
>> +#define UART_EXTINP_RI_MASK (1 << UART_EXTINP_RI_SHIFT)
>> +#define UART_EXTINP_CTS_SHIFT 1
>> +#define UART_EXTINP_CTS_MASK (1 << UART_EXTINP_CTS_SHIFT)
>> +#define UART_EXTINP_DCD_SHIFT 2
>> +#define UART_EXTINP_DCD_MASK (1 << UART_EXTINP_DCD_SHIFT)
>> +#define UART_EXTINP_DSR_SHIFT 3
>> +#define UART_EXTINP_DSR_MASK (1 << UART_EXTINP_DSR_SHIFT)
>> +#define UART_EXTINP_IRSTAT(x) (1 << (x + 4))
>> +#define UART_EXTINP_IRMASK(x) (1 << (x + 8))
>> +#define UART_EXTINP_IR_RI 0
>> +#define UART_EXTINP_IR_CTS 1
>> +#define UART_EXTINP_IR_DCD 2
>> +#define UART_EXTINP_IR_DSR 3
>> +#define UART_EXTINP_RI_NOSENSE_SHIFT 16
>> +#define UART_EXTINP_RI_NOSENSE_MASK (1 << UART_EXTINP_RI_NOSENSE_SHIFT)
>> +#define UART_EXTINP_CTS_NOSENSE_SHIFT 17
>> +#define UART_EXTINP_CTS_NOSENSE_MASK (1 << UART_EXTINP_CTS_NOSENSE_SHIFT)
>> +#define UART_EXTINP_DCD_NOSENSE_SHIFT 18
>> +#define UART_EXTINP_DCD_NOSENSE_MASK (1 << UART_EXTINP_DCD_NOSENSE_SHIFT)
>> +#define UART_EXTINP_DSR_NOSENSE_SHIFT 19
>> +#define UART_EXTINP_DSR_NOSENSE_MASK (1 << UART_EXTINP_DSR_NOSENSE_SHIFT)
>> +
>> +/* UART Interrupt register */
>> +#define UART_IR_REG 0x10
>> +#define UART_IR_MASK(x) (1 << (x + 16))
>> +#define UART_IR_STAT(x) (1 << (x))
>> +#define UART_IR_EXTIP 0
>> +#define UART_IR_TXUNDER 1
>> +#define UART_IR_TXOVER 2
>> +#define UART_IR_TXTRESH 3
>> +#define UART_IR_TXRDLATCH 4
>> +#define UART_IR_TXEMPTY 5
>> +#define UART_IR_RXUNDER 6
>> +#define UART_IR_RXOVER 7
>> +#define UART_IR_RXTIMEOUT 8
>> +#define UART_IR_RXFULL 9
>> +#define UART_IR_RXTHRESH 10
>> +#define UART_IR_RXNOTEMPTY 11
>> +#define UART_IR_RXFRAMEERR 12
>> +#define UART_IR_RXPARERR 13
>> +#define UART_IR_RXBRK 14
>> +#define UART_IR_TXDONE 15
>> +
>> +/* UART Fifo register */
>> +#define UART_FIFO_REG 0x14
>> +#define UART_FIFO_VALID_SHIFT 0
>> +#define UART_FIFO_VALID_MASK 0xff
>> +#define UART_FIFO_FRAMEERR_SHIFT 8
>> +#define UART_FIFO_FRAMEERR_MASK (1 << UART_FIFO_FRAMEERR_SHIFT)
>> +#define UART_FIFO_PARERR_SHIFT 9
>> +#define UART_FIFO_PARERR_MASK (1 << UART_FIFO_PARERR_SHIFT)
>> +#define UART_FIFO_BRKDET_SHIFT 10
>> +#define UART_FIFO_BRKDET_MASK (1 << UART_FIFO_BRKDET_SHIFT)
>> +#define UART_FIFO_ANYERR_MASK (UART_FIFO_FRAMEERR_MASK | \
>> + UART_FIFO_PARERR_MASK | \
>> + UART_FIFO_BRKDET_MASK)
>> +
>
> you should remove all defines you don't need in your driver code
Ok, I will remove them.
>
>> +struct bcm6345_serial_priv {
>> + void __iomem *base;
>> + ulong uartclk;
>> +};
>> +
>> +/*
>> + * enable rx & tx operation on uart
>> + */
>> +static void bcm6345_serial_enable(void __iomem *base)
>> +{
>> + u32 val;
>> +
>> + val = __raw_readl(base + UART_CTL_REG);
>> + val |= (UART_CTL_BRGEN_MASK | UART_CTL_TXEN_MASK | UART_CTL_RXEN_MASK);
>> + __raw_writel(val, base + UART_CTL_REG);
>
> in general you should try to use readl/writel as well as
> setbits_32/clrbits_32/clrsetbit_32. But because you have
> CONFIG_SWAP_IO_SPACE enabled in all your defconfigs, you have to switch
> to readl_be32/writel_be32 and setbits_be32/clrbits_be32/clrsetbits_be32
Is this really needed?
I want to be as close as posible to linux driver...
>
>> +}
>> +
>> +/*
>> + * disable rx & tx operation on uart
>> + */
>> +static void bcm6345_serial_disable(void __iomem *base)
>> +{
>> + u32 val;
>> +
>> + val = __raw_readl(base + UART_CTL_REG);
>> + val &= ~(UART_CTL_BRGEN_MASK | UART_CTL_TXEN_MASK |
>> + UART_CTL_RXEN_MASK);
>> + __raw_writel(val, base + UART_CTL_REG);
>> +}
>> +
>> +/*
>> + * clear all unread data in rx fifo and unsent data in tx fifo
>> + */
>> +static void bcm6345_serial_flush(void __iomem *base)
>> +{
>> + u32 val;
>> +
>> + /* empty rx and tx fifo */
>> + val = __raw_readl(base + UART_CTL_REG);
>> + val |= UART_CTL_RSTRXFIFO_MASK | UART_CTL_RSTTXFIFO_MASK;
>> + __raw_writel(val, base + UART_CTL_REG);
>> +
>> + /* read any pending char to make sure all irq status are
>> + * cleared */
>
> incorrect multi-line comment
Yup, inherited from linux driver, I will fix it.
>
>> + __raw_readl(base + UART_FIFO_REG);
>> +}
>> +
>> +static int bcm6345_serial_init(void __iomem *base, ulong clk, u32 baudrate)
>> +{
>> + u32 val;
>> +
>> + /* mask all irq and flush port */
>> + bcm6345_serial_disable(base);
>> + bcm6345_serial_flush(base);
>> +
>> + /* set bits per sym and stop bits */
>> + val = __raw_readl(base + UART_CTL_REG);
>> + val &= ~UART_CTL_BITSPERSYM_MASK;
>> + val |= (3 << UART_CTL_BITSPERSYM_SHIFT);
>> + val |= UART_CTL_STOPBITS_1;
>> + __raw_writel(val, base + UART_CTL_REG);
>> +
>> + /* set baud rate */
>> + val = (clk / baudrate) / 16;
>> + if (val & 0x1)
>> + val = val;
>> + else
>> + val = val / 2 - 1;
>> + __raw_writel(val, base + UART_BAUD_REG);
>> +
>> + /* clear interrupts */
>> + __raw_writel(0, base + UART_IR_REG);
>> +
>> + /* enable uart */
>> + bcm6345_serial_enable(base);
>> +
>> + return 0;
>> +}
>> +
>> +static int bcm6345_serial_pending(struct udevice *dev, bool input)
>> +{
>> + struct bcm6345_serial_priv *priv = dev_get_priv(dev);
>> + u32 val = __raw_readl(priv->base + UART_IR_REG);
>> +
>> + if (input)
>> + return (val & UART_IR_STAT(UART_IR_RXNOTEMPTY)) ? 1 : 0;
>> + else
>> + return (val & UART_IR_STAT(UART_IR_TXEMPTY)) ? 0 : 1;
>> +}
>> +
>> +static int bcm6345_serial_setbrg(struct udevice *dev, int baudrate)
>> +{
>> + struct bcm6345_serial_priv *priv = dev_get_priv(dev);
>> +
>> + return bcm6345_serial_init(priv->base, priv->uartclk, baudrate);
>> +}
>> +
>> +static int bcm6345_serial_putc(struct udevice *dev, const char ch)
>> +{
>> + struct bcm6345_serial_priv *priv = dev_get_priv(dev);
>> + u32 val;
>> +
>> + val = __raw_readl(priv->base + UART_IR_REG);
>> + if (!(val & UART_IR_STAT(UART_IR_TXEMPTY)))
>> + return -EAGAIN;
>> +
>> + __raw_writel(ch, priv->base + UART_FIFO_REG);
>> +
>> + return 0;
>> +}
>> +
>> +static int bcm6345_serial_getc(struct udevice *dev)
>> +{
>> + struct bcm6345_serial_priv *priv = dev_get_priv(dev);
>> + u32 val;
>> +
>> + val = __raw_readl(priv->base + UART_IR_REG);
>> + if (val & UART_IR_STAT(UART_IR_RXOVER)) {
>> + /* fifo reset is required to clear interrupt */
>> + val = __raw_readl(priv->base + UART_CTL_REG);
>> + val |= UART_CTL_RSTRXFIFO_MASK;
>> + __raw_writel(val, priv->base + UART_CTL_REG);
>> + }
>> + if (!(val & UART_IR_STAT(UART_IR_RXNOTEMPTY)))
>> + return -EAGAIN;
>> +
>> + val = __raw_readl(priv->base + UART_FIFO_REG);
>> + if (val & UART_FIFO_ANYERR_MASK)
>> + return -EAGAIN;
>> +
>> + return (val & UART_FIFO_VALID_MASK);
>> +}
>> +
>> +static int bcm6345_serial_probe(struct udevice *dev)
>> +{
>> + struct bcm6345_serial_priv *priv = dev_get_priv(dev);
>> + struct clk clk;
>> + fdt_addr_t addr;
>> + fdt_size_t size;
>> + int ret;
>> +
>> + /* get address */
>> + addr = dev_get_addr_size_index(dev, 0, &size);
>> + if (addr == FDT_ADDR_T_NONE)
>> + return -EINVAL;
>> +
>> + priv->base = ioremap(addr, size);
>> +
>> + /* get clock rate */
>> + ret = clk_get_by_index(dev, 0, &clk);
>> + if (ret < 0)
>> + return ret;
>> + priv->uartclk = clk_get_rate(&clk) / 2;
>> + clk_free(&clk);
>> +
>> + /* initialize serial */
>> + return bcm6345_serial_init(priv->base, priv->uartclk, CONFIG_BAUDRATE);
>> +}
>> +
>> +static const struct dm_serial_ops bcm6345_serial_ops = {
>> + .putc = bcm6345_serial_putc,
>> + .pending = bcm6345_serial_pending,
>> + .getc = bcm6345_serial_getc,
>> + .setbrg = bcm6345_serial_setbrg,
>> +};
>> +
>> +static const struct udevice_id bcm6345_serial_of_match[] = {
>> + { .compatible = "brcm,bcm6345-uart" },
>> + { /* sentinel */ }
>> +};
>> +
>> +U_BOOT_DRIVER(bcm6345_serial) = {
>> + .name = "bcm6345-uart",
>> + .id = UCLASS_SERIAL,
>> + .of_match = bcm6345_serial_of_match,
>> + .probe = bcm6345_serial_probe,
>> + .priv_auto_alloc_size = sizeof(struct bcm6345_serial_priv),
>> + .ops = &bcm6345_serial_ops,
>> + .flags = DM_FLAG_PRE_RELOC,
>> +};
>> +
>> +#ifdef CONFIG_DEBUG_UART_BCM6345
>> +static inline void _debug_uart_init(void)
>> +{
>> + void __iomem *base = (void __iomem *)CONFIG_DEBUG_UART_BASE;
>> +
>> + bcm6345_serial_init(base, CONFIG_DEBUG_UART_CLOCK, CONFIG_BAUDRATE);
>> +}
>> +
>> +static inline void wait_xfered(void __iomem *base)
>> +{
>> + do {
>> + u32 val = __raw_readl(base + UART_IR_REG);
>> + if (val & UART_IR_STAT(UART_IR_TXEMPTY))
>> + break;
>> + } while (1);
>> +}
>> +
>> +static inline void _debug_uart_putc(int ch)
>> +{
>> + void __iomem *base = (void __iomem *)CONFIG_DEBUG_UART_BASE;
>> +
>> + wait_xfered(base);
>> + __raw_writel(ch, base + UART_FIFO_REG);
>> + wait_xfered(base);
>> +}
>> +
>> +DEBUG_UART_FUNCS
>> +#endif
>>
>
More information about the U-Boot
mailing list