]> git.sur5r.net Git - u-boot/commitdiff
serial: add serial driver for BCM6345
authorÁlvaro Fernández Rojas <noltari@gmail.com>
Mon, 24 Apr 2017 22:39:16 +0000 (00:39 +0200)
committerDaniel Schwierzeck <daniel.schwierzeck@gmail.com>
Wed, 10 May 2017 14:16:09 +0000 (16:16 +0200)
It is based on linux/drivers/tty/serial/bcm63xx_uart.c

Signed-off-by: Álvaro Fernández Rojas <noltari@gmail.com>
Reviewed-by: Simon Glass <sjg@chromium.org>
drivers/serial/Kconfig
drivers/serial/Makefile
drivers/serial/serial_bcm6345.c [new file with mode: 0644]

index 58320666b7d427710c5783c4e1d53936813e8a0e..724994568dfce711a6b5891205ec2c7cfb9163d0 100644 (file)
@@ -145,6 +145,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.
 
          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
 config DEBUG_UART_NS16550
        bool "ns16550"
        help
@@ -350,6 +358,12 @@ config ATMEL_USART
          configured in the device tree, and input clock frequency can
          be got from the clk node.
 
          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 && ARCH_BMIPS
+       help
+         Select this to enable UART on BCM6345 SoCs.
+
 config FSL_LPUART
        bool "Freescale LPUART support"
        help
 config FSL_LPUART
        bool "Freescale LPUART support"
        help
index 4382cf93297b25a4ae35044bbd1dd31e813f795c..dca31b295c14acb2aa326d74daa29122dfa93428 100644 (file)
@@ -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_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
 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 (file)
index 0000000..db270e3
--- /dev/null
@@ -0,0 +1,300 @@
+/*
+ * Copyright (C) 2017 Álvaro Fernández Rojas <noltari@gmail.com>
+ *
+ * Derived from linux/drivers/tty/serial/bcm63xx_uart.c:
+ *     Copyright (C) 2008 Maxime Bizon <mbizon@freebox.fr>
+ *
+ * SPDX-License-Identifier:    GPL-2.0+
+ */
+
+#include <clk.h>
+#include <debug_uart.h>
+#include <errno.h>
+#include <serial.h>
+#include <asm/io.h>
+#include <asm/types.h>
+#include <dm/device.h>
+
+/* UART Control register */
+#define UART_CTL_REG                   0x0
+#define UART_CTL_RXTIMEOUT_MASK                0x1f
+#define UART_CTL_RXTIMEOUT_5           0x5
+#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_BITSPERSYM_SHIFT      12
+#define UART_CTL_BITSPERSYM_MASK       (0x3 << UART_CTL_BITSPERSYM_SHIFT)
+#define UART_CTL_BITSPERSYM_8          (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         19
+#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 FIFO Config register */
+#define UART_FIFO_CFG_REG              0x8
+#define UART_FIFO_CFG_RX_SHIFT         8
+#define UART_FIFO_CFG_RX_MASK          (0xf << UART_FIFO_CFG_RX_SHIFT)
+#define UART_FIFO_CFG_RX_4             (0x4 << UART_FIFO_CFG_RX_SHIFT)
+#define UART_FIFO_CFG_TX_SHIFT         12
+#define UART_FIFO_CFG_TX_MASK          (0xf << UART_FIFO_CFG_TX_SHIFT)
+#define UART_FIFO_CFG_TX_4             (0x4 << UART_FIFO_CFG_TX_SHIFT)
+
+/* UART Interrupt register */
+#define UART_IR_REG                    0x10
+#define UART_IR_STAT(x)                        (1 << (x))
+#define UART_IR_TXEMPTY                        5
+#define UART_IR_RXOVER                 7
+#define UART_IR_RXNOTEMPTY             11
+
+/* UART FIFO register */
+#define UART_FIFO_REG                  0x14
+#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)
+
+struct bcm6345_serial_priv {
+       void __iomem *base;
+       ulong uartclk;
+};
+
+/* enable rx & tx operation on uart */
+static void bcm6345_serial_enable(void __iomem *base)
+{
+       setbits_be32(base + UART_CTL_REG, UART_CTL_BRGEN_MASK |
+                    UART_CTL_TXEN_MASK | UART_CTL_RXEN_MASK);
+}
+
+/* disable rx & tx operation on uart */
+static void bcm6345_serial_disable(void __iomem *base)
+{
+       clrbits_be32(base + UART_CTL_REG, UART_CTL_BRGEN_MASK |
+                    UART_CTL_TXEN_MASK | UART_CTL_RXEN_MASK);
+}
+
+/* clear all unread data in rx fifo and unsent data in tx fifo */
+static void bcm6345_serial_flush(void __iomem *base)
+{
+       /* empty rx and tx fifo */
+       setbits_be32(base + UART_CTL_REG, UART_CTL_RSTRXFIFO_MASK |
+                    UART_CTL_RSTTXFIFO_MASK);
+
+       /* read any pending char to make sure all irq status are cleared */
+       readl_be(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 uart control config */
+       clrsetbits_be32(base + UART_CTL_REG,
+                       /* clear rx timeout */
+                       UART_CTL_RXTIMEOUT_MASK |
+                       /* clear stop bits */
+                       UART_CTL_STOPBITS_MASK |
+                       /* clear bits per symbol */
+                       UART_CTL_BITSPERSYM_MASK |
+                       /* clear xmit break */
+                       UART_CTL_XMITBRK_MASK |
+                       /* clear reserved bit */
+                       UART_CTL_RSVD_MASK |
+                       /* disable parity */
+                       UART_CTL_RXPAREN_MASK |
+                       UART_CTL_TXPAREN_MASK |
+                       /* disable loopback */
+                       UART_CTL_LOOPBACK_MASK,
+                       /* set timeout to 5 */
+                       UART_CTL_RXTIMEOUT_5 |
+                       /* set 8 bits/symbol */
+                       UART_CTL_BITSPERSYM_8 |
+                       /* set parity to even */
+                       UART_CTL_RXPAREVEN_MASK |
+                       UART_CTL_TXPAREVEN_MASK);
+
+       /* set uart fifo config */
+       clrsetbits_be32(base + UART_FIFO_CFG_REG,
+                       /* clear fifo config */
+                       UART_FIFO_CFG_RX_MASK |
+                       UART_FIFO_CFG_TX_MASK,
+                       /* set fifo config to 4 */
+                       UART_FIFO_CFG_RX_4 |
+                       UART_FIFO_CFG_TX_4);
+
+       /* set baud rate */
+       val = (clk / baudrate) / 16;
+       if (val & 0x1)
+               val = val;
+       else
+               val = val / 2 - 1;
+       writel_be(val, base + UART_BAUD_REG);
+
+       /* clear interrupts */
+       writel_be(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 = readl_be(priv->base + UART_IR_REG);
+
+       if (input)
+               return !!(val & UART_IR_STAT(UART_IR_RXNOTEMPTY));
+       else
+               return !(val & UART_IR_STAT(UART_IR_TXEMPTY));
+}
+
+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 = readl_be(priv->base + UART_IR_REG);
+       if (!(val & UART_IR_STAT(UART_IR_TXEMPTY)))
+               return -EAGAIN;
+
+       writel_be(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 = readl_be(priv->base + UART_IR_REG);
+       if (val & UART_IR_STAT(UART_IR_RXOVER))
+               setbits_be32(priv->base + UART_CTL_REG,
+                            UART_CTL_RSTRXFIFO_MASK);
+       if (!(val & UART_IR_STAT(UART_IR_RXNOTEMPTY)))
+               return -EAGAIN;
+
+       val = readl_be(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_ids[] = {
+       { .compatible = "brcm,bcm6345-uart" },
+       { /* sentinel */ }
+};
+
+U_BOOT_DRIVER(bcm6345_serial) = {
+       .name = "bcm6345-uart",
+       .id = UCLASS_SERIAL,
+       .of_match = bcm6345_serial_ids,
+       .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 = readl_be(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);
+       writel_be(ch, base + UART_FIFO_REG);
+       wait_xfered(base);
+}
+
+DEBUG_UART_FUNCS
+#endif