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 18fa97bdf9SPekka Enberg #define DEFAULT_SERIAL_PORT 0x3f8 /* ttyS0 */ 19fa97bdf9SPekka Enberg 20fa97bdf9SPekka Enberg static int early_serial_base; 21fa97bdf9SPekka Enberg 22fa97bdf9SPekka Enberg #define XMTRDY 0x20 23fa97bdf9SPekka Enberg 24fa97bdf9SPekka Enberg #define DLAB 0x80 25fa97bdf9SPekka Enberg 26fa97bdf9SPekka Enberg #define TXR 0 /* Transmit register (WRITE) */ 27fa97bdf9SPekka Enberg #define RXR 0 /* Receive register (READ) */ 28fa97bdf9SPekka Enberg #define IER 1 /* Interrupt Enable */ 29fa97bdf9SPekka Enberg #define IIR 2 /* Interrupt ID */ 30fa97bdf9SPekka Enberg #define FCR 2 /* FIFO control */ 31fa97bdf9SPekka Enberg #define LCR 3 /* Line control */ 32fa97bdf9SPekka Enberg #define MCR 4 /* Modem control */ 33fa97bdf9SPekka Enberg #define LSR 5 /* Line Status */ 34fa97bdf9SPekka Enberg #define MSR 6 /* Modem Status */ 35fa97bdf9SPekka Enberg #define DLL 0 /* Divisor Latch Low */ 36fa97bdf9SPekka Enberg #define DLH 1 /* Divisor latch High */ 37fa97bdf9SPekka Enberg 38fa97bdf9SPekka Enberg #define DEFAULT_BAUD 9600 39fa97bdf9SPekka Enberg 4096ae6ea0SThomas Gleixner /* 4196ae6ea0SThomas Gleixner * These functions are in .inittext so they can be used to signal 4296ae6ea0SThomas Gleixner * error during initialization. 4396ae6ea0SThomas Gleixner */ 4496ae6ea0SThomas Gleixner 45fa97bdf9SPekka Enberg static void __attribute__((section(".inittext"))) serial_putchar(int ch) 46fa97bdf9SPekka Enberg { 47fa97bdf9SPekka Enberg unsigned timeout = 0xffff; 48fa97bdf9SPekka Enberg 49fa97bdf9SPekka Enberg while ((inb(early_serial_base + LSR) & XMTRDY) == 0 && --timeout) 50fa97bdf9SPekka Enberg cpu_relax(); 51fa97bdf9SPekka Enberg 52fa97bdf9SPekka Enberg outb(ch, early_serial_base + TXR); 53fa97bdf9SPekka Enberg } 54fa97bdf9SPekka Enberg 55fa97bdf9SPekka Enberg static void __attribute__((section(".inittext"))) bios_putchar(int ch) 5696ae6ea0SThomas Gleixner { 57df7699c5SH. Peter Anvin struct biosregs ireg; 5896ae6ea0SThomas Gleixner 59df7699c5SH. Peter Anvin initregs(&ireg); 60df7699c5SH. Peter Anvin ireg.bx = 0x0007; 61df7699c5SH. Peter Anvin ireg.cx = 0x0001; 62df7699c5SH. Peter Anvin ireg.ah = 0x0e; 63df7699c5SH. Peter Anvin ireg.al = ch; 64df7699c5SH. Peter Anvin intcall(0x10, &ireg, NULL); 6596ae6ea0SThomas Gleixner } 6696ae6ea0SThomas Gleixner 67fa97bdf9SPekka Enberg void __attribute__((section(".inittext"))) putchar(int ch) 68fa97bdf9SPekka Enberg { 69fa97bdf9SPekka Enberg if (ch == '\n') 70fa97bdf9SPekka Enberg putchar('\r'); /* \n -> \r\n */ 71fa97bdf9SPekka Enberg 72fa97bdf9SPekka Enberg bios_putchar(ch); 73fa97bdf9SPekka Enberg 74fa97bdf9SPekka Enberg if (early_serial_base != 0) 75fa97bdf9SPekka Enberg serial_putchar(ch); 76fa97bdf9SPekka Enberg } 77fa97bdf9SPekka Enberg 7896ae6ea0SThomas Gleixner void __attribute__((section(".inittext"))) puts(const char *str) 7996ae6ea0SThomas Gleixner { 80df7699c5SH. Peter Anvin while (*str) 8196ae6ea0SThomas Gleixner putchar(*str++); 8296ae6ea0SThomas Gleixner } 8396ae6ea0SThomas Gleixner 8496ae6ea0SThomas Gleixner /* 8596ae6ea0SThomas Gleixner * Read the CMOS clock through the BIOS, and return the 8696ae6ea0SThomas Gleixner * seconds in BCD. 8796ae6ea0SThomas Gleixner */ 8896ae6ea0SThomas Gleixner 8996ae6ea0SThomas Gleixner static u8 gettime(void) 9096ae6ea0SThomas Gleixner { 91df7699c5SH. Peter Anvin struct biosregs ireg, oreg; 9296ae6ea0SThomas Gleixner 93df7699c5SH. Peter Anvin initregs(&ireg); 94df7699c5SH. Peter Anvin ireg.ah = 0x02; 95df7699c5SH. Peter Anvin intcall(0x1a, &ireg, &oreg); 9696ae6ea0SThomas Gleixner 97df7699c5SH. Peter Anvin return oreg.dh; 9896ae6ea0SThomas Gleixner } 9996ae6ea0SThomas Gleixner 10096ae6ea0SThomas Gleixner /* 10196ae6ea0SThomas Gleixner * Read from the keyboard 10296ae6ea0SThomas Gleixner */ 10396ae6ea0SThomas Gleixner int getchar(void) 10496ae6ea0SThomas Gleixner { 105df7699c5SH. Peter Anvin struct biosregs ireg, oreg; 10696ae6ea0SThomas Gleixner 107df7699c5SH. Peter Anvin initregs(&ireg); 108df7699c5SH. Peter Anvin /* ireg.ah = 0x00; */ 109df7699c5SH. Peter Anvin intcall(0x16, &ireg, &oreg); 110df7699c5SH. Peter Anvin 111df7699c5SH. Peter Anvin return oreg.al; 11296ae6ea0SThomas Gleixner } 11396ae6ea0SThomas Gleixner 11496ae6ea0SThomas Gleixner static int kbd_pending(void) 11596ae6ea0SThomas Gleixner { 116df7699c5SH. Peter Anvin struct biosregs ireg, oreg; 117df7699c5SH. Peter Anvin 118df7699c5SH. Peter Anvin initregs(&ireg); 119df7699c5SH. Peter Anvin ireg.ah = 0x01; 120df7699c5SH. Peter Anvin intcall(0x16, &ireg, &oreg); 121df7699c5SH. Peter Anvin 122df7699c5SH. Peter Anvin return !(oreg.eflags & X86_EFLAGS_ZF); 12396ae6ea0SThomas Gleixner } 12496ae6ea0SThomas Gleixner 12596ae6ea0SThomas Gleixner void kbd_flush(void) 12696ae6ea0SThomas Gleixner { 12796ae6ea0SThomas Gleixner for (;;) { 12896ae6ea0SThomas Gleixner if (!kbd_pending()) 12996ae6ea0SThomas Gleixner break; 13096ae6ea0SThomas Gleixner getchar(); 13196ae6ea0SThomas Gleixner } 13296ae6ea0SThomas Gleixner } 13396ae6ea0SThomas Gleixner 13496ae6ea0SThomas Gleixner int getchar_timeout(void) 13596ae6ea0SThomas Gleixner { 13696ae6ea0SThomas Gleixner int cnt = 30; 13796ae6ea0SThomas Gleixner int t0, t1; 13896ae6ea0SThomas Gleixner 13996ae6ea0SThomas Gleixner t0 = gettime(); 14096ae6ea0SThomas Gleixner 14196ae6ea0SThomas Gleixner while (cnt) { 14296ae6ea0SThomas Gleixner if (kbd_pending()) 14396ae6ea0SThomas Gleixner return getchar(); 14496ae6ea0SThomas Gleixner 14596ae6ea0SThomas Gleixner t1 = gettime(); 14696ae6ea0SThomas Gleixner if (t0 != t1) { 14796ae6ea0SThomas Gleixner cnt--; 14896ae6ea0SThomas Gleixner t0 = t1; 14996ae6ea0SThomas Gleixner } 15096ae6ea0SThomas Gleixner } 15196ae6ea0SThomas Gleixner 15296ae6ea0SThomas Gleixner return 0; /* Timeout! */ 15396ae6ea0SThomas Gleixner } 154fa97bdf9SPekka Enberg 155fa97bdf9SPekka Enberg static void early_serial_init(int baud) 156fa97bdf9SPekka Enberg { 157fa97bdf9SPekka Enberg unsigned char c; 158fa97bdf9SPekka Enberg unsigned divisor; 159fa97bdf9SPekka Enberg 160fa97bdf9SPekka Enberg outb(0x3, early_serial_base + LCR); /* 8n1 */ 161fa97bdf9SPekka Enberg outb(0, early_serial_base + IER); /* no interrupt */ 162fa97bdf9SPekka Enberg outb(0, early_serial_base + FCR); /* no fifo */ 163fa97bdf9SPekka Enberg outb(0x3, early_serial_base + MCR); /* DTR + RTS */ 164fa97bdf9SPekka Enberg 165fa97bdf9SPekka Enberg divisor = 115200 / baud; 166fa97bdf9SPekka Enberg c = inb(early_serial_base + LCR); 167fa97bdf9SPekka Enberg outb(c | DLAB, early_serial_base + LCR); 168fa97bdf9SPekka Enberg outb(divisor & 0xff, early_serial_base + DLL); 169fa97bdf9SPekka Enberg outb((divisor >> 8) & 0xff, early_serial_base + DLH); 170fa97bdf9SPekka Enberg outb(c & ~DLAB, early_serial_base + LCR); 171fa97bdf9SPekka Enberg } 172fa97bdf9SPekka Enberg 173fa97bdf9SPekka Enberg void console_init(void) 174fa97bdf9SPekka Enberg { 175fa97bdf9SPekka Enberg int baud = DEFAULT_BAUD; 176fa97bdf9SPekka Enberg char arg[32]; 177fa97bdf9SPekka Enberg int pos = 0; 178fa97bdf9SPekka Enberg 179fa97bdf9SPekka Enberg if (cmdline_find_option("earlyprintk", arg, sizeof arg) > 0) { 180fa97bdf9SPekka Enberg char *e; 181fa97bdf9SPekka Enberg 182fa97bdf9SPekka Enberg if (!strncmp(arg, "serial", 6)) { 183fa97bdf9SPekka Enberg early_serial_base = DEFAULT_SERIAL_PORT; 184fa97bdf9SPekka Enberg pos += 6; 185fa97bdf9SPekka Enberg } 186fa97bdf9SPekka Enberg 187fa97bdf9SPekka Enberg if (arg[pos] == ',') 188fa97bdf9SPekka Enberg pos++; 189fa97bdf9SPekka Enberg 190fa97bdf9SPekka Enberg if (!strncmp(arg, "ttyS", 4)) { 191fa97bdf9SPekka Enberg static const int bases[] = { 0x3f8, 0x2f8 }; 192fa97bdf9SPekka Enberg int port = 0; 193fa97bdf9SPekka Enberg 194fa97bdf9SPekka Enberg if (!strncmp(arg + pos, "ttyS", 4)) 195fa97bdf9SPekka Enberg pos += 4; 196fa97bdf9SPekka Enberg 197fa97bdf9SPekka Enberg if (arg[pos++] == '1') 198fa97bdf9SPekka Enberg port = 1; 199fa97bdf9SPekka Enberg 200fa97bdf9SPekka Enberg early_serial_base = bases[port]; 201fa97bdf9SPekka Enberg } 202fa97bdf9SPekka Enberg 203fa97bdf9SPekka Enberg if (arg[pos] == ',') 204fa97bdf9SPekka Enberg pos++; 205fa97bdf9SPekka Enberg 206fa97bdf9SPekka Enberg baud = simple_strtoull(arg + pos, &e, 0); 207fa97bdf9SPekka Enberg if (baud == 0 || arg + pos == e) 208fa97bdf9SPekka Enberg baud = DEFAULT_BAUD; 209fa97bdf9SPekka Enberg } 210fa97bdf9SPekka Enberg 211fa97bdf9SPekka Enberg if (early_serial_base != 0) 212fa97bdf9SPekka Enberg early_serial_init(baud); 213fa97bdf9SPekka Enberg } 214