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 28fa97bdf9SPekka Enberg static void __attribute__((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 38fa97bdf9SPekka Enberg static void __attribute__((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 50fa97bdf9SPekka Enberg void __attribute__((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 6196ae6ea0SThomas Gleixner void __attribute__((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 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 */ 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 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 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 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