Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
torvalds
GitHub Repository: torvalds/linux
Path: blob/master/arch/x86/boot/early_serial_console.c
26424 views
1
// SPDX-License-Identifier: GPL-2.0
2
/*
3
* Serial port routines for use during early boot reporting. This code is
4
* included from both the compressed kernel and the regular kernel.
5
*/
6
#include "boot.h"
7
8
#define DEFAULT_SERIAL_PORT 0x3f8 /* ttyS0 */
9
10
#define DLAB 0x80
11
12
#define TXR 0 /* Transmit register (WRITE) */
13
#define RXR 0 /* Receive register (READ) */
14
#define IER 1 /* Interrupt Enable */
15
#define IIR 2 /* Interrupt ID */
16
#define FCR 2 /* FIFO control */
17
#define LCR 3 /* Line control */
18
#define MCR 4 /* Modem control */
19
#define LSR 5 /* Line Status */
20
#define MSR 6 /* Modem Status */
21
#define DLL 0 /* Divisor Latch Low */
22
#define DLH 1 /* Divisor latch High */
23
24
#define DEFAULT_BAUD 9600
25
26
static void early_serial_init(int port, int baud)
27
{
28
unsigned char c;
29
unsigned divisor;
30
31
outb(0x3, port + LCR); /* 8n1 */
32
outb(0, port + IER); /* no interrupt */
33
outb(0, port + FCR); /* no fifo */
34
outb(0x3, port + MCR); /* DTR + RTS */
35
36
divisor = 115200 / baud;
37
c = inb(port + LCR);
38
outb(c | DLAB, port + LCR);
39
outb(divisor & 0xff, port + DLL);
40
outb((divisor >> 8) & 0xff, port + DLH);
41
outb(c & ~DLAB, port + LCR);
42
43
early_serial_base = port;
44
}
45
46
static void parse_earlyprintk(void)
47
{
48
int baud = DEFAULT_BAUD;
49
char arg[32];
50
int pos = 0;
51
int port = 0;
52
53
if (cmdline_find_option("earlyprintk", arg, sizeof(arg)) > 0) {
54
char *e;
55
56
if (!strncmp(arg, "serial", 6)) {
57
port = DEFAULT_SERIAL_PORT;
58
pos += 6;
59
}
60
61
if (arg[pos] == ',')
62
pos++;
63
64
/*
65
* make sure we have
66
* "serial,0x3f8,115200"
67
* "serial,ttyS0,115200"
68
* "ttyS0,115200"
69
*/
70
if (pos == 7 && !strncmp(arg + pos, "0x", 2)) {
71
port = simple_strtoull(arg + pos, &e, 16);
72
if (port == 0 || arg + pos == e)
73
port = DEFAULT_SERIAL_PORT;
74
else
75
pos = e - arg;
76
} else if (!strncmp(arg + pos, "ttyS", 4)) {
77
static const int bases[] = { 0x3f8, 0x2f8 };
78
int idx = 0;
79
80
/* += strlen("ttyS"); */
81
pos += 4;
82
83
if (arg[pos++] == '1')
84
idx = 1;
85
86
port = bases[idx];
87
}
88
89
if (arg[pos] == ',')
90
pos++;
91
92
baud = simple_strtoull(arg + pos, &e, 0);
93
if (baud == 0 || arg + pos == e)
94
baud = DEFAULT_BAUD;
95
}
96
97
if (port)
98
early_serial_init(port, baud);
99
}
100
101
#define BASE_BAUD (1843200/16)
102
static unsigned int probe_baud(int port)
103
{
104
unsigned char lcr, dll, dlh;
105
unsigned int quot;
106
107
lcr = inb(port + LCR);
108
outb(lcr | DLAB, port + LCR);
109
dll = inb(port + DLL);
110
dlh = inb(port + DLH);
111
outb(lcr, port + LCR);
112
quot = (dlh << 8) | dll;
113
114
return BASE_BAUD / quot;
115
}
116
117
static void parse_console_uart8250(void)
118
{
119
char optstr[64], *options;
120
int baud = DEFAULT_BAUD;
121
int port = 0;
122
123
/*
124
* console=uart8250,io,0x3f8,115200n8
125
* need to make sure it is last one console !
126
*/
127
if (cmdline_find_option("console", optstr, sizeof(optstr)) <= 0)
128
return;
129
130
options = optstr;
131
132
if (!strncmp(options, "uart8250,io,", 12))
133
port = simple_strtoull(options + 12, &options, 0);
134
else if (!strncmp(options, "uart,io,", 8))
135
port = simple_strtoull(options + 8, &options, 0);
136
else
137
return;
138
139
if (options && (options[0] == ','))
140
baud = simple_strtoull(options + 1, &options, 0);
141
else
142
baud = probe_baud(port);
143
144
if (port)
145
early_serial_init(port, baud);
146
}
147
148
void console_init(void)
149
{
150
parse_earlyprintk();
151
152
if (!early_serial_base)
153
parse_console_uart8250();
154
}
155
156