xref: /inferno-os/os/sa1110/devuart.c (revision 871e0f01ca6cdef953c81d1a5018d90fbd01838a)
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 #include	"../port/netif.h"
9 
10 /*
11  * currently no DMA or flow control (hardware or software)
12  */
13 
14 enum
15 {
16 	Stagesize= 1024,
17 	Dmabufsize=Stagesize/2,
18 	Nuart=7,		/* max per machine */
19 
20 	CTLS= 023,
21 	CTLQ= 021,
22 };
23 
24 typedef struct Uart Uart;
25 struct Uart
26 {
27 	QLock;
28 
29 	int	opens;
30 
31 	int	enabled;
32 
33 	int	frame;		/* framing errors */
34 	int	overrun;	/* rcvr overruns */
35 	int	soverrun;	/* software overruns */
36 	int	perror;		/* parity error */
37 	int	bps;		/* baud rate */
38 	uchar	bits;
39 	char	parity;
40 
41 	int	inters;		/* total interrupt count */
42 	int	rinters;	/* interrupts due to read */
43 	int	winters;	/* interrupts due to write */
44 
45 	int	rcount;		/* total read count */
46 	int	wcount;		/* total output count */
47 
48 	int	xonoff;		/* software flow control on */
49 	int	blocked;		/* output blocked */
50 
51 	/* buffers */
52 	int	(*putc)(Queue*, int);
53 	Queue	*iq;
54 	Queue	*oq;
55 
56 	int	port;
57 	UartReg	*reg;
58 
59 	/* staging areas to avoid some of the per character costs */
60 	uchar	*ip;
61 	uchar	*ie;
62 	uchar	*op;
63 	uchar	*oe;
64 
65 	/* put large buffers last to aid register-offset optimizations: */
66 	char	name[KNAMELEN];
67 	uchar	istage[Stagesize];
68 	uchar	ostage[Stagesize];
69 };
70 
71 enum {
72 	UTCR0_PE=	0x01,
73 	UTCR0_OES=	0x02,
74 	UTCR0_SBS=	0x04,
75 	UTCR0_DSS=	0x08,
76 	UTCR0_SCE=	0x10,
77 	UTCR0_RCE=	0x20,
78 	UTCR0_TCE=	0x40,
79 
80 	UTCR3_RXE=	0x01,
81 	UTCR3_TXE=	0x02,
82 	UTCR3_BRK=	0x04,
83 	UTCR3_RIM=	0x08,
84 	UTCR3_TIM=	0x10,
85 	UTCR3_LBM=	0x20,
86 
87 	UTSR0_TFS=	0x01,
88 	UTSR0_RFS=	0x02,
89 	UTSR0_RID=	0x04,
90 	UTSR0_RBB=	0x08,
91 	UTSR0_REB=	0x10,
92 	UTSR0_EIF=	0x20,
93 
94 	UTSR1_TBY=	0x01,
95 	UTSR1_RNE=	0x02,
96 	UTSR1_TNF=	0x04,
97 	UTSR1_PRE=	0x08,
98 	UTSR1_FRE=	0x10,
99 	UTSR1_ROR=	0x20,
100 };
101 
102 static Uart *uart[Nuart];
103 static int nuart;
104 static int uartspcl;
105 int redirectconsole;
106 
107 static void
108 uartset(Uart *p)
109 {
110 	UartReg *reg = p->reg;
111 	ulong ocr3;
112 	ulong brdiv;
113 	int n;
114 
115 	brdiv = CLOCKFREQ/16/p->bps - 1;
116 	ocr3 = reg->utcr3;
117 	reg->utcr3 = ocr3&~(UTCR3_RXE|UTCR3_TXE);
118 	reg->utcr1 = brdiv >> 8;
119 	reg->utcr2 = brdiv & 0xff;
120 	/* set PE and OES appropriately for o/e/n: */
121 	reg->utcr0 = ((p->parity&3)^UTCR0_OES)|(p->bits&UTCR0_DSS);
122 	reg->utcr3 = ocr3;
123 
124 	/* set buffer length according to speed, to allow
125 	 * at most a 200ms delay before dumping the staging buffer
126 	 * into the input queue
127 	 */
128 	n = p->bps/(10*1000/200);
129 	p->ie = &p->istage[n < Stagesize ? n : Stagesize];
130 }
131 
132 /*
133  *  send break
134  */
135 static void
136 uartbreak(Uart *p, int ms)
137 {
138 	UartReg *reg = p->reg;
139 	if(ms == 0)
140 		ms = 200;
141 	reg->utcr3 |= UTCR3_BRK;
142 	tsleep(&up->sleep, return0, 0, ms);
143 	reg->utcr3 &= ~UTCR3_BRK;
144 }
145 
146 /*
147  *  turn on a port
148  */
149 static void
150 uartenable(Uart *p)
151 {
152 	UartReg *reg = p->reg;
153 
154 	if(p->enabled)
155 		return;
156 
157 	archuartpower(p->port, 1);
158 	uartset(p);
159 	reg->utsr0 = 0xff;		// clear all sticky status bits
160 	// enable receive, transmit, and receive interrupt:
161 	reg->utcr3 = UTCR3_RXE|UTCR3_TXE|UTCR3_RIM;
162 	p->blocked = 0;
163 	p->xonoff = 0;
164 	p->enabled = 1;
165 }
166 
167 /*
168  *  turn off a port
169  */
170 static void
171 uartdisable(Uart *p)
172 {
173 	p->reg->utcr3 = 0;		// disable TX, RX, and ints
174 	p->blocked = 0;
175 	p->xonoff = 0;
176 	p->enabled = 0;
177 	archuartpower(p->port, 0);
178 }
179 
180 /*
181  *  put some bytes into the local queue to avoid calling
182  *  qconsume for every character
183  */
184 static int
185 stageoutput(Uart *p)
186 {
187 	int n;
188 	Queue *q = p->oq;
189 
190 	if(q == nil)
191 		return 0;
192 	n = qconsume(q, p->ostage, Stagesize);
193 	if(n <= 0)
194 		return 0;
195 	p->op = p->ostage;
196 	p->oe = p->ostage + n;
197 	return n;
198 }
199 
200 static void
201 uartxmit(Uart *p)
202 {
203 	UartReg *reg = p->reg;
204 	ulong e = 0;
205 
206 	if(!p->blocked) {
207 		while(p->op < p->oe || stageoutput(p)) {
208 			if(reg->utsr1 & UTSR1_TNF) {
209 				reg->utdr = *(p->op++);
210 				p->wcount++;
211 			} else {
212 				e = UTCR3_TIM;
213 				break;
214 			}
215 		}
216 	}
217 	reg->utcr3 = (reg->utcr3&~UTCR3_TIM)|e;
218 }
219 
220 static void
221 uartrecvq(Uart *p)
222 {
223 	uchar *cp = p->istage;
224 	int n = p->ip - cp;
225 
226 	if(n == 0)
227 		return;
228 	if(p->putc)
229 		while(n-- > 0)
230 			p->putc(p->iq, *cp++);
231 	else if(p->iq)
232 		if(qproduce(p->iq, p->istage, n) < n){
233 			/* if xonoff, should send XOFF when qwindow(p->iq) < threshold */
234 			p->soverrun++;
235 			//print("qproduce flow control");
236 		}
237 	p->ip = p->istage;
238 }
239 
240 static void
241 uartrecv(Uart *p)
242 {
243 	UartReg *reg = p->reg;
244 	ulong n;
245 	while(reg->utsr1 & UTSR1_RNE) {
246 		int c;
247 		n = reg->utsr1;
248 		c = reg->utdr;
249 		if(n & (UTSR1_PRE|UTSR1_FRE|UTSR1_ROR)) {
250 			if(n & UTSR1_PRE)
251 				p->perror++;
252 			if(n & UTSR1_FRE)
253 				p->frame++;
254 			if(n & UTSR1_ROR)
255 				p->overrun++;
256 			continue;
257 		}
258 		if(p->xonoff){
259 			if(c == CTLS){
260 				p->blocked = 1;
261 			}else if (c == CTLQ){
262 				p->blocked = 0;
263 			}
264 		}
265 		*p->ip++ = c;
266 		if(p->ip >= p->ie)
267 			uartrecvq(p);
268 		p->rcount++;
269 	}
270 	if(reg->utsr0 & UTSR0_RID) {
271 		reg->utsr0 = UTSR0_RID;
272 		uartrecvq(p);
273 	}
274 }
275 
276 static void
277 uartclock(void)
278 {
279 	Uart *p;
280 	int i;
281 
282 	for(i=0; i<nuart; i++){
283 		p = uart[i];
284 		if(p != nil)
285 			uartrecvq(p);
286 	}
287 }
288 
289 static void
290 uartkick(void *a)
291 {
292 	Uart *p = a;
293 	int x;
294 
295 	x = splhi();
296 	uartxmit(p);
297 	splx(x);
298 }
299 
300 /*
301  *  UART Interrupt Handler
302  */
303 static void
304 uartintr(Ureg*, void* arg)
305 {
306 	Uart *p = arg;
307 	UartReg *reg = p->reg;
308 	ulong m = reg->utsr0;
309 	int dokick;
310 
311 	dokick = p->blocked;
312 	p->inters++;
313 	if(m & (UTSR0_RFS|UTSR0_RID|UTSR0_EIF)) {
314 		p->rinters++;
315 		uartrecv(p);
316 	}
317 	if(p->blocked)
318 		dokick = 0;
319 	if((m & UTSR0_TFS) && (reg->utcr3&UTCR3_TIM || dokick)) {
320 		p->winters++;
321 		uartxmit(p);
322 	}
323 
324 	if(m & (UTSR0_RBB|UTSR0_REB)) {
325 		//print("<BREAK>");
326 		/* reg->utsr0 = UTSR0_RBB|UTSR0_REB; */
327 		reg->utsr0 = m & (UTSR0_RBB|UTSR0_REB);
328 		/* what to do? if anything */
329 	}
330 }
331 
332 static void
333 uartsetup(ulong port, char *name)
334 {
335 	Uart *p;
336 
337 	if(nuart >= Nuart)
338 		return;
339 
340 	p = xalloc(sizeof(Uart));
341 	uart[nuart++] = p;
342 	strcpy(p->name, name);
343 
344 	p->port = port;
345 	p->reg = UARTREG(port);
346 	p->bps = 9600;
347 	p->bits = 8;
348 	p->parity = 'n';
349 
350 	p->iq = qopen(4*1024, 0, 0 , p);
351 	p->oq = qopen(4*1024, 0, uartkick, p);
352 
353 	p->ip = p->istage;
354 	p->ie = &p->istage[Stagesize];
355 	p->op = p->ostage;
356 	p->oe = p->ostage;
357 	if(port == 1)
358 		GPCLKREG->gpclkr0 |= 1;	/* SUS=1 for uart on serial 1 */
359 
360 	intrenable(UARTbit(port), uartintr, p, BusCPU, name);
361 }
362 
363 static void
364 uartinstall(void)
365 {
366 	static int already;
367 
368 	if(already)
369 		return;
370 	already = 1;
371 
372 	uartsetup(3, "eia0");
373 	uartsetup(1, "eia1");
374 	addclock0link(uartclock, 22);
375 }
376 
377 /*
378  *  called by main() to configure a duart port as a console or a mouse
379  */
380 void
381 uartspecial(int port, int bps, char parity, Queue **in, Queue **out, int (*putc)(Queue*, int))
382 {
383 	Uart *p;
384 
385 	uartinstall();
386 	if(port >= nuart)
387 		return;
388 	p = uart[port];
389 	if(bps)
390 		p->bps = bps;
391 	if(parity)
392 		p->parity = parity;
393 	uartenable(p);
394 	p->putc = putc;
395 	if(in)
396 		*in = p->iq;
397 	if(out)
398 		*out = p->oq;
399 	p->opens++;
400 	uartspcl = 1;
401 }
402 
403 Dirtab *uartdir;
404 int ndir;
405 
406 static void
407 setlength(int i)
408 {
409 	Uart *p;
410 
411 	if(i > 0){
412 		p = uart[i];
413 		if(p && p->opens && p->iq)
414 			uartdir[1+3*i].length = qlen(p->iq);
415 	} else for(i = 0; i < nuart; i++){
416 		p = uart[i];
417 		if(p && p->opens && p->iq)
418 			uartdir[1+3*i].length = qlen(p->iq);
419 	}
420 }
421 
422 /*
423  *  all uarts must be uartsetup() by this point or inside of uartinstall()
424  */
425 static void
426 uartreset(void)
427 {
428 	int i;
429 	Dirtab *dp;
430 
431 	uartinstall();
432 
433 	ndir = 1+3*nuart;
434 	uartdir = xalloc(ndir * sizeof(Dirtab));
435 	dp = uartdir;
436 	strcpy(dp->name, ".");
437 	mkqid(&dp->qid, 0, 0, QTDIR);
438 	dp->length = 0;
439 	dp->perm = DMDIR|0555;
440 	dp++;
441 	for(i = 0; i < nuart; i++){
442 		/* 3 directory entries per port */
443 		strcpy(dp->name, uart[i]->name);
444 		dp->qid.path = NETQID(i, Ndataqid);
445 		dp->perm = 0660;
446 		dp++;
447 		sprint(dp->name, "%sctl", uart[i]->name);
448 		dp->qid.path = NETQID(i, Nctlqid);
449 		dp->perm = 0660;
450 		dp++;
451 		sprint(dp->name, "%sstatus", uart[i]->name);
452 		dp->qid.path = NETQID(i, Nstatqid);
453 		dp->perm = 0444;
454 		dp++;
455 	}
456 }
457 
458 static Chan*
459 uartattach(char *spec)
460 {
461 	return devattach('t', spec);
462 }
463 
464 static Walkqid*
465 uartwalk(Chan *c, Chan *nc, char **name, int nname)
466 {
467 	return devwalk(c, nc, name, nname, uartdir, ndir, devgen);
468 }
469 
470 static int
471 uartstat(Chan *c, uchar *dp, int n)
472 {
473 	if(NETTYPE(c->qid.path) == Ndataqid)
474 		setlength(NETID(c->qid.path));
475 	return devstat(c, dp, n, uartdir, ndir, devgen);
476 }
477 
478 static Chan*
479 uartopen(Chan *c, int omode)
480 {
481 	Uart *p;
482 
483 	c = devopen(c, omode, uartdir, ndir, devgen);
484 
485 	switch(NETTYPE(c->qid.path)){
486 	case Nctlqid:
487 	case Ndataqid:
488 		p = uart[NETID(c->qid.path)];
489 		qlock(p);
490 		if(p->opens++ == 0){
491 			uartenable(p);
492 			qreopen(p->iq);
493 			qreopen(p->oq);
494 		}
495 		qunlock(p);
496 		break;
497 	}
498 
499 	return c;
500 }
501 
502 static void
503 uartclose(Chan *c)
504 {
505 	Uart *p;
506 
507 	if(c->qid.type & QTDIR)
508 		return;
509 	if((c->flag & COPEN) == 0)
510 		return;
511 	switch(NETTYPE(c->qid.path)){
512 	case Ndataqid:
513 	case Nctlqid:
514 		p = uart[NETID(c->qid.path)];
515 		qlock(p);
516 		if(--(p->opens) == 0){
517 			uartdisable(p);
518 			qclose(p->iq);
519 			qclose(p->oq);
520 			p->ip = p->istage;
521 		}
522 		qunlock(p);
523 		break;
524 	}
525 }
526 
527 static long
528 uartstatus(Chan *c, Uart *p, void *buf, long n, long offset)
529 {
530 	char str[256];
531 	USED(c);
532 
533 	str[0] = 0;
534 	snprint(str, sizeof(str),
535 			"b%d l%d p%c s%d x%d\n"
536 			"opens %d ferr %d oerr %d perr %d baud %d parity %c"
537 			" intr %d rintr %d wintr %d"
538 			" rcount %d wcount %d",
539 		p->bps, p->bits, p->parity, (p->reg->utcr0&UTCR0_SBS)?2:1, p->xonoff,
540 		p->opens, p->frame, p->overrun+p->soverrun, p->perror, p->bps, p->parity,
541 		p->inters, p->rinters, p->winters,
542 		p->rcount, p->wcount);
543 
544 	strcat(str, "\n");
545 	return readstr(offset, buf, n, str);
546 }
547 
548 static long
549 uartread(Chan *c, void *buf, long n, vlong offset)
550 {
551 	Uart *p;
552 
553 	if(c->qid.type & QTDIR){
554 		setlength(-1);
555 		return devdirread(c, buf, n, uartdir, ndir, devgen);
556 	}
557 
558 	p = uart[NETID(c->qid.path)];
559 	switch(NETTYPE(c->qid.path)){
560 	case Ndataqid:
561 		return qread(p->iq, buf, n);
562 	case Nctlqid:
563 		return readnum(offset, buf, n, NETID(c->qid.path), NUMSIZE);
564 	case Nstatqid:
565 		return uartstatus(c, p, buf, n, offset);
566 	}
567 
568 	return 0;
569 }
570 
571 static void
572 uartctl(Uart *p, char *cmd)
573 {
574 	int i, n;
575 
576 	/* let output drain for a while (up to 4 secs) */
577 	for(i = 0; i < 200 && (qlen(p->oq) || p->reg->utsr1 & UTSR1_TBY); i++)
578 		tsleep(&up->sleep, return0, 0, 20);
579 
580 	if(strncmp(cmd, "break", 5) == 0){
581 		uartbreak(p, 0);
582 		return;
583 	}
584 
585 	n = atoi(cmd+1);
586 	switch(*cmd){
587 	case 'B':
588 	case 'b':
589 		if(n <= 0)
590 			error(Ebadarg);
591 		p->bps = n;
592 		uartset(p);
593 		break;
594 	case 'f':
595 	case 'F':
596 		qflush(p->oq);
597 		break;
598 	case 'H':
599 	case 'h':
600 		qhangup(p->iq, 0);
601 		qhangup(p->oq, 0);
602 		break;
603 	case 'L':
604 	case 'l':
605 		if(n < 7 || n > 8)
606 			error(Ebadarg);
607 		p->bits = n;
608 		uartset(p);
609 		break;
610 	case 'n':
611 	case 'N':
612 		qnoblock(p->oq, n);
613 		break;
614 	case 'P':
615 	case 'p':
616 		p->parity = *(cmd+1);
617 		uartset(p);
618 		break;
619 	case 'K':
620 	case 'k':
621 		uartbreak(p, n);
622 		break;
623 	case 'Q':
624 	case 'q':
625 		qsetlimit(p->iq, n);
626 		qsetlimit(p->oq, n);
627 		break;
628 	case 'X':
629 	case 'x':
630 		p->xonoff = n;
631 		break;
632 	}
633 }
634 
635 static long
636 uartwrite(Chan *c, void *buf, long n, vlong offset)
637 {
638 	Uart *p;
639 	char cmd[32];
640 
641 	USED(offset);
642 
643 	if(c->qid.type & QTDIR)
644 		error(Eperm);
645 
646 	p = uart[NETID(c->qid.path)];
647 
648 	switch(NETTYPE(c->qid.path)){
649 	case Ndataqid:
650 		return qwrite(p->oq, buf, n);
651 	case Nctlqid:
652 
653 		if(n >= sizeof(cmd))
654 			n = sizeof(cmd)-1;
655 		memmove(cmd, buf, n);
656 		cmd[n] = 0;
657 		uartctl(p, cmd);
658 		return n;
659 	default:
660 		error(Egreg);
661 		return 0;
662 	}
663 }
664 
665 static int
666 uartwstat(Chan *c, uchar *dp, int n)
667 {
668 	Dir d;
669 	Dirtab *dt;
670 
671 	if(!iseve())
672 		error(Eperm);
673 	if(c->qid.type & QTDIR)
674 		error(Eperm);
675 	if(NETTYPE(c->qid.path) == Nstatqid)
676 		error(Eperm);
677 
678 	dt = &uartdir[1+3 * NETID(c->qid.path)];
679 	n = convM2D(dp, n, &d, nil);
680 	if(d.mode != ~0UL){
681 		d.mode &= 0666;
682 		dt[0].perm = dt[1].perm = d.mode;
683 	}
684 	return n;
685 }
686 
687 void
688 uartpower(int on)
689 {
690 	Uart *p;
691 	int i;
692 
693 	for(i=0; i<nuart; i++){
694 		p = uart[i];
695 		if(p != nil && p->opens){
696 			if(on && !p->enabled){
697 				p->enabled = 0;
698 				uartenable(p);
699 				uartkick(p);
700 			}else{
701 				if(p->port != 3)	/* leave the console */
702 					uartdisable(p);
703 				p->enabled = 0;
704 			}
705 		}
706 	}
707 }
708 
709 Dev uartdevtab = {
710 	't',
711 	"uart",
712 
713 	uartreset,
714 	devinit,
715 	devshutdown,
716 	uartattach,
717 	uartwalk,
718 	uartstat,
719 	uartopen,
720 	devcreate,
721 	uartclose,
722 	uartread,
723 	devbread,
724 	uartwrite,
725 	devbwrite,
726 	devremove,
727 	uartwstat,
728 	uartpower,
729 };
730 
731 /*
732  * for use by iprint
733  */
734 void
735 uartputc(int c)
736 {
737 	UartReg *r;
738 
739 	if(!uartspcl && !redirectconsole)
740 		return;
741 	if(c == 0)
742 		return;
743 	r = UARTREG(3);
744 	while((r->utsr1 & UTSR1_TNF) == 0)
745 		{}
746 	r->utdr = c;
747 	if(c == '\n')
748 		while(r->utsr1 & UTSR1_TBY)	/* flush xmit fifo */
749 			{}
750 }
751 
752 void
753 uartputs(char *data, int len)
754 {
755 	int s;
756 
757 	if(!uartspcl && !redirectconsole)
758 		return;
759 	clockpoll();
760 	s = splfhi();
761 	while(--len >= 0){
762 		if(*data == '\n')
763 			uartputc('\r');
764 		uartputc(*data++);
765 	}
766 	splx(s);
767 }
768 
769 /*
770  * for use by debugger
771  */
772 int
773 uartgetc(void)
774 {
775 	UartReg *r;
776 
777 	if(!uartspcl)
778 		return -1;
779 	clockcheck();
780 	r = UARTREG(3);
781 	while(!(r->utsr1 & UTSR1_RNE))
782 		clockcheck();
783 	return r->utdr;
784 }
785