[U-Boot] [PATCH v3 14/15] bcm2835_pl011_serial: Add BCM2835 specific serial driver

Alexander Graf
2018-01-25T11:05:55Z

On bcm2835 we need to ensure we only access serial devices that are
muxed to the serial output pins of the pin header. To achieve this
for the pl011 device, add a bcm2835 specific pl011 wrapper device
that does this check but otherwise behaves like a pl011 device.

Signed-off-by: Alexander Graf <agraf@suse.de>
---
MAINTAINERS | 1 +
drivers/serial/Kconfig | 9 +++++
drivers/serial/Makefile | 1 +
drivers/serial/serial_bcm283x_pl011.c | 73 ++++++++++++++++++++++++++++++++++
drivers/serial/serial_pl01x.c | 11 ++---
drivers/serial/serial_pl01x_internal.h | 15 ++++++-
6 files changed, 101 insertions(+), 9 deletions(-)
create mode 100644 drivers/serial/serial_bcm283x_pl011.c

diff --git a/MAINTAINERS b/MAINTAINERS
index 19f645d825..c614ff2d5c 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -99,6 +99,7 @@ F: drivers/gpio/bcm2835_gpio.c
F: drivers/mmc/bcm2835_sdhci.c
F: drivers/mmc/bcm2835_sdhost.c
F: drivers/serial/serial_bcm283x_mu.c
+F: drivers/serial/serial_bcm283x_pl011.c
F: drivers/video/bcm2835.c
F: include/dm/platform_data/serial_bcm283x_mu.h
F: drivers/pinctrl/broadcom/
diff --git a/drivers/serial/Kconfig b/drivers/serial/Kconfig
index 72281a7b64..3ffedba525 100644
--- a/drivers/serial/Kconfig
+++ b/drivers/serial/Kconfig
@@ -395,6 +395,15 @@ config BCM283X_MU_SERIAL
help
Select this to enable Mini-UART support on BCM283X family of SoCs.

+config BCM283X_PL011_SERIAL
+ bool "Support for BCM283x PL011 UART"
+ depends on PL01X_SERIAL && ARCH_BCM283X
+ default y
+ help
+ Select this to enable an overriding PL011 driver for BCM283X SoCs
+ that supports automatic disable, so that it only gets used when
+ the UART is actually muxed.
+
config BCM6345_SERIAL
bool "Support for BCM6345 UART"
depends on DM_SERIAL && ARCH_BMIPS
diff --git a/drivers/serial/Makefile b/drivers/serial/Makefile
index 5ef603ab15..cac9a8b312 100644
--- a/drivers/serial/Makefile
+++ b/drivers/serial/Makefile
@@ -46,6 +46,7 @@ obj-$(CONFIG_STI_ASC_SERIAL) += serial_sti_asc.o
obj-$(CONFIG_PIC32_SERIAL) += serial_pic32.o
obj-$(CONFIG_STM32_SERIAL) += serial_stm32.o
obj-$(CONFIG_BCM283X_MU_SERIAL) += serial_bcm283x_mu.o
+obj-$(CONFIG_BCM283X_PL011_SERIAL) += serial_bcm283x_pl011.o
obj-$(CONFIG_MSM_SERIAL) += serial_msm.o
obj-$(CONFIG_MVEBU_A3700_UART) += serial_mvebu_a3700.o
obj-$(CONFIG_MPC8XX_CONS) += serial_mpc8xx.o
diff --git a/drivers/serial/serial_bcm283x_pl011.c b/drivers/serial/serial_bcm283x_pl011.c
new file mode 100644
index 0000000000..bfd39f84f3
--- /dev/null
+++ b/drivers/serial/serial_bcm283x_pl011.c
@@ -0,0 +1,73 @@
+/*
+ * Copyright (c) 2018 Alexander Graf <agraf@suse.de>
+ *
+ * SPDX-License-Identifier: GPL-2.0+
+ */
+
+#include <common.h>
+#include <dm.h>
+#include <asm/gpio.h>
+#include <dm/pinctrl.h>
+#include <dm/platform_data/serial_pl01x.h>
+#include "serial_pl01x_internal.h"
+
+/*
+ * Check if this serial device is muxed
+ *
+ * The serial device will only work properly if it has been muxed to the serial
+ * pins by firmware. Check whether that happened here.
+ *
+ * @return true if serial device is muxed, false if not
+ */
+static bool bcm283x_is_serial_muxed(void)
+{
+ int serial_gpio = 15;
+ struct udevice *dev;
+
+ if (uclass_first_device(UCLASS_PINCTRL, &dev) || !dev)
+ return false;
+
+ if (pinctrl_get_gpio_mux(dev, 0, serial_gpio) != BCM2835_GPIO_ALT0)
+ return false;
+
+ return true;
+}
+
+static int bcm283x_pl011_serial_ofdata_to_platdata(struct udevice *dev)
+{
+ struct pl01x_serial_platdata *plat = dev_get_platdata(dev);
+ int ret;
+
+ /* Don't spawn the device if it's not muxed */
+ if (!bcm283x_is_serial_muxed())
+ return -ENODEV;
+
+ ret = pl01x_serial_ofdata_to_platdata(dev);
+ if (ret)
+ return ret;
+
+ /*
+ * TODO: Reinitialization doesn't always work for now, just skip
+ * init always - we know we're already initialized
+ */
+ plat->skip_init = true;
+
+ return 0;
+}
+
+static const struct udevice_id bcm283x_pl011_serial_id[] = {
+ {.compatible = "brcm,bcm2835-pl011", .data = TYPE_PL011},
+ {}
+};
+
+U_BOOT_DRIVER(bcm283x_pl011_uart) = {
+ .name = "bcm283x_pl011",
+ .id = UCLASS_SERIAL,
+ .of_match = of_match_ptr(bcm283x_pl011_serial_id),
+ .ofdata_to_platdata = of_match_ptr(bcm283x_pl011_serial_ofdata_to_platdata),
+ .platdata_auto_alloc_size = sizeof(struct pl01x_serial_platdata),
+ .probe = pl01x_serial_probe,
+ .ops = &pl01x_serial_ops,
+ .flags = DM_FLAG_PRE_RELOC,
+ .priv_auto_alloc_size = sizeof(struct pl01x_priv),
+};
diff --git a/drivers/serial/serial_pl01x.c b/drivers/serial/serial_pl01x.c
index 2cbc84c1b4..23d9d839cb 100644
--- a/drivers/serial/serial_pl01x.c
+++ b/drivers/serial/serial_pl01x.c
@@ -273,11 +273,6 @@ __weak struct serial_device *default_serial_console(void)

#ifdef CONFIG_DM_SERIAL

-struct pl01x_priv {
- struct pl01x_regs *regs;
- enum pl01x_type type;
-};
-
static int pl01x_serial_setbrg(struct udevice *dev, int baudrate)
{
struct pl01x_serial_platdata *plat = dev_get_platdata(dev);
@@ -291,7 +286,7 @@ static int pl01x_serial_setbrg(struct udevice *dev, int baudrate)
return 0;
}

-static int pl01x_serial_probe(struct udevice *dev)
+int pl01x_serial_probe(struct udevice *dev)
{
struct pl01x_serial_platdata *plat = dev_get_platdata(dev);
struct pl01x_priv *priv = dev_get_priv(dev);
@@ -329,7 +324,7 @@ static int pl01x_serial_pending(struct udevice *dev, bool input)
return fr & UART_PL01x_FR_TXFF ? 0 : 1;
}

-static const struct dm_serial_ops pl01x_serial_ops = {
+const struct dm_serial_ops pl01x_serial_ops = {
.putc = pl01x_serial_putc,
.pending = pl01x_serial_pending,
.getc = pl01x_serial_getc,
@@ -343,7 +338,7 @@ static const struct udevice_id pl01x_serial_id[] ={
{}
};

-static int pl01x_serial_ofdata_to_platdata(struct udevice *dev)
+int pl01x_serial_ofdata_to_platdata(struct udevice *dev)
{
struct pl01x_serial_platdata *plat = dev_get_platdata(dev);
fdt_addr_t addr;
diff --git a/drivers/serial/serial_pl01x_internal.h b/drivers/serial/serial_pl01x_internal.h
index 288a4f19f5..c56dd54c7b 100644
--- a/drivers/serial/serial_pl01x_internal.h
+++ b/drivers/serial/serial_pl01x_internal.h
@@ -38,7 +38,20 @@ struct pl01x_regs {
u32 pl011_lcrh; /* 0x2C Line control register */
u32 pl011_cr; /* 0x30 Control register */
};
-#endif
+
+#ifdef CONFIG_DM_SERIAL
+
+int pl01x_serial_ofdata_to_platdata(struct udevice *dev);
+int pl01x_serial_probe(struct udevice *dev);
+extern const struct dm_serial_ops pl01x_serial_ops;
+
+struct pl01x_priv {
+ struct pl01x_regs *regs;
+ enum pl01x_type type;
+};
+
+#endif /* CONFIG_DM_SERIAL */
+#endif /* !__ASSEMBLY__ */

#define UART_PL01x_RSR_OE 0x08
#define UART_PL01x_RSR_BE 0x04
--
2.12.3