xref: /inferno-os/os/boot/pc/8250.c (revision 74a4d8c26dd3c1e9febcb717cfd6cb6512991a7a)
1 #include "u.h"
2 #include "lib.h"
3 #include "mem.h"
4 #include "dat.h"
5 #include "fns.h"
6 #include "io.h"
7 
8 /*
9  *  INS8250 uart
10  */
11 enum
12 {
13 	/*
14 	 *  register numbers
15 	 */
16 	Data=	0,		/* xmit/rcv buffer */
17 	Iena=	1,		/* interrupt enable */
18 	 Ircv=	(1<<0),		/*  for char rcv'd */
19 	 Ixmt=	(1<<1),		/*  for xmit buffer empty */
20 	 Irstat=(1<<2),		/*  for change in rcv'er status */
21 	 Imstat=(1<<3),		/*  for change in modem status */
22 	Istat=	2,		/* interrupt flag (read) */
23 	Tctl=	2,		/* test control (write) */
24 	Format=	3,		/* byte format */
25 	 Bits8=	(3<<0),		/*  8 bits/byte */
26 	 Stop2=	(1<<2),		/*  2 stop bits */
27 	 Pena=	(1<<3),		/*  generate parity */
28 	 Peven=	(1<<4),		/*  even parity */
29 	 Pforce=(1<<5),		/*  force parity */
30 	 Break=	(1<<6),		/*  generate a break */
31 	 Dra=	(1<<7),		/*  address the divisor */
32 	Mctl=	4,		/* modem control */
33 	 Dtr=	(1<<0),		/*  data terminal ready */
34 	 Rts=	(1<<1),		/*  request to send */
35 	 Ri=	(1<<2),		/*  ring */
36 	 Inton=	(1<<3),		/*  turn on interrupts */
37 	 Loop=	(1<<4),		/*  loop back */
38 	Lstat=	5,		/* line status */
39 	 Inready=(1<<0),	/*  receive buffer full */
40 	 Oerror=(1<<1),		/*  receiver overrun */
41 	 Perror=(1<<2),		/*  receiver parity error */
42 	 Ferror=(1<<3),		/*  rcv framing error */
43 	 Outready=(1<<5),	/*  output buffer empty */
44 	Mstat=	6,		/* modem status */
45 	 Ctsc=	(1<<0),		/*  clear to send changed */
46 	 Dsrc=	(1<<1),		/*  data set ready changed */
47 	 Rire=	(1<<2),		/*  rising edge of ring indicator */
48 	 Dcdc=	(1<<3),		/*  data carrier detect changed */
49 	 Cts=	(1<<4),		/*  complement of clear to send line */
50 	 Dsr=	(1<<5),		/*  complement of data set ready line */
51 	 Ring=	(1<<6),		/*  complement of ring indicator line */
52 	 Dcd=	(1<<7),		/*  complement of data carrier detect line */
53 	Scratch=7,		/* scratchpad */
54 	Dlsb=	0,		/* divisor lsb */
55 	Dmsb=	1,		/* divisor msb */
56 
57 	Serial=	0,
58 	Modem=	1,
59 };
60 
61 typedef struct Uart	Uart;
62 struct Uart
63 {
64 	int	port;
65 	uchar	sticky[8];	/* sticky write register values */
66 	uchar	txbusy;
67 
68 	void	(*rx)(int);	/* routine to take a received character */
69 	int	(*tx)(void);	/* routine to get a character to transmit */
70 
71 	ulong	frame;
72 	ulong	overrun;
73 };
74 
75 static Uart com[2];
76 static Uart* uart;
77 
78 #define UartFREQ 1843200
79 
80 #define uartwrreg(u,r,v)	outb((u)->port + r, (u)->sticky[r] | (v))
81 #define uartrdreg(u,r)		inb((u)->port + r)
82 
83 /*
84  *  set the baud rate by calculating and setting the baudrate
85  *  generator constant.  This will work with fairly non-standard
86  *  baud rates.
87  */
88 static void
uartsetbaud(Uart * up,int rate)89 uartsetbaud(Uart *up, int rate)
90 {
91 	ulong brconst;
92 
93 	brconst = (UartFREQ+8*rate-1)/(16*rate);
94 
95 	uartwrreg(up, Format, Dra);
96 	outb(up->port+Dmsb, (brconst>>8) & 0xff);
97 	outb(up->port+Dlsb, brconst & 0xff);
98 	uartwrreg(up, Format, 0);
99 }
100 
101 /*
102  *  toggle DTR
103  */
104 static void
uartdtr(Uart * up,int n)105 uartdtr(Uart *up, int n)
106 {
107 	if(n)
108 		up->sticky[Mctl] |= Dtr;
109 	else
110 		up->sticky[Mctl] &= ~Dtr;
111 	uartwrreg(up, Mctl, 0);
112 }
113 
114 /*
115  *  toggle RTS
116  */
117 static void
uartrts(Uart * up,int n)118 uartrts(Uart *up, int n)
119 {
120 	if(n)
121 		up->sticky[Mctl] |= Rts;
122 	else
123 		up->sticky[Mctl] &= ~Rts;
124 	uartwrreg(up, Mctl, 0);
125 }
126 
127 static void
uartintr(Ureg *,void * arg)128 uartintr(Ureg*, void *arg)
129 {
130 	Uart *up;
131 	int ch;
132 	int s, l, loops;
133 
134 	up = arg;
135 	for(loops = 0; loops < 1024; loops++){
136 		s = uartrdreg(up, Istat);
137 		switch(s & 0x3F){
138 		case 6:	/* receiver line status */
139 			l = uartrdreg(up, Lstat);
140 			if(l & Ferror)
141 				up->frame++;
142 			if(l & Oerror)
143 				up->overrun++;
144 			break;
145 
146 		case 4:	/* received data available */
147 		case 12:
148 			ch = inb(up->port+Data);
149 			if(up->rx)
150 				(*up->rx)(ch);
151 			break;
152 
153 		case 2:	/* transmitter empty */
154 			ch = -1;
155 			if(up->tx)
156 				ch = (*up->tx)();
157 			if(ch != -1)
158 				outb(up->port+Data, ch);
159 			else
160 				up->txbusy = 0;
161 			break;
162 
163 		case 0:	/* modem status */
164 			uartrdreg(up, Mstat);
165 			break;
166 
167 		default:
168 			if(s&1)
169 				return;
170 			print("weird modem interrupt #%2.2ux\n", s);
171 			break;
172 		}
173 	}
174 	panic("uartintr: 0x%2.2ux\n", uartrdreg(up, Istat));
175 }
176 
177 /*
178  *  turn on a port's interrupts.  set DTR and RTS
179  */
180 static void
uartenable(Uart * up)181 uartenable(Uart *up)
182 {
183 	/*
184  	 *  turn on interrupts
185 	 */
186 	up->sticky[Iena] = 0;
187 	if(up->tx)
188 		up->sticky[Iena] |= Ixmt;
189 	if(up->rx)
190 		up->sticky[Iena] |= Ircv|Irstat;
191 	uartwrreg(up, Iena, 0);
192 
193 	/*
194 	 *  turn on DTR and RTS
195 	 */
196 	uartdtr(up, 1);
197 	uartrts(up, 1);
198 }
199 
200 static void
uartdisable(Uart * up)201 uartdisable(Uart* up)
202 {
203 	/*
204  	 * Disable interrupts.
205 	 */
206 	up->sticky[Iena] = 0;
207 	uartwrreg(up, Iena, 0);
208 	uartdtr(up, 0);
209 	uartrts(up, 0);
210 }
211 
212 void
uartspecial(int port,void (* rx)(int),int (* tx)(void),int baud)213 uartspecial(int port, void (*rx)(int), int (*tx)(void), int baud)
214 {
215 	Uart *up;
216 	int vector;
217 
218 	switch(port){
219 	case 0:
220 		port = 0x3F8;
221 		vector = VectorUART0;
222 		up = &com[0];
223 		break;
224 	case 1:
225 		port = 0x2F8;
226 		vector = VectorUART1;
227 		up = &com[1];
228 		break;
229 	default:
230 		return;
231 	}
232 
233 	if(uart != nil && uart != up)
234 		uartdisable(uart);
235 	uart = up;
236 
237 	if(up->port == 0){
238 		up->port = port;
239 		setvec(vector, uartintr, up);
240 	}
241 
242 	/*
243 	 *  set rate to 9600 baud.
244 	 *  8 bits/character.
245 	 *  1 stop bit.
246 	 *  interrupts enabled.
247 	 */
248 	uartsetbaud(up, 9600);
249 	up->sticky[Format] = Bits8;
250 	uartwrreg(up, Format, 0);
251 	up->sticky[Mctl] |= Inton;
252 	uartwrreg(up, Mctl, 0x0);
253 
254 	up->rx = rx;
255 	up->tx = tx;
256 	uartenable(up);
257 	if(baud)
258 		uartsetbaud(up, baud);
259 }
260 
261 void
uartputc(int c)262 uartputc(int c)
263 {
264 	int i;
265 	Uart *up;
266 
267 	if((up = uart) == nil)
268 		return;
269 	for(i = 0; i < 100; i++){
270 		if(uartrdreg(up, Lstat) & Outready)
271 			break;
272 		delay(1);
273 	}
274 	outb(up->port+Data, c);
275 }
276 
277 void
uartputs(IOQ * q,char * s,int n)278 uartputs(IOQ *q, char *s, int n)
279 {
280 	Uart *up;
281 	int c, x;
282 
283 	if((up = uart) == nil)
284 		return;
285 	while(n--){
286 		if(*s == '\n')
287 			q->putc(q, '\r');
288 		q->putc(q, *s++);
289 	}
290 	x = splhi();
291 	if(up->txbusy == 0 && (c = q->getc(q)) != -1){
292 		uartputc(c & 0xFF);
293 		up->txbusy = 1;
294 	}
295 	splx(x);
296 }
297 
298 void
uartdrain(void)299 uartdrain(void)
300 {
301 	Uart *up;
302 	int timeo;
303 
304 	if((up = uart) == nil)
305 		return;
306 	for(timeo = 0; timeo < 10000 && up->txbusy; timeo++)
307 		delay(1);
308 }
309