1 #include <linux/console.h>
2 #include <linux/kernel.h>
3 #include <linux/init.h>
4 #include <linux/string.h>
5 #include <asm/io.h>
6 #include <asm/proto.h>
7
8 /* Simple VGA output */
9
10 #define VGABASE 0xffffffff800b8000UL
11
12 #define MAX_YPOS 25
13 #define MAX_XPOS 80
14
15 static int current_ypos = 1, current_xpos = 0;
16
early_vga_write(struct console * con,const char * str,unsigned n)17 static void early_vga_write(struct console *con, const char *str, unsigned n)
18 {
19 char c;
20 int i, k, j;
21
22 while ((c = *str++) != '\0' && n-- > 0) {
23 if (current_ypos >= MAX_YPOS) {
24 /* scroll 1 line up */
25 for(k = 1, j = 0; k < MAX_YPOS; k++, j++) {
26 for(i = 0; i < MAX_XPOS; i++) {
27 writew(readw(VGABASE + 2*(MAX_XPOS*k + i)),
28 VGABASE + 2*(MAX_XPOS*j + i));
29 }
30 }
31 for(i = 0; i < MAX_XPOS; i++) {
32 writew(0x720, VGABASE + 2*(MAX_XPOS*j + i));
33 }
34 current_ypos = MAX_YPOS-1;
35 }
36 if (c == '\n') {
37 current_xpos = 0;
38 current_ypos++;
39 } else if (c != '\r') {
40 writew(((0x7 << 8) | (unsigned short) c),
41 VGABASE + 2*(MAX_XPOS*current_ypos + current_xpos++));
42 if (current_xpos >= MAX_XPOS) {
43 current_xpos = 0;
44 current_ypos++;
45 }
46 }
47 }
48 }
49
50 static struct console early_vga_console = {
51 name: "earlyvga",
52 write: early_vga_write,
53 flags: CON_PRINTBUFFER,
54 index: -1,
55 };
56
57 /* Serial functions losely based on a similar package from Klaus P. Gerlicher */
58
59 int early_serial_base = 0x3f8; /* ttyS0 */
60
61 #define XMTRDY 0x20
62
63 #define DLAB 0x80
64
65 #define TXR 0 /* Transmit register (WRITE) */
66 #define RXR 0 /* Receive register (READ) */
67 #define IER 1 /* Interrupt Enable */
68 #define IIR 2 /* Interrupt ID */
69 #define FCR 2 /* FIFO control */
70 #define LCR 3 /* Line control */
71 #define MCR 4 /* Modem control */
72 #define LSR 5 /* Line Status */
73 #define MSR 6 /* Modem Status */
74 #define DLL 0 /* Divisor Latch Low */
75 #define DLH 1 /* Divisor latch High */
76
early_serial_putc(unsigned char ch)77 static int early_serial_putc(unsigned char ch)
78 {
79 unsigned timeout = 0xffff;
80 while ((inb(early_serial_base + LSR) & XMTRDY) == 0 && --timeout)
81 rep_nop();
82 outb(ch, early_serial_base + TXR);
83 return timeout ? 0 : -1;
84 }
85
early_serial_write(struct console * con,const char * s,unsigned n)86 static void early_serial_write(struct console *con, const char *s, unsigned n)
87 {
88 while (*s && n-- > 0) {
89 early_serial_putc(*s);
90 if (*s == '\n')
91 early_serial_putc('\r');
92 s++;
93 }
94 }
95
early_serial_init(char * opt)96 static __init void early_serial_init(char *opt)
97 {
98 unsigned char c;
99 unsigned divisor, baud = 38400;
100 char *s, *e;
101
102 if (*opt == ',')
103 ++opt;
104
105 s = strsep(&opt, ",");
106 if (s != NULL) {
107 unsigned port;
108 if (!strncmp(s,"0x",2))
109 early_serial_base = simple_strtoul(s, &e, 16);
110 else {
111 static int bases[] = { 0x3f8, 0x2f8 };
112 if (!strncmp(s,"ttyS",4))
113 s+=4;
114 port = simple_strtoul(s, &e, 10);
115 if (port > 1 || s == e)
116 port = 0;
117 early_serial_base = bases[port];
118 }
119 }
120
121 outb(0x3, early_serial_base + LCR); /* 8n1 */
122 outb(0, early_serial_base + IER); /* no interrupt */
123 outb(0, early_serial_base + FCR); /* no fifo */
124 outb(0x3, early_serial_base + MCR); /* DTR + RTS */
125
126 s = strsep(&opt, ",");
127 if (s != NULL) {
128 baud = simple_strtoul(s, &e, 0);
129 if (baud == 0 || s == e)
130 baud = 38400;
131 }
132
133 divisor = 115200 / baud;
134 c = inb(early_serial_base + LCR);
135 outb(c | DLAB, early_serial_base + LCR);
136 outb(divisor & 0xff, early_serial_base + DLL);
137 outb((divisor >> 8) & 0xff, early_serial_base + DLH);
138 outb(c & ~DLAB, early_serial_base + LCR);
139 }
140
141 static struct console early_serial_console = {
142 name: "earlyser",
143 write: early_serial_write,
144 flags: CON_PRINTBUFFER,
145 index: -1,
146 };
147
148 /* Direct interface for emergencies */
149 struct console *early_console = &early_vga_console;
150 static int early_console_initialized = 0;
151
early_printk(const char * fmt,...)152 void early_printk(const char *fmt, ...)
153 {
154 char buf[512];
155 int n;
156 va_list ap;
157 va_start(ap,fmt);
158 n = vsnprintf(buf,512,fmt,ap);
159 early_console->write(early_console,buf,n);
160 va_end(ap);
161 }
162
163 static int keep_early;
164
setup_early_printk(char * opt)165 int __init setup_early_printk(char *opt)
166 {
167 char *space;
168 char buf[256];
169
170 if (early_console_initialized)
171 return -1;
172
173 strncpy(buf,opt,256);
174 buf[255] = 0;
175 space = strchr(buf, ' ');
176 if (space)
177 *space = 0;
178
179 if (strstr(buf,"keep"))
180 keep_early = 1;
181
182 if (!strncmp(buf, "serial", 6)) {
183 early_serial_init(buf + 6);
184 early_console = &early_serial_console;
185 } else if (!strncmp(buf, "vga", 3)) {
186 early_console = &early_vga_console;
187 } else {
188 early_console = NULL;
189 return -1;
190 }
191 early_console_initialized = 1;
192 register_console(early_console);
193 return 0;
194 }
195
disable_early_printk(void)196 void __init disable_early_printk(void)
197 {
198 if (!early_console_initialized || !early_console)
199 return;
200 if (!keep_early) {
201 printk("Disabling early console\n");
202 unregister_console(early_console);
203 early_console_initialized = 0;
204 }
205 }
206
207 /* syntax: earlyprintk=vga
208 earlyprintk=serial[,ttySn[,baudrate]]
209 Append ,keep to not disable it when the real console takes over.
210 Only vga or serial at a time, not both.
211 Currently only ttyS0 and ttyS1 are supported.
212 Interaction with the standard serial driver is not very good.
213 The VGA output is eventually overwritten by the real console. */
214 __setup("earlyprintk=", setup_early_printk);
215