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#include <stddef.h>
15#include <sys/io.h>
16#include "serial.h"
17
18/* Set default values if none specified */
19
20#ifndef COMCONSOLE
21#define COMCONSOLE	0x3f8
22#endif
23
24#ifndef COMSPEED
25#define COMSPEED	9600
26#endif
27
28#ifndef COMDATA
29#define COMDATA		8
30#endif
31
32#ifndef COMPARITY
33#define COMPARITY	0
34#endif
35
36#ifndef COMSTOP
37#define COMSTOP		1
38#endif
39
40#undef UART_BASE
41#define UART_BASE ( COMCONSOLE )
42
43#undef UART_BAUD
44#define UART_BAUD ( COMSPEED )
45
46#if ((115200%UART_BAUD) != 0)
47#error Bad ttys0 baud rate
48#endif
49
50#define COMBRD (115200/UART_BAUD)
51
52/* Line Control Settings */
53#define UART_LCS ( ( ( (COMDATA) - 5 )	<< 0 ) | \
54		   ( ( (COMPARITY) )	<< 3 ) | \
55		   ( ( (COMSTOP) - 1 )	<< 2 ) )
56
57/* Data */
58#define UART_RBR 0x00
59#define UART_TBR 0x00
60
61/* Control */
62#define UART_IER 0x01
63#define UART_IIR 0x02
64#define UART_FCR 0x02
65#define UART_LCR 0x03
66#define UART_MCR 0x04
67#define UART_DLL 0x00
68#define UART_DLM 0x01
69
70/* Status */
71#define UART_LSR 0x05
72#define  UART_LSR_TEMPT 0x40	/* Transmitter empty */
73#define  UART_LSR_THRE  0x20	/* Transmit-hold-register empty */
74#define  UART_LSR_BI	0x10	/* Break interrupt indicator */
75#define  UART_LSR_FE	0x08	/* Frame error indicator */
76#define  UART_LSR_PE	0x04	/* Parity error indicator */
77#define  UART_LSR_OE	0x02	/* Overrun error indicator */
78#define  UART_LSR_DR	0x01	/* Receiver data ready */
79
80#define UART_MSR 0x06
81#define UART_SCR 0x07
82
83#define uart_readb(addr) inb(addr)
84#define uart_writeb(val,addr) outb((val),(addr))
85
86/*
87 * void serial_putc(int ch);
88 *	Write character `ch' to port UART_BASE.
89 */
90void serial_putc(int ch)
91{
92    int status;
93    for (;;) {
94	status = uart_readb(UART_BASE + UART_LSR);
95	if (status & UART_LSR_THRE) {
96	    /* TX buffer emtpy */
97	    uart_writeb(ch, UART_BASE + UART_TBR);
98	    break;
99	}
100    }
101}
102
103/*
104 * int serial_getc(void);
105 *	Read a character from port UART_BASE.
106 */
107int serial_getc(void)
108{
109    int status;
110    int ch;
111    do {
112	status = uart_readb(UART_BASE + UART_LSR);
113    } while ((status & 1) == 0);
114    ch = uart_readb(UART_BASE + UART_RBR);	/* fetch (first) character */
115    ch &= 0x7f;			/* remove any parity bits we get */
116    if (ch == 0x7f) {		/* Make DEL... look like BS */
117	ch = 0x08;
118    }
119    return ch;
120}
121
122/*
123 * int serial_init(void);
124 *	Initialize port UART_BASE to speed COMSPEED, line settings 8N1.
125 */
126void serial_init(void)
127{
128    int status;
129    int divisor, lcs;
130
131    divisor = COMBRD;
132    lcs = UART_LCS;
133
134#ifdef COMPRESERVE
135    lcs = uart_readb(UART_BASE + UART_LCR) & 0x7f;
136    uart_writeb(0x80 | lcs, UART_BASE + UART_LCR);
137    divisor =
138	(uart_readb(UART_BASE + UART_DLM) << 8) | uart_readb(UART_BASE +
139							     UART_DLL);
140    uart_writeb(lcs, UART_BASE + UART_LCR);
141#endif
142
143    /* Set Baud Rate Divisor to COMSPEED, and test to see if the
144     * serial port appears to be present.
145     */
146    uart_writeb(0x80 | lcs, UART_BASE + UART_LCR);
147    uart_writeb(0xaa, UART_BASE + UART_DLL);
148    if (uart_readb(UART_BASE + UART_DLL) != 0xaa) {
149	goto out;
150    }
151    uart_writeb(0x55, UART_BASE + UART_DLL);
152    if (uart_readb(UART_BASE + UART_DLL) != 0x55) {
153	goto out;
154    }
155    uart_writeb(divisor & 0xff, UART_BASE + UART_DLL);
156    if (uart_readb(UART_BASE + UART_DLL) != (divisor & 0xff)) {
157	goto out;
158    }
159    uart_writeb(0xaa, UART_BASE + UART_DLM);
160    if (uart_readb(UART_BASE + UART_DLM) != 0xaa) {
161	goto out;
162    }
163    uart_writeb(0x55, UART_BASE + UART_DLM);
164    if (uart_readb(UART_BASE + UART_DLM) != 0x55) {
165	goto out;
166    }
167    uart_writeb((divisor >> 8) & 0xff, UART_BASE + UART_DLM);
168    if (uart_readb(UART_BASE + UART_DLM) != ((divisor >> 8) & 0xff)) {
169	goto out;
170    }
171    uart_writeb(lcs, UART_BASE + UART_LCR);
172
173    /* disable interrupts */
174    uart_writeb(0x0, UART_BASE + UART_IER);
175
176    /* disable fifo's */
177    uart_writeb(0x00, UART_BASE + UART_FCR);
178
179    /* Set clear to send, so flow control works... */
180    uart_writeb((1 << 1), UART_BASE + UART_MCR);
181
182    /* Flush the input buffer. */
183    do {
184	/* rx buffer reg
185	 * throw away (unconditionally the first time)
186	 */
187	(void)uart_readb(UART_BASE + UART_RBR);
188	/* line status reg */
189	status = uart_readb(UART_BASE + UART_LSR);
190    } while (status & UART_LSR_DR);
191out:
192    return;
193}
194