xref: /openbmc/linux/arch/x86/boot/tty.c (revision fa97bdf9)
196ae6ea0SThomas Gleixner /* -*- linux-c -*- ------------------------------------------------------- *
296ae6ea0SThomas Gleixner  *
396ae6ea0SThomas Gleixner  *   Copyright (C) 1991, 1992 Linus Torvalds
496ae6ea0SThomas Gleixner  *   Copyright 2007 rPath, Inc. - All Rights Reserved
5df7699c5SH. Peter Anvin  *   Copyright 2009 Intel Corporation; author H. Peter Anvin
696ae6ea0SThomas Gleixner  *
796ae6ea0SThomas Gleixner  *   This file is part of the Linux kernel, and is made available under
896ae6ea0SThomas Gleixner  *   the terms of the GNU General Public License version 2.
996ae6ea0SThomas Gleixner  *
1096ae6ea0SThomas Gleixner  * ----------------------------------------------------------------------- */
1196ae6ea0SThomas Gleixner 
1296ae6ea0SThomas Gleixner /*
13fa97bdf9SPekka Enberg  * Very simple screen and serial I/O
1496ae6ea0SThomas Gleixner  */
1596ae6ea0SThomas Gleixner 
1696ae6ea0SThomas Gleixner #include "boot.h"
1796ae6ea0SThomas Gleixner 
18fa97bdf9SPekka Enberg #define DEFAULT_SERIAL_PORT 0x3f8 /* ttyS0 */
19fa97bdf9SPekka Enberg 
20fa97bdf9SPekka Enberg static int	early_serial_base;
21fa97bdf9SPekka Enberg 
22fa97bdf9SPekka Enberg #define XMTRDY          0x20
23fa97bdf9SPekka Enberg 
24fa97bdf9SPekka Enberg #define DLAB		0x80
25fa97bdf9SPekka Enberg 
26fa97bdf9SPekka Enberg #define TXR             0       /*  Transmit register (WRITE) */
27fa97bdf9SPekka Enberg #define RXR             0       /*  Receive register  (READ)  */
28fa97bdf9SPekka Enberg #define IER             1       /*  Interrupt Enable          */
29fa97bdf9SPekka Enberg #define IIR             2       /*  Interrupt ID              */
30fa97bdf9SPekka Enberg #define FCR             2       /*  FIFO control              */
31fa97bdf9SPekka Enberg #define LCR             3       /*  Line control              */
32fa97bdf9SPekka Enberg #define MCR             4       /*  Modem control             */
33fa97bdf9SPekka Enberg #define LSR             5       /*  Line Status               */
34fa97bdf9SPekka Enberg #define MSR             6       /*  Modem Status              */
35fa97bdf9SPekka Enberg #define DLL             0       /*  Divisor Latch Low         */
36fa97bdf9SPekka Enberg #define DLH             1       /*  Divisor latch High        */
37fa97bdf9SPekka Enberg 
38fa97bdf9SPekka Enberg #define DEFAULT_BAUD 9600
39fa97bdf9SPekka Enberg 
4096ae6ea0SThomas Gleixner /*
4196ae6ea0SThomas Gleixner  * These functions are in .inittext so they can be used to signal
4296ae6ea0SThomas Gleixner  * error during initialization.
4396ae6ea0SThomas Gleixner  */
4496ae6ea0SThomas Gleixner 
45fa97bdf9SPekka Enberg static void __attribute__((section(".inittext"))) serial_putchar(int ch)
46fa97bdf9SPekka Enberg {
47fa97bdf9SPekka Enberg 	unsigned timeout = 0xffff;
48fa97bdf9SPekka Enberg 
49fa97bdf9SPekka Enberg 	while ((inb(early_serial_base + LSR) & XMTRDY) == 0 && --timeout)
50fa97bdf9SPekka Enberg 		cpu_relax();
51fa97bdf9SPekka Enberg 
52fa97bdf9SPekka Enberg 	outb(ch, early_serial_base + TXR);
53fa97bdf9SPekka Enberg }
54fa97bdf9SPekka Enberg 
55fa97bdf9SPekka Enberg static void __attribute__((section(".inittext"))) bios_putchar(int ch)
5696ae6ea0SThomas Gleixner {
57df7699c5SH. Peter Anvin 	struct biosregs ireg;
5896ae6ea0SThomas Gleixner 
59df7699c5SH. Peter Anvin 	initregs(&ireg);
60df7699c5SH. Peter Anvin 	ireg.bx = 0x0007;
61df7699c5SH. Peter Anvin 	ireg.cx = 0x0001;
62df7699c5SH. Peter Anvin 	ireg.ah = 0x0e;
63df7699c5SH. Peter Anvin 	ireg.al = ch;
64df7699c5SH. Peter Anvin 	intcall(0x10, &ireg, NULL);
6596ae6ea0SThomas Gleixner }
6696ae6ea0SThomas Gleixner 
67fa97bdf9SPekka Enberg void __attribute__((section(".inittext"))) putchar(int ch)
68fa97bdf9SPekka Enberg {
69fa97bdf9SPekka Enberg 	if (ch == '\n')
70fa97bdf9SPekka Enberg 		putchar('\r');	/* \n -> \r\n */
71fa97bdf9SPekka Enberg 
72fa97bdf9SPekka Enberg 	bios_putchar(ch);
73fa97bdf9SPekka Enberg 
74fa97bdf9SPekka Enberg 	if (early_serial_base != 0)
75fa97bdf9SPekka Enberg 		serial_putchar(ch);
76fa97bdf9SPekka Enberg }
77fa97bdf9SPekka Enberg 
7896ae6ea0SThomas Gleixner void __attribute__((section(".inittext"))) puts(const char *str)
7996ae6ea0SThomas Gleixner {
80df7699c5SH. Peter Anvin 	while (*str)
8196ae6ea0SThomas Gleixner 		putchar(*str++);
8296ae6ea0SThomas Gleixner }
8396ae6ea0SThomas Gleixner 
8496ae6ea0SThomas Gleixner /*
8596ae6ea0SThomas Gleixner  * Read the CMOS clock through the BIOS, and return the
8696ae6ea0SThomas Gleixner  * seconds in BCD.
8796ae6ea0SThomas Gleixner  */
8896ae6ea0SThomas Gleixner 
8996ae6ea0SThomas Gleixner static u8 gettime(void)
9096ae6ea0SThomas Gleixner {
91df7699c5SH. Peter Anvin 	struct biosregs ireg, oreg;
9296ae6ea0SThomas Gleixner 
93df7699c5SH. Peter Anvin 	initregs(&ireg);
94df7699c5SH. Peter Anvin 	ireg.ah = 0x02;
95df7699c5SH. Peter Anvin 	intcall(0x1a, &ireg, &oreg);
9696ae6ea0SThomas Gleixner 
97df7699c5SH. Peter Anvin 	return oreg.dh;
9896ae6ea0SThomas Gleixner }
9996ae6ea0SThomas Gleixner 
10096ae6ea0SThomas Gleixner /*
10196ae6ea0SThomas Gleixner  * Read from the keyboard
10296ae6ea0SThomas Gleixner  */
10396ae6ea0SThomas Gleixner int getchar(void)
10496ae6ea0SThomas Gleixner {
105df7699c5SH. Peter Anvin 	struct biosregs ireg, oreg;
10696ae6ea0SThomas Gleixner 
107df7699c5SH. Peter Anvin 	initregs(&ireg);
108df7699c5SH. Peter Anvin 	/* ireg.ah = 0x00; */
109df7699c5SH. Peter Anvin 	intcall(0x16, &ireg, &oreg);
110df7699c5SH. Peter Anvin 
111df7699c5SH. Peter Anvin 	return oreg.al;
11296ae6ea0SThomas Gleixner }
11396ae6ea0SThomas Gleixner 
11496ae6ea0SThomas Gleixner static int kbd_pending(void)
11596ae6ea0SThomas Gleixner {
116df7699c5SH. Peter Anvin 	struct biosregs ireg, oreg;
117df7699c5SH. Peter Anvin 
118df7699c5SH. Peter Anvin 	initregs(&ireg);
119df7699c5SH. Peter Anvin 	ireg.ah = 0x01;
120df7699c5SH. Peter Anvin 	intcall(0x16, &ireg, &oreg);
121df7699c5SH. Peter Anvin 
122df7699c5SH. Peter Anvin 	return !(oreg.eflags & X86_EFLAGS_ZF);
12396ae6ea0SThomas Gleixner }
12496ae6ea0SThomas Gleixner 
12596ae6ea0SThomas Gleixner void kbd_flush(void)
12696ae6ea0SThomas Gleixner {
12796ae6ea0SThomas Gleixner 	for (;;) {
12896ae6ea0SThomas Gleixner 		if (!kbd_pending())
12996ae6ea0SThomas Gleixner 			break;
13096ae6ea0SThomas Gleixner 		getchar();
13196ae6ea0SThomas Gleixner 	}
13296ae6ea0SThomas Gleixner }
13396ae6ea0SThomas Gleixner 
13496ae6ea0SThomas Gleixner int getchar_timeout(void)
13596ae6ea0SThomas Gleixner {
13696ae6ea0SThomas Gleixner 	int cnt = 30;
13796ae6ea0SThomas Gleixner 	int t0, t1;
13896ae6ea0SThomas Gleixner 
13996ae6ea0SThomas Gleixner 	t0 = gettime();
14096ae6ea0SThomas Gleixner 
14196ae6ea0SThomas Gleixner 	while (cnt) {
14296ae6ea0SThomas Gleixner 		if (kbd_pending())
14396ae6ea0SThomas Gleixner 			return getchar();
14496ae6ea0SThomas Gleixner 
14596ae6ea0SThomas Gleixner 		t1 = gettime();
14696ae6ea0SThomas Gleixner 		if (t0 != t1) {
14796ae6ea0SThomas Gleixner 			cnt--;
14896ae6ea0SThomas Gleixner 			t0 = t1;
14996ae6ea0SThomas Gleixner 		}
15096ae6ea0SThomas Gleixner 	}
15196ae6ea0SThomas Gleixner 
15296ae6ea0SThomas Gleixner 	return 0;		/* Timeout! */
15396ae6ea0SThomas Gleixner }
154fa97bdf9SPekka Enberg 
155fa97bdf9SPekka Enberg static void early_serial_init(int baud)
156fa97bdf9SPekka Enberg {
157fa97bdf9SPekka Enberg 	unsigned char c;
158fa97bdf9SPekka Enberg 	unsigned divisor;
159fa97bdf9SPekka Enberg 
160fa97bdf9SPekka Enberg 	outb(0x3, early_serial_base + LCR);	/* 8n1 */
161fa97bdf9SPekka Enberg 	outb(0, early_serial_base + IER);	/* no interrupt */
162fa97bdf9SPekka Enberg 	outb(0, early_serial_base + FCR);	/* no fifo */
163fa97bdf9SPekka Enberg 	outb(0x3, early_serial_base + MCR);	/* DTR + RTS */
164fa97bdf9SPekka Enberg 
165fa97bdf9SPekka Enberg 	divisor	= 115200 / baud;
166fa97bdf9SPekka Enberg 	c = inb(early_serial_base + LCR);
167fa97bdf9SPekka Enberg 	outb(c | DLAB, early_serial_base + LCR);
168fa97bdf9SPekka Enberg 	outb(divisor & 0xff, early_serial_base + DLL);
169fa97bdf9SPekka Enberg 	outb((divisor >> 8) & 0xff, early_serial_base + DLH);
170fa97bdf9SPekka Enberg 	outb(c & ~DLAB, early_serial_base + LCR);
171fa97bdf9SPekka Enberg }
172fa97bdf9SPekka Enberg 
173fa97bdf9SPekka Enberg void console_init(void)
174fa97bdf9SPekka Enberg {
175fa97bdf9SPekka Enberg 	int baud = DEFAULT_BAUD;
176fa97bdf9SPekka Enberg 	char arg[32];
177fa97bdf9SPekka Enberg 	int pos = 0;
178fa97bdf9SPekka Enberg 
179fa97bdf9SPekka Enberg 	if (cmdline_find_option("earlyprintk", arg, sizeof arg) > 0) {
180fa97bdf9SPekka Enberg 		char *e;
181fa97bdf9SPekka Enberg 
182fa97bdf9SPekka Enberg 		if (!strncmp(arg, "serial", 6)) {
183fa97bdf9SPekka Enberg 			early_serial_base = DEFAULT_SERIAL_PORT;
184fa97bdf9SPekka Enberg 			pos += 6;
185fa97bdf9SPekka Enberg 		}
186fa97bdf9SPekka Enberg 
187fa97bdf9SPekka Enberg 		if (arg[pos] == ',')
188fa97bdf9SPekka Enberg 			pos++;
189fa97bdf9SPekka Enberg 
190fa97bdf9SPekka Enberg 		if (!strncmp(arg, "ttyS", 4)) {
191fa97bdf9SPekka Enberg 			static const int bases[] = { 0x3f8, 0x2f8 };
192fa97bdf9SPekka Enberg 			int port = 0;
193fa97bdf9SPekka Enberg 
194fa97bdf9SPekka Enberg 			if (!strncmp(arg + pos, "ttyS", 4))
195fa97bdf9SPekka Enberg 				pos += 4;
196fa97bdf9SPekka Enberg 
197fa97bdf9SPekka Enberg 			if (arg[pos++] == '1')
198fa97bdf9SPekka Enberg 				port = 1;
199fa97bdf9SPekka Enberg 
200fa97bdf9SPekka Enberg 			early_serial_base = bases[port];
201fa97bdf9SPekka Enberg 		}
202fa97bdf9SPekka Enberg 
203fa97bdf9SPekka Enberg 		if (arg[pos] == ',')
204fa97bdf9SPekka Enberg 			pos++;
205fa97bdf9SPekka Enberg 
206fa97bdf9SPekka Enberg 		baud = simple_strtoull(arg + pos, &e, 0);
207fa97bdf9SPekka Enberg 		if (baud == 0 || arg + pos == e)
208fa97bdf9SPekka Enberg 			baud = DEFAULT_BAUD;
209fa97bdf9SPekka Enberg 	}
210fa97bdf9SPekka Enberg 
211fa97bdf9SPekka Enberg 	if (early_serial_base != 0)
212fa97bdf9SPekka Enberg 		early_serial_init(baud);
213fa97bdf9SPekka Enberg }
214