xref: /plan9-contrib/sys/src/9k/386/uartpci.c (revision 277b6efdcc262cd4d08d05822b6e10d78be9f0bc)
19ef1f84bSDavid du Colombier #include "u.h"
29ef1f84bSDavid du Colombier #include "../port/lib.h"
39ef1f84bSDavid du Colombier #include "mem.h"
49ef1f84bSDavid du Colombier #include "dat.h"
59ef1f84bSDavid du Colombier #include "fns.h"
69ef1f84bSDavid du Colombier 
79ef1f84bSDavid du Colombier #include "io.h"
8*277b6efdSDavid du Colombier #include "../port/error.h"
99ef1f84bSDavid du Colombier 
109ef1f84bSDavid du Colombier extern PhysUart i8250physuart;
119ef1f84bSDavid du Colombier extern PhysUart pciphysuart;
129ef1f84bSDavid du Colombier extern void* i8250alloc(int, int, int);
139ef1f84bSDavid du Colombier 
14*277b6efdSDavid du Colombier static Uart *perlehead, *perletail;
15*277b6efdSDavid du Colombier 
169ef1f84bSDavid du Colombier static Uart*
uartpci(int ctlrno,Pcidev * p,int barno,int n,int freq,char * name,int iosize)17*277b6efdSDavid du Colombier uartpci(int ctlrno, Pcidev* p, int barno, int n, int freq, char* name,
18*277b6efdSDavid du Colombier 	int iosize)
199ef1f84bSDavid du Colombier {
209ef1f84bSDavid du Colombier 	int i, io;
219ef1f84bSDavid du Colombier 	void *ctlr;
229ef1f84bSDavid du Colombier 	char buf[64];
239ef1f84bSDavid du Colombier 	Uart *head, *uart;
249ef1f84bSDavid du Colombier 
259ef1f84bSDavid du Colombier 	io = p->mem[barno].bar & ~0x01;
269ef1f84bSDavid du Colombier 	snprint(buf, sizeof(buf), "%s%d", pciphysuart.name, ctlrno);
279ef1f84bSDavid du Colombier 	if(ioalloc(io, p->mem[barno].size, 0, buf) < 0){
289ef1f84bSDavid du Colombier 		print("uartpci: I/O %#ux in use\n", io);
299ef1f84bSDavid du Colombier 		return nil;
309ef1f84bSDavid du Colombier 	}
319ef1f84bSDavid du Colombier 
329ef1f84bSDavid du Colombier 	head = uart = malloc(sizeof(Uart)*n);
33*277b6efdSDavid du Colombier 	if(uart == nil)
34*277b6efdSDavid du Colombier 		error(Enomem);
359ef1f84bSDavid du Colombier 	for(i = 0; i < n; i++){
369ef1f84bSDavid du Colombier 		ctlr = i8250alloc(io, p->intl, p->tbdf);
37*277b6efdSDavid du Colombier 		io += iosize;
389ef1f84bSDavid du Colombier 		if(ctlr == nil)
399ef1f84bSDavid du Colombier 			continue;
409ef1f84bSDavid du Colombier 
419ef1f84bSDavid du Colombier 		uart->regs = ctlr;
429ef1f84bSDavid du Colombier 		snprint(buf, sizeof(buf), "%s.%8.8ux", name, p->tbdf);
439ef1f84bSDavid du Colombier 		kstrdup(&uart->name, buf);
449ef1f84bSDavid du Colombier 		uart->freq = freq;
459ef1f84bSDavid du Colombier 		uart->phys = &i8250physuart;
46*277b6efdSDavid du Colombier 		/* print("#t: %s port %#x freq %,ldHz irq %d\n",
47*277b6efdSDavid du Colombier 			uart->name, io - iosize, uart->freq, p->intl); /**/
489ef1f84bSDavid du Colombier 		if(uart != head)
499ef1f84bSDavid du Colombier 			(uart-1)->next = uart;
509ef1f84bSDavid du Colombier 		uart++;
519ef1f84bSDavid du Colombier 	}
529ef1f84bSDavid du Colombier 
53*277b6efdSDavid du Colombier 	if (head) {
54*277b6efdSDavid du Colombier 		if(perlehead != nil)
55*277b6efdSDavid du Colombier 			perletail->next = head;
56*277b6efdSDavid du Colombier 		else
57*277b6efdSDavid du Colombier 			perlehead = head;
58*277b6efdSDavid du Colombier 		for(perletail = head; perletail->next != nil;
59*277b6efdSDavid du Colombier 		    perletail = perletail->next)
60*277b6efdSDavid du Colombier 			;
61*277b6efdSDavid du Colombier 	}
629ef1f84bSDavid du Colombier 	return head;
639ef1f84bSDavid du Colombier }
649ef1f84bSDavid du Colombier 
659ef1f84bSDavid du Colombier static Uart *
ultraport16si(int ctlrno,Pcidev * p,ulong freq)66*277b6efdSDavid du Colombier ultraport16si(int ctlrno, Pcidev *p, ulong freq)
67*277b6efdSDavid du Colombier {
68*277b6efdSDavid du Colombier 	int io, i;
69*277b6efdSDavid du Colombier 	char *name;
70*277b6efdSDavid du Colombier 	Uart *uart;
71*277b6efdSDavid du Colombier 
72*277b6efdSDavid du Colombier 	name = "Ultraport16si";		/* 16L788 UARTs */
73*277b6efdSDavid du Colombier 	io = p->mem[4].bar & ~1;
74*277b6efdSDavid du Colombier 	if (ioalloc(io, p->mem[4].size, 0, name) < 0) {
75*277b6efdSDavid du Colombier 		print("uartpci: can't get IO space to set %s to rs-232\n", name);
76*277b6efdSDavid du Colombier 		return nil;
77*277b6efdSDavid du Colombier 	}
78*277b6efdSDavid du Colombier 	for (i = 0; i < 16; i++) {
79*277b6efdSDavid du Colombier 		outb(io, i << 4);
80*277b6efdSDavid du Colombier 		outb(io, (i << 4) + 1);	/* set to RS232 mode  (Don't ask!) */
81*277b6efdSDavid du Colombier 	}
82*277b6efdSDavid du Colombier 
83*277b6efdSDavid du Colombier 	uart = uartpci(ctlrno, p, 2, 8, freq, name, 16);
84*277b6efdSDavid du Colombier 	if(uart)
85*277b6efdSDavid du Colombier 		uart = uartpci(ctlrno, p, 3, 8, freq, name, 16);
86*277b6efdSDavid du Colombier 	return uart;
87*277b6efdSDavid du Colombier }
88*277b6efdSDavid du Colombier 
89*277b6efdSDavid du Colombier static Uart*
uartpcipnp(void)909ef1f84bSDavid du Colombier uartpcipnp(void)
919ef1f84bSDavid du Colombier {
929ef1f84bSDavid du Colombier 	Pcidev *p;
939ef1f84bSDavid du Colombier 	char *name;
94*277b6efdSDavid du Colombier 	int ctlrno, subid;
95*277b6efdSDavid du Colombier 	ulong freq;
96*277b6efdSDavid du Colombier 	Uart *uart;
979ef1f84bSDavid du Colombier 
989ef1f84bSDavid du Colombier 	/*
999ef1f84bSDavid du Colombier 	 * Loop through all PCI devices looking for simple serial
100*277b6efdSDavid du Colombier 	 * controllers (ccrb == Pcibccomm (7)) and configure the ones which
1019ef1f84bSDavid du Colombier 	 * are familiar. All suitable devices are configured to
1029ef1f84bSDavid du Colombier 	 * simply point to the generic i8250 driver.
1039ef1f84bSDavid du Colombier 	 */
104*277b6efdSDavid du Colombier 	perlehead = perletail = nil;
1059ef1f84bSDavid du Colombier 	ctlrno = 0;
1069ef1f84bSDavid du Colombier 	for(p = pcimatch(nil, 0, 0); p != nil; p = pcimatch(p, 0, 0)){
107*277b6efdSDavid du Colombier 		/* StarTech PCI8S9503V has ccru == 0x80 (other) */
108*277b6efdSDavid du Colombier 		if(p->ccrb != Pcibccomm || p->ccru > 2 && p->ccru != 0x80)
1099ef1f84bSDavid du Colombier 			continue;
1109ef1f84bSDavid du Colombier 
111*277b6efdSDavid du Colombier 		switch(p->did<<16 | p->vid){
1129ef1f84bSDavid du Colombier 		default:
1139ef1f84bSDavid du Colombier 			continue;
1149ef1f84bSDavid du Colombier 		case (0x9835<<16)|0x9710:	/* StarTech PCI2S550 */
115*277b6efdSDavid du Colombier 			uart = uartpci(ctlrno, p, 0, 1, 1843200, "PCI2S550-0", 8);
1169ef1f84bSDavid du Colombier 			if(uart == nil)
1179ef1f84bSDavid du Colombier 				continue;
118*277b6efdSDavid du Colombier 			uart->next = uartpci(ctlrno, p, 1, 1, 1843200,
119*277b6efdSDavid du Colombier 				"PCI2S550-1", 8);
120*277b6efdSDavid du Colombier 			if(uart->next == nil)
121*277b6efdSDavid du Colombier 				continue;
1229ef1f84bSDavid du Colombier 			break;
123*277b6efdSDavid du Colombier 		case (0x950A<<16)|0x1415:	/* Oxford Semi OX16PCI954 */
124*277b6efdSDavid du Colombier 		case (0x9501<<16)|0x1415:
125*277b6efdSDavid du Colombier 		case (0x9521<<16)|0x1415:
1269ef1f84bSDavid du Colombier 			/*
1279ef1f84bSDavid du Colombier 			 * These are common devices used by 3rd-party
1289ef1f84bSDavid du Colombier 			 * manufacturers.
1299ef1f84bSDavid du Colombier 			 * Should check the subsystem VID and DID for correct
1309ef1f84bSDavid du Colombier 			 * match, mostly to get the clock frequency right.
1319ef1f84bSDavid du Colombier 			 */
1329ef1f84bSDavid du Colombier 			subid = pcicfgr16(p, PciSVID);
1339ef1f84bSDavid du Colombier 			subid |= pcicfgr16(p, PciSID)<<16;
1349ef1f84bSDavid du Colombier 			switch(subid){
1359ef1f84bSDavid du Colombier 			default:
136*277b6efdSDavid du Colombier 				print("oxsemi uart %.8#ux of vid %#ux did %#ux unknown\n",
137*277b6efdSDavid du Colombier 					subid, p->vid, p->did);
1389ef1f84bSDavid du Colombier 				continue;
1399ef1f84bSDavid du Colombier 			case (0<<16)|0x1415:	/* StarTech PCI4S550 */
140*277b6efdSDavid du Colombier 				uart = uartpci(ctlrno, p, 0, 4, 1843200,
141*277b6efdSDavid du Colombier 					"starport-pex4s", 8);
142*277b6efdSDavid du Colombier 				break;
143*277b6efdSDavid du Colombier 			case (1<<16)|0x1415:
144*277b6efdSDavid du Colombier 				uart = uartpci(ctlrno, p, 0, 2, 14745600,
145*277b6efdSDavid du Colombier 					"starport-pex2s", 8);
1469ef1f84bSDavid du Colombier 				break;
1479ef1f84bSDavid du Colombier 			case (0x2000<<16)|0x131F:/* SIIG CyberSerial PCIe */
148*277b6efdSDavid du Colombier 				uart = uartpci(ctlrno, p, 0, 1, 18432000,
149*277b6efdSDavid du Colombier 					"CyberSerial-1S", 8);
1509ef1f84bSDavid du Colombier 				break;
1519ef1f84bSDavid du Colombier 			}
1529ef1f84bSDavid du Colombier 			break;
153*277b6efdSDavid du Colombier 		case (0x9505<<16)|0x1415:	/* Oxford Semi OXuPCI952 */
154*277b6efdSDavid du Colombier 			name = "SATAGear-IOI-102";  /* PciSVID=1415, PciSID=0 */
155*277b6efdSDavid du Colombier 			if (uartpci(ctlrno, p, 0, 1, 14745600, name, 8) != nil)
156*277b6efdSDavid du Colombier 				ctlrno++;
157*277b6efdSDavid du Colombier 			if (uartpci(ctlrno, p, 1, 1, 14745600, name, 8) != nil)
158*277b6efdSDavid du Colombier 				ctlrno++;
159*277b6efdSDavid du Colombier 			uart = nil;		/* don't ctlrno++ below */
160*277b6efdSDavid du Colombier 			break;
1619ef1f84bSDavid du Colombier 		case (0x9050<<16)|0x10B5:	/* Perle PCI-Fast4 series */
1629ef1f84bSDavid du Colombier 		case (0x9030<<16)|0x10B5:	/* Perle Ultraport series */
1639ef1f84bSDavid du Colombier 			/*
1649ef1f84bSDavid du Colombier 			 * These devices consists of a PLX bridge (the above
1659ef1f84bSDavid du Colombier 			 * PCI VID+DID) behind which are some 16C654 UARTs.
1669ef1f84bSDavid du Colombier 			 * Must check the subsystem VID and DID for correct
1679ef1f84bSDavid du Colombier 			 * match.
1689ef1f84bSDavid du Colombier 			 */
1699ef1f84bSDavid du Colombier 			subid = pcicfgr16(p, PciSVID);
1709ef1f84bSDavid du Colombier 			subid |= pcicfgr16(p, PciSID)<<16;
171*277b6efdSDavid du Colombier 			freq = 7372800;
1729ef1f84bSDavid du Colombier 			switch(subid){
1739ef1f84bSDavid du Colombier 			default:
174*277b6efdSDavid du Colombier 				print("uartpci: unknown perle subid %#ux\n",
175*277b6efdSDavid du Colombier 					subid);
1769ef1f84bSDavid du Colombier 				continue;
177*277b6efdSDavid du Colombier 			case (0x1588<<16)|0x10B5: /* StarTech PCI8S9503V (P588UG) */
178*277b6efdSDavid du Colombier 				name = "P588UG";
179*277b6efdSDavid du Colombier 				/* max. baud rate is 921,600 */
180*277b6efdSDavid du Colombier 				freq = 1843200;
181*277b6efdSDavid du Colombier 				uart = uartpci(ctlrno, p, 2, 8, freq, name, 8);
182*277b6efdSDavid du Colombier 				break;
1839ef1f84bSDavid du Colombier 			case (0x0011<<16)|0x12E0:	/* Perle PCI-Fast16 */
1849ef1f84bSDavid du Colombier 				name = "PCI-Fast16";
185*277b6efdSDavid du Colombier 				uart = uartpci(ctlrno, p, 2, 16, freq, name, 8);
1869ef1f84bSDavid du Colombier 				break;
1879ef1f84bSDavid du Colombier 			case (0x0021<<16)|0x12E0:	/* Perle PCI-Fast8 */
1889ef1f84bSDavid du Colombier 				name = "PCI-Fast8";
189*277b6efdSDavid du Colombier 				uart = uartpci(ctlrno, p, 2, 8, freq, name, 8);
1909ef1f84bSDavid du Colombier 				break;
1919ef1f84bSDavid du Colombier 			case (0x0031<<16)|0x12E0:	/* Perle PCI-Fast4 */
1929ef1f84bSDavid du Colombier 				name = "PCI-Fast4";
193*277b6efdSDavid du Colombier 				uart = uartpci(ctlrno, p, 2, 4, freq, name, 8);
1949ef1f84bSDavid du Colombier 				break;
1959ef1f84bSDavid du Colombier 			case (0x0021<<16)|0x155F:	/* Perle Ultraport8 */
1969ef1f84bSDavid du Colombier 				name = "Ultraport8";	/* 16C754 UARTs */
197*277b6efdSDavid du Colombier 				uart = uartpci(ctlrno, p, 2, 8, freq, name, 8);
198*277b6efdSDavid du Colombier 				break;
199*277b6efdSDavid du Colombier 			case (0x0041<<16)|0x155F:	/* Perle Ultraport16 */
200*277b6efdSDavid du Colombier 				name = "Ultraport16";
201*277b6efdSDavid du Colombier 				uart = uartpci(ctlrno, p, 2, 16, 2 * freq,
202*277b6efdSDavid du Colombier 					name, 8);
203*277b6efdSDavid du Colombier 				break;
204*277b6efdSDavid du Colombier 			case (0x0241<<16)|0x155F:	/* Perle Ultraport16 */
205*277b6efdSDavid du Colombier 				uart = ultraport16si(ctlrno, p, 4 * freq);
2069ef1f84bSDavid du Colombier 				break;
2079ef1f84bSDavid du Colombier 			}
2089ef1f84bSDavid du Colombier 			break;
2099ef1f84bSDavid du Colombier 		}
210*277b6efdSDavid du Colombier 		if(uart)
2119ef1f84bSDavid du Colombier 			ctlrno++;
2129ef1f84bSDavid du Colombier 	}
2139ef1f84bSDavid du Colombier 
214*277b6efdSDavid du Colombier 	return perlehead;
2159ef1f84bSDavid du Colombier }
2169ef1f84bSDavid du Colombier 
2179ef1f84bSDavid du Colombier PhysUart pciphysuart = {
2189ef1f84bSDavid du Colombier 	.name		= "UartPCI",
2199ef1f84bSDavid du Colombier 	.pnp		= uartpcipnp,
2209ef1f84bSDavid du Colombier 	.enable		= nil,
2219ef1f84bSDavid du Colombier 	.disable	= nil,
2229ef1f84bSDavid du Colombier 	.kick		= nil,
2239ef1f84bSDavid du Colombier 	.dobreak	= nil,
2249ef1f84bSDavid du Colombier 	.baud		= nil,
2259ef1f84bSDavid du Colombier 	.bits		= nil,
2269ef1f84bSDavid du Colombier 	.stop		= nil,
2279ef1f84bSDavid du Colombier 	.parity		= nil,
2289ef1f84bSDavid du Colombier 	.modemctl	= nil,
2299ef1f84bSDavid du Colombier 	.rts		= nil,
2309ef1f84bSDavid du Colombier 	.dtr		= nil,
2319ef1f84bSDavid du Colombier 	.status		= nil,
2329ef1f84bSDavid du Colombier 	.fifo		= nil,
2339ef1f84bSDavid du Colombier };
234