xref: /plan9/sys/src/9/mtx/uarti8250.c (revision 9a747e4fd48b9f4522c70c07e8f882a15030f964)
1 #include "u.h"
2 #include "../port/lib.h"
3 #include "mem.h"
4 #include "dat.h"
5 #include "fns.h"
6 #include "io.h"
7 #include "../port/error.h"
8 
9 /*
10  * 8250 UART and compatibles.
11  */
12 enum {
13 	Uart0		= 0x3F8,	/* COM1 */
14 	Uart0IRQ	= 4,
15 	Uart1		= 0x2F8,	/* COM2 */
16 	Uart1IRQ	= 3,
17 
18 	UartFREQ	= 1843200,
19 };
20 
21 enum {					/* I/O ports */
22 	Rbr		= 0,		/* Receiver Buffer (RO) */
23 	Thr		= 0,		/* Transmitter Holding (WO) */
24 	Ier		= 1,		/* Interrupt Enable */
25 	Iir		= 2,		/* Interrupt Identification (RO) */
26 	Fcr		= 2,		/* FIFO Control (WO) */
27 	Lcr		= 3,		/* Line Control */
28 	Mcr		= 4,		/* Modem Control */
29 	Lsr		= 5,		/* Line Status */
30 	Msr		= 6,		/* Modem Status */
31 	Scr		= 7,		/* Scratch Pad */
32 	Dll		= 0,		/* Divisor Latch LSB */
33 	Dlm		= 1,		/* Divisor Latch MSB */
34 };
35 
36 enum {					/* Ier */
37 	Erda		= 0x01,		/* Enable Received Data Available */
38 	Ethre		= 0x02,		/* Enable Thr Empty */
39 	Erls		= 0x04,		/* Enable Receiver Line Status */
40 	Ems		= 0x08,		/* Enable Modem Status */
41 };
42 
43 enum {					/* Iir */
44 	Ims		= 0x00,		/* Ms interrupt */
45 	Ip		= 0x01,		/* Interrupt Pending (not) */
46 	Ithre		= 0x02,		/* Thr Empty */
47 	Irda		= 0x04,		/* Received Data Available */
48 	Irls		= 0x06,		/* Receiver Line Status */
49 	Ictoi		= 0x0C,		/* Character Time-out Indication */
50 	IirMASK		= 0x3F,
51 	Ife		= 0xC0,		/* FIFOs enabled */
52 };
53 
54 enum {					/* Fcr */
55 	FIFOena		= 0x01,		/* FIFO enable */
56 	FIFOrclr	= 0x02,		/* clear Rx FIFO */
57 	FIFOtclr	= 0x04,		/* clear Tx FIFO */
58 	FIFO1		= 0x00,		/* Rx FIFO trigger level 1 byte */
59 	FIFO4		= 0x40,		/*	4 bytes */
60 	FIFO8		= 0x80,		/*	8 bytes */
61 	FIFO14		= 0xC0,		/*	14 bytes */
62 };
63 
64 enum {					/* Lcr */
65 	Wls5		= 0x00,		/* Word Length Select 5 bits/byte */
66 	Wls6		= 0x01,		/*	6 bits/byte */
67 	Wls7		= 0x02,		/*	7 bits/byte */
68 	Wls8		= 0x03,		/*	8 bits/byte */
69 	WlsMASK		= 0x03,
70 	Stb		= 0x04,		/* 2 stop bits */
71 	Pen		= 0x08,		/* Parity Enable */
72 	Eps		= 0x10,		/* Even Parity Select */
73 	Stp		= 0x20,		/* Stick Parity */
74 	Brk		= 0x40,		/* Break */
75 	Dlab		= 0x80,		/* Divisor Latch Access Bit */
76 };
77 
78 enum {					/* Mcr */
79 	Dtr		= 0x01,		/* Data Terminal Ready */
80 	Rts		= 0x02,		/* Ready To Send */
81 	Out1		= 0x04,		/* no longer in use */
82 	Ie		= 0x08,		/* IRQ Enable */
83 	Dm		= 0x10,		/* Diagnostic Mode loopback */
84 };
85 
86 enum {					/* Lsr */
87 	Dr		= 0x01,		/* Data Ready */
88 	Oe		= 0x02,		/* Overrun Error */
89 	Pe		= 0x04,		/* Parity Error */
90 	Fe		= 0x08,		/* Framing Error */
91 	Bi		= 0x10,		/* Break Interrupt */
92 	Thre		= 0x20,		/* Thr Empty */
93 	Temt		= 0x40,		/* Tramsmitter Empty */
94 	FIFOerr		= 0x80,		/* error in receiver FIFO */
95 };
96 
97 enum {					/* Msr */
98 	Dcts		= 0x01,		/* Delta Cts */
99 	Ddsr		= 0x02,		/* Delta Dsr */
100 	Teri		= 0x04,		/* Trailing Edge of Ri */
101 	Ddcd		= 0x08,		/* Delta Dcd */
102 	Cts		= 0x10,		/* Clear To Send */
103 	Dsr		= 0x20,		/* Data Set Ready */
104 	Ri		= 0x40,		/* Ring Indicator */
105 	Dcd		= 0x80,		/* Data Set Ready */
106 };
107 
108 typedef struct Ctlr {
109 	int	io;
110 	int	irq;
111 	int	tbdf;
112 	int	iena;
113 
114 	uchar	sticky[8];
115 
116 	Lock;
117 	int	fifo;
118 	int	fena;
119 } Ctlr;
120 
121 extern PhysUart i8250physuart;
122 
123 static Ctlr i8250ctlr[2] = {
124 {	.io	= Uart0,
125 	.irq	= Uart0IRQ,
126 	.tbdf	= BUSUNKNOWN, },
127 
128 {	.io	= Uart1,
129 	.irq	= Uart1IRQ,
130 	.tbdf	= BUSUNKNOWN, },
131 };
132 
133 static Uart i8250uart[2] = {
134 {	.regs	= &i8250ctlr[0],
135 	.name	= "COM1",
136 	.freq	= UartFREQ,
137 	.phys	= &i8250physuart,
138 	.special=0,
139 	.next	= &i8250uart[1], },
140 
141 {	.regs	= &i8250ctlr[1],
142 	.name	= "COM2",
143 	.freq	= UartFREQ,
144 	.phys	= &i8250physuart,
145 	.special=0,
146 	.next	= nil, },
147 };
148 
149 #define csr8r(c, r)	inb((c)->io+(r))
150 #define csr8w(c, r, v)	outb((c)->io+(r), (c)->sticky[(r)]|(v))
151 
152 static long
i8250status(Uart * uart,void * buf,long n,long offset)153 i8250status(Uart* uart, void* buf, long n, long offset)
154 {
155 	char *p;
156 	Ctlr *ctlr;
157 	uchar ier, lcr, mcr, msr;
158 
159 	ctlr = uart->regs;
160 	p = malloc(READSTR);
161 	mcr = ctlr->sticky[Mcr];
162 	msr = csr8r(ctlr, Msr);
163 	ier = ctlr->sticky[Ier];
164 	lcr = ctlr->sticky[Lcr];
165 	snprint(p, READSTR,
166 		"b%d c%d d%d e%d l%d m%d p%c r%d s%d i%d\n"
167 		"dev(%d) type(%d) framing(%d) overruns(%d)%s%s%s%s\n",
168 
169 		uart->baud,
170 		uart->hup_dcd,
171 		(msr & Dsr) != 0,
172 		uart->hup_dsr,
173 		(lcr & WlsMASK) + 5,
174 		(ier & Ems) != 0,
175 		(lcr & Pen) ? ((lcr & Eps) ? 'e': 'o'): 'n',
176 		(mcr & Rts) != 0,
177 		(lcr & Stb) ? 2: 1,
178 		ctlr->fena,
179 
180 		uart->dev,
181 		uart->type,
182 		uart->ferr,
183 		uart->oerr,
184 		(msr & Cts) ? " cts": "",
185 		(msr & Dsr) ? " dsr": "",
186 		(msr & Dcd) ? " dcd": "",
187 		(msr & Ri) ? " ring": ""
188 	);
189 	n = readstr(offset, buf, n, p);
190 	free(p);
191 
192 	return n;
193 }
194 
195 static void
i8250fifo(Uart * uart,int on)196 i8250fifo(Uart* uart, int on)
197 {
198 	int i;
199 	Ctlr *ctlr;
200 
201 	/*
202 	 * Toggle FIFOs:
203 	 * if none, do nothing;
204 	 * reset the Rx and Tx FIFOs;
205 	 * empty the Rx buffer and clear any interrupt conditions;
206 	 * if enabling, try to turn them on.
207 	 */
208 	ctlr = uart->regs;
209 
210 	ilock(ctlr);
211 	if(!ctlr->fifo){
212 		csr8w(ctlr, Fcr, FIFOtclr|FIFOrclr);
213 		for(i = 0; i < 16; i++){
214 			csr8r(ctlr, Iir);
215 			csr8r(ctlr, Rbr);
216 		}
217 
218 		ctlr->fena = 0;
219 		if(on){
220 			csr8w(ctlr, Fcr, FIFO4|FIFOena);
221 			if(!(csr8r(ctlr, Iir) & Ife))
222 				ctlr->fifo = 1;
223 			ctlr->fena = 1;
224 		}
225 	}
226 	iunlock(ctlr);
227 }
228 
229 static void
i8250dtr(Uart * uart,int on)230 i8250dtr(Uart* uart, int on)
231 {
232 	Ctlr *ctlr;
233 
234 	/*
235 	 * Toggle DTR.
236 	 */
237 	ctlr = uart->regs;
238 	if(on)
239 		ctlr->sticky[Mcr] |= Dtr;
240 	else
241 		ctlr->sticky[Mcr] &= ~Dtr;
242 	csr8w(ctlr, Mcr, 0);
243 }
244 
245 static void
i8250rts(Uart * uart,int on)246 i8250rts(Uart* uart, int on)
247 {
248 	Ctlr *ctlr;
249 
250 	/*
251 	 * Toggle RTS.
252 	 */
253 	ctlr = uart->regs;
254 	if(on)
255 		ctlr->sticky[Mcr] |= Rts;
256 	else
257 		ctlr->sticky[Mcr] &= ~Rts;
258 	csr8w(ctlr, Mcr, 0);
259 }
260 
261 static void
i8250modemctl(Uart * uart,int on)262 i8250modemctl(Uart* uart, int on)
263 {
264 	Ctlr *ctlr;
265 
266 	ctlr = uart->regs;
267 	ilock(&uart->tlock);
268 	if(on){
269 		ctlr->sticky[Ier] |= Ems;
270 		csr8w(ctlr, Ier, 0);
271 		uart->modem = 1;
272 		uart->cts = csr8r(ctlr, Msr) & Cts;
273 	}
274 	else{
275 		ctlr->sticky[Ier] &= ~Ems;
276 		csr8w(ctlr, Ier, 0);
277 		uart->modem = 0;
278 		uart->cts = 1;
279 	}
280 	iunlock(&uart->tlock);
281 
282 	/* modem needs fifo */
283 	(*uart->phys->fifo)(uart, on);
284 }
285 
286 static int
i8250parity(Uart * uart,int parity)287 i8250parity(Uart* uart, int parity)
288 {
289 	int lcr;
290 	Ctlr *ctlr;
291 
292 	ctlr = uart->regs;
293 	lcr = ctlr->sticky[Lcr] & ~(Eps|Pen);
294 
295 	switch(parity){
296 	case 'e':
297 		lcr |= Eps|Pen;
298 		break;
299 	case 'o':
300 		lcr |= Pen;
301 		break;
302 	case 'n':
303 	default:
304 		break;
305 	}
306 	ctlr->sticky[Lcr] = lcr;
307 	csr8w(ctlr, Lcr, 0);
308 
309 	uart->parity = parity;
310 
311 	return 0;
312 }
313 
314 static int
i8250stop(Uart * uart,int stop)315 i8250stop(Uart* uart, int stop)
316 {
317 	int lcr;
318 	Ctlr *ctlr;
319 
320 	ctlr = uart->regs;
321 	lcr = ctlr->sticky[Lcr] & ~Stb;
322 
323 	switch(stop){
324 	case 1:
325 		break;
326 	case 2:
327 		lcr |= Stb;
328 		break;
329 	default:
330 		return -1;
331 	}
332 	ctlr->sticky[Lcr] = lcr;
333 	csr8w(ctlr, Lcr, 0);
334 
335 	uart->stop = stop;
336 
337 	return 0;
338 }
339 
340 static int
i8250bits(Uart * uart,int bits)341 i8250bits(Uart* uart, int bits)
342 {
343 	int lcr;
344 	Ctlr *ctlr;
345 
346 	ctlr = uart->regs;
347 	lcr = ctlr->sticky[Lcr] & ~WlsMASK;
348 
349 	switch(bits){
350 	case 5:
351 		lcr |= Wls5;
352 		break;
353 	case 6:
354 		lcr |= Wls6;
355 		break;
356 	case 7:
357 		lcr |= Wls7;
358 		break;
359 	case 8:
360 		lcr |= Wls8;
361 		break;
362 	default:
363 		return -1;
364 	}
365 	ctlr->sticky[Lcr] = lcr;
366 	csr8w(ctlr, Lcr, 0);
367 
368 	uart->bits = bits;
369 
370 	return 0;
371 }
372 
373 static int
i8250baud(Uart * uart,int baud)374 i8250baud(Uart* uart, int baud)
375 {
376 	ulong bgc;
377 	Ctlr *ctlr;
378 
379 	/*
380 	 * Set the Baud rate by calculating and setting the Baud rate
381 	 * Generator Constant. This will work with fairly non-standard
382 	 * Baud rates.
383 	 */
384 	if(uart->freq == 0 || baud <= 0)
385 		return -1;
386 	bgc = (uart->freq+8*baud-1)/(16*baud);
387 
388 	ctlr = uart->regs;
389 	csr8w(ctlr, Lcr, Dlab);
390 	outb(ctlr->io+Dlm, bgc>>8);
391 	outb(ctlr->io+Dll, bgc);
392 	csr8w(ctlr, Lcr, 0);
393 
394 	uart->baud = baud;
395 
396 	return 0;
397 }
398 
399 static void
i8250break(Uart * uart,int ms)400 i8250break(Uart* uart, int ms)
401 {
402 	Ctlr *ctlr;
403 
404 	/*
405 	 * Send a break.
406 	 */
407 	if(ms == 0)
408 		ms = 200;
409 
410 	ctlr = uart->regs;
411 	csr8w(ctlr, Lcr, Brk);
412 	tsleep(&up->sleep, return0, 0, ms);
413 	csr8w(ctlr, Lcr, 0);
414 }
415 
416 static void
i8250kick(Uart * uart)417 i8250kick(Uart* uart)
418 {
419 	int i;
420 	Ctlr *ctlr;
421 
422 	if(uart->cts == 0 || uart->blocked)
423 		return;
424 
425 	/*
426 	 *  128 here is an arbitrary limit to make sure
427 	 *  we don't stay in this loop too long.  If the
428 	 *  chip's output queue is longer than 128, too
429 	 *  bad -- presotto
430 	 */
431 	ctlr = uart->regs;
432 	for(i = 0; i < 128; i++){
433 		if(!(csr8r(ctlr, Lsr) & Thre))
434 			break;
435 		if(uart->op >= uart->oe && uartstageoutput(uart) == 0)
436 			break;
437 		outb(ctlr->io+Thr, *(uart->op++));
438 	}
439 }
440 
441 static void
i8250interrupt(Ureg *,void * arg)442 i8250interrupt(Ureg*, void* arg)
443 {
444 	Ctlr *ctlr;
445 	Uart *uart;
446 	int iir, lsr, old, r;
447 
448 	uart = arg;
449 
450 	ctlr = uart->regs;
451 	for(iir = csr8r(ctlr, Iir); !(iir & Ip); iir = csr8r(ctlr, Iir)){
452 		switch(iir & IirMASK){
453 		case Ims:		/* Ms interrupt */
454 			r = csr8r(ctlr, Msr);
455 			if(r & Dcts){
456 				ilock(&uart->tlock);
457 				old = uart->cts;
458 				uart->cts = r & Cts;
459 				if(old == 0 && uart->cts)
460 					uart->ctsbackoff = 2;
461 				iunlock(&uart->tlock);
462 			}
463 		 	if(r & Ddsr){
464 				old = r & Dsr;
465 				if(uart->hup_dsr && uart->dsr && !old)
466 					uart->dohup = 1;
467 				uart->dsr = old;
468 			}
469 		 	if(r & Ddcd){
470 				old = r & Dcd;
471 				if(uart->hup_dcd && uart->dcd && !old)
472 					uart->dohup = 1;
473 				uart->dcd = old;
474 			}
475 			break;
476 		case Ithre:		/* Thr Empty */
477 			uartkick(uart);
478 			break;
479 		case Irda:		/* Received Data Available */
480 		case Ictoi:		/* Character Time-out Indication */
481 			/*
482 			 * Consume any received data.
483 			 * If the received byte came in with a break,
484 			 * parity or framing error, throw it away;
485 			 * overrun is an indication that something has
486 			 * already been tossed.
487 			 */
488 			while((lsr = csr8r(ctlr, Lsr)) & Dr){
489 				if(lsr & Oe)
490 					uart->oerr++;
491 				if(lsr & Pe)
492 					uart->perr++;
493 				if(lsr & Fe)
494 					uart->ferr++;
495 				r = csr8r(ctlr, Rbr);
496 				if(!(lsr & (Bi|Fe|Pe)))
497 					uartrecv(uart, r);
498 			}
499 			break;
500 		default:
501 			iprint("weird uart interrupt 0x%2.2uX\n", iir);
502 			break;
503 		}
504 	}
505 }
506 
507 static void
i8250disable(Uart * uart)508 i8250disable(Uart* uart)
509 {
510 	Ctlr *ctlr;
511 
512 	/*
513  	 * Turn off DTR and RTS, disable interrupts and fifos.
514 	 */
515 	(*uart->phys->dtr)(uart, 0);
516 	(*uart->phys->rts)(uart, 0);
517 	(*uart->phys->fifo)(uart, 0);
518 
519 	ctlr = uart->regs;
520 	ctlr->sticky[Ier] = 0;
521 	csr8w(ctlr, Ier, 0);
522 }
523 
524 static void
i8250enable(Uart * uart,int ie)525 i8250enable(Uart* uart, int ie)
526 {
527 	Ctlr *ctlr;
528 
529 	/*
530  	 * Enable interrupts and turn on DTR and RTS.
531 	 * Be careful if this is called to set up a polled serial line
532 	 * early on not to try to enable interrupts as interrupt-
533 	 * -enabling mechanisms might not be set up yet.
534 	 */
535 	ctlr = uart->regs;
536 	if(ie){
537 		if(ctlr->iena == 0){
538 			intrenable(ctlr->irq, i8250interrupt, uart, ctlr->tbdf, uart->name);
539 			ctlr->iena = 1;
540 		}
541 		ctlr->sticky[Ier] = Ethre|Erda;
542 		ctlr->sticky[Mcr] |= Ie;
543 	}
544 	else{
545 		ctlr->sticky[Ier] = 0;
546 		ctlr->sticky[Mcr] = 0;
547 	}
548 	csr8w(ctlr, Ier, ctlr->sticky[Ier]);
549 	csr8w(ctlr, Mcr, ctlr->sticky[Mcr]);
550 
551 	(*uart->phys->dtr)(uart, 1);
552 	(*uart->phys->rts)(uart, 1);
553 }
554 
555 static Uart*
i8250pnp(void)556 i8250pnp(void)
557 {
558 	return i8250uart;
559 }
560 
561 static int
i8250getc(Uart * uart)562 i8250getc(Uart *uart)
563 {
564 	Ctlr *ctlr;
565 
566 	ctlr = uart->regs;
567 	while(!(csr8r(ctlr, Lsr)&Dr))
568 		delay(1);
569 	return csr8r(ctlr, Rbr);
570 }
571 
572 static void
i8250putc(Uart * uart,int c)573 i8250putc(Uart *uart, int c)
574 {
575 	int i;
576 	Ctlr *ctlr;
577 
578 	ctlr = uart->regs;
579 	for(i = 0; !(csr8r(ctlr, Lsr)&Thre) && i < 128; i++)
580 		delay(1);
581 	outb(ctlr->io+Thr, c);
582 	for(i = 0; !(csr8r(ctlr, Lsr)&Thre) && i < 128; i++)
583 		delay(1);
584 }
585 
586 PhysUart i8250physuart = {
587 	.name		= "i8250",
588 	.pnp		= i8250pnp,
589 	.enable		= i8250enable,
590 	.disable	= i8250disable,
591 	.kick		= i8250kick,
592 	.dobreak	= i8250break,
593 	.baud		= i8250baud,
594 	.bits		= i8250bits,
595 	.stop		= i8250stop,
596 	.parity		= i8250parity,
597 	.modemctl	= i8250modemctl,
598 	.rts		= i8250rts,
599 	.dtr		= i8250dtr,
600 	.status		= i8250status,
601 	.fifo		= i8250fifo,
602 	.getc		= i8250getc,
603 	.putc		= i8250putc,
604 };
605 
606 void
i8250console(void)607 i8250console(void)
608 {
609 	Uart *uart;
610 	int n;
611 	char *cmd, *p;
612 
613 	if((p = getconf("console")) == nil)
614 		return;
615 	n = strtoul(p, &cmd, 0);
616 	if(p == cmd)
617 		return;
618 	switch(n){
619 	default:
620 		return;
621 	case 0:
622 		uart = &i8250uart[0];
623 		break;
624 	case 1:
625 		uart = &i8250uart[1];
626 		break;
627 	}
628 
629 	uartctl(uart, "b9600 l8 pn s1");
630 	if(*cmd != '\0')
631 		uartctl(uart, cmd);
632 	(*uart->phys->enable)(uart, 0);
633 
634 	consuart = uart;
635 	uart->console = 1;
636 }
637