/* * Copyright (c) 2011 The Chromium OS Authors. * See file CREDITS for list of people who contributed to this * project. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of * the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, * MA 02111-1307 USA */ /* * This is a serial port provided by the flattened device tree. It works * by selecting a compiled in driver according to the setting in the FDT. */ #include #include #include #include #include DECLARE_GLOBAL_DATA_PTR; /* * We need these structures to be outside BSS since they are accessed before * relocation. */ static struct serial_device console = { "not_in_bss" }; struct fdt_uart console_uart = { -1U }; /* Access the console - this may need to be a function */ #define DECLARE_CONSOLE struct fdt_uart *uart = &console_uart /* Initialize the serial port */ static int fserial_init(void) { DECLARE_CONSOLE; switch (uart->compat) { #ifdef CONFIG_SYS_NS16550 case COMPAT_SERIAL_NS16550: #ifdef CONFIG_SYS_NS16550_RUNTIME_MAPPED NS16550_is_io_mapped(uart->io_mapped); #endif NS16550_init((NS16550_t)uart->reg, uart->divisor); break; #endif default: break; } #ifdef CONFIG_SILENT_CONSOLE /* if the console UART wants to be silent, do this now */ if (uart->silent) gd->flags |= GD_FLG_SILENT; #endif return 0; } static void fserial_putc(const char c) { DECLARE_CONSOLE; if (c == '\n') fserial_putc('\r'); switch (uart->compat) { #ifdef CONFIG_SYS_NS16550 case COMPAT_SERIAL_NS16550 : #ifdef CONFIG_SYS_NS16550_RUNTIME_MAPPED NS16550_is_io_mapped(uart->io_mapped); #endif NS16550_putc((NS16550_t)uart->reg, c); break; #endif default: break; } } static void fserial_puts(const char *s) { while (*s) fserial_putc(*s++); } static int fserial_getc(void) { DECLARE_CONSOLE; switch (uart->compat) { #ifdef CONFIG_SYS_NS16550 case COMPAT_SERIAL_NS16550 : #ifdef CONFIG_SYS_NS16550_RUNTIME_MAPPED NS16550_is_io_mapped(uart->io_mapped); #endif return NS16550_getc((NS16550_t)uart->reg, uart->id); #endif default: break; } /* hang */ for (;;) ; return 0; } static int fserial_tstc(void) { DECLARE_CONSOLE; switch (uart->compat) { #ifdef CONFIG_SYS_NS16550 case COMPAT_SERIAL_NS16550 : #ifdef CONFIG_SYS_NS16550_RUNTIME_MAPPED NS16550_is_io_mapped(uart->io_mapped); #endif return NS16550_tstc((NS16550_t)uart->reg, uart->id); #endif default: break; } return 0; } static void fserial_setbrg(void) { DECLARE_CONSOLE; uart->baudrate = gd->baudrate; fdt_decode_uart_calc_divisor(uart); switch (uart->compat) { #ifdef CONFIG_SYS_NS16550 case COMPAT_SERIAL_NS16550 : #ifdef CONFIG_SYS_NS16550_RUNTIME_MAPPED NS16550_is_io_mapped(uart->io_mapped); #endif NS16550_reinit((NS16550_t)uart->reg, uart->divisor); break; #endif default: break; } } static struct serial_device *get_console(void) { if (fdt_decode_uart_console(gd->blob, &console_uart, gd->baudrate)) return NULL; strcpy(console.name, "serial"); strcpy(console.ctlr, "fdt"); console.init = fserial_init; console.uninit = NULL; console.setbrg = fserial_setbrg; console.getc = fserial_getc; console.tstc = fserial_tstc; console.putc = fserial_putc; console.puts = fserial_puts; return &console; } struct serial_device *serial_fdt_get_console_f(void) { /* if the uart isn't already set up, do it now */ if (console_uart.reg == -1U) return get_console(); /* otherwise just return the current information */ return &console; } struct serial_device *serial_fdt_get_console_r(void) { /* * Relocation moves all our function pointers, so we need to set up * things again. This function will only be called once. * * We cannot do the -1 check as in serial_fdt_get_console_f * because it will be -1 if that function has been ever been called. * However, the function pointers set up by serial_fdt_get_console_f * will be pre-relocation values, so we must re-calculate them. */ return get_console(); }