Commit | Line | Data |
---|---|---|
2475ff9d CM |
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> | |
e53f2f4b | 27 | #include <linux/serial_reg.h> |
2475ff9d CM |
28 | |
29 | static void __iomem *early_base; | |
30 | static void (*printch)(char ch); | |
31 | ||
32 | /* | |
33 | * PL011 single character TX. | |
34 | */ | |
35 | static void pl011_printch(char ch) | |
36 | { | |
37 | while (readl_relaxed(early_base + UART01x_FR) & UART01x_FR_TXFF) | |
38 | ; | |
39 | writeb_relaxed(ch, early_base + UART01x_DR); | |
40 | while (readl_relaxed(early_base + UART01x_FR) & UART01x_FR_BUSY) | |
41 | ; | |
42 | } | |
43 | ||
0492f725 MZ |
44 | /* |
45 | * Semihosting-based debug console | |
46 | */ | |
47 | static void smh_printch(char ch) | |
48 | { | |
49 | asm volatile("mov x1, %0\n" | |
50 | "mov x0, #3\n" | |
51 | "hlt 0xf000\n" | |
52 | : : "r" (&ch) : "x0", "x1", "memory"); | |
53 | } | |
54 | ||
e53f2f4b AP |
55 | /* |
56 | * 8250/16550 (8-bit aligned registers) single character TX. | |
57 | */ | |
58 | static void uart8250_8bit_printch(char ch) | |
59 | { | |
60 | while (!(readb_relaxed(early_base + UART_LSR) & UART_LSR_THRE)) | |
61 | ; | |
62 | writeb_relaxed(ch, early_base + UART_TX); | |
63 | } | |
64 | ||
65 | /* | |
66 | * 8250/16550 (32-bit aligned registers) single character TX. | |
67 | */ | |
68 | static void uart8250_32bit_printch(char ch) | |
69 | { | |
70 | while (!(readl_relaxed(early_base + (UART_LSR << 2)) & UART_LSR_THRE)) | |
71 | ; | |
72 | writel_relaxed(ch, early_base + (UART_TX << 2)); | |
73 | } | |
74 | ||
2475ff9d CM |
75 | struct earlycon_match { |
76 | const char *name; | |
77 | void (*printch)(char ch); | |
78 | }; | |
79 | ||
80 | static const struct earlycon_match earlycon_match[] __initconst = { | |
81 | { .name = "pl011", .printch = pl011_printch, }, | |
0492f725 | 82 | { .name = "smh", .printch = smh_printch, }, |
e53f2f4b AP |
83 | { .name = "uart8250-8bit", .printch = uart8250_8bit_printch, }, |
84 | { .name = "uart8250-32bit", .printch = uart8250_32bit_printch, }, | |
2475ff9d CM |
85 | {} |
86 | }; | |
87 | ||
88 | static void early_write(struct console *con, const char *s, unsigned n) | |
89 | { | |
90 | while (n-- > 0) { | |
91 | if (*s == '\n') | |
92 | printch('\r'); | |
93 | printch(*s); | |
94 | s++; | |
95 | } | |
96 | } | |
97 | ||
98 | static struct console early_console = { | |
99 | .name = "earlycon", | |
100 | .write = early_write, | |
101 | .flags = CON_PRINTBUFFER | CON_BOOT, | |
102 | .index = -1, | |
103 | }; | |
104 | ||
105 | /* | |
106 | * Parse earlyprintk=... parameter in the format: | |
107 | * | |
108 | * <name>[,<addr>][,<options>] | |
109 | * | |
110 | * and register the early console. It is assumed that the UART has been | |
111 | * initialised by the bootloader already. | |
112 | */ | |
113 | static int __init setup_early_printk(char *buf) | |
114 | { | |
115 | const struct earlycon_match *match = earlycon_match; | |
116 | phys_addr_t paddr = 0; | |
117 | ||
118 | if (!buf) { | |
119 | pr_warning("No earlyprintk arguments passed.\n"); | |
120 | return 0; | |
121 | } | |
122 | ||
123 | while (match->name) { | |
124 | size_t len = strlen(match->name); | |
125 | if (!strncmp(buf, match->name, len)) { | |
126 | buf += len; | |
127 | break; | |
128 | } | |
129 | match++; | |
130 | } | |
131 | if (!match->name) { | |
132 | pr_warning("Unknown earlyprintk arguments: %s\n", buf); | |
133 | return 0; | |
134 | } | |
135 | ||
136 | /* I/O address */ | |
137 | if (!strncmp(buf, ",0x", 3)) { | |
138 | char *e; | |
139 | paddr = simple_strtoul(buf + 1, &e, 16); | |
140 | buf = e; | |
141 | } | |
142 | /* no options parsing yet */ | |
143 | ||
144 | if (paddr) | |
145 | early_base = early_io_map(paddr, EARLYCON_IOBASE); | |
146 | ||
147 | printch = match->printch; | |
148 | register_console(&early_console); | |
149 | ||
150 | return 0; | |
151 | } | |
152 | ||
153 | early_param("earlyprintk", setup_early_printk); |