xref: /inferno-os/os/sa1110/devuart.c (revision e34c0431c44a07ee598e31d67e77d663413649fa)
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