Commit | Line | Data |
---|---|---|
1543610a PA |
1 | /* -*- linux-c -*- ------------------------------------------------------- * |
2 | * | |
3 | * Copyright (C) 1991, 1992 Linus Torvalds | |
4 | * Copyright 2007 rPath, Inc. - All Rights Reserved | |
df7699c5 | 5 | * Copyright 2009 Intel Corporation; author H. Peter Anvin |
1543610a PA |
6 | * |
7 | * This file is part of the Linux kernel, and is made available under | |
8 | * the terms of the GNU General Public License version 2. | |
9 | * | |
10 | * ----------------------------------------------------------------------- */ | |
11 | ||
12 | /* | |
fa97bdf9 | 13 | * Very simple screen and serial I/O |
1543610a PA |
14 | */ |
15 | ||
16 | #include "boot.h" | |
17 | ||
fa97bdf9 PE |
18 | #define DEFAULT_SERIAL_PORT 0x3f8 /* ttyS0 */ |
19 | ||
20 | static int early_serial_base; | |
21 | ||
22 | #define XMTRDY 0x20 | |
23 | ||
24 | #define DLAB 0x80 | |
25 | ||
26 | #define TXR 0 /* Transmit register (WRITE) */ | |
27 | #define RXR 0 /* Receive register (READ) */ | |
28 | #define IER 1 /* Interrupt Enable */ | |
29 | #define IIR 2 /* Interrupt ID */ | |
30 | #define FCR 2 /* FIFO control */ | |
31 | #define LCR 3 /* Line control */ | |
32 | #define MCR 4 /* Modem control */ | |
33 | #define LSR 5 /* Line Status */ | |
34 | #define MSR 6 /* Modem Status */ | |
35 | #define DLL 0 /* Divisor Latch Low */ | |
36 | #define DLH 1 /* Divisor latch High */ | |
37 | ||
38 | #define DEFAULT_BAUD 9600 | |
39 | ||
1543610a PA |
40 | /* |
41 | * These functions are in .inittext so they can be used to signal | |
42 | * error during initialization. | |
43 | */ | |
44 | ||
fa97bdf9 | 45 | static void __attribute__((section(".inittext"))) serial_putchar(int ch) |
1543610a | 46 | { |
fa97bdf9 | 47 | unsigned timeout = 0xffff; |
1543610a | 48 | |
fa97bdf9 PE |
49 | while ((inb(early_serial_base + LSR) & XMTRDY) == 0 && --timeout) |
50 | cpu_relax(); | |
51 | ||
52 | outb(ch, early_serial_base + TXR); | |
53 | } | |
54 | ||
55 | static void __attribute__((section(".inittext"))) bios_putchar(int ch) | |
56 | { | |
57 | struct biosregs ireg; | |
1543610a | 58 | |
df7699c5 PA |
59 | initregs(&ireg); |
60 | ireg.bx = 0x0007; | |
61 | ireg.cx = 0x0001; | |
62 | ireg.ah = 0x0e; | |
63 | ireg.al = ch; | |
64 | intcall(0x10, &ireg, NULL); | |
1543610a PA |
65 | } |
66 | ||
fa97bdf9 PE |
67 | void __attribute__((section(".inittext"))) putchar(int ch) |
68 | { | |
69 | if (ch == '\n') | |
70 | putchar('\r'); /* \n -> \r\n */ | |
71 | ||
72 | bios_putchar(ch); | |
73 | ||
74 | if (early_serial_base != 0) | |
75 | serial_putchar(ch); | |
76 | } | |
77 | ||
1543610a PA |
78 | void __attribute__((section(".inittext"))) puts(const char *str) |
79 | { | |
df7699c5 | 80 | while (*str) |
1543610a | 81 | putchar(*str++); |
1543610a PA |
82 | } |
83 | ||
84 | /* | |
85 | * Read the CMOS clock through the BIOS, and return the | |
86 | * seconds in BCD. | |
87 | */ | |
88 | ||
89 | static u8 gettime(void) | |
90 | { | |
df7699c5 | 91 | struct biosregs ireg, oreg; |
1543610a | 92 | |
df7699c5 PA |
93 | initregs(&ireg); |
94 | ireg.ah = 0x02; | |
95 | intcall(0x1a, &ireg, &oreg); | |
1543610a | 96 | |
df7699c5 | 97 | return oreg.dh; |
1543610a PA |
98 | } |
99 | ||
100 | /* | |
101 | * Read from the keyboard | |
102 | */ | |
103 | int getchar(void) | |
104 | { | |
df7699c5 PA |
105 | struct biosregs ireg, oreg; |
106 | ||
107 | initregs(&ireg); | |
108 | /* ireg.ah = 0x00; */ | |
109 | intcall(0x16, &ireg, &oreg); | |
1543610a | 110 | |
df7699c5 | 111 | return oreg.al; |
1543610a PA |
112 | } |
113 | ||
114 | static int kbd_pending(void) | |
115 | { | |
df7699c5 PA |
116 | struct biosregs ireg, oreg; |
117 | ||
118 | initregs(&ireg); | |
119 | ireg.ah = 0x01; | |
120 | intcall(0x16, &ireg, &oreg); | |
121 | ||
122 | return !(oreg.eflags & X86_EFLAGS_ZF); | |
1543610a PA |
123 | } |
124 | ||
125 | void kbd_flush(void) | |
126 | { | |
127 | for (;;) { | |
128 | if (!kbd_pending()) | |
129 | break; | |
130 | getchar(); | |
131 | } | |
132 | } | |
133 | ||
134 | int getchar_timeout(void) | |
135 | { | |
136 | int cnt = 30; | |
137 | int t0, t1; | |
138 | ||
139 | t0 = gettime(); | |
140 | ||
141 | while (cnt) { | |
142 | if (kbd_pending()) | |
143 | return getchar(); | |
144 | ||
145 | t1 = gettime(); | |
146 | if (t0 != t1) { | |
147 | cnt--; | |
148 | t0 = t1; | |
149 | } | |
150 | } | |
151 | ||
152 | return 0; /* Timeout! */ | |
153 | } | |
fa97bdf9 | 154 | |
70b0d22d | 155 | static void early_serial_init(int port, int baud) |
fa97bdf9 PE |
156 | { |
157 | unsigned char c; | |
158 | unsigned divisor; | |
159 | ||
70b0d22d YL |
160 | outb(0x3, port + LCR); /* 8n1 */ |
161 | outb(0, port + IER); /* no interrupt */ | |
162 | outb(0, port + FCR); /* no fifo */ | |
163 | outb(0x3, port + MCR); /* DTR + RTS */ | |
fa97bdf9 PE |
164 | |
165 | divisor = 115200 / baud; | |
70b0d22d YL |
166 | c = inb(port + LCR); |
167 | outb(c | DLAB, port + LCR); | |
168 | outb(divisor & 0xff, port + DLL); | |
169 | outb((divisor >> 8) & 0xff, port + DLH); | |
170 | outb(c & ~DLAB, port + LCR); | |
171 | ||
172 | early_serial_base = port; | |
173 | ||
174 | printf("Early serial console at I/O port 0x%x baud: %d\n", port, baud); | |
fa97bdf9 PE |
175 | } |
176 | ||
70b0d22d | 177 | static void parse_earlyprintk(void) |
fa97bdf9 PE |
178 | { |
179 | int baud = DEFAULT_BAUD; | |
180 | char arg[32]; | |
181 | int pos = 0; | |
70b0d22d | 182 | int port = 0; |
fa97bdf9 PE |
183 | |
184 | if (cmdline_find_option("earlyprintk", arg, sizeof arg) > 0) { | |
185 | char *e; | |
186 | ||
187 | if (!strncmp(arg, "serial", 6)) { | |
70b0d22d | 188 | port = DEFAULT_SERIAL_PORT; |
fa97bdf9 PE |
189 | pos += 6; |
190 | } | |
191 | ||
192 | if (arg[pos] == ',') | |
193 | pos++; | |
194 | ||
195 | if (!strncmp(arg, "ttyS", 4)) { | |
196 | static const int bases[] = { 0x3f8, 0x2f8 }; | |
70b0d22d | 197 | int idx = 0; |
fa97bdf9 PE |
198 | |
199 | if (!strncmp(arg + pos, "ttyS", 4)) | |
200 | pos += 4; | |
201 | ||
202 | if (arg[pos++] == '1') | |
70b0d22d | 203 | idx = 1; |
fa97bdf9 | 204 | |
70b0d22d | 205 | port = bases[idx]; |
fa97bdf9 PE |
206 | } |
207 | ||
208 | if (arg[pos] == ',') | |
209 | pos++; | |
210 | ||
211 | baud = simple_strtoull(arg + pos, &e, 0); | |
212 | if (baud == 0 || arg + pos == e) | |
213 | baud = DEFAULT_BAUD; | |
214 | } | |
215 | ||
70b0d22d YL |
216 | if (port) |
217 | early_serial_init(port, baud); | |
ce0aa5dd YL |
218 | } |
219 | ||
220 | #define BASE_BAUD (1843200/16) | |
221 | static unsigned int probe_baud(int port) | |
222 | { | |
223 | unsigned char lcr, dll, dlh; | |
224 | unsigned int quot; | |
225 | ||
226 | lcr = inb(port + LCR); | |
227 | outb(lcr | DLAB, port + LCR); | |
228 | dll = inb(port + DLL); | |
229 | dlh = inb(port + DLH); | |
230 | outb(lcr, port + LCR); | |
231 | quot = (dlh << 8) | dll; | |
232 | ||
233 | return BASE_BAUD / quot; | |
234 | } | |
235 | ||
70b0d22d | 236 | static void parse_console_uart8250(void) |
ce0aa5dd YL |
237 | { |
238 | char optstr[64], *options; | |
239 | int baud = DEFAULT_BAUD; | |
70b0d22d | 240 | int port = 0; |
ce0aa5dd YL |
241 | |
242 | /* | |
243 | * console=uart8250,io,0x3f8,115200n8 | |
244 | * need to make sure it is last one console ! | |
245 | */ | |
246 | if (cmdline_find_option("console", optstr, sizeof optstr) <= 0) | |
70b0d22d | 247 | return; |
ce0aa5dd YL |
248 | |
249 | options = optstr; | |
250 | ||
251 | if (!strncmp(options, "uart8250,io,", 12)) | |
70b0d22d | 252 | port = simple_strtoull(options + 12, &options, 0); |
ce0aa5dd | 253 | else if (!strncmp(options, "uart,io,", 8)) |
70b0d22d | 254 | port = simple_strtoull(options + 8, &options, 0); |
ce0aa5dd | 255 | else |
70b0d22d | 256 | return; |
ce0aa5dd YL |
257 | |
258 | if (options && (options[0] == ',')) | |
259 | baud = simple_strtoull(options + 1, &options, 0); | |
260 | else | |
70b0d22d | 261 | baud = probe_baud(port); |
ce0aa5dd | 262 | |
70b0d22d YL |
263 | if (port) |
264 | early_serial_init(port, baud); | |
ce0aa5dd YL |
265 | } |
266 | ||
267 | void console_init(void) | |
268 | { | |
70b0d22d | 269 | parse_earlyprintk(); |
ce0aa5dd YL |
270 | |
271 | if (!early_serial_base) | |
70b0d22d | 272 | parse_console_uart8250(); |
fa97bdf9 | 273 | } |