Merge branch 'for-linus' of git://git.infradead.org/users/vkoul/slave-dma
[cascardo/linux.git] / arch / x86 / 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 <linux/screen_info.h>
6 #include <linux/usb/ch9.h>
7 #include <linux/pci_regs.h>
8 #include <linux/pci_ids.h>
9 #include <linux/errno.h>
10 #include <asm/io.h>
11 #include <asm/processor.h>
12 #include <asm/fcntl.h>
13 #include <asm/setup.h>
14 #include <xen/hvc-console.h>
15 #include <asm/pci-direct.h>
16 #include <asm/fixmap.h>
17 #include <asm/intel-mid.h>
18 #include <asm/pgtable.h>
19 #include <linux/usb/ehci_def.h>
20 #include <linux/efi.h>
21 #include <asm/efi.h>
22
23 /* Simple VGA output */
24 #define VGABASE         (__ISA_IO_base + 0xb8000)
25
26 static int max_ypos = 25, max_xpos = 80;
27 static int current_ypos = 25, current_xpos;
28
29 static void early_vga_write(struct console *con, const char *str, unsigned n)
30 {
31         char c;
32         int  i, k, j;
33
34         while ((c = *str++) != '\0' && n-- > 0) {
35                 if (current_ypos >= max_ypos) {
36                         /* scroll 1 line up */
37                         for (k = 1, j = 0; k < max_ypos; k++, j++) {
38                                 for (i = 0; i < max_xpos; i++) {
39                                         writew(readw(VGABASE+2*(max_xpos*k+i)),
40                                                VGABASE + 2*(max_xpos*j + i));
41                                 }
42                         }
43                         for (i = 0; i < max_xpos; i++)
44                                 writew(0x720, VGABASE + 2*(max_xpos*j + i));
45                         current_ypos = max_ypos-1;
46                 }
47 #ifdef CONFIG_KGDB_KDB
48                 if (c == '\b') {
49                         if (current_xpos > 0)
50                                 current_xpos--;
51                 } else if (c == '\r') {
52                         current_xpos = 0;
53                 } else
54 #endif
55                 if (c == '\n') {
56                         current_xpos = 0;
57                         current_ypos++;
58                 } else if (c != '\r')  {
59                         writew(((0x7 << 8) | (unsigned short) c),
60                                VGABASE + 2*(max_xpos*current_ypos +
61                                                 current_xpos++));
62                         if (current_xpos >= max_xpos) {
63                                 current_xpos = 0;
64                                 current_ypos++;
65                         }
66                 }
67         }
68 }
69
70 static struct console early_vga_console = {
71         .name =         "earlyvga",
72         .write =        early_vga_write,
73         .flags =        CON_PRINTBUFFER,
74         .index =        -1,
75 };
76
77 /* Serial functions loosely based on a similar package from Klaus P. Gerlicher */
78
79 static int early_serial_base = 0x3f8;  /* ttyS0 */
80
81 #define XMTRDY          0x20
82
83 #define DLAB            0x80
84
85 #define TXR             0       /*  Transmit register (WRITE) */
86 #define RXR             0       /*  Receive register  (READ)  */
87 #define IER             1       /*  Interrupt Enable          */
88 #define IIR             2       /*  Interrupt ID              */
89 #define FCR             2       /*  FIFO control              */
90 #define LCR             3       /*  Line control              */
91 #define MCR             4       /*  Modem control             */
92 #define LSR             5       /*  Line Status               */
93 #define MSR             6       /*  Modem Status              */
94 #define DLL             0       /*  Divisor Latch Low         */
95 #define DLH             1       /*  Divisor latch High        */
96
97 static int early_serial_putc(unsigned char ch)
98 {
99         unsigned timeout = 0xffff;
100
101         while ((inb(early_serial_base + LSR) & XMTRDY) == 0 && --timeout)
102                 cpu_relax();
103         outb(ch, early_serial_base + TXR);
104         return timeout ? 0 : -1;
105 }
106
107 static void early_serial_write(struct console *con, const char *s, unsigned n)
108 {
109         while (*s && n-- > 0) {
110                 if (*s == '\n')
111                         early_serial_putc('\r');
112                 early_serial_putc(*s);
113                 s++;
114         }
115 }
116
117 #define DEFAULT_BAUD 9600
118
119 static __init void early_serial_init(char *s)
120 {
121         unsigned char c;
122         unsigned divisor;
123         unsigned baud = DEFAULT_BAUD;
124         char *e;
125
126         if (*s == ',')
127                 ++s;
128
129         if (*s) {
130                 unsigned port;
131                 if (!strncmp(s, "0x", 2)) {
132                         early_serial_base = simple_strtoul(s, &e, 16);
133                 } else {
134                         static const int __initconst bases[] = { 0x3f8, 0x2f8 };
135
136                         if (!strncmp(s, "ttyS", 4))
137                                 s += 4;
138                         port = simple_strtoul(s, &e, 10);
139                         if (port > 1 || s == e)
140                                 port = 0;
141                         early_serial_base = bases[port];
142                 }
143                 s += strcspn(s, ",");
144                 if (*s == ',')
145                         s++;
146         }
147
148         outb(0x3, early_serial_base + LCR);     /* 8n1 */
149         outb(0, early_serial_base + IER);       /* no interrupt */
150         outb(0, early_serial_base + FCR);       /* no fifo */
151         outb(0x3, early_serial_base + MCR);     /* DTR + RTS */
152
153         if (*s) {
154                 baud = simple_strtoul(s, &e, 0);
155                 if (baud == 0 || s == e)
156                         baud = DEFAULT_BAUD;
157         }
158
159         divisor = 115200 / baud;
160         c = inb(early_serial_base + LCR);
161         outb(c | DLAB, early_serial_base + LCR);
162         outb(divisor & 0xff, early_serial_base + DLL);
163         outb((divisor >> 8) & 0xff, early_serial_base + DLH);
164         outb(c & ~DLAB, early_serial_base + LCR);
165 }
166
167 static struct console early_serial_console = {
168         .name =         "earlyser",
169         .write =        early_serial_write,
170         .flags =        CON_PRINTBUFFER,
171         .index =        -1,
172 };
173
174 static inline void early_console_register(struct console *con, int keep_early)
175 {
176         if (con->index != -1) {
177                 printk(KERN_CRIT "ERROR: earlyprintk= %s already used\n",
178                        con->name);
179                 return;
180         }
181         early_console = con;
182         if (keep_early)
183                 early_console->flags &= ~CON_BOOT;
184         else
185                 early_console->flags |= CON_BOOT;
186         register_console(early_console);
187 }
188
189 static int __init setup_early_printk(char *buf)
190 {
191         int keep;
192
193         if (!buf)
194                 return 0;
195
196         if (early_console)
197                 return 0;
198
199         keep = (strstr(buf, "keep") != NULL);
200
201         while (*buf != '\0') {
202                 if (!strncmp(buf, "serial", 6)) {
203                         buf += 6;
204                         early_serial_init(buf);
205                         early_console_register(&early_serial_console, keep);
206                         if (!strncmp(buf, ",ttyS", 5))
207                                 buf += 5;
208                 }
209                 if (!strncmp(buf, "ttyS", 4)) {
210                         early_serial_init(buf + 4);
211                         early_console_register(&early_serial_console, keep);
212                 }
213                 if (!strncmp(buf, "vga", 3) &&
214                     boot_params.screen_info.orig_video_isVGA == 1) {
215                         max_xpos = boot_params.screen_info.orig_video_cols;
216                         max_ypos = boot_params.screen_info.orig_video_lines;
217                         current_ypos = boot_params.screen_info.orig_y;
218                         early_console_register(&early_vga_console, keep);
219                 }
220 #ifdef CONFIG_EARLY_PRINTK_DBGP
221                 if (!strncmp(buf, "dbgp", 4) && !early_dbgp_init(buf + 4))
222                         early_console_register(&early_dbgp_console, keep);
223 #endif
224 #ifdef CONFIG_HVC_XEN
225                 if (!strncmp(buf, "xen", 3))
226                         early_console_register(&xenboot_console, keep);
227 #endif
228 #ifdef CONFIG_EARLY_PRINTK_INTEL_MID
229                 if (!strncmp(buf, "mrst", 4)) {
230                         mrst_early_console_init();
231                         early_console_register(&early_mrst_console, keep);
232                 }
233
234                 if (!strncmp(buf, "hsu", 3)) {
235                         hsu_early_console_init(buf + 3);
236                         early_console_register(&early_hsu_console, keep);
237                 }
238 #endif
239 #ifdef CONFIG_EARLY_PRINTK_EFI
240                 if (!strncmp(buf, "efi", 3))
241                         early_console_register(&early_efi_console, keep);
242 #endif
243
244                 buf++;
245         }
246         return 0;
247 }
248
249 early_param("earlyprintk", setup_early_printk);