blob: f3ceee20ff122f8793d39c91b85f8c5b45269a7d [file] [log] [blame]
H. Peter Anvin15436102007-07-11 12:18:45 -07001/* -*- linux-c -*- ------------------------------------------------------- *
2 *
3 * Copyright (C) 1991, 1992 Linus Torvalds
4 * Copyright 2007 rPath, Inc. - All Rights Reserved
H. Peter Anvindf7699c2009-04-01 18:13:46 -07005 * Copyright 2009 Intel Corporation; author H. Peter Anvin
H. Peter Anvin15436102007-07-11 12:18:45 -07006 *
7 * This file is part of the Linux kernel, and is made available under
8 * the terms of the GNU General Public License version 2.
9 *
10 * ----------------------------------------------------------------------- */
11
12/*
Pekka Enbergfa97bdf2010-07-11 11:06:57 +030013 * Very simple screen and serial I/O
H. Peter Anvin15436102007-07-11 12:18:45 -070014 */
15
16#include "boot.h"
17
Pekka Enbergfa97bdf2010-07-11 11:06:57 +030018#define DEFAULT_SERIAL_PORT 0x3f8 /* ttyS0 */
19
20static int early_serial_base;
21
22#define XMTRDY 0x20
23
24#define DLAB 0x80
25
26#define TXR 0 /* Transmit register (WRITE) */
27#define RXR 0 /* Receive register (READ) */
28#define IER 1 /* Interrupt Enable */
29#define IIR 2 /* Interrupt ID */
30#define FCR 2 /* FIFO control */
31#define LCR 3 /* Line control */
32#define MCR 4 /* Modem control */
33#define LSR 5 /* Line Status */
34#define MSR 6 /* Modem Status */
35#define DLL 0 /* Divisor Latch Low */
36#define DLH 1 /* Divisor latch High */
37
38#define DEFAULT_BAUD 9600
39
H. Peter Anvin15436102007-07-11 12:18:45 -070040/*
41 * These functions are in .inittext so they can be used to signal
42 * error during initialization.
43 */
44
Pekka Enbergfa97bdf2010-07-11 11:06:57 +030045static void __attribute__((section(".inittext"))) serial_putchar(int ch)
46{
47 unsigned timeout = 0xffff;
48
49 while ((inb(early_serial_base + LSR) & XMTRDY) == 0 && --timeout)
50 cpu_relax();
51
52 outb(ch, early_serial_base + TXR);
53}
54
55static void __attribute__((section(".inittext"))) bios_putchar(int ch)
H. Peter Anvin15436102007-07-11 12:18:45 -070056{
H. Peter Anvindf7699c2009-04-01 18:13:46 -070057 struct biosregs ireg;
H. Peter Anvin15436102007-07-11 12:18:45 -070058
H. Peter Anvindf7699c2009-04-01 18:13:46 -070059 initregs(&ireg);
60 ireg.bx = 0x0007;
61 ireg.cx = 0x0001;
62 ireg.ah = 0x0e;
63 ireg.al = ch;
64 intcall(0x10, &ireg, NULL);
H. Peter Anvin15436102007-07-11 12:18:45 -070065}
66
Pekka Enbergfa97bdf2010-07-11 11:06:57 +030067void __attribute__((section(".inittext"))) putchar(int ch)
68{
69 if (ch == '\n')
70 putchar('\r'); /* \n -> \r\n */
71
72 bios_putchar(ch);
73
74 if (early_serial_base != 0)
75 serial_putchar(ch);
76}
77
H. Peter Anvin15436102007-07-11 12:18:45 -070078void __attribute__((section(".inittext"))) puts(const char *str)
79{
H. Peter Anvindf7699c2009-04-01 18:13:46 -070080 while (*str)
H. Peter Anvin15436102007-07-11 12:18:45 -070081 putchar(*str++);
H. Peter Anvin15436102007-07-11 12:18:45 -070082}
83
84/*
85 * Read the CMOS clock through the BIOS, and return the
86 * seconds in BCD.
87 */
88
89static u8 gettime(void)
90{
H. Peter Anvindf7699c2009-04-01 18:13:46 -070091 struct biosregs ireg, oreg;
H. Peter Anvin15436102007-07-11 12:18:45 -070092
H. Peter Anvindf7699c2009-04-01 18:13:46 -070093 initregs(&ireg);
94 ireg.ah = 0x02;
95 intcall(0x1a, &ireg, &oreg);
H. Peter Anvin15436102007-07-11 12:18:45 -070096
H. Peter Anvindf7699c2009-04-01 18:13:46 -070097 return oreg.dh;
H. Peter Anvin15436102007-07-11 12:18:45 -070098}
99
100/*
101 * Read from the keyboard
102 */
103int getchar(void)
104{
H. Peter Anvindf7699c2009-04-01 18:13:46 -0700105 struct biosregs ireg, oreg;
H. Peter Anvin15436102007-07-11 12:18:45 -0700106
H. Peter Anvindf7699c2009-04-01 18:13:46 -0700107 initregs(&ireg);
108 /* ireg.ah = 0x00; */
109 intcall(0x16, &ireg, &oreg);
110
111 return oreg.al;
H. Peter Anvin15436102007-07-11 12:18:45 -0700112}
113
114static int kbd_pending(void)
115{
H. Peter Anvindf7699c2009-04-01 18:13:46 -0700116 struct biosregs ireg, oreg;
117
118 initregs(&ireg);
119 ireg.ah = 0x01;
120 intcall(0x16, &ireg, &oreg);
121
122 return !(oreg.eflags & X86_EFLAGS_ZF);
H. Peter Anvin15436102007-07-11 12:18:45 -0700123}
124
125void kbd_flush(void)
126{
127 for (;;) {
128 if (!kbd_pending())
129 break;
130 getchar();
131 }
132}
133
134int getchar_timeout(void)
135{
136 int cnt = 30;
137 int t0, t1;
138
139 t0 = gettime();
140
141 while (cnt) {
142 if (kbd_pending())
143 return getchar();
144
145 t1 = gettime();
146 if (t0 != t1) {
147 cnt--;
148 t0 = t1;
149 }
150 }
151
152 return 0; /* Timeout! */
153}
Pekka Enbergfa97bdf2010-07-11 11:06:57 +0300154
155static void early_serial_init(int baud)
156{
157 unsigned char c;
158 unsigned divisor;
159
160 outb(0x3, early_serial_base + LCR); /* 8n1 */
161 outb(0, early_serial_base + IER); /* no interrupt */
162 outb(0, early_serial_base + FCR); /* no fifo */
163 outb(0x3, early_serial_base + MCR); /* DTR + RTS */
164
165 divisor = 115200 / baud;
166 c = inb(early_serial_base + LCR);
167 outb(c | DLAB, early_serial_base + LCR);
168 outb(divisor & 0xff, early_serial_base + DLL);
169 outb((divisor >> 8) & 0xff, early_serial_base + DLH);
170 outb(c & ~DLAB, early_serial_base + LCR);
171}
172
173void console_init(void)
174{
175 int baud = DEFAULT_BAUD;
176 char arg[32];
177 int pos = 0;
178
179 if (cmdline_find_option("earlyprintk", arg, sizeof arg) > 0) {
180 char *e;
181
182 if (!strncmp(arg, "serial", 6)) {
183 early_serial_base = DEFAULT_SERIAL_PORT;
184 pos += 6;
185 }
186
187 if (arg[pos] == ',')
188 pos++;
189
190 if (!strncmp(arg, "ttyS", 4)) {
191 static const int bases[] = { 0x3f8, 0x2f8 };
192 int port = 0;
193
194 if (!strncmp(arg + pos, "ttyS", 4))
195 pos += 4;
196
197 if (arg[pos++] == '1')
198 port = 1;
199
200 early_serial_base = bases[port];
201 }
202
203 if (arg[pos] == ',')
204 pos++;
205
206 baud = simple_strtoull(arg + pos, &e, 0);
207 if (baud == 0 || arg + pos == e)
208 baud = DEFAULT_BAUD;
209 }
210
211 if (early_serial_base != 0)
212 early_serial_init(baud);
213}