197873a3dSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
296ae6ea0SThomas Gleixner /* -*- linux-c -*- ------------------------------------------------------- *
396ae6ea0SThomas Gleixner *
496ae6ea0SThomas Gleixner * Copyright (C) 1991, 1992 Linus Torvalds
596ae6ea0SThomas Gleixner * Copyright 2007 rPath, Inc. - All Rights Reserved
6df7699c5SH. Peter Anvin * Copyright 2009 Intel Corporation; author H. Peter Anvin
796ae6ea0SThomas Gleixner *
896ae6ea0SThomas Gleixner * ----------------------------------------------------------------------- */
996ae6ea0SThomas Gleixner
1096ae6ea0SThomas Gleixner /*
11fa97bdf9SPekka Enberg * Very simple screen and serial I/O
1296ae6ea0SThomas Gleixner */
1396ae6ea0SThomas Gleixner
1496ae6ea0SThomas Gleixner #include "boot.h"
1596ae6ea0SThomas Gleixner
16f4ed2877SYinghai Lu int early_serial_base;
17fa97bdf9SPekka Enberg
18fa97bdf9SPekka Enberg #define XMTRDY 0x20
19fa97bdf9SPekka Enberg
20fa97bdf9SPekka Enberg #define TXR 0 /* Transmit register (WRITE) */
21fa97bdf9SPekka Enberg #define LSR 5 /* Line Status */
22fa97bdf9SPekka Enberg
2396ae6ea0SThomas Gleixner /*
2496ae6ea0SThomas Gleixner * These functions are in .inittext so they can be used to signal
2596ae6ea0SThomas Gleixner * error during initialization.
2696ae6ea0SThomas Gleixner */
2796ae6ea0SThomas Gleixner
serial_putchar(int ch)2833def849SJoe Perches static void __section(".inittext") serial_putchar(int ch)
29fa97bdf9SPekka Enberg {
30fa97bdf9SPekka Enberg unsigned timeout = 0xffff;
31fa97bdf9SPekka Enberg
32fa97bdf9SPekka Enberg while ((inb(early_serial_base + LSR) & XMTRDY) == 0 && --timeout)
33fa97bdf9SPekka Enberg cpu_relax();
34fa97bdf9SPekka Enberg
35fa97bdf9SPekka Enberg outb(ch, early_serial_base + TXR);
36fa97bdf9SPekka Enberg }
37fa97bdf9SPekka Enberg
bios_putchar(int ch)3833def849SJoe Perches static void __section(".inittext") bios_putchar(int ch)
3996ae6ea0SThomas Gleixner {
40df7699c5SH. Peter Anvin struct biosregs ireg;
4196ae6ea0SThomas Gleixner
42df7699c5SH. Peter Anvin initregs(&ireg);
43df7699c5SH. Peter Anvin ireg.bx = 0x0007;
44df7699c5SH. Peter Anvin ireg.cx = 0x0001;
45df7699c5SH. Peter Anvin ireg.ah = 0x0e;
46df7699c5SH. Peter Anvin ireg.al = ch;
47df7699c5SH. Peter Anvin intcall(0x10, &ireg, NULL);
4896ae6ea0SThomas Gleixner }
4996ae6ea0SThomas Gleixner
putchar(int ch)5033def849SJoe Perches void __section(".inittext") putchar(int ch)
51fa97bdf9SPekka Enberg {
52fa97bdf9SPekka Enberg if (ch == '\n')
53fa97bdf9SPekka Enberg putchar('\r'); /* \n -> \r\n */
54fa97bdf9SPekka Enberg
55fa97bdf9SPekka Enberg bios_putchar(ch);
56fa97bdf9SPekka Enberg
57fa97bdf9SPekka Enberg if (early_serial_base != 0)
58fa97bdf9SPekka Enberg serial_putchar(ch);
59fa97bdf9SPekka Enberg }
60fa97bdf9SPekka Enberg
puts(const char * str)6133def849SJoe Perches void __section(".inittext") puts(const char *str)
6296ae6ea0SThomas Gleixner {
63df7699c5SH. Peter Anvin while (*str)
6496ae6ea0SThomas Gleixner putchar(*str++);
6596ae6ea0SThomas Gleixner }
6696ae6ea0SThomas Gleixner
6796ae6ea0SThomas Gleixner /*
6896ae6ea0SThomas Gleixner * Read the CMOS clock through the BIOS, and return the
6996ae6ea0SThomas Gleixner * seconds in BCD.
7096ae6ea0SThomas Gleixner */
7196ae6ea0SThomas Gleixner
gettime(void)7296ae6ea0SThomas Gleixner static u8 gettime(void)
7396ae6ea0SThomas Gleixner {
74df7699c5SH. Peter Anvin struct biosregs ireg, oreg;
7596ae6ea0SThomas Gleixner
76df7699c5SH. Peter Anvin initregs(&ireg);
77df7699c5SH. Peter Anvin ireg.ah = 0x02;
78df7699c5SH. Peter Anvin intcall(0x1a, &ireg, &oreg);
7996ae6ea0SThomas Gleixner
80df7699c5SH. Peter Anvin return oreg.dh;
8196ae6ea0SThomas Gleixner }
8296ae6ea0SThomas Gleixner
8396ae6ea0SThomas Gleixner /*
8496ae6ea0SThomas Gleixner * Read from the keyboard
8596ae6ea0SThomas Gleixner */
getchar(void)8696ae6ea0SThomas Gleixner int getchar(void)
8796ae6ea0SThomas Gleixner {
88df7699c5SH. Peter Anvin struct biosregs ireg, oreg;
8996ae6ea0SThomas Gleixner
90df7699c5SH. Peter Anvin initregs(&ireg);
91df7699c5SH. Peter Anvin /* ireg.ah = 0x00; */
92df7699c5SH. Peter Anvin intcall(0x16, &ireg, &oreg);
93df7699c5SH. Peter Anvin
94df7699c5SH. Peter Anvin return oreg.al;
9596ae6ea0SThomas Gleixner }
9696ae6ea0SThomas Gleixner
kbd_pending(void)9796ae6ea0SThomas Gleixner static int kbd_pending(void)
9896ae6ea0SThomas Gleixner {
99df7699c5SH. Peter Anvin struct biosregs ireg, oreg;
100df7699c5SH. Peter Anvin
101df7699c5SH. Peter Anvin initregs(&ireg);
102df7699c5SH. Peter Anvin ireg.ah = 0x01;
103df7699c5SH. Peter Anvin intcall(0x16, &ireg, &oreg);
104df7699c5SH. Peter Anvin
105df7699c5SH. Peter Anvin return !(oreg.eflags & X86_EFLAGS_ZF);
10696ae6ea0SThomas Gleixner }
10796ae6ea0SThomas Gleixner
kbd_flush(void)10896ae6ea0SThomas Gleixner void kbd_flush(void)
10996ae6ea0SThomas Gleixner {
11096ae6ea0SThomas Gleixner for (;;) {
11196ae6ea0SThomas Gleixner if (!kbd_pending())
11296ae6ea0SThomas Gleixner break;
11396ae6ea0SThomas Gleixner getchar();
11496ae6ea0SThomas Gleixner }
11596ae6ea0SThomas Gleixner }
11696ae6ea0SThomas Gleixner
getchar_timeout(void)11796ae6ea0SThomas Gleixner int getchar_timeout(void)
11896ae6ea0SThomas Gleixner {
11996ae6ea0SThomas Gleixner int cnt = 30;
12096ae6ea0SThomas Gleixner int t0, t1;
12196ae6ea0SThomas Gleixner
12296ae6ea0SThomas Gleixner t0 = gettime();
12396ae6ea0SThomas Gleixner
12496ae6ea0SThomas Gleixner while (cnt) {
12596ae6ea0SThomas Gleixner if (kbd_pending())
12696ae6ea0SThomas Gleixner return getchar();
12796ae6ea0SThomas Gleixner
12896ae6ea0SThomas Gleixner t1 = gettime();
12996ae6ea0SThomas Gleixner if (t0 != t1) {
13096ae6ea0SThomas Gleixner cnt--;
13196ae6ea0SThomas Gleixner t0 = t1;
13296ae6ea0SThomas Gleixner }
13396ae6ea0SThomas Gleixner }
13496ae6ea0SThomas Gleixner
13596ae6ea0SThomas Gleixner return 0; /* Timeout! */
13696ae6ea0SThomas Gleixner }
137fa97bdf9SPekka Enberg
138