|
@@ -0,0 +1,118 @@
|
|
|
+/*
|
|
|
+ * Earlyprintk support.
|
|
|
+ *
|
|
|
+ * Copyright (C) 2012 ARM Ltd.
|
|
|
+ * Author: Catalin Marinas <catalin.marinas@arm.com>
|
|
|
+ *
|
|
|
+ * This program is free software: you can redistribute it and/or modify
|
|
|
+ * it under the terms of the GNU General Public License version 2 as
|
|
|
+ * published by the Free Software Foundation.
|
|
|
+ *
|
|
|
+ * 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, see <http://www.gnu.org/licenses/>.
|
|
|
+ */
|
|
|
+#include <linux/kernel.h>
|
|
|
+#include <linux/console.h>
|
|
|
+#include <linux/init.h>
|
|
|
+#include <linux/string.h>
|
|
|
+#include <linux/mm.h>
|
|
|
+#include <linux/io.h>
|
|
|
+
|
|
|
+#include <linux/amba/serial.h>
|
|
|
+
|
|
|
+static void __iomem *early_base;
|
|
|
+static void (*printch)(char ch);
|
|
|
+
|
|
|
+/*
|
|
|
+ * PL011 single character TX.
|
|
|
+ */
|
|
|
+static void pl011_printch(char ch)
|
|
|
+{
|
|
|
+ while (readl_relaxed(early_base + UART01x_FR) & UART01x_FR_TXFF)
|
|
|
+ ;
|
|
|
+ writeb_relaxed(ch, early_base + UART01x_DR);
|
|
|
+ while (readl_relaxed(early_base + UART01x_FR) & UART01x_FR_BUSY)
|
|
|
+ ;
|
|
|
+}
|
|
|
+
|
|
|
+struct earlycon_match {
|
|
|
+ const char *name;
|
|
|
+ void (*printch)(char ch);
|
|
|
+};
|
|
|
+
|
|
|
+static const struct earlycon_match earlycon_match[] __initconst = {
|
|
|
+ { .name = "pl011", .printch = pl011_printch, },
|
|
|
+ {}
|
|
|
+};
|
|
|
+
|
|
|
+static void early_write(struct console *con, const char *s, unsigned n)
|
|
|
+{
|
|
|
+ while (n-- > 0) {
|
|
|
+ if (*s == '\n')
|
|
|
+ printch('\r');
|
|
|
+ printch(*s);
|
|
|
+ s++;
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+static struct console early_console = {
|
|
|
+ .name = "earlycon",
|
|
|
+ .write = early_write,
|
|
|
+ .flags = CON_PRINTBUFFER | CON_BOOT,
|
|
|
+ .index = -1,
|
|
|
+};
|
|
|
+
|
|
|
+/*
|
|
|
+ * Parse earlyprintk=... parameter in the format:
|
|
|
+ *
|
|
|
+ * <name>[,<addr>][,<options>]
|
|
|
+ *
|
|
|
+ * and register the early console. It is assumed that the UART has been
|
|
|
+ * initialised by the bootloader already.
|
|
|
+ */
|
|
|
+static int __init setup_early_printk(char *buf)
|
|
|
+{
|
|
|
+ const struct earlycon_match *match = earlycon_match;
|
|
|
+ phys_addr_t paddr = 0;
|
|
|
+
|
|
|
+ if (!buf) {
|
|
|
+ pr_warning("No earlyprintk arguments passed.\n");
|
|
|
+ return 0;
|
|
|
+ }
|
|
|
+
|
|
|
+ while (match->name) {
|
|
|
+ size_t len = strlen(match->name);
|
|
|
+ if (!strncmp(buf, match->name, len)) {
|
|
|
+ buf += len;
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ match++;
|
|
|
+ }
|
|
|
+ if (!match->name) {
|
|
|
+ pr_warning("Unknown earlyprintk arguments: %s\n", buf);
|
|
|
+ return 0;
|
|
|
+ }
|
|
|
+
|
|
|
+ /* I/O address */
|
|
|
+ if (!strncmp(buf, ",0x", 3)) {
|
|
|
+ char *e;
|
|
|
+ paddr = simple_strtoul(buf + 1, &e, 16);
|
|
|
+ buf = e;
|
|
|
+ }
|
|
|
+ /* no options parsing yet */
|
|
|
+
|
|
|
+ if (paddr)
|
|
|
+ early_base = early_io_map(paddr, EARLYCON_IOBASE);
|
|
|
+
|
|
|
+ printch = match->printch;
|
|
|
+ register_console(&early_console);
|
|
|
+
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
+early_param("earlyprintk", setup_early_printk);
|