/* * Copyright (C) 2012-2015 Masahiro Yamada * * SPDX-License-Identifier: GPL-2.0+ */ #include #include #include #include #include #include #include #include /* * Note: Register map is slightly different from that of 16550. */ struct uniphier_serial { u32 rx; /* In: Receive buffer */ #define tx rx /* Out: Transmit buffer */ u32 ier; /* Interrupt Enable Register */ u32 iir; /* In: Interrupt ID Register */ u32 char_fcr; /* Charactor / FIFO Control Register */ u32 lcr_mcr; /* Line/Modem Control Register */ #define LCR_SHIFT 8 #define LCR_MASK (0xff << (LCR_SHIFT)) u32 lsr; /* In: Line Status Register */ u32 msr; /* In: Modem Status Register */ u32 __rsv0; u32 __rsv1; u32 dlr; /* Divisor Latch Register */ }; struct uniphier_serial_private_data { struct uniphier_serial __iomem *membase; unsigned int uartclk; }; #define uniphier_serial_port(dev) \ ((struct uniphier_serial_private_data *)dev_get_priv(dev))->membase static int uniphier_serial_setbrg(struct udevice *dev, int baudrate) { struct uniphier_serial_private_data *priv = dev_get_priv(dev); struct uniphier_serial __iomem *port = uniphier_serial_port(dev); const unsigned int mode_x_div = 16; unsigned int divisor; divisor = DIV_ROUND_CLOSEST(priv->uartclk, mode_x_div * baudrate); writel(divisor, &port->dlr); return 0; } static int uniphier_serial_getc(struct udevice *dev) { struct uniphier_serial __iomem *port = uniphier_serial_port(dev); if (!(readl(&port->lsr) & UART_LSR_DR)) return -EAGAIN; return readl(&port->rx); } static int uniphier_serial_putc(struct udevice *dev, const char c) { struct uniphier_serial __iomem *port = uniphier_serial_port(dev); if (!(readl(&port->lsr) & UART_LSR_THRE)) return -EAGAIN; writel(c, &port->tx); return 0; } static int uniphier_serial_pending(struct udevice *dev, bool input) { struct uniphier_serial __iomem *port = uniphier_serial_port(dev); if (input) return readl(&port->lsr) & UART_LSR_DR; else return !(readl(&port->lsr) & UART_LSR_THRE); } static int uniphier_serial_probe(struct udevice *dev) { DECLARE_GLOBAL_DATA_PTR; struct uniphier_serial_private_data *priv = dev_get_priv(dev); struct uniphier_serial __iomem *port; fdt_addr_t base; u32 tmp; base = dev_get_addr(dev); if (base == FDT_ADDR_T_NONE) return -EINVAL; port = map_sysmem(base, SZ_64); if (!port) return -ENOMEM; priv->membase = port; priv->uartclk = fdtdec_get_int(gd->fdt_blob, dev->of_offset, "clock-frequency", 0); tmp = readl(&port->lcr_mcr); tmp &= ~LCR_MASK; tmp |= UART_LCR_WLEN8 << LCR_SHIFT; writel(tmp, &port->lcr_mcr); return 0; } static int uniphier_serial_remove(struct udevice *dev) { unmap_sysmem(uniphier_serial_port(dev)); 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, .remove = uniphier_serial_remove, .priv_auto_alloc_size = sizeof(struct uniphier_serial_private_data), .ops = &uniphier_serial_ops, };