aboutsummaryrefslogtreecommitdiffstats
path: root/arch/arm64/kernel
diff options
context:
space:
mode:
authorCatalin Marinas <catalin.marinas@arm.com>2012-10-23 09:55:08 -0400
committerCatalin Marinas <catalin.marinas@arm.com>2013-01-22 12:51:01 -0500
commit2475ff9d2c6ea3bbfed55c4635426c371f9ad327 (patch)
treec705c9a55b2d9ea2d270b7002f568b6be0465ecd /arch/arm64/kernel
parentd6bafb9b821a3a5ddeb600a9fd015085760d818e (diff)
arm64: Add simple earlyprintk support
This patch adds support for "earlyprintk=" parameter on the kernel command line. The format is: earlyprintk=<name>[,<addr>][,<options>] where <name> is the name of the (UART) device, e.g. "pl011", <addr> is the I/O address. The <options> aren't currently used. The mapping of the earlyprintk device is done very early during kernel boot and there are restrictions on which functions it can call. A special early_io_map() function is added which creates the mapping from the pre-defined EARLY_IOBASE to the device I/O address passed via the kernel parameter. The pgd entry corresponding to EARLY_IOBASE is pre-populated in head.S during kernel boot. Only PL011 is currently supported and it is assumed that the interface is already initialised by the boot loader before the kernel is started. Signed-off-by: Catalin Marinas <catalin.marinas@arm.com> Acked-by: Arnd Bergmann <arnd@arndb.de>
Diffstat (limited to 'arch/arm64/kernel')
-rw-r--r--arch/arm64/kernel/Makefile1
-rw-r--r--arch/arm64/kernel/early_printk.c118
-rw-r--r--arch/arm64/kernel/head.S12
3 files changed, 129 insertions, 2 deletions
diff --git a/arch/arm64/kernel/Makefile b/arch/arm64/kernel/Makefile
index 74239c31e25a..a1cace45e970 100644
--- a/arch/arm64/kernel/Makefile
+++ b/arch/arm64/kernel/Makefile
@@ -17,6 +17,7 @@ arm64-obj-$(CONFIG_MODULES) += arm64ksyms.o module.o
17arm64-obj-$(CONFIG_SMP) += smp.o 17arm64-obj-$(CONFIG_SMP) += smp.o
18arm64-obj-$(CONFIG_HW_PERF_EVENTS) += perf_event.o 18arm64-obj-$(CONFIG_HW_PERF_EVENTS) += perf_event.o
19arm64-obj-$(CONFIG_HAVE_HW_BREAKPOINT)+= hw_breakpoint.o 19arm64-obj-$(CONFIG_HAVE_HW_BREAKPOINT)+= hw_breakpoint.o
20arm64-obj-$(CONFIG_EARLY_PRINTK) += early_printk.o
20 21
21obj-y += $(arm64-obj-y) vdso/ 22obj-y += $(arm64-obj-y) vdso/
22obj-m += $(arm64-obj-m) 23obj-m += $(arm64-obj-m)
diff --git a/arch/arm64/kernel/early_printk.c b/arch/arm64/kernel/early_printk.c
new file mode 100644
index 000000000000..7e320a2edb9b
--- /dev/null
+++ b/arch/arm64/kernel/early_printk.c
@@ -0,0 +1,118 @@
1/*
2 * Earlyprintk support.
3 *
4 * Copyright (C) 2012 ARM Ltd.
5 * Author: Catalin Marinas <catalin.marinas@arm.com>
6 *
7 * This program is free software: you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License version 2 as
9 * published by the Free Software Foundation.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program. If not, see <http://www.gnu.org/licenses/>.
18 */
19#include <linux/kernel.h>
20#include <linux/console.h>
21#include <linux/init.h>
22#include <linux/string.h>
23#include <linux/mm.h>
24#include <linux/io.h>
25
26#include <linux/amba/serial.h>
27
28static void __iomem *early_base;
29static void (*printch)(char ch);
30
31/*
32 * PL011 single character TX.
33 */
34static void pl011_printch(char ch)
35{
36 while (readl_relaxed(early_base + UART01x_FR) & UART01x_FR_TXFF)
37 ;
38 writeb_relaxed(ch, early_base + UART01x_DR);
39 while (readl_relaxed(early_base + UART01x_FR) & UART01x_FR_BUSY)
40 ;
41}
42
43struct earlycon_match {
44 const char *name;
45 void (*printch)(char ch);
46};
47
48static const struct earlycon_match earlycon_match[] __initconst = {
49 { .name = "pl011", .printch = pl011_printch, },
50 {}
51};
52
53static void early_write(struct console *con, const char *s, unsigned n)
54{
55 while (n-- > 0) {
56 if (*s == '\n')
57 printch('\r');
58 printch(*s);
59 s++;
60 }
61}
62
63static struct console early_console = {
64 .name = "earlycon",
65 .write = early_write,
66 .flags = CON_PRINTBUFFER | CON_BOOT,
67 .index = -1,
68};
69
70/*
71 * Parse earlyprintk=... parameter in the format:
72 *
73 * <name>[,<addr>][,<options>]
74 *
75 * and register the early console. It is assumed that the UART has been
76 * initialised by the bootloader already.
77 */
78static int __init setup_early_printk(char *buf)
79{
80 const struct earlycon_match *match = earlycon_match;
81 phys_addr_t paddr = 0;
82
83 if (!buf) {
84 pr_warning("No earlyprintk arguments passed.\n");
85 return 0;
86 }
87
88 while (match->name) {
89 size_t len = strlen(match->name);
90 if (!strncmp(buf, match->name, len)) {
91 buf += len;
92 break;
93 }
94 match++;
95 }
96 if (!match->name) {
97 pr_warning("Unknown earlyprintk arguments: %s\n", buf);
98 return 0;
99 }
100
101 /* I/O address */
102 if (!strncmp(buf, ",0x", 3)) {
103 char *e;
104 paddr = simple_strtoul(buf + 1, &e, 16);
105 buf = e;
106 }
107 /* no options parsing yet */
108
109 if (paddr)
110 early_base = early_io_map(paddr, EARLYCON_IOBASE);
111
112 printch = match->printch;
113 register_console(&early_console);
114
115 return 0;
116}
117
118early_param("earlyprintk", setup_early_printk);
diff --git a/arch/arm64/kernel/head.S b/arch/arm64/kernel/head.S
index 368ad1f7c36c..0a0a49756826 100644
--- a/arch/arm64/kernel/head.S
+++ b/arch/arm64/kernel/head.S
@@ -82,10 +82,8 @@
82 82
83#ifdef CONFIG_ARM64_64K_PAGES 83#ifdef CONFIG_ARM64_64K_PAGES
84#define MM_MMUFLAGS PTE_ATTRINDX(MT_NORMAL) | PTE_FLAGS 84#define MM_MMUFLAGS PTE_ATTRINDX(MT_NORMAL) | PTE_FLAGS
85#define IO_MMUFLAGS PTE_ATTRINDX(MT_DEVICE_nGnRE) | PTE_XN | PTE_FLAGS
86#else 85#else
87#define MM_MMUFLAGS PMD_ATTRINDX(MT_NORMAL) | PMD_FLAGS 86#define MM_MMUFLAGS PMD_ATTRINDX(MT_NORMAL) | PMD_FLAGS
88#define IO_MMUFLAGS PMD_ATTRINDX(MT_DEVICE_nGnRE) | PMD_SECT_XN | PMD_FLAGS
89#endif 87#endif
90 88
91/* 89/*
@@ -368,6 +366,7 @@ ENDPROC(__calc_phys_offset)
368 * - identity mapping to enable the MMU (low address, TTBR0) 366 * - identity mapping to enable the MMU (low address, TTBR0)
369 * - first few MB of the kernel linear mapping to jump to once the MMU has 367 * - first few MB of the kernel linear mapping to jump to once the MMU has
370 * been enabled, including the FDT blob (TTBR1) 368 * been enabled, including the FDT blob (TTBR1)
369 * - UART mapping if CONFIG_EARLY_PRINTK is enabled (TTBR1)
371 */ 370 */
372__create_page_tables: 371__create_page_tables:
373 pgtbl x25, x26, x24 // idmap_pg_dir and swapper_pg_dir addresses 372 pgtbl x25, x26, x24 // idmap_pg_dir and swapper_pg_dir addresses
@@ -420,6 +419,15 @@ __create_page_tables:
420 sub x6, x6, #1 // inclusive range 419 sub x6, x6, #1 // inclusive range
421 create_block_map x0, x7, x3, x5, x6 420 create_block_map x0, x7, x3, x5, x6
4221: 4211:
422#ifdef CONFIG_EARLY_PRINTK
423 /*
424 * Create the pgd entry for the UART mapping. The full mapping is done
425 * later based earlyprintk kernel parameter.
426 */
427 ldr x5, =EARLYCON_IOBASE // UART virtual address
428 add x0, x26, #2 * PAGE_SIZE // section table address
429 create_pgd_entry x26, x0, x5, x6, x7
430#endif
423 ret 431 ret
424ENDPROC(__create_page_tables) 432ENDPROC(__create_page_tables)
425 .ltorg 433 .ltorg