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