xref: /plan9/sys/src/9/pc/uartox.c (revision 6bbfed0d85c6d7248503ef0614d0f1e40438b735)
1*6bbfed0dSDavid du Colombier /*
2*6bbfed0dSDavid du Colombier  * Oxford Semiconductor OXPCIe95x UART driver
3*6bbfed0dSDavid du Colombier  */
4*6bbfed0dSDavid du Colombier #include "u.h"
5*6bbfed0dSDavid du Colombier #include "../port/lib.h"
6*6bbfed0dSDavid du Colombier #include "mem.h"
7*6bbfed0dSDavid du Colombier #include "dat.h"
8*6bbfed0dSDavid du Colombier #include "fns.h"
9*6bbfed0dSDavid du Colombier #include "io.h"
10*6bbfed0dSDavid du Colombier #include "../port/error.h"
11*6bbfed0dSDavid du Colombier 
12*6bbfed0dSDavid du Colombier extern PhysUart oxphysuart;
13*6bbfed0dSDavid du Colombier 
14*6bbfed0dSDavid du Colombier enum {
15*6bbfed0dSDavid du Colombier 	Ccr		= 0x0000/4,	/* Class Code and Revision ID */
16*6bbfed0dSDavid du Colombier 	Nuart		= 0x0004/4,	/* Decimal Number of UARTs */
17*6bbfed0dSDavid du Colombier 	Gis		= 0x0008/4,	/* Global UART IRQ Status */
18*6bbfed0dSDavid du Colombier 	Gie		= 0x000C/4,	/* Global UART IRQ Enable */
19*6bbfed0dSDavid du Colombier 	Gid		= 0x0010/4,	/* Global UART IRQ Disable */
20*6bbfed0dSDavid du Colombier 	Gwe		= 0x0014/4,	/* Global UART Wake Enable */
21*6bbfed0dSDavid du Colombier 	Gwd		= 0x0018/4,	/* Global UART Wake Disable */
22*6bbfed0dSDavid du Colombier };
23*6bbfed0dSDavid du Colombier 
24*6bbfed0dSDavid du Colombier enum {
25*6bbfed0dSDavid du Colombier 	Thr		= 0x00,		/* Transmitter Holding */
26*6bbfed0dSDavid du Colombier 	Rhr		= 0x00,		/* Receiver Holding */
27*6bbfed0dSDavid du Colombier 	Ier		= 0x01,		/* Interrupt Enable */
28*6bbfed0dSDavid du Colombier 	Fcr		= 0x02,		/* FIFO Control */
29*6bbfed0dSDavid du Colombier 	Isr		= 0x02,		/* Interrupt Status */
30*6bbfed0dSDavid du Colombier 	Lcr		= 0x03,		/* Line Control */
31*6bbfed0dSDavid du Colombier 	Mcr		= 0x04,		/* Modem Control */
32*6bbfed0dSDavid du Colombier 	Lsr		= 0x05,		/* Line Status */
33*6bbfed0dSDavid du Colombier 	Msr		= 0x06,		/* Modem Status */
34*6bbfed0dSDavid du Colombier 	Spr		= 0x07,		/* Scratch Pad */
35*6bbfed0dSDavid du Colombier 	Dll		= 0x00,		/* Divisor Latch LSB */
36*6bbfed0dSDavid du Colombier 	Dlm		= 0x01,		/* Divisor Latch MSB */
37*6bbfed0dSDavid du Colombier 	Efr		= 0x02,		/* Enhanced Feature */
38*6bbfed0dSDavid du Colombier };
39*6bbfed0dSDavid du Colombier 
40*6bbfed0dSDavid du Colombier typedef struct Port Port;
41*6bbfed0dSDavid du Colombier typedef struct Ctlr Ctlr;
42*6bbfed0dSDavid du Colombier 
43*6bbfed0dSDavid du Colombier struct Port {
44*6bbfed0dSDavid du Colombier 	Uart;
45*6bbfed0dSDavid du Colombier 	Ctlr	*ctlr;
46*6bbfed0dSDavid du Colombier 	u8int	*mem;
47*6bbfed0dSDavid du Colombier 
48*6bbfed0dSDavid du Colombier 	int	level;
49*6bbfed0dSDavid du Colombier 	int	dtr, rts;
50*6bbfed0dSDavid du Colombier 	int	ri;
51*6bbfed0dSDavid du Colombier };
52*6bbfed0dSDavid du Colombier 
53*6bbfed0dSDavid du Colombier struct Ctlr {
54*6bbfed0dSDavid du Colombier 	Lock;
55*6bbfed0dSDavid du Colombier 	char	*name;
56*6bbfed0dSDavid du Colombier 	Pcidev	*pcidev;
57*6bbfed0dSDavid du Colombier 	u32int	*mem;
58*6bbfed0dSDavid du Colombier 
59*6bbfed0dSDavid du Colombier 	u32int	im;
60*6bbfed0dSDavid du Colombier 
61*6bbfed0dSDavid du Colombier 	Port	port[0x10];
62*6bbfed0dSDavid du Colombier 	int	nport;
63*6bbfed0dSDavid du Colombier };
64*6bbfed0dSDavid du Colombier 
65*6bbfed0dSDavid du Colombier static Uart *
oxpnp(void)66*6bbfed0dSDavid du Colombier oxpnp(void)
67*6bbfed0dSDavid du Colombier {
68*6bbfed0dSDavid du Colombier 	Pcidev *p;
69*6bbfed0dSDavid du Colombier 	Ctlr *ctlr;
70*6bbfed0dSDavid du Colombier 	Port *port;
71*6bbfed0dSDavid du Colombier 	int i;
72*6bbfed0dSDavid du Colombier 	char *model;
73*6bbfed0dSDavid du Colombier 	char name[12+1];
74*6bbfed0dSDavid du Colombier 	Uart *head, *tail;
75*6bbfed0dSDavid du Colombier 	static int ctlrno;
76*6bbfed0dSDavid du Colombier 
77*6bbfed0dSDavid du Colombier 	p = nil;
78*6bbfed0dSDavid du Colombier 	head = tail = nil;
79*6bbfed0dSDavid du Colombier 	while(p = pcimatch(p, 0x1415, 0)){
80*6bbfed0dSDavid du Colombier 		switch(p->did){
81*6bbfed0dSDavid du Colombier 		case 0xc101:
82*6bbfed0dSDavid du Colombier 		case 0xc105:
83*6bbfed0dSDavid du Colombier 		case 0xc11b:
84*6bbfed0dSDavid du Colombier 		case 0xc11f:
85*6bbfed0dSDavid du Colombier 		case 0xc120:
86*6bbfed0dSDavid du Colombier 		case 0xc124:
87*6bbfed0dSDavid du Colombier 		case 0xc138:
88*6bbfed0dSDavid du Colombier 		case 0xc13d:
89*6bbfed0dSDavid du Colombier 		case 0xc140:
90*6bbfed0dSDavid du Colombier 		case 0xc141:
91*6bbfed0dSDavid du Colombier 		case 0xc144:
92*6bbfed0dSDavid du Colombier 		case 0xc145:
93*6bbfed0dSDavid du Colombier 		case 0xc158:
94*6bbfed0dSDavid du Colombier 		case 0xc15d:
95*6bbfed0dSDavid du Colombier 			model = "OXPCIe952";
96*6bbfed0dSDavid du Colombier 			break;
97*6bbfed0dSDavid du Colombier 		case 0xc208:
98*6bbfed0dSDavid du Colombier 		case 0xc20d:
99*6bbfed0dSDavid du Colombier 			model = "OXPCIe954";
100*6bbfed0dSDavid du Colombier 			break;
101*6bbfed0dSDavid du Colombier 		case 0xc308:
102*6bbfed0dSDavid du Colombier 		case 0xc30d:
103*6bbfed0dSDavid du Colombier 			model = "OXPCIe958";
104*6bbfed0dSDavid du Colombier 			break;
105*6bbfed0dSDavid du Colombier 		default:
106*6bbfed0dSDavid du Colombier 			continue;
107*6bbfed0dSDavid du Colombier 		}
108*6bbfed0dSDavid du Colombier 		ctlr = malloc(sizeof *ctlr);
109*6bbfed0dSDavid du Colombier 		if(ctlr == nil){
110*6bbfed0dSDavid du Colombier 			print("oxpnp: out of memory\n");
111*6bbfed0dSDavid du Colombier 			continue;
112*6bbfed0dSDavid du Colombier 		}
113*6bbfed0dSDavid du Colombier 		ctlr->pcidev = p;
114*6bbfed0dSDavid du Colombier 		ctlr->mem = vmap(p->mem[0].bar & ~0xf, p->mem[0].size);
115*6bbfed0dSDavid du Colombier 		if(ctlr->mem == nil){
116*6bbfed0dSDavid du Colombier 			print("oxpnp: vmap failed\n");
117*6bbfed0dSDavid du Colombier 			free(ctlr);
118*6bbfed0dSDavid du Colombier 			continue;
119*6bbfed0dSDavid du Colombier 		}
120*6bbfed0dSDavid du Colombier 		snprint(name, sizeof name, "uartox%d", ctlrno);
121*6bbfed0dSDavid du Colombier 		kstrdup(&ctlr->name, name);
122*6bbfed0dSDavid du Colombier 		ctlr->nport = ctlr->mem[Nuart] & 0x1f;
123*6bbfed0dSDavid du Colombier 		for(i = 0; i < ctlr->nport; ++i){
124*6bbfed0dSDavid du Colombier 			port = &ctlr->port[i];
125*6bbfed0dSDavid du Colombier 			port->ctlr = ctlr;
126*6bbfed0dSDavid du Colombier 			port->mem = (u8int *)ctlr->mem + 0x1000 + 0x200*i;
127*6bbfed0dSDavid du Colombier 			port->regs = port;
128*6bbfed0dSDavid du Colombier 			snprint(name, sizeof name, "%s.%d", ctlr->name, i);
129*6bbfed0dSDavid du Colombier 			kstrdup(&port->name, name);
130*6bbfed0dSDavid du Colombier 			port->phys = &oxphysuart;
131*6bbfed0dSDavid du Colombier 			if(head == nil)
132*6bbfed0dSDavid du Colombier 				head = port;
133*6bbfed0dSDavid du Colombier 			else
134*6bbfed0dSDavid du Colombier 				tail->next = port;
135*6bbfed0dSDavid du Colombier 			tail = port;
136*6bbfed0dSDavid du Colombier 		}
137*6bbfed0dSDavid du Colombier 		print("%s: %s: %d ports irq %d\n",
138*6bbfed0dSDavid du Colombier 		    ctlr->name, model, ctlr->nport, p->intl);
139*6bbfed0dSDavid du Colombier 		ctlrno++;
140*6bbfed0dSDavid du Colombier 	}
141*6bbfed0dSDavid du Colombier 	return head;
142*6bbfed0dSDavid du Colombier }
143*6bbfed0dSDavid du Colombier 
144*6bbfed0dSDavid du Colombier static void
oxinterrupt(Ureg *,void * arg)145*6bbfed0dSDavid du Colombier oxinterrupt(Ureg *, void *arg)
146*6bbfed0dSDavid du Colombier {
147*6bbfed0dSDavid du Colombier 	Ctlr *ctlr;
148*6bbfed0dSDavid du Colombier 	Port *port;
149*6bbfed0dSDavid du Colombier 	Uart *uart;
150*6bbfed0dSDavid du Colombier 	int i, old;
151*6bbfed0dSDavid du Colombier 	u8int val;
152*6bbfed0dSDavid du Colombier 	char ch;
153*6bbfed0dSDavid du Colombier 
154*6bbfed0dSDavid du Colombier 	ctlr = arg;
155*6bbfed0dSDavid du Colombier 
156*6bbfed0dSDavid du Colombier 	ilock(ctlr);
157*6bbfed0dSDavid du Colombier 	if(!(ctlr->im & ctlr->mem[Gis])){
158*6bbfed0dSDavid du Colombier 		iunlock(ctlr);
159*6bbfed0dSDavid du Colombier 		return;
160*6bbfed0dSDavid du Colombier 	}
161*6bbfed0dSDavid du Colombier 	for(i = 0; i < ctlr->nport; ++i){
162*6bbfed0dSDavid du Colombier 		if(!(ctlr->im & 1<<i))
163*6bbfed0dSDavid du Colombier 			continue;
164*6bbfed0dSDavid du Colombier 		port = &ctlr->port[i];
165*6bbfed0dSDavid du Colombier 		uart = port;	/* "Come Clarity" */
166*6bbfed0dSDavid du Colombier 		switch(port->mem[Isr] & 0x3f){
167*6bbfed0dSDavid du Colombier 		case 0x06:	/* Receiver status error */
168*6bbfed0dSDavid du Colombier 		case 0x04:	/* Receiver data available */
169*6bbfed0dSDavid du Colombier 		case 0x0c:	/* Receiver time-out */
170*6bbfed0dSDavid du Colombier 			for(;;){
171*6bbfed0dSDavid du Colombier 				val = port->mem[Lsr];
172*6bbfed0dSDavid du Colombier 				if(!(val & 1<<0))	/* RxRDY */
173*6bbfed0dSDavid du Colombier 					break;
174*6bbfed0dSDavid du Colombier 				if(val & 1<<1)		/* Overrun Error */
175*6bbfed0dSDavid du Colombier 					uart->oerr++;
176*6bbfed0dSDavid du Colombier 				if(val & 1<<2)		/* Parity Error */
177*6bbfed0dSDavid du Colombier 					uart->perr++;
178*6bbfed0dSDavid du Colombier 				if(val & 1<<3)		/* Framing Error */
179*6bbfed0dSDavid du Colombier 					uart->ferr++;
180*6bbfed0dSDavid du Colombier 				ch = port->mem[Rhr];
181*6bbfed0dSDavid du Colombier 				if(!(val & 1<<7))	/* Data Error */
182*6bbfed0dSDavid du Colombier 					uartrecv(uart, ch);
183*6bbfed0dSDavid du Colombier 			}
184*6bbfed0dSDavid du Colombier 			break;
185*6bbfed0dSDavid du Colombier 		case 0x02:	/* Transmitter THR empty */
186*6bbfed0dSDavid du Colombier 			uartkick(uart);
187*6bbfed0dSDavid du Colombier 			break;
188*6bbfed0dSDavid du Colombier 		case 0x00:	/* Modem status change */
189*6bbfed0dSDavid du Colombier 			val = port->mem[Msr];
190*6bbfed0dSDavid du Colombier 			if(val & 1<<0){			/* Delta nCTS */
191*6bbfed0dSDavid du Colombier 				ilock(&uart->tlock);
192*6bbfed0dSDavid du Colombier 				old = uart->cts;
193*6bbfed0dSDavid du Colombier 				uart->cts = val & 1<<4;	/* CTS */
194*6bbfed0dSDavid du Colombier 				if(!old && uart->cts)
195*6bbfed0dSDavid du Colombier 					uart->ctsbackoff = 2;
196*6bbfed0dSDavid du Colombier 				iunlock(&uart->tlock);
197*6bbfed0dSDavid du Colombier 			}
198*6bbfed0dSDavid du Colombier 			if(val & 1<<1){			/* Delta nDSR */
199*6bbfed0dSDavid du Colombier 				old = val & 1<<5;	/* DSR */
200*6bbfed0dSDavid du Colombier 				if(!old && uart->dsr && uart->hup_dsr)
201*6bbfed0dSDavid du Colombier 					uart->dohup = 1;
202*6bbfed0dSDavid du Colombier 				uart->dsr = old;
203*6bbfed0dSDavid du Colombier 			}
204*6bbfed0dSDavid du Colombier 			port->ri = val & 1<<6;		/* RI */
205*6bbfed0dSDavid du Colombier 			if(val & 1<<3){			/* Delta nDCD */
206*6bbfed0dSDavid du Colombier 				old = val & 1<<7;	/* DCD */
207*6bbfed0dSDavid du Colombier 				if(!old && uart->dcd && uart->hup_dcd)
208*6bbfed0dSDavid du Colombier 					uart->dohup = 1;
209*6bbfed0dSDavid du Colombier 				uart->dcd = old;
210*6bbfed0dSDavid du Colombier 			}
211*6bbfed0dSDavid du Colombier 			break;
212*6bbfed0dSDavid du Colombier 		}
213*6bbfed0dSDavid du Colombier 	}
214*6bbfed0dSDavid du Colombier 	iunlock(ctlr);
215*6bbfed0dSDavid du Colombier }
216*6bbfed0dSDavid du Colombier 
217*6bbfed0dSDavid du Colombier #define MASK(p)	(1UL<<((p)-(p)->ctlr->port))
218*6bbfed0dSDavid du Colombier 
219*6bbfed0dSDavid du Colombier static void
oxenable(Uart * uart,int)220*6bbfed0dSDavid du Colombier oxenable(Uart *uart, int)
221*6bbfed0dSDavid du Colombier {
222*6bbfed0dSDavid du Colombier 	Ctlr *ctlr;
223*6bbfed0dSDavid du Colombier 	Port *port;
224*6bbfed0dSDavid du Colombier 
225*6bbfed0dSDavid du Colombier 	port = uart->regs;
226*6bbfed0dSDavid du Colombier 	ctlr = port->ctlr;
227*6bbfed0dSDavid du Colombier 
228*6bbfed0dSDavid du Colombier 	ilock(ctlr);
229*6bbfed0dSDavid du Colombier 	if(ctlr->im == 0)
230*6bbfed0dSDavid du Colombier 		intrenable(ctlr->pcidev->intl, oxinterrupt, ctlr,
231*6bbfed0dSDavid du Colombier 		    ctlr->pcidev->tbdf, ctlr->name);
232*6bbfed0dSDavid du Colombier 	ctlr->im |= MASK(port);
233*6bbfed0dSDavid du Colombier 	iunlock(ctlr);
234*6bbfed0dSDavid du Colombier 
235*6bbfed0dSDavid du Colombier 	/* Enable 950 Mode */
236*6bbfed0dSDavid du Colombier 	port->mem[Lcr] |= 1<<7;			/* Divisor latch access */
237*6bbfed0dSDavid du Colombier 	port->mem[Efr] = 1<<4;			/* Enhanced mode */
238*6bbfed0dSDavid du Colombier 	port->mem[Lcr] &= ~(1<<7);
239*6bbfed0dSDavid du Colombier 
240*6bbfed0dSDavid du Colombier 	port->mem[Ier] = 1<<2|1<<1|1<<0;	/* Rx Stat, THRE, RxRDY */
241*6bbfed0dSDavid du Colombier 
242*6bbfed0dSDavid du Colombier 	(*uart->phys->dtr)(uart, 1);
243*6bbfed0dSDavid du Colombier 	(*uart->phys->rts)(uart, 1);
244*6bbfed0dSDavid du Colombier 
245*6bbfed0dSDavid du Colombier 	/* Enable FIFO */
246*6bbfed0dSDavid du Colombier 	(*uart->phys->fifo)(uart, ~0);
247*6bbfed0dSDavid du Colombier }
248*6bbfed0dSDavid du Colombier 
249*6bbfed0dSDavid du Colombier static void
oxdisable(Uart * uart)250*6bbfed0dSDavid du Colombier oxdisable(Uart *uart)
251*6bbfed0dSDavid du Colombier {
252*6bbfed0dSDavid du Colombier 	Ctlr *ctlr;
253*6bbfed0dSDavid du Colombier 	Port *port;
254*6bbfed0dSDavid du Colombier 
255*6bbfed0dSDavid du Colombier 	port = uart->regs;
256*6bbfed0dSDavid du Colombier 	ctlr = port->ctlr;
257*6bbfed0dSDavid du Colombier 
258*6bbfed0dSDavid du Colombier 	(*uart->phys->dtr)(uart, 0);
259*6bbfed0dSDavid du Colombier 	(*uart->phys->rts)(uart, 0);
260*6bbfed0dSDavid du Colombier 	(*uart->phys->fifo)(uart, 0);
261*6bbfed0dSDavid du Colombier 
262*6bbfed0dSDavid du Colombier 	port->mem[Ier] = 0;
263*6bbfed0dSDavid du Colombier 
264*6bbfed0dSDavid du Colombier 	ilock(ctlr);
265*6bbfed0dSDavid du Colombier 	ctlr->im &= ~MASK(port);
266*6bbfed0dSDavid du Colombier 	if(ctlr->im == 0)
267*6bbfed0dSDavid du Colombier 		intrdisable(ctlr->pcidev->intl, oxinterrupt, ctlr,
268*6bbfed0dSDavid du Colombier 		    ctlr->pcidev->tbdf, ctlr->name);
269*6bbfed0dSDavid du Colombier 	iunlock(ctlr);
270*6bbfed0dSDavid du Colombier }
271*6bbfed0dSDavid du Colombier 
272*6bbfed0dSDavid du Colombier static void
oxkick(Uart * uart)273*6bbfed0dSDavid du Colombier oxkick(Uart *uart)
274*6bbfed0dSDavid du Colombier {
275*6bbfed0dSDavid du Colombier 	Port *port;
276*6bbfed0dSDavid du Colombier 
277*6bbfed0dSDavid du Colombier 	if(uart->cts == 0 || uart->blocked)
278*6bbfed0dSDavid du Colombier 		return;
279*6bbfed0dSDavid du Colombier 
280*6bbfed0dSDavid du Colombier 	port = uart->regs;
281*6bbfed0dSDavid du Colombier 
282*6bbfed0dSDavid du Colombier 	for(;;){
283*6bbfed0dSDavid du Colombier 		if(!(port->mem[Lsr] & 1<<5))	/* THR Empty */
284*6bbfed0dSDavid du Colombier 			break;
285*6bbfed0dSDavid du Colombier 		if(uart->op >= uart->oe && uartstageoutput(uart) == 0)
286*6bbfed0dSDavid du Colombier 			break;
287*6bbfed0dSDavid du Colombier 		port->mem[Thr] = *(uart->op++);
288*6bbfed0dSDavid du Colombier 	}
289*6bbfed0dSDavid du Colombier }
290*6bbfed0dSDavid du Colombier 
291*6bbfed0dSDavid du Colombier static void
oxdobreak(Uart * uart,int ms)292*6bbfed0dSDavid du Colombier oxdobreak(Uart *uart, int ms)
293*6bbfed0dSDavid du Colombier {
294*6bbfed0dSDavid du Colombier 	Port *port;
295*6bbfed0dSDavid du Colombier 
296*6bbfed0dSDavid du Colombier 	if(ms <= 0)
297*6bbfed0dSDavid du Colombier 		ms = 200;
298*6bbfed0dSDavid du Colombier 
299*6bbfed0dSDavid du Colombier 	port = uart->regs;
300*6bbfed0dSDavid du Colombier 
301*6bbfed0dSDavid du Colombier 	port->mem[Lcr] |= 1<<6;			/* Transmission break */
302*6bbfed0dSDavid du Colombier 	if(!waserror()){
303*6bbfed0dSDavid du Colombier 		tsleep(&up->sleep, return0, nil, ms);
304*6bbfed0dSDavid du Colombier 		poperror();
305*6bbfed0dSDavid du Colombier 	}
306*6bbfed0dSDavid du Colombier 	port->mem[Lcr] &= ~(1<<6);
307*6bbfed0dSDavid du Colombier }
308*6bbfed0dSDavid du Colombier 
309*6bbfed0dSDavid du Colombier static int
oxbaud(Uart * uart,int baud)310*6bbfed0dSDavid du Colombier oxbaud(Uart *uart, int baud)
311*6bbfed0dSDavid du Colombier {
312*6bbfed0dSDavid du Colombier 	Port *port;
313*6bbfed0dSDavid du Colombier 	u16int val;
314*6bbfed0dSDavid du Colombier 
315*6bbfed0dSDavid du Colombier 	if(baud <= 0)
316*6bbfed0dSDavid du Colombier 		return -1;
317*6bbfed0dSDavid du Colombier 
318*6bbfed0dSDavid du Colombier 	port = uart->regs;
319*6bbfed0dSDavid du Colombier 
320*6bbfed0dSDavid du Colombier 	/*
321*6bbfed0dSDavid du Colombier 	 * We aren't terribly interested in non-standard baud rates.
322*6bbfed0dSDavid du Colombier 	 * Rather than mess about with generator constants, we instead
323*6bbfed0dSDavid du Colombier 	 * program DLM and DLL according to Table 37 in the datasheet.
324*6bbfed0dSDavid du Colombier 	 */
325*6bbfed0dSDavid du Colombier 	switch(baud){
326*6bbfed0dSDavid du Colombier 	case 1200:
327*6bbfed0dSDavid du Colombier 		val = 0x0cb6;
328*6bbfed0dSDavid du Colombier 		break;
329*6bbfed0dSDavid du Colombier 	case 2400:
330*6bbfed0dSDavid du Colombier 		val = 0x065b;
331*6bbfed0dSDavid du Colombier 		break;
332*6bbfed0dSDavid du Colombier 	case 4800:
333*6bbfed0dSDavid du Colombier 		val = 0x032d;
334*6bbfed0dSDavid du Colombier 		break;
335*6bbfed0dSDavid du Colombier 	case 9600:
336*6bbfed0dSDavid du Colombier 		val = 0x0196;
337*6bbfed0dSDavid du Colombier 		break;
338*6bbfed0dSDavid du Colombier 	case 19200:
339*6bbfed0dSDavid du Colombier 		val = 0x00cb;
340*6bbfed0dSDavid du Colombier 		break;
341*6bbfed0dSDavid du Colombier 	case 38400:
342*6bbfed0dSDavid du Colombier 		val = 0x0066;
343*6bbfed0dSDavid du Colombier 		break;
344*6bbfed0dSDavid du Colombier 	case 57600:
345*6bbfed0dSDavid du Colombier 		val = 0x0044;
346*6bbfed0dSDavid du Colombier 		break;
347*6bbfed0dSDavid du Colombier 	case 115200:
348*6bbfed0dSDavid du Colombier 		val = 0x0022;
349*6bbfed0dSDavid du Colombier 		break;
350*6bbfed0dSDavid du Colombier 	default:
351*6bbfed0dSDavid du Colombier 		return -1;
352*6bbfed0dSDavid du Colombier 	}
353*6bbfed0dSDavid du Colombier 	port->mem[Lcr] |= 1<<7;			/* Divisor latch access */
354*6bbfed0dSDavid du Colombier 	port->mem[Dlm] = val>>8;
355*6bbfed0dSDavid du Colombier 	port->mem[Dll] = val;
356*6bbfed0dSDavid du Colombier 	port->mem[Lcr] &= ~(1<<7);
357*6bbfed0dSDavid du Colombier 	uart->baud = baud;
358*6bbfed0dSDavid du Colombier 	return 0;
359*6bbfed0dSDavid du Colombier }
360*6bbfed0dSDavid du Colombier 
361*6bbfed0dSDavid du Colombier static int
oxbits(Uart * uart,int bits)362*6bbfed0dSDavid du Colombier oxbits(Uart *uart, int bits)
363*6bbfed0dSDavid du Colombier {
364*6bbfed0dSDavid du Colombier 	Port *port;
365*6bbfed0dSDavid du Colombier 	u8int val;
366*6bbfed0dSDavid du Colombier 
367*6bbfed0dSDavid du Colombier 	port = uart->regs;
368*6bbfed0dSDavid du Colombier 
369*6bbfed0dSDavid du Colombier 	val = port->mem[Lcr] & 0x7c;
370*6bbfed0dSDavid du Colombier 	switch(bits){
371*6bbfed0dSDavid du Colombier 	case 8:
372*6bbfed0dSDavid du Colombier 		val |= 0x3;			/* Data length */
373*6bbfed0dSDavid du Colombier 		break;
374*6bbfed0dSDavid du Colombier 	case 7:
375*6bbfed0dSDavid du Colombier 		val |= 0x2;
376*6bbfed0dSDavid du Colombier 		break;
377*6bbfed0dSDavid du Colombier 	case 6:
378*6bbfed0dSDavid du Colombier 		val |= 0x1;
379*6bbfed0dSDavid du Colombier 		break;
380*6bbfed0dSDavid du Colombier 	case 5:
381*6bbfed0dSDavid du Colombier 		break;
382*6bbfed0dSDavid du Colombier 	default:
383*6bbfed0dSDavid du Colombier 		return -1;
384*6bbfed0dSDavid du Colombier 	}
385*6bbfed0dSDavid du Colombier 	port->mem[Lcr] = val;
386*6bbfed0dSDavid du Colombier 	uart->bits = bits;
387*6bbfed0dSDavid du Colombier 	return 0;
388*6bbfed0dSDavid du Colombier }
389*6bbfed0dSDavid du Colombier 
390*6bbfed0dSDavid du Colombier static int
oxstop(Uart * uart,int stop)391*6bbfed0dSDavid du Colombier oxstop(Uart *uart, int stop)
392*6bbfed0dSDavid du Colombier {
393*6bbfed0dSDavid du Colombier 	Port *port;
394*6bbfed0dSDavid du Colombier 	u8int val;
395*6bbfed0dSDavid du Colombier 
396*6bbfed0dSDavid du Colombier 	port = uart->regs;
397*6bbfed0dSDavid du Colombier 
398*6bbfed0dSDavid du Colombier 	val = port->mem[Lcr] & 0x7b;
399*6bbfed0dSDavid du Colombier 	switch(stop){
400*6bbfed0dSDavid du Colombier 	case 2:
401*6bbfed0dSDavid du Colombier 		val |= 1<<2;			/* Number of Stop Bits */
402*6bbfed0dSDavid du Colombier 		break;
403*6bbfed0dSDavid du Colombier 	case 1:
404*6bbfed0dSDavid du Colombier 		break;
405*6bbfed0dSDavid du Colombier 	default:
406*6bbfed0dSDavid du Colombier 		return -1;
407*6bbfed0dSDavid du Colombier 	}
408*6bbfed0dSDavid du Colombier 	port->mem[Lcr] = val;
409*6bbfed0dSDavid du Colombier 	uart->stop = stop;
410*6bbfed0dSDavid du Colombier 	return 0;
411*6bbfed0dSDavid du Colombier }
412*6bbfed0dSDavid du Colombier 
413*6bbfed0dSDavid du Colombier static int
oxparity(Uart * uart,int parity)414*6bbfed0dSDavid du Colombier oxparity(Uart *uart, int parity)
415*6bbfed0dSDavid du Colombier {
416*6bbfed0dSDavid du Colombier 	Port *port;
417*6bbfed0dSDavid du Colombier 	u8int val;
418*6bbfed0dSDavid du Colombier 
419*6bbfed0dSDavid du Colombier 	port = uart->regs;
420*6bbfed0dSDavid du Colombier 
421*6bbfed0dSDavid du Colombier 	val = port->mem[Lcr] & 0x67;
422*6bbfed0dSDavid du Colombier 	switch(parity){
423*6bbfed0dSDavid du Colombier 	case 'e':
424*6bbfed0dSDavid du Colombier 		val |= 1<<4;			/* Even/Odd Parity */
425*6bbfed0dSDavid du Colombier 	case 'o':
426*6bbfed0dSDavid du Colombier 		val |= 1<<3;			/* Parity Enable */
427*6bbfed0dSDavid du Colombier 		break;
428*6bbfed0dSDavid du Colombier 	case 'n':
429*6bbfed0dSDavid du Colombier 		break;
430*6bbfed0dSDavid du Colombier 	default:
431*6bbfed0dSDavid du Colombier 		return -1;
432*6bbfed0dSDavid du Colombier 	}
433*6bbfed0dSDavid du Colombier 	port->mem[Lcr] = val;
434*6bbfed0dSDavid du Colombier 	uart->parity = parity;
435*6bbfed0dSDavid du Colombier 	return 0;
436*6bbfed0dSDavid du Colombier }
437*6bbfed0dSDavid du Colombier 
438*6bbfed0dSDavid du Colombier static void
oxmodemctl(Uart * uart,int on)439*6bbfed0dSDavid du Colombier oxmodemctl(Uart *uart, int on)
440*6bbfed0dSDavid du Colombier {
441*6bbfed0dSDavid du Colombier 	Ctlr *ctlr;
442*6bbfed0dSDavid du Colombier 	Port *port;
443*6bbfed0dSDavid du Colombier 
444*6bbfed0dSDavid du Colombier 	port = uart->regs;
445*6bbfed0dSDavid du Colombier 	ctlr = port->ctlr;
446*6bbfed0dSDavid du Colombier 
447*6bbfed0dSDavid du Colombier 	ilock(ctlr);
448*6bbfed0dSDavid du Colombier 	ilock(&uart->tlock);
449*6bbfed0dSDavid du Colombier 	if(on){
450*6bbfed0dSDavid du Colombier 		port->mem[Ier] |= 1<<3;			/* Modem */
451*6bbfed0dSDavid du Colombier 		uart->cts = port->mem[Msr] & 1<<4;	/* CTS */
452*6bbfed0dSDavid du Colombier 	}else{
453*6bbfed0dSDavid du Colombier 		port->mem[Ier] &= ~(1<<3);
454*6bbfed0dSDavid du Colombier 		uart->cts = 1;
455*6bbfed0dSDavid du Colombier 	}
456*6bbfed0dSDavid du Colombier 	uart->modem = on;
457*6bbfed0dSDavid du Colombier 	iunlock(&uart->tlock);
458*6bbfed0dSDavid du Colombier 	iunlock(ctlr);
459*6bbfed0dSDavid du Colombier }
460*6bbfed0dSDavid du Colombier 
461*6bbfed0dSDavid du Colombier static void
oxrts(Uart * uart,int on)462*6bbfed0dSDavid du Colombier oxrts(Uart *uart, int on)
463*6bbfed0dSDavid du Colombier {
464*6bbfed0dSDavid du Colombier 	Port *port;
465*6bbfed0dSDavid du Colombier 
466*6bbfed0dSDavid du Colombier 	port = uart->regs;
467*6bbfed0dSDavid du Colombier 
468*6bbfed0dSDavid du Colombier 	if(on)
469*6bbfed0dSDavid du Colombier 		port->mem[Mcr] |= 1<<1;		/* RTS */
470*6bbfed0dSDavid du Colombier 	else
471*6bbfed0dSDavid du Colombier 		port->mem[Mcr] &= ~(1<<1);
472*6bbfed0dSDavid du Colombier 	port->rts = on;
473*6bbfed0dSDavid du Colombier }
474*6bbfed0dSDavid du Colombier 
475*6bbfed0dSDavid du Colombier static void
oxdtr(Uart * uart,int on)476*6bbfed0dSDavid du Colombier oxdtr(Uart *uart, int on)
477*6bbfed0dSDavid du Colombier {
478*6bbfed0dSDavid du Colombier 	Port *port;
479*6bbfed0dSDavid du Colombier 
480*6bbfed0dSDavid du Colombier 	port = uart->regs;
481*6bbfed0dSDavid du Colombier 
482*6bbfed0dSDavid du Colombier 	if(on)
483*6bbfed0dSDavid du Colombier 		port->mem[Mcr] |= 1<<0;		/* DTR */
484*6bbfed0dSDavid du Colombier 	else
485*6bbfed0dSDavid du Colombier 		port->mem[Mcr] &= ~(1<<0);
486*6bbfed0dSDavid du Colombier 	port->dtr = on;
487*6bbfed0dSDavid du Colombier }
488*6bbfed0dSDavid du Colombier 
489*6bbfed0dSDavid du Colombier static long
oxstatus(Uart * uart,void * buf,long n,long offset)490*6bbfed0dSDavid du Colombier oxstatus(Uart *uart, void *buf, long n, long offset)
491*6bbfed0dSDavid du Colombier {
492*6bbfed0dSDavid du Colombier 	Port *port;
493*6bbfed0dSDavid du Colombier 
494*6bbfed0dSDavid du Colombier 	if(offset > 0)
495*6bbfed0dSDavid du Colombier 		return 0;
496*6bbfed0dSDavid du Colombier 
497*6bbfed0dSDavid du Colombier 	port = uart->regs;
498*6bbfed0dSDavid du Colombier 
499*6bbfed0dSDavid du Colombier 	return snprint(buf, n,
500*6bbfed0dSDavid du Colombier 	    "b%d c%d d%d e%d l%d m%d p%c r%d s%d i%d\n"
501*6bbfed0dSDavid du Colombier 	    "dev(%d) type(%d) framing(%d) overruns(%d) "
502*6bbfed0dSDavid du Colombier 	    "berr(%d) serr(%d)%s%s%s%s\n",
503*6bbfed0dSDavid du Colombier 
504*6bbfed0dSDavid du Colombier 	    uart->baud,
505*6bbfed0dSDavid du Colombier 	    uart->hup_dcd,
506*6bbfed0dSDavid du Colombier 	    port->dtr,
507*6bbfed0dSDavid du Colombier 	    uart->hup_dsr,
508*6bbfed0dSDavid du Colombier 	    uart->bits,
509*6bbfed0dSDavid du Colombier 	    uart->modem,
510*6bbfed0dSDavid du Colombier 	    uart->parity,
511*6bbfed0dSDavid du Colombier 	    port->rts,
512*6bbfed0dSDavid du Colombier 	    uart->stop,
513*6bbfed0dSDavid du Colombier 	    port->level,
514*6bbfed0dSDavid du Colombier 
515*6bbfed0dSDavid du Colombier 	    uart->dev,
516*6bbfed0dSDavid du Colombier 	    uart->type,
517*6bbfed0dSDavid du Colombier 	    uart->ferr,
518*6bbfed0dSDavid du Colombier 	    uart->oerr,
519*6bbfed0dSDavid du Colombier 	    uart->berr,
520*6bbfed0dSDavid du Colombier 	    uart->serr,
521*6bbfed0dSDavid du Colombier 	    uart->cts ? " cts": "",
522*6bbfed0dSDavid du Colombier 	    uart->dsr ? " dsr": "",
523*6bbfed0dSDavid du Colombier 	    port->ri ? " ring": "",
524*6bbfed0dSDavid du Colombier 	    uart->dcd ? " dcd": ""
525*6bbfed0dSDavid du Colombier 	);
526*6bbfed0dSDavid du Colombier }
527*6bbfed0dSDavid du Colombier 
528*6bbfed0dSDavid du Colombier static void
oxfifo(Uart * uart,int level)529*6bbfed0dSDavid du Colombier oxfifo(Uart *uart, int level)
530*6bbfed0dSDavid du Colombier {
531*6bbfed0dSDavid du Colombier 	Ctlr *ctlr;
532*6bbfed0dSDavid du Colombier 	Port *port;
533*6bbfed0dSDavid du Colombier 
534*6bbfed0dSDavid du Colombier 	port = uart->regs;
535*6bbfed0dSDavid du Colombier 	ctlr = port->ctlr;
536*6bbfed0dSDavid du Colombier 
537*6bbfed0dSDavid du Colombier 	/*
538*6bbfed0dSDavid du Colombier 	 * 950 Mode FIFOs have a depth of 128 bytes; devuart only
539*6bbfed0dSDavid du Colombier 	 * cares about setting RHR trigger levels.  THR trigger
540*6bbfed0dSDavid du Colombier 	 * levels are not supported.
541*6bbfed0dSDavid du Colombier 	 */
542*6bbfed0dSDavid du Colombier 	ilock(ctlr);
543*6bbfed0dSDavid du Colombier 	if(level == 0)
544*6bbfed0dSDavid du Colombier 		port->mem[Fcr] = 0;		/* Disable FIFO */
545*6bbfed0dSDavid du Colombier 	else{
546*6bbfed0dSDavid du Colombier 		port->mem[Fcr] = 1<<0;		/* Enable FIFO */
547*6bbfed0dSDavid du Colombier 		switch(level){
548*6bbfed0dSDavid du Colombier 		default:
549*6bbfed0dSDavid du Colombier 			level = 112;
550*6bbfed0dSDavid du Colombier 		case 112:
551*6bbfed0dSDavid du Colombier 			port->mem[Fcr] = 0x03<<6|1<<0;	/* RHR Trigger Level */
552*6bbfed0dSDavid du Colombier 			break;
553*6bbfed0dSDavid du Colombier 		case 64:
554*6bbfed0dSDavid du Colombier 			port->mem[Fcr] = 0x02<<6|1<<0;
555*6bbfed0dSDavid du Colombier 			break;
556*6bbfed0dSDavid du Colombier 		case 32:
557*6bbfed0dSDavid du Colombier 			port->mem[Fcr] = 0x01<<6|1<<0;
558*6bbfed0dSDavid du Colombier 			break;
559*6bbfed0dSDavid du Colombier 		case 16:
560*6bbfed0dSDavid du Colombier 			break;
561*6bbfed0dSDavid du Colombier 		}
562*6bbfed0dSDavid du Colombier 	}
563*6bbfed0dSDavid du Colombier 	port->level = level;
564*6bbfed0dSDavid du Colombier 	iunlock(ctlr);
565*6bbfed0dSDavid du Colombier }
566*6bbfed0dSDavid du Colombier 
567*6bbfed0dSDavid du Colombier PhysUart oxphysuart = {
568*6bbfed0dSDavid du Colombier 	.name		= "OXPCIe95x",
569*6bbfed0dSDavid du Colombier 	.pnp		= oxpnp,
570*6bbfed0dSDavid du Colombier 	.enable		= oxenable,
571*6bbfed0dSDavid du Colombier 	.disable	= oxdisable,
572*6bbfed0dSDavid du Colombier 	.kick		= oxkick,
573*6bbfed0dSDavid du Colombier 	.dobreak	= oxdobreak,
574*6bbfed0dSDavid du Colombier 	.baud		= oxbaud,
575*6bbfed0dSDavid du Colombier 	.bits		= oxbits,
576*6bbfed0dSDavid du Colombier 	.stop		= oxstop,
577*6bbfed0dSDavid du Colombier 	.parity		= oxparity,
578*6bbfed0dSDavid du Colombier 	.modemctl	= oxmodemctl,
579*6bbfed0dSDavid du Colombier 	.rts		= oxrts,
580*6bbfed0dSDavid du Colombier 	.dtr		= oxdtr,
581*6bbfed0dSDavid du Colombier 	.status		= oxstatus,
582*6bbfed0dSDavid du Colombier 	.fifo		= oxfifo,
583*6bbfed0dSDavid du Colombier };
584