| H. Peter Anvin | 1543610 | 2007-07-11 12:18:45 -0700 | [diff] [blame] | 1 | /* -*- linux-c -*- ------------------------------------------------------- * | 
 | 2 |  * | 
 | 3 |  *   Copyright (C) 1991, 1992 Linus Torvalds | 
 | 4 |  *   Copyright 2007 rPath, Inc. - All Rights Reserved | 
| H. Peter Anvin | df7699c | 2009-04-01 18:13:46 -0700 | [diff] [blame] | 5 |  *   Copyright 2009 Intel Corporation; author H. Peter Anvin | 
| H. Peter Anvin | 1543610 | 2007-07-11 12:18:45 -0700 | [diff] [blame] | 6 |  * | 
 | 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 Enberg | fa97bdf | 2010-07-11 11:06:57 +0300 | [diff] [blame] | 13 |  * Very simple screen and serial I/O | 
| H. Peter Anvin | 1543610 | 2007-07-11 12:18:45 -0700 | [diff] [blame] | 14 |  */ | 
 | 15 |  | 
 | 16 | #include "boot.h" | 
 | 17 |  | 
| Yinghai Lu | f4ed287 | 2010-08-02 02:17:31 -0700 | [diff] [blame] | 18 | int early_serial_base; | 
| Pekka Enberg | fa97bdf | 2010-07-11 11:06:57 +0300 | [diff] [blame] | 19 |  | 
 | 20 | #define XMTRDY          0x20 | 
 | 21 |  | 
| Pekka Enberg | fa97bdf | 2010-07-11 11:06:57 +0300 | [diff] [blame] | 22 | #define TXR             0       /*  Transmit register (WRITE) */ | 
| Pekka Enberg | fa97bdf | 2010-07-11 11:06:57 +0300 | [diff] [blame] | 23 | #define LSR             5       /*  Line Status               */ | 
| Pekka Enberg | fa97bdf | 2010-07-11 11:06:57 +0300 | [diff] [blame] | 24 |  | 
| H. Peter Anvin | 1543610 | 2007-07-11 12:18:45 -0700 | [diff] [blame] | 25 | /* | 
 | 26 |  * These functions are in .inittext so they can be used to signal | 
 | 27 |  * error during initialization. | 
 | 28 |  */ | 
 | 29 |  | 
| Pekka Enberg | fa97bdf | 2010-07-11 11:06:57 +0300 | [diff] [blame] | 30 | static void __attribute__((section(".inittext"))) serial_putchar(int ch) | 
 | 31 | { | 
 | 32 | 	unsigned timeout = 0xffff; | 
 | 33 |  | 
 | 34 | 	while ((inb(early_serial_base + LSR) & XMTRDY) == 0 && --timeout) | 
 | 35 | 		cpu_relax(); | 
 | 36 |  | 
 | 37 | 	outb(ch, early_serial_base + TXR); | 
 | 38 | } | 
 | 39 |  | 
 | 40 | static void __attribute__((section(".inittext"))) bios_putchar(int ch) | 
| H. Peter Anvin | 1543610 | 2007-07-11 12:18:45 -0700 | [diff] [blame] | 41 | { | 
| H. Peter Anvin | df7699c | 2009-04-01 18:13:46 -0700 | [diff] [blame] | 42 | 	struct biosregs ireg; | 
| H. Peter Anvin | 1543610 | 2007-07-11 12:18:45 -0700 | [diff] [blame] | 43 |  | 
| H. Peter Anvin | df7699c | 2009-04-01 18:13:46 -0700 | [diff] [blame] | 44 | 	initregs(&ireg); | 
 | 45 | 	ireg.bx = 0x0007; | 
 | 46 | 	ireg.cx = 0x0001; | 
 | 47 | 	ireg.ah = 0x0e; | 
 | 48 | 	ireg.al = ch; | 
 | 49 | 	intcall(0x10, &ireg, NULL); | 
| H. Peter Anvin | 1543610 | 2007-07-11 12:18:45 -0700 | [diff] [blame] | 50 | } | 
 | 51 |  | 
| Pekka Enberg | fa97bdf | 2010-07-11 11:06:57 +0300 | [diff] [blame] | 52 | void __attribute__((section(".inittext"))) putchar(int ch) | 
 | 53 | { | 
 | 54 | 	if (ch == '\n') | 
 | 55 | 		putchar('\r');	/* \n -> \r\n */ | 
 | 56 |  | 
 | 57 | 	bios_putchar(ch); | 
 | 58 |  | 
 | 59 | 	if (early_serial_base != 0) | 
 | 60 | 		serial_putchar(ch); | 
 | 61 | } | 
 | 62 |  | 
| H. Peter Anvin | 1543610 | 2007-07-11 12:18:45 -0700 | [diff] [blame] | 63 | void __attribute__((section(".inittext"))) puts(const char *str) | 
 | 64 | { | 
| H. Peter Anvin | df7699c | 2009-04-01 18:13:46 -0700 | [diff] [blame] | 65 | 	while (*str) | 
| H. Peter Anvin | 1543610 | 2007-07-11 12:18:45 -0700 | [diff] [blame] | 66 | 		putchar(*str++); | 
| H. Peter Anvin | 1543610 | 2007-07-11 12:18:45 -0700 | [diff] [blame] | 67 | } | 
 | 68 |  | 
 | 69 | /* | 
 | 70 |  * Read the CMOS clock through the BIOS, and return the | 
 | 71 |  * seconds in BCD. | 
 | 72 |  */ | 
 | 73 |  | 
 | 74 | static u8 gettime(void) | 
 | 75 | { | 
| H. Peter Anvin | df7699c | 2009-04-01 18:13:46 -0700 | [diff] [blame] | 76 | 	struct biosregs ireg, oreg; | 
| H. Peter Anvin | 1543610 | 2007-07-11 12:18:45 -0700 | [diff] [blame] | 77 |  | 
| H. Peter Anvin | df7699c | 2009-04-01 18:13:46 -0700 | [diff] [blame] | 78 | 	initregs(&ireg); | 
 | 79 | 	ireg.ah = 0x02; | 
 | 80 | 	intcall(0x1a, &ireg, &oreg); | 
| H. Peter Anvin | 1543610 | 2007-07-11 12:18:45 -0700 | [diff] [blame] | 81 |  | 
| H. Peter Anvin | df7699c | 2009-04-01 18:13:46 -0700 | [diff] [blame] | 82 | 	return oreg.dh; | 
| H. Peter Anvin | 1543610 | 2007-07-11 12:18:45 -0700 | [diff] [blame] | 83 | } | 
 | 84 |  | 
 | 85 | /* | 
 | 86 |  * Read from the keyboard | 
 | 87 |  */ | 
 | 88 | int getchar(void) | 
 | 89 | { | 
| H. Peter Anvin | df7699c | 2009-04-01 18:13:46 -0700 | [diff] [blame] | 90 | 	struct biosregs ireg, oreg; | 
| H. Peter Anvin | 1543610 | 2007-07-11 12:18:45 -0700 | [diff] [blame] | 91 |  | 
| H. Peter Anvin | df7699c | 2009-04-01 18:13:46 -0700 | [diff] [blame] | 92 | 	initregs(&ireg); | 
 | 93 | 	/* ireg.ah = 0x00; */ | 
 | 94 | 	intcall(0x16, &ireg, &oreg); | 
 | 95 |  | 
 | 96 | 	return oreg.al; | 
| H. Peter Anvin | 1543610 | 2007-07-11 12:18:45 -0700 | [diff] [blame] | 97 | } | 
 | 98 |  | 
 | 99 | static int kbd_pending(void) | 
 | 100 | { | 
| H. Peter Anvin | df7699c | 2009-04-01 18:13:46 -0700 | [diff] [blame] | 101 | 	struct biosregs ireg, oreg; | 
 | 102 |  | 
 | 103 | 	initregs(&ireg); | 
 | 104 | 	ireg.ah = 0x01; | 
 | 105 | 	intcall(0x16, &ireg, &oreg); | 
 | 106 |  | 
 | 107 | 	return !(oreg.eflags & X86_EFLAGS_ZF); | 
| H. Peter Anvin | 1543610 | 2007-07-11 12:18:45 -0700 | [diff] [blame] | 108 | } | 
 | 109 |  | 
 | 110 | void kbd_flush(void) | 
 | 111 | { | 
 | 112 | 	for (;;) { | 
 | 113 | 		if (!kbd_pending()) | 
 | 114 | 			break; | 
 | 115 | 		getchar(); | 
 | 116 | 	} | 
 | 117 | } | 
 | 118 |  | 
 | 119 | int getchar_timeout(void) | 
 | 120 | { | 
 | 121 | 	int cnt = 30; | 
 | 122 | 	int t0, t1; | 
 | 123 |  | 
 | 124 | 	t0 = gettime(); | 
 | 125 |  | 
 | 126 | 	while (cnt) { | 
 | 127 | 		if (kbd_pending()) | 
 | 128 | 			return getchar(); | 
 | 129 |  | 
 | 130 | 		t1 = gettime(); | 
 | 131 | 		if (t0 != t1) { | 
 | 132 | 			cnt--; | 
 | 133 | 			t0 = t1; | 
 | 134 | 		} | 
 | 135 | 	} | 
 | 136 |  | 
 | 137 | 	return 0;		/* Timeout! */ | 
 | 138 | } | 
| Pekka Enberg | fa97bdf | 2010-07-11 11:06:57 +0300 | [diff] [blame] | 139 |  |