From 7f368553fcada0b38e8a096fcba4fcd64e0d2020 Mon Sep 17 00:00:00 2001 From: Masahiro Yamada Date: Fri, 3 Oct 2014 19:21:05 +0900 Subject: [PATCH] serial: add UniPhier serial driver The driver for on-chip UART used on Panasonic UniPhier platform. Signed-off-by: Masahiro Yamada --- drivers/serial/Makefile | 1 + drivers/serial/serial.c | 2 + drivers/serial/serial_uniphier.c | 204 +++++++++++++++++++++++++++++++ 3 files changed, 207 insertions(+) create mode 100644 drivers/serial/serial_uniphier.c diff --git a/drivers/serial/Makefile b/drivers/serial/Makefile index 853a8c6919..b4f299bb0e 100644 --- a/drivers/serial/Makefile +++ b/drivers/serial/Makefile @@ -39,6 +39,7 @@ obj-$(CONFIG_FSL_LPUART) += serial_lpuart.o obj-$(CONFIG_MXS_AUART) += mxs_auart.o obj-$(CONFIG_ARC_SERIAL) += serial_arc.o obj-$(CONFIG_TEGRA_SERIAL) += serial_tegra.o +obj-$(CONFIG_UNIPHIER_SERIAL) += serial_uniphier.o ifndef CONFIG_SPL_BUILD obj-$(CONFIG_USB_TTY) += usbtty.o diff --git a/drivers/serial/serial.c b/drivers/serial/serial.c index bbe60af627..82fbbd92e2 100644 --- a/drivers/serial/serial.c +++ b/drivers/serial/serial.c @@ -157,6 +157,7 @@ serial_initfunc(sh_serial_initialize); serial_initfunc(arm_dcc_initialize); serial_initfunc(mxs_auart_initialize); serial_initfunc(arc_serial_initialize); +serial_initfunc(uniphier_serial_initialize); /** * serial_register() - Register serial driver with serial driver core @@ -250,6 +251,7 @@ void serial_initialize(void) arm_dcc_initialize(); mxs_auart_initialize(); arc_serial_initialize(); + uniphier_serial_initialize(); serial_assign(default_serial_console()->name); } diff --git a/drivers/serial/serial_uniphier.c b/drivers/serial/serial_uniphier.c new file mode 100644 index 0000000000..f8c9d921e2 --- /dev/null +++ b/drivers/serial/serial_uniphier.c @@ -0,0 +1,204 @@ +/* + * Copyright (C) 2012-2014 Panasonic Corporation + * Author: Masahiro Yamada + * + * Based on serial_ns16550.c + * (C) Copyright 2000 + * Rob Taylor, Flying Pig Systems. robt@flyingpig.com. + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include +#include + +#define UART_REG(x) \ + u8 x; \ + u8 postpad_##x[3]; + +/* + * Note: Register map is slightly different from that of 16550. + */ +struct uniphier_serial { + UART_REG(rbr); /* 0x00 */ + UART_REG(ier); /* 0x04 */ + UART_REG(iir); /* 0x08 */ + UART_REG(fcr); /* 0x0c */ + u8 mcr; /* 0x10 */ + u8 lcr; + u16 __postpad; + UART_REG(lsr); /* 0x14 */ + UART_REG(msr); /* 0x18 */ + u32 __none1; + u32 __none2; + u16 dlr; + u16 __postpad2; +}; + +#define thr rbr + +/* + * These are the definitions for the Line Control Register + */ +#define UART_LCR_WLS_8 0x03 /* 8 bit character length */ + +/* + * These are the definitions for the Line Status Register + */ +#define UART_LSR_DR 0x01 /* Data ready */ +#define UART_LSR_THRE 0x20 /* Xmit holding register empty */ + +DECLARE_GLOBAL_DATA_PTR; + +static void uniphier_serial_init(struct uniphier_serial *port) +{ + const unsigned int mode_x_div = 16; + unsigned int divisor; + + writeb(UART_LCR_WLS_8, &port->lcr); + + divisor = DIV_ROUND_CLOSEST(CONFIG_SYS_UNIPHIER_UART_CLK, + mode_x_div * gd->baudrate); + + writew(divisor, &port->dlr); +} + +static void uniphier_serial_setbrg(struct uniphier_serial *port) +{ + uniphier_serial_init(port); +} + +static int uniphier_serial_tstc(struct uniphier_serial *port) +{ + return (readb(&port->lsr) & UART_LSR_DR) != 0; +} + +static int uniphier_serial_getc(struct uniphier_serial *port) +{ + while (!uniphier_serial_tstc(port)) + ; + + return readb(&port->rbr); +} + +static void uniphier_serial_putc(struct uniphier_serial *port, const char c) +{ + if (c == '\n') + uniphier_serial_putc(port, '\r'); + + while (!(readb(&port->lsr) & UART_LSR_THRE)) + ; + + writeb(c, &port->thr); +} + +static struct uniphier_serial *serial_ports[4] = { +#ifdef CONFIG_SYS_UNIPHIER_SERIAL_BASE0 + (struct uniphier_serial *)CONFIG_SYS_UNIPHIER_SERIAL_BASE0, +#else + NULL, +#endif +#ifdef CONFIG_SYS_UNIPHIER_SERIAL_BASE1 + (struct uniphier_serial *)CONFIG_SYS_UNIPHIER_SERIAL_BASE1, +#else + NULL, +#endif +#ifdef CONFIG_SYS_UNIPHIER_SERIAL_BASE2 + (struct uniphier_serial *)CONFIG_SYS_UNIPHIER_SERIAL_BASE2, +#else + NULL, +#endif +#ifdef CONFIG_SYS_UNIPHIER_SERIAL_BASE3 + (struct uniphier_serial *)CONFIG_SYS_UNIPHIER_SERIAL_BASE3, +#else + NULL, +#endif +}; + +/* Multi serial device functions */ +#define DECLARE_ESERIAL_FUNCTIONS(port) \ + static int eserial##port##_init(void) \ + { \ + uniphier_serial_init(serial_ports[port]); \ + return 0 ; \ + } \ + static void eserial##port##_setbrg(void) \ + { \ + uniphier_serial_setbrg(serial_ports[port]); \ + } \ + static int eserial##port##_getc(void) \ + { \ + return uniphier_serial_getc(serial_ports[port]); \ + } \ + static int eserial##port##_tstc(void) \ + { \ + return uniphier_serial_tstc(serial_ports[port]); \ + } \ + static void eserial##port##_putc(const char c) \ + { \ + uniphier_serial_putc(serial_ports[port], c); \ + } + +/* Serial device descriptor */ +#define INIT_ESERIAL_STRUCTURE(port, __name) { \ + .name = __name, \ + .start = eserial##port##_init, \ + .stop = NULL, \ + .setbrg = eserial##port##_setbrg, \ + .getc = eserial##port##_getc, \ + .tstc = eserial##port##_tstc, \ + .putc = eserial##port##_putc, \ + .puts = default_serial_puts, \ +} + +#if defined(CONFIG_SYS_UNIPHIER_SERIAL_BASE0) +DECLARE_ESERIAL_FUNCTIONS(0); +struct serial_device uniphier_serial0_device = + INIT_ESERIAL_STRUCTURE(0, "ttyS0"); +#endif +#if defined(CONFIG_SYS_UNIPHIER_SERIAL_BASE1) +DECLARE_ESERIAL_FUNCTIONS(1); +struct serial_device uniphier_serial1_device = + INIT_ESERIAL_STRUCTURE(1, "ttyS1"); +#endif +#if defined(CONFIG_SYS_UNIPHIER_SERIAL_BASE2) +DECLARE_ESERIAL_FUNCTIONS(2); +struct serial_device uniphier_serial2_device = + INIT_ESERIAL_STRUCTURE(2, "ttyS2"); +#endif +#if defined(CONFIG_SYS_UNIPHIER_SERIAL_BASE3) +DECLARE_ESERIAL_FUNCTIONS(3); +struct serial_device uniphier_serial3_device = + INIT_ESERIAL_STRUCTURE(3, "ttyS3"); +#endif + +__weak struct serial_device *default_serial_console(void) +{ +#if defined(CONFIG_SYS_UNIPHIER_SERIAL_BASE0) + return &uniphier_serial0_device; +#elif defined(CONFIG_SYS_UNIPHIER_SERIAL_BASE1) + return &uniphier_serial1_device; +#elif defined(CONFIG_SYS_UNIPHIER_SERIAL_BASE2) + return &uniphier_serial2_device; +#elif defined(CONFIG_SYS_UNIPHIER_SERIAL_BASE3) + return &uniphier_serial3_device; +#else +#error "No uniphier serial ports configured." +#endif +} + +void uniphier_serial_initialize(void) +{ +#if defined(CONFIG_SYS_UNIPHIER_SERIAL_BASE0) + serial_register(&uniphier_serial0_device); +#endif +#if defined(CONFIG_SYS_UNIPHIER_SERIAL_BASE1) + serial_register(&uniphier_serial1_device); +#endif +#if defined(CONFIG_SYS_UNIPHIER_SERIAL_BASE2) + serial_register(&uniphier_serial2_device); +#endif +#if defined(CONFIG_SYS_UNIPHIER_SERIAL_BASE3) + serial_register(&uniphier_serial3_device); +#endif +} -- 2.39.5