import of upstream 2.4.34.4 from kernel.org
[linux-2.4.git] / arch / x86_64 / kernel / early_printk.c
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
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
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
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
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
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
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
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);