xref: /inferno-os/os/boot/rpcg/devuart.c (revision ecc9caba0e344ed50c05ee8156b2734f4d76e463)
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  *  SMC1 in UART mode
10  */
11 
12 typedef struct Uartsmc Uartsmc;
13 struct Uartsmc {
14 	IOCparam;
15 	ushort	maxidl;
16 	ushort	idlc;
17 	ushort	brkln;
18 	ushort	brkec;
19 	ushort	brkcr;
20 	ushort	rmask;
21 };
22 
23 typedef struct Uart	Uart;
24 struct Uart
25 {
26 	int	port;
27 	int	setup;
28 	uchar	txbusy;
29 
30 	Queue*	iq;
31 	Queue*	oq;
32 	void	(*rx)(Queue*, int);
33 	void	(*boot)(uchar*, int);
34 
35 	ulong	frame;
36 	ulong	overrun;
37 	uchar	rxbuf[128];
38 	char	txbuf[16];
39 	BD*	rxb;
40 	BD*	txb;
41 };
42 
43 Uart	uart[1];
44 int	predawn = 1;
45 
46 static	void	uartintr(Ureg*, void*);
47 static	void	uartkick(void*);
48 
49 static int
50 baudgen(int baud)
51 {
52 	int d;
53 
54 	d = ((m->cpuhz/baud)+8)>>4;
55 	if(d >= (1<<12))
56 		return ((d+15)>>3)|1;
57 	return d<<1;
58 }
59 
60 static void
61 smcsetup(Uart *up, int baud)
62 {
63 	IMM *io;
64 	Uartsmc *p;
65 	BD *bd;
66 	SMC *smc;
67 
68 	archenableuart(SMC1ID, 0);
69 	io = m->iomem;
70 	io->pbpar |= IBIT(24)|IBIT(25);	/* enable SMC1 TX/RX */
71 	io->pbdir &= ~(IBIT(24)|IBIT(25));
72 	io->brgc1 = baudgen(baud) | BaudEnable;
73 	io->simode &= ~0xF000;	/* SMC1 to NMSI mode, Tx/Rx clocks are BRG1 */
74 
75 	bd = bdalloc(1);
76 	p = (Uartsmc*)KADDR(SMC1P);
77 	p->rbase = (ushort)bd;
78 	up->rxb = bd;
79 	bd->status = BDEmpty|BDWrap|BDInt;
80 	bd->length = 0;
81 	bd->addr = PADDR(up->rxbuf);
82 	bd = bdalloc(1);
83 	p->tbase = (ushort)bd;
84 	up->txb = bd;
85 	bd->status = BDWrap|BDInt;
86 	bd->length = 0;
87 	bd->addr = PADDR(up->txbuf);
88 
89 	cpmop(InitRxTx, SMC1ID, 0);
90 
91 	/* protocol parameters */
92 	p->rfcr = 0x18;
93 	p->tfcr = 0x18;
94 	p->mrblr = 1;
95 	p->maxidl = 1;
96 	p->brkln = 0;
97 	p->brkec = 0;
98 	p->brkcr = 1;
99 	smc = IOREGS(0xA80, SMC);
100 	smc->smce = 0xff;	/* clear events */
101 	smc->smcm = 0x17;	/* enable all possible interrupts */
102 	setvec(VectorCPIC+4, uartintr, up);
103 	smc->smcmr = 0x4820;	/* 8-bit mode, no parity, 1 stop bit, UART mode, ... */
104 	smc->smcmr |= 3;	/* enable rx/tx */
105 }
106 
107 static void
108 uartintr(Ureg*, void *arg)
109 {
110 	Uart *up;
111 	int ch, i;
112 	BD *bd;
113 	SMC *smc;
114 	Block *b;
115 
116 	up = arg;
117 	smc = IOREGS(0xA80, SMC);
118 	smc->smce = 0xff;	/* clear all events */
119 	if((bd = up->rxb) != nil && (bd->status & BDEmpty) == 0){
120 		if(up->iq != nil && bd->length > 0){
121 			if(up->boot != nil){
122 				up->boot(up->rxbuf, bd->length);
123 			}else if(up->rx != nil){
124 				for(i=0; i<bd->length; i++){
125 					ch = up->rxbuf[i];
126 					up->rx(up->iq, ch);
127 				}
128 			}else{
129 				b = iallocb(bd->length);
130 				memmove(b->wp, up->rxbuf, bd->length);
131 				b->wp += bd->length;
132 				qbwrite(up->iq, b);
133 			}
134 		}
135 		bd->status |= BDEmpty|BDInt;
136 	} else if((bd = up->txb) != nil && (bd->status & BDReady) == 0){
137 		ch = -1;
138 		if(up->oq)
139 			ch = qbgetc(up->oq);
140 		if(ch != -1){
141 			up->txbuf[0] = ch;
142 			bd->length = 1;
143 			bd->status |= BDReady;
144 		}else
145 			up->txbusy = 0;
146 	}
147 	/* TO DO: modem status, errors, etc */
148 }
149 
150 static void
151 uartkick(void *arg)
152 {
153 	Uart *up = arg;
154 	int s, c, i;
155 
156 	s = splhi();
157 	while(up->txbusy == 0 && (c = qbgetc(up->oq)) != -1){
158 		if(predawn){
159 			while(up->txb->status & BDReady)
160 				;
161 		} else {
162 			for(i = 0; i < 100; i++){
163 				if((up->txb->status & BDReady) == 0)
164 					break;
165 				delay(1);
166 			}
167 		}
168 		up->txbuf[0] = c;
169 		up->txb->length = 1;
170 		up->txb->status |= BDReady;
171 		up->txbusy = !predawn;
172 	}
173 	splx(s);
174 }
175 
176 void
177 uartspecial(int port, int baud, Queue **iq, Queue **oq, void (*rx)(Queue*,int))
178 {
179 	Uart *up = &uart[0];
180 
181 	if(up->setup)
182 		return;
183 	up->setup = 1;
184 
185 	*iq = up->iq = qopen(4*1024, 0, 0, 0);
186 	*oq = up->oq = qopen(16*1024, 0, uartkick, up);
187 	up->rx = rx;
188 	USED(port);
189 	up->port = SMC1ID;
190 	if(baud == 0)
191 		baud = 9600;
192 	smcsetup(up, baud);
193 	/* if using SCCn's UART, would also set DTR and RTS, but SMC doesn't use them */
194 }
195 
196 void
197 uartsetboot(void (*f)(uchar*, int))
198 {
199 	uart[0].boot = f;
200 }
201 
202 void
203 uartputs(char *s, int n)
204 {
205 	Uart *up = &uart[0];
206 	Block *b;
207 	int nl;
208 	char *p;
209 
210 	nl = 0;
211 	for(p = s; p < s+n; p++)
212 		if(*p == '\n')
213 			nl++;
214 	b = iallocb(n+nl);
215 	while(n--){
216 		if(*s == '\n')
217 			*b->wp++ = '\r';
218 		*b->wp++ = *s++;
219 	}
220 	qbwrite(up->oq, b);
221 }
222 
223 void
224 uartwait(void)
225 {
226 	Uart *up = &uart[0];
227 
228 	while(up->txbusy)
229 		;
230 }
231