Home | History | Annotate | Download | only in core
      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