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 */
serial_putc(int ch)90 void 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 */
serial_getc(void)107 int 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 */
serial_init(void)126 void 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);
191 out:
192 return;
193 }
194