aboutsummaryrefslogtreecommitdiffstats
path: root/roms/u-boot/drivers/serial/serial_uniphier.c
diff options
context:
space:
mode:
Diffstat (limited to 'roms/u-boot/drivers/serial/serial_uniphier.c')
-rw-r--r--roms/u-boot/drivers/serial/serial_uniphier.c177
1 files changed, 177 insertions, 0 deletions
diff --git a/roms/u-boot/drivers/serial/serial_uniphier.c b/roms/u-boot/drivers/serial/serial_uniphier.c
new file mode 100644
index 000000000..27e4b92c3
--- /dev/null
+++ b/roms/u-boot/drivers/serial/serial_uniphier.c
@@ -0,0 +1,177 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (C) 2012-2015 Panasonic Corporation
+ * Copyright (C) 2015-2016 Socionext Inc.
+ * Author: Masahiro Yamada <yamada.masahiro@socionext.com>
+ */
+
+#include <common.h>
+#include <dm.h>
+#include <linux/bitfield.h>
+#include <linux/bitops.h>
+#include <linux/bug.h>
+#include <linux/io.h>
+#include <linux/serial_reg.h>
+#include <linux/sizes.h>
+#include <linux/errno.h>
+#include <serial.h>
+#include <fdtdec.h>
+
+#define UNIPHIER_UART_REGSHIFT 2
+
+#define UNIPHIER_UART_RX (0 << (UNIPHIER_UART_REGSHIFT))
+#define UNIPHIER_UART_TX UNIPHIER_UART_RX
+/* bit[15:8] = CHAR, bit[7:0] = FCR */
+#define UNIPHIER_UART_CHAR_FCR (3 << (UNIPHIER_UART_REGSHIFT))
+#define UNIPHIER_UART_FCR_MASK GENMASK(7, 0)
+/* bit[15:8] = LCR, bit[7:0] = MCR */
+#define UNIPHIER_UART_LCR_MCR (4 << (UNIPHIER_UART_REGSHIFT))
+#define UNIPHIER_UART_LCR_MASK GENMASK(15, 8)
+#define UNIPHIER_UART_LSR (5 << (UNIPHIER_UART_REGSHIFT))
+/* Divisor Latch Register */
+#define UNIPHIER_UART_DLR (9 << (UNIPHIER_UART_REGSHIFT))
+
+struct uniphier_serial_priv {
+ void __iomem *membase;
+ unsigned int uartclk;
+};
+
+static int uniphier_serial_setbrg(struct udevice *dev, int baudrate)
+{
+ struct uniphier_serial_priv *priv = dev_get_priv(dev);
+ static const unsigned int mode_x_div = 16;
+ unsigned int divisor;
+
+ divisor = DIV_ROUND_CLOSEST(priv->uartclk, mode_x_div * baudrate);
+
+ /* flush the trasmitter before changing hw setting */
+ while (!(readl(priv->membase + UNIPHIER_UART_LSR) & UART_LSR_TEMT))
+ ;
+
+ writel(divisor, priv->membase + UNIPHIER_UART_DLR);
+
+ return 0;
+}
+
+static int uniphier_serial_getc(struct udevice *dev)
+{
+ struct uniphier_serial_priv *priv = dev_get_priv(dev);
+
+ if (!(readl(priv->membase + UNIPHIER_UART_LSR) & UART_LSR_DR))
+ return -EAGAIN;
+
+ return readl(priv->membase + UNIPHIER_UART_RX);
+}
+
+static int uniphier_serial_putc(struct udevice *dev, const char c)
+{
+ struct uniphier_serial_priv *priv = dev_get_priv(dev);
+
+ if (!(readl(priv->membase + UNIPHIER_UART_LSR) & UART_LSR_THRE))
+ return -EAGAIN;
+
+ writel(c, priv->membase + UNIPHIER_UART_TX);
+
+ return 0;
+}
+
+static int uniphier_serial_pending(struct udevice *dev, bool input)
+{
+ struct uniphier_serial_priv *priv = dev_get_priv(dev);
+
+ if (input)
+ return readl(priv->membase + UNIPHIER_UART_LSR) & UART_LSR_DR;
+ else
+ return !(readl(priv->membase + UNIPHIER_UART_LSR) & UART_LSR_THRE);
+}
+
+/*
+ * SPL does not have enough memory footprint for the clock driver.
+ * Hardcode clock frequency for each SoC.
+ */
+struct uniphier_serial_clk_data {
+ const char *compatible;
+ unsigned int clk_rate;
+};
+
+static const struct uniphier_serial_clk_data uniphier_serial_clk_data[] = {
+ { .compatible = "socionext,uniphier-ld4", .clk_rate = 36864000 },
+ { .compatible = "socionext,uniphier-pro4", .clk_rate = 73728000 },
+ { .compatible = "socionext,uniphier-sld8", .clk_rate = 80000000 },
+ { .compatible = "socionext,uniphier-pro5", .clk_rate = 73728000 },
+ { .compatible = "socionext,uniphier-pxs2", .clk_rate = 88888888 },
+ { .compatible = "socionext,uniphier-ld6b", .clk_rate = 88888888 },
+ { .compatible = "socionext,uniphier-ld11", .clk_rate = 58823529 },
+ { .compatible = "socionext,uniphier-ld20", .clk_rate = 58823529 },
+ { .compatible = "socionext,uniphier-pxs3", .clk_rate = 58823529 },
+ { /* sentinel */ },
+};
+
+static int uniphier_serial_probe(struct udevice *dev)
+{
+ struct uniphier_serial_priv *priv = dev_get_priv(dev);
+ const struct uniphier_serial_clk_data *clk_data;
+ ofnode root_node;
+ fdt_addr_t base;
+ u32 tmp;
+
+ base = dev_read_addr(dev);
+ if (base == FDT_ADDR_T_NONE)
+ return -EINVAL;
+
+ priv->membase = devm_ioremap(dev, base, SZ_64);
+ if (!priv->membase)
+ return -ENOMEM;
+
+ root_node = ofnode_path("/");
+ clk_data = uniphier_serial_clk_data;
+ while (clk_data->compatible) {
+ if (ofnode_device_is_compatible(root_node,
+ clk_data->compatible))
+ break;
+ clk_data++;
+ }
+
+ if (WARN_ON(!clk_data->compatible))
+ return -ENOTSUPP;
+
+ priv->uartclk = clk_data->clk_rate;
+
+ /* flush the trasmitter before changing hw setting */
+ while (!(readl(priv->membase + UNIPHIER_UART_LSR) & UART_LSR_TEMT))
+ ;
+
+ /* enable FIFO */
+ tmp = readl(priv->membase + UNIPHIER_UART_CHAR_FCR);
+ tmp &= ~UNIPHIER_UART_FCR_MASK;
+ tmp |= FIELD_PREP(UNIPHIER_UART_FCR_MASK, UART_FCR_ENABLE_FIFO);
+ writel(tmp, priv->membase + UNIPHIER_UART_CHAR_FCR);
+
+ tmp = readl(priv->membase + UNIPHIER_UART_LCR_MCR);
+ tmp &= ~UNIPHIER_UART_LCR_MASK;
+ tmp |= FIELD_PREP(UNIPHIER_UART_LCR_MASK, UART_LCR_WLEN8);
+ writel(tmp, priv->membase + UNIPHIER_UART_LCR_MCR);
+
+ return 0;
+}
+
+static const struct udevice_id uniphier_uart_of_match[] = {
+ { .compatible = "socionext,uniphier-uart" },
+ { /* sentinel */ }
+};
+
+static const struct dm_serial_ops uniphier_serial_ops = {
+ .setbrg = uniphier_serial_setbrg,
+ .getc = uniphier_serial_getc,
+ .putc = uniphier_serial_putc,
+ .pending = uniphier_serial_pending,
+};
+
+U_BOOT_DRIVER(uniphier_serial) = {
+ .name = "uniphier-uart",
+ .id = UCLASS_SERIAL,
+ .of_match = uniphier_uart_of_match,
+ .probe = uniphier_serial_probe,
+ .priv_auto = sizeof(struct uniphier_serial_priv),
+ .ops = &uniphier_serial_ops,
+};