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