1 /* 2 * The serial port interface routines implement a simple polled i/o 3 * interface to a standard serial port. Due to the space restrictions 4 * for the boot blocks, no BIOS support is used (since BIOS requires 5 * expensive real/protected mode switches), instead the rudimentary 6 * BIOS support is duplicated here. 7 * 8 * The base address and speed for the i/o port are passed from the 9 * Makefile in the COMCONSOLE and CONSPEED preprocessor macros. The 10 * line control parameters are currently hard-coded to 8 bits, no 11 * parity, 1 stop bit (8N1). This can be changed in init_serial(). 12 */ 13 14 FILE_LICENCE ( GPL2_OR_LATER ); 15 16 #include "stddef.h" 17 #include <gpxe/init.h> 18 #include <gpxe/io.h> 19 #include <unistd.h> 20 #include <gpxe/serial.h> 21 #include "config/serial.h" 22 23 /* Set default values if none specified */ 24 25 #ifndef COMCONSOLE 26 #define COMCONSOLE 0x3f8 27 #endif 28 29 #ifndef COMSPEED 30 #define COMSPEED 9600 31 #endif 32 33 #ifndef COMDATA 34 #define COMDATA 8 35 #endif 36 37 #ifndef COMPARITY 38 #define COMPARITY 0 39 #endif 40 41 #ifndef COMSTOP 42 #define COMSTOP 1 43 #endif 44 45 #undef UART_BASE 46 #define UART_BASE ( COMCONSOLE ) 47 48 #undef UART_BAUD 49 #define UART_BAUD ( COMSPEED ) 50 51 #if ((115200%UART_BAUD) != 0) 52 #error Bad ttys0 baud rate 53 #endif 54 55 #define COMBRD (115200/UART_BAUD) 56 57 /* Line Control Settings */ 58 #define UART_LCS ( ( ( (COMDATA) - 5 ) << 0 ) | \ 59 ( ( (COMPARITY) ) << 3 ) | \ 60 ( ( (COMSTOP) - 1 ) << 2 ) ) 61 62 /* Data */ 63 #define UART_RBR 0x00 64 #define UART_TBR 0x00 65 66 /* Control */ 67 #define UART_IER 0x01 68 #define UART_IIR 0x02 69 #define UART_FCR 0x02 70 #define UART_LCR 0x03 71 #define UART_MCR 0x04 72 #define UART_DLL 0x00 73 #define UART_DLM 0x01 74 75 /* Status */ 76 #define UART_LSR 0x05 77 #define UART_LSR_TEMPT 0x40 /* Transmitter empty */ 78 #define UART_LSR_THRE 0x20 /* Transmit-hold-register empty */ 79 #define UART_LSR_BI 0x10 /* Break interrupt indicator */ 80 #define UART_LSR_FE 0x08 /* Frame error indicator */ 81 #define UART_LSR_PE 0x04 /* Parity error indicator */ 82 #define UART_LSR_OE 0x02 /* Overrun error indicator */ 83 #define UART_LSR_DR 0x01 /* Receiver data ready */ 84 85 #define UART_MSR 0x06 86 #define UART_SCR 0x07 87 88 #if defined(UART_MEM) 89 #define uart_readb(addr) readb((addr)) 90 #define uart_writeb(val,addr) writeb((val),(addr)) 91 #else 92 #define uart_readb(addr) inb((addr)) 93 #define uart_writeb(val,addr) outb((val),(addr)) 94 #endif 95 96 /* 97 * void serial_putc(int ch); 98 * Write character `ch' to port UART_BASE. 99 */ 100 void serial_putc ( int ch ) { 101 int i; 102 int status; 103 i = 1000; /* timeout */ 104 while(--i > 0) { 105 status = uart_readb(UART_BASE + UART_LSR); 106 if (status & UART_LSR_THRE) { 107 /* TX buffer emtpy */ 108 uart_writeb(ch, UART_BASE + UART_TBR); 109 break; 110 } 111 mdelay(2); 112 } 113 } 114 115 /* 116 * int serial_getc(void); 117 * Read a character from port UART_BASE. 118 */ 119 int serial_getc ( void ) { 120 int status; 121 int ch; 122 do { 123 status = uart_readb(UART_BASE + UART_LSR); 124 } while((status & 1) == 0); 125 ch = uart_readb(UART_BASE + UART_RBR); /* fetch (first) character */ 126 ch &= 0x7f; /* remove any parity bits we get */ 127 if (ch == 0x7f) { /* Make DEL... look like BS */ 128 ch = 0x08; 129 } 130 return ch; 131 } 132 133 /* 134 * int serial_ischar(void); 135 * If there is a character in the input buffer of port UART_BASE, 136 * return nonzero; otherwise return 0. 137 */ 138 int serial_ischar ( void ) { 139 int status; 140 status = uart_readb(UART_BASE + UART_LSR); /* line status reg; */ 141 return status & 1; /* rx char available */ 142 } 143 144 /* 145 * int serial_init(void); 146 * Initialize port UART_BASE to speed COMSPEED, line settings 8N1. 147 */ 148 static void serial_init ( void ) { 149 int status; 150 int divisor, lcs; 151 152 DBG ( "Serial port %#x initialising\n", UART_BASE ); 153 154 divisor = COMBRD; 155 lcs = UART_LCS; 156 157 158 #ifdef COMPRESERVE 159 lcs = uart_readb(UART_BASE + UART_LCR) & 0x7f; 160 uart_writeb(0x80 | lcs, UART_BASE + UART_LCR); 161 divisor = (uart_readb(UART_BASE + UART_DLM) << 8) | uart_readb(UART_BASE + UART_DLL); 162 uart_writeb(lcs, UART_BASE + UART_LCR); 163 #endif 164 165 /* Set Baud Rate Divisor to COMSPEED, and test to see if the 166 * serial port appears to be present. 167 */ 168 uart_writeb(0x80 | lcs, UART_BASE + UART_LCR); 169 uart_writeb(0xaa, UART_BASE + UART_DLL); 170 if (uart_readb(UART_BASE + UART_DLL) != 0xaa) { 171 DBG ( "Serial port %#x UART_DLL failed\n", UART_BASE ); 172 goto out; 173 } 174 uart_writeb(0x55, UART_BASE + UART_DLL); 175 if (uart_readb(UART_BASE + UART_DLL) != 0x55) { 176 DBG ( "Serial port %#x UART_DLL failed\n", UART_BASE ); 177 goto out; 178 } 179 uart_writeb(divisor & 0xff, UART_BASE + UART_DLL); 180 if (uart_readb(UART_BASE + UART_DLL) != (divisor & 0xff)) { 181 DBG ( "Serial port %#x UART_DLL failed\n", UART_BASE ); 182 goto out; 183 } 184 uart_writeb(0xaa, UART_BASE + UART_DLM); 185 if (uart_readb(UART_BASE + UART_DLM) != 0xaa) { 186 DBG ( "Serial port %#x UART_DLM failed\n", UART_BASE ); 187 goto out; 188 } 189 uart_writeb(0x55, UART_BASE + UART_DLM); 190 if (uart_readb(UART_BASE + UART_DLM) != 0x55) { 191 DBG ( "Serial port %#x UART_DLM failed\n", UART_BASE ); 192 goto out; 193 } 194 uart_writeb((divisor >> 8) & 0xff, UART_BASE + UART_DLM); 195 if (uart_readb(UART_BASE + UART_DLM) != ((divisor >> 8) & 0xff)) { 196 DBG ( "Serial port %#x UART_DLM failed\n", UART_BASE ); 197 goto out; 198 } 199 uart_writeb(lcs, UART_BASE + UART_LCR); 200 201 /* disable interrupts */ 202 uart_writeb(0x0, UART_BASE + UART_IER); 203 204 /* disable fifo's */ 205 uart_writeb(0x00, UART_BASE + UART_FCR); 206 207 /* Set clear to send, so flow control works... */ 208 uart_writeb((1<<1), UART_BASE + UART_MCR); 209 210 211 /* Flush the input buffer. */ 212 do { 213 /* rx buffer reg 214 * throw away (unconditionally the first time) 215 */ 216 (void) uart_readb(UART_BASE + UART_RBR); 217 /* line status reg */ 218 status = uart_readb(UART_BASE + UART_LSR); 219 } while(status & UART_LSR_DR); 220 out: 221 return; 222 } 223 224 /* 225 * void serial_fini(void); 226 * Cleanup our use of the serial port, in particular flush the 227 * output buffer so we don't accidentially lose characters. 228 */ 229 static void serial_fini ( int flags __unused ) { 230 int i, status; 231 /* Flush the output buffer to avoid dropping characters, 232 * if we are reinitializing the serial port. 233 */ 234 i = 10000; /* timeout */ 235 do { 236 status = uart_readb(UART_BASE + UART_LSR); 237 } while((--i > 0) && !(status & UART_LSR_TEMPT)); 238 /* Don't mark it as disabled; it's still usable */ 239 } 240 241 /** 242 * Serial driver initialisation function 243 * 244 * Initialise serial port early on so that it is available to capture 245 * early debug messages. 246 */ 247 struct init_fn serial_init_fn __init_fn ( INIT_SERIAL ) = { 248 .initialise = serial_init, 249 }; 250 251 /** Serial driver startup function */ 252 struct startup_fn serial_startup_fn __startup_fn ( STARTUP_EARLY ) = { 253 .shutdown = serial_fini, 254 }; 255