@@ -28,10 +28,13 @@
#include <linux/serial_8250.h>
#include <linux/pm_runtime.h>
+#include <linux/console.h>
+
#ifdef CONFIG_SERIAL_OMAP
#include <plat/omap-serial.h>
#endif
+#include <plat/serial.h>
#include <plat/common.h>
#include <plat/board.h>
#include <plat/clock.h>
@@ -39,6 +42,8 @@
#include <plat/omap_hwmod.h>
#include <plat/omap_device.h>
+#include <asm/memory.h>
+
#include "prm.h"
#include "pm.h"
#include "cm.h"
@@ -51,6 +56,8 @@
#define UART_ERRATA_FIFO_FULL_ABORT (0x1 << 0)
#define UART_ERRATA_i202_MDR1_ACCESS (0x1 << 1)
+#define uart_id_to_ttys_id(u) (u - 1)
+
/*
* NOTE: By default the serial timeout is disabled as it causes lost characters
* over the serial ports. This means that the UART clocks will stay on until
@@ -106,6 +113,83 @@ static LIST_HEAD(uart_list);
static u8 num_uarts;
/*
+ * early_console_uart: if earlyconsole is enabled and active, the UART
+ * ID (e.g., 1, 2, 3, 4) will be stored here. '0' means earlyconsole
+ * is disabled or some problem occurred during earlyconsole handling.
+ */
+static int early_console_uart;
+
+#define for_each_console(con) \
+ for (con = console_drivers; con != NULL; con = con->next)
+
+/* XXX belongs in kernel/printk.c or some earlyconsole file */
+/* XXX The "earlycon" string is an ugly hack */
+int _is_early_console_enabled(void)
+{
+ int ret = 0;
+ struct console *c;
+
+ acquire_console_sem();
+ for_each_console(c)
+ if (!strcmp("earlycon", c->name))
+ ret = 1;
+ release_console_sem();
+
+ return ret;
+}
+
+/* XXX document */
+static int _get_early_console_uart(void)
+{
+ u32 v;
+ int u = -EINVAL;
+
+ v = __raw_readl(phys_to_virt(OMAP_UART_INFO));
+ /* XXX see the OMAP debug-macro.S for this table */
+ switch (v) {
+ case 0:
+ case OMAP2UART1:
+ u = 1;
+ break;
+ case OMAP2UART2:
+ u = 2;
+ break;
+ case OMAP2UART3:
+ case OMAP3UART3:
+ case OMAP4UART3:
+ u = 3;
+ break;
+ case OMAP3UART4:
+ case OMAP4UART4:
+ u = 4;
+ break;
+ case ZOOM_UART:
+ WARN(1, "omap serial: ZOOM UART in use - does that go through "
+ "the OMAP serial code?\n");
+ break;
+ default:
+ WARN(1, "omap serial: unknown serial port in use!\n");
+ }
+
+ return u;
+}
+
+/* XXX document */
+static void _store_early_console_uart_id(void)
+{
+ int uart_id;
+
+ if (_is_early_console_enabled()) {
+ uart_id = _get_early_console_uart();
+ if (uart_id > 0) {
+ early_console_uart = uart_id;
+ pr_info("omap serial: early console active on UART%d (ttyS%d)\n",
+ uart_id, uart_id_to_ttys_id(uart_id));
+ }
+ }
+}
+
+/*
* Since these idle/enable hooks are used in the idle path itself
* which has interrupts disabled, use the non-locking versions of
* the hwmod enable/disable functions.
@@ -801,6 +885,17 @@ void __init omap_serial_init_port(int port)
oh->dev_attr = uart;
/*
+ * XXX How do we know whether the console is on this UART or not?
+ * We should only call acquire_console_sem() if the console is on
+ * this UART.
+ *
+ * Of course, the console is not the only thing that could be using
+ * this UART.
+ */
+ if (uart->num == uart_id_to_ttys_id(early_console_uart))
+ acquire_console_sem();
+
+ /*
* Because of early UART probing, UART did not get idled
* on init. Now that omap_device is ready, ensure full idle
* before doing omap_device_enable().
@@ -824,6 +919,9 @@ void __init omap_serial_init_port(int port)
omap_uart_block_sleep(uart);
uart->timeout = DEFAULT_TIMEOUT;
+ if (uart->num == uart_id_to_ttys_id(early_console_uart))
+ release_console_sem();
+
if ((cpu_is_omap34xx() && uart->padconf) ||
(uart->wk_en && uart->wk_mask)) {
device_init_wakeup(&od->pdev.dev, true);
@@ -846,6 +944,8 @@ void __init omap_serial_init(void)
{
struct omap_uart_state *uart;
+ _store_early_console_uart_id();
+
list_for_each_entry(uart, &uart_list, node)
omap_serial_init_port(uart->num);
}