| /* |
| * Copyright (C) 2012-2014 Panasonic Corporation |
| * Author: Masahiro Yamada <yamada.m@jp.panasonic.com> |
| * |
| * Based on serial_ns16550.c |
| * (C) Copyright 2000 |
| * Rob Taylor, Flying Pig Systems. robt@flyingpig.com. |
| * |
| * SPDX-License-Identifier: GPL-2.0+ |
| */ |
| |
| #include <common.h> |
| #include <serial.h> |
| |
| #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 |
| } |