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 18f4ed2877SYinghai Lu int early_serial_base; 19fa97bdf9SPekka Enberg 20fa97bdf9SPekka Enberg #define XMTRDY 0x20 21fa97bdf9SPekka Enberg 22fa97bdf9SPekka Enberg #define TXR 0 /* Transmit register (WRITE) */ 23fa97bdf9SPekka Enberg #define LSR 5 /* Line Status */ 24fa97bdf9SPekka Enberg 2596ae6ea0SThomas Gleixner /* 2696ae6ea0SThomas Gleixner * These functions are in .inittext so they can be used to signal 2796ae6ea0SThomas Gleixner * error during initialization. 2896ae6ea0SThomas Gleixner */ 2996ae6ea0SThomas Gleixner 30fa97bdf9SPekka Enberg static void __attribute__((section(".inittext"))) serial_putchar(int ch) 31fa97bdf9SPekka Enberg { 32fa97bdf9SPekka Enberg unsigned timeout = 0xffff; 33fa97bdf9SPekka Enberg 34fa97bdf9SPekka Enberg while ((inb(early_serial_base + LSR) & XMTRDY) == 0 && --timeout) 35fa97bdf9SPekka Enberg cpu_relax(); 36fa97bdf9SPekka Enberg 37fa97bdf9SPekka Enberg outb(ch, early_serial_base + TXR); 38fa97bdf9SPekka Enberg } 39fa97bdf9SPekka Enberg 40fa97bdf9SPekka Enberg static void __attribute__((section(".inittext"))) bios_putchar(int ch) 4196ae6ea0SThomas Gleixner { 42df7699c5SH. Peter Anvin struct biosregs ireg; 4396ae6ea0SThomas Gleixner 44df7699c5SH. Peter Anvin initregs(&ireg); 45df7699c5SH. Peter Anvin ireg.bx = 0x0007; 46df7699c5SH. Peter Anvin ireg.cx = 0x0001; 47df7699c5SH. Peter Anvin ireg.ah = 0x0e; 48df7699c5SH. Peter Anvin ireg.al = ch; 49df7699c5SH. Peter Anvin intcall(0x10, &ireg, NULL); 5096ae6ea0SThomas Gleixner } 5196ae6ea0SThomas Gleixner 52fa97bdf9SPekka Enberg void __attribute__((section(".inittext"))) putchar(int ch) 53fa97bdf9SPekka Enberg { 54fa97bdf9SPekka Enberg if (ch == '\n') 55fa97bdf9SPekka Enberg putchar('\r'); /* \n -> \r\n */ 56fa97bdf9SPekka Enberg 57fa97bdf9SPekka Enberg bios_putchar(ch); 58fa97bdf9SPekka Enberg 59fa97bdf9SPekka Enberg if (early_serial_base != 0) 60fa97bdf9SPekka Enberg serial_putchar(ch); 61fa97bdf9SPekka Enberg } 62fa97bdf9SPekka Enberg 6396ae6ea0SThomas Gleixner void __attribute__((section(".inittext"))) puts(const char *str) 6496ae6ea0SThomas Gleixner { 65df7699c5SH. Peter Anvin while (*str) 6696ae6ea0SThomas Gleixner putchar(*str++); 6796ae6ea0SThomas Gleixner } 6896ae6ea0SThomas Gleixner 6996ae6ea0SThomas Gleixner /* 7096ae6ea0SThomas Gleixner * Read the CMOS clock through the BIOS, and return the 7196ae6ea0SThomas Gleixner * seconds in BCD. 7296ae6ea0SThomas Gleixner */ 7396ae6ea0SThomas Gleixner 7496ae6ea0SThomas Gleixner static u8 gettime(void) 7596ae6ea0SThomas Gleixner { 76df7699c5SH. Peter Anvin struct biosregs ireg, oreg; 7796ae6ea0SThomas Gleixner 78df7699c5SH. Peter Anvin initregs(&ireg); 79df7699c5SH. Peter Anvin ireg.ah = 0x02; 80df7699c5SH. Peter Anvin intcall(0x1a, &ireg, &oreg); 8196ae6ea0SThomas Gleixner 82df7699c5SH. Peter Anvin return oreg.dh; 8396ae6ea0SThomas Gleixner } 8496ae6ea0SThomas Gleixner 8596ae6ea0SThomas Gleixner /* 8696ae6ea0SThomas Gleixner * Read from the keyboard 8796ae6ea0SThomas Gleixner */ 8896ae6ea0SThomas Gleixner int getchar(void) 8996ae6ea0SThomas Gleixner { 90df7699c5SH. Peter Anvin struct biosregs ireg, oreg; 9196ae6ea0SThomas Gleixner 92df7699c5SH. Peter Anvin initregs(&ireg); 93df7699c5SH. Peter Anvin /* ireg.ah = 0x00; */ 94df7699c5SH. Peter Anvin intcall(0x16, &ireg, &oreg); 95df7699c5SH. Peter Anvin 96df7699c5SH. Peter Anvin return oreg.al; 9796ae6ea0SThomas Gleixner } 9896ae6ea0SThomas Gleixner 9996ae6ea0SThomas Gleixner static int kbd_pending(void) 10096ae6ea0SThomas Gleixner { 101df7699c5SH. Peter Anvin struct biosregs ireg, oreg; 102df7699c5SH. Peter Anvin 103df7699c5SH. Peter Anvin initregs(&ireg); 104df7699c5SH. Peter Anvin ireg.ah = 0x01; 105df7699c5SH. Peter Anvin intcall(0x16, &ireg, &oreg); 106df7699c5SH. Peter Anvin 107df7699c5SH. Peter Anvin return !(oreg.eflags & X86_EFLAGS_ZF); 10896ae6ea0SThomas Gleixner } 10996ae6ea0SThomas Gleixner 11096ae6ea0SThomas Gleixner void kbd_flush(void) 11196ae6ea0SThomas Gleixner { 11296ae6ea0SThomas Gleixner for (;;) { 11396ae6ea0SThomas Gleixner if (!kbd_pending()) 11496ae6ea0SThomas Gleixner break; 11596ae6ea0SThomas Gleixner getchar(); 11696ae6ea0SThomas Gleixner } 11796ae6ea0SThomas Gleixner } 11896ae6ea0SThomas Gleixner 11996ae6ea0SThomas Gleixner int getchar_timeout(void) 12096ae6ea0SThomas Gleixner { 12196ae6ea0SThomas Gleixner int cnt = 30; 12296ae6ea0SThomas Gleixner int t0, t1; 12396ae6ea0SThomas Gleixner 12496ae6ea0SThomas Gleixner t0 = gettime(); 12596ae6ea0SThomas Gleixner 12696ae6ea0SThomas Gleixner while (cnt) { 12796ae6ea0SThomas Gleixner if (kbd_pending()) 12896ae6ea0SThomas Gleixner return getchar(); 12996ae6ea0SThomas Gleixner 13096ae6ea0SThomas Gleixner t1 = gettime(); 13196ae6ea0SThomas Gleixner if (t0 != t1) { 13296ae6ea0SThomas Gleixner cnt--; 13396ae6ea0SThomas Gleixner t0 = t1; 13496ae6ea0SThomas Gleixner } 13596ae6ea0SThomas Gleixner } 13696ae6ea0SThomas Gleixner 13796ae6ea0SThomas Gleixner return 0; /* Timeout! */ 13896ae6ea0SThomas Gleixner } 139fa97bdf9SPekka Enberg 140