/* * Earlyprintk support. * * Copyright (C) 2012 ARM Ltd. * Author: Catalin Marinas * * 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 . */ #include #include #include #include #include #include #include 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) ; } /* * Semihosting-based debug console */ static void smh_printch(char ch) { asm volatile("mov x1, %0\n" "mov x0, #3\n" "hlt 0xf000\n" : : "r" (&ch) : "x0", "x1", "memory"); } struct earlycon_match { const char *name; void (*printch)(char ch); }; static const struct earlycon_match earlycon_match[] __initconst = { { .name = "pl011", .printch = pl011_printch, }, { .name = "smh", .printch = smh_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: * * [,][,] * * 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);