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