diff options
author | Stephen Warren <swarren@wwwdotorg.org> | 2016-03-19 03:41:38 (GMT) |
---|---|---|
committer | Tom Rini <trini@konsulko.com> | 2016-03-27 13:12:52 (GMT) |
commit | 3917c26909e4021c73073672865ec456eb51d640 (patch) | |
tree | 80cdf6b549ce0af90fc66d43cee1a8a10c83e455 /drivers | |
parent | 92667e89fc5906531a7a1ff247e1ffec61b55b13 (diff) | |
download | u-boot-3917c26909e4021c73073672865ec456eb51d640.tar.xz |
serial: add BCM283x mini UART driver
The RPi3 typically uses the regular UART for high-speed communication with
the Bluetooth device, leaving us the mini UART to use for the serial
console. Add support for this UART so we can use it.
Signed-off-by: Stephen Warren <swarren@wwwdotorg.org>
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/serial/Makefile | 1 | ||||
-rw-r--r-- | drivers/serial/serial_bcm283x_mu.c | 140 |
2 files changed, 141 insertions, 0 deletions
diff --git a/drivers/serial/Makefile b/drivers/serial/Makefile index 05bdf56..ee7147a 100644 --- a/drivers/serial/Makefile +++ b/drivers/serial/Makefile @@ -38,6 +38,7 @@ obj-$(CONFIG_UNIPHIER_SERIAL) += serial_uniphier.o obj-$(CONFIG_STM32_SERIAL) += serial_stm32.o obj-$(CONFIG_PIC32_SERIAL) += serial_pic32.o obj-$(CONFIG_STM32X7_SERIAL) += serial_stm32x7.o +obj-$(CONFIG_BCM283X_MU_SERIAL) += serial_bcm283x_mu.o ifndef CONFIG_SPL_BUILD obj-$(CONFIG_USB_TTY) += usbtty.o diff --git a/drivers/serial/serial_bcm283x_mu.c b/drivers/serial/serial_bcm283x_mu.c new file mode 100644 index 0000000..fc36bc0 --- /dev/null +++ b/drivers/serial/serial_bcm283x_mu.c @@ -0,0 +1,140 @@ +/* + * (C) Copyright 2016 Stephen Warren <swarren@wwwdotorg.org> + * + * Derived from pl01x code: + * + * (C) Copyright 2000 + * Rob Taylor, Flying Pig Systems. robt@flyingpig.com. + * + * (C) Copyright 2004 + * ARM Ltd. + * Philippe Robin, <philippe.robin@arm.com> + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +/* Simple U-Boot driver for the BCM283x mini UART */ + +#include <common.h> +#include <dm.h> +#include <errno.h> +#include <watchdog.h> +#include <asm/io.h> +#include <serial.h> +#include <dm/platform_data/serial_bcm283x_mu.h> +#include <linux/compiler.h> +#include <fdtdec.h> + +struct bcm283x_mu_regs { + u32 io; + u32 iir; + u32 ier; + u32 lcr; + u32 mcr; + u32 lsr; + u32 msr; + u32 scratch; + u32 cntl; + u32 stat; + u32 baud; +}; + +#define BCM283X_MU_LCR_DATA_SIZE_8 3 + +#define BCM283X_MU_LSR_TX_IDLE BIT(6) +/* This actually means not full, but is named not empty in the docs */ +#define BCM283X_MU_LSR_TX_EMPTY BIT(5) +#define BCM283X_MU_LSR_RX_READY BIT(0) + +struct bcm283x_mu_priv { + struct bcm283x_mu_regs *regs; +}; + +static int bcm283x_mu_serial_setbrg(struct udevice *dev, int baudrate) +{ + struct bcm283x_mu_serial_platdata *plat = dev_get_platdata(dev); + struct bcm283x_mu_priv *priv = dev_get_priv(dev); + struct bcm283x_mu_regs *regs = priv->regs; + u32 divider; + + if (plat->skip_init) + return 0; + + divider = plat->clock / (baudrate * 8); + + writel(BCM283X_MU_LCR_DATA_SIZE_8, ®s->lcr); + writel(divider - 1, ®s->baud); + + return 0; +} + +static int bcm283x_mu_serial_probe(struct udevice *dev) +{ + struct bcm283x_mu_serial_platdata *plat = dev_get_platdata(dev); + struct bcm283x_mu_priv *priv = dev_get_priv(dev); + + priv->regs = (struct bcm283x_mu_regs *)plat->base; + + return 0; +} + +static int bcm283x_mu_serial_getc(struct udevice *dev) +{ + struct bcm283x_mu_priv *priv = dev_get_priv(dev); + struct bcm283x_mu_regs *regs = priv->regs; + u32 data; + + /* Wait until there is data in the FIFO */ + if (!(readl(®s->lsr) & BCM283X_MU_LSR_RX_READY)) + return -EAGAIN; + + data = readl(®s->io); + + return (int)data; +} + +static int bcm283x_mu_serial_putc(struct udevice *dev, const char data) +{ + struct bcm283x_mu_priv *priv = dev_get_priv(dev); + struct bcm283x_mu_regs *regs = priv->regs; + + /* Wait until there is space in the FIFO */ + if (!(readl(®s->lsr) & BCM283X_MU_LSR_TX_EMPTY)) + return -EAGAIN; + + /* Send the character */ + writel(data, ®s->io); + + return 0; +} + +static int bcm283x_mu_serial_pending(struct udevice *dev, bool input) +{ + struct bcm283x_mu_priv *priv = dev_get_priv(dev); + struct bcm283x_mu_regs *regs = priv->regs; + unsigned int lsr = readl(®s->lsr); + + if (input) { + WATCHDOG_RESET(); + return lsr & BCM283X_MU_LSR_RX_READY; + } else { + return !(lsr & BCM283X_MU_LSR_TX_IDLE); + } +} + +static const struct dm_serial_ops bcm283x_mu_serial_ops = { + .putc = bcm283x_mu_serial_putc, + .pending = bcm283x_mu_serial_pending, + .getc = bcm283x_mu_serial_getc, + .setbrg = bcm283x_mu_serial_setbrg, +}; + +U_BOOT_DRIVER(serial_bcm283x_mu) = { + .name = "serial_bcm283x_mu", + .id = UCLASS_SERIAL, + .platdata_auto_alloc_size = sizeof(struct bcm283x_mu_serial_platdata), + .probe = bcm283x_mu_serial_probe, + .ops = &bcm283x_mu_serial_ops, + .flags = DM_FLAG_PRE_RELOC, + .priv_auto_alloc_size = sizeof(struct bcm283x_mu_priv), +}; |