xref: /plan9/sys/src/9/pc/uartpci.c (revision 6872394c4d4bc26251e04b982c57150852a1ce91)
19a747e4fSDavid du Colombier #include "u.h"
29a747e4fSDavid du Colombier #include "../port/lib.h"
39a747e4fSDavid du Colombier #include "mem.h"
49a747e4fSDavid du Colombier #include "dat.h"
59a747e4fSDavid du Colombier #include "fns.h"
69a747e4fSDavid du Colombier #include "io.h"
79a747e4fSDavid du Colombier #include "../port/error.h"
89a747e4fSDavid du Colombier 
99a747e4fSDavid du Colombier extern PhysUart i8250physuart;
109a747e4fSDavid du Colombier extern PhysUart pciphysuart;
119a747e4fSDavid du Colombier extern void* i8250alloc(int, int, int);
129a747e4fSDavid du Colombier 
13d5099b52SDavid du Colombier static Uart *perlehead, *perletail;
14d5099b52SDavid du Colombier 
159a747e4fSDavid du Colombier static Uart*
uartpci(int ctlrno,Pcidev * p,int barno,int n,int freq,char * name,int iosize)16d5099b52SDavid du Colombier uartpci(int ctlrno, Pcidev* p, int barno, int n, int freq, char* name,
17d5099b52SDavid du Colombier 	int iosize)
189a747e4fSDavid du Colombier {
199a747e4fSDavid du Colombier 	int i, io;
209a747e4fSDavid du Colombier 	void *ctlr;
219a747e4fSDavid du Colombier 	char buf[64];
229a747e4fSDavid du Colombier 	Uart *head, *uart;
239a747e4fSDavid du Colombier 
249a747e4fSDavid du Colombier 	io = p->mem[barno].bar & ~0x01;
259a747e4fSDavid du Colombier 	snprint(buf, sizeof(buf), "%s%d", pciphysuart.name, ctlrno);
269a747e4fSDavid du Colombier 	if(ioalloc(io, p->mem[barno].size, 0, buf) < 0){
279a747e4fSDavid du Colombier 		print("uartpci: I/O 0x%uX in use\n", io);
289a747e4fSDavid du Colombier 		return nil;
299a747e4fSDavid du Colombier 	}
309a747e4fSDavid du Colombier 
319a747e4fSDavid du Colombier 	head = uart = malloc(sizeof(Uart)*n);
32aa72973aSDavid du Colombier 	if(uart == nil)
33aa72973aSDavid du Colombier 		error(Enomem);
349a747e4fSDavid du Colombier 	for(i = 0; i < n; i++){
359a747e4fSDavid du Colombier 		ctlr = i8250alloc(io, p->intl, p->tbdf);
36d5099b52SDavid du Colombier 		io += iosize;
379a747e4fSDavid du Colombier 		if(ctlr == nil)
389a747e4fSDavid du Colombier 			continue;
399a747e4fSDavid du Colombier 
409a747e4fSDavid du Colombier 		uart->regs = ctlr;
419a747e4fSDavid du Colombier 		snprint(buf, sizeof(buf), "%s.%8.8uX", name, p->tbdf);
429a747e4fSDavid du Colombier 		kstrdup(&uart->name, buf);
439a747e4fSDavid du Colombier 		uart->freq = freq;
449a747e4fSDavid du Colombier 		uart->phys = &i8250physuart;
45*6872394cSDavid du Colombier 		/* print("#t: %s port %#x freq %,ldHz irq %d\n",
46*6872394cSDavid du Colombier 			uart->name, io - iosize, uart->freq, p->intl); /**/
479a747e4fSDavid du Colombier 		if(uart != head)
489a747e4fSDavid du Colombier 			(uart-1)->next = uart;
499a747e4fSDavid du Colombier 		uart++;
509a747e4fSDavid du Colombier 	}
519a747e4fSDavid du Colombier 
52d5099b52SDavid du Colombier 	if (head) {
53d5099b52SDavid du Colombier 		if(perlehead != nil)
54d5099b52SDavid du Colombier 			perletail->next = head;
55d5099b52SDavid du Colombier 		else
56d5099b52SDavid du Colombier 			perlehead = head;
57d5099b52SDavid du Colombier 		for(perletail = head; perletail->next != nil;
58d5099b52SDavid du Colombier 		    perletail = perletail->next)
59d5099b52SDavid du Colombier 			;
60d5099b52SDavid du Colombier 	}
619a747e4fSDavid du Colombier 	return head;
629a747e4fSDavid du Colombier }
639a747e4fSDavid du Colombier 
649a747e4fSDavid du Colombier static Uart *
ultraport16si(int ctlrno,Pcidev * p,ulong freq)6537a6523bSDavid du Colombier ultraport16si(int ctlrno, Pcidev *p, ulong freq)
66d5099b52SDavid du Colombier {
67d5099b52SDavid du Colombier 	int io, i;
68d5099b52SDavid du Colombier 	char *name;
69d5099b52SDavid du Colombier 	Uart *uart;
70d5099b52SDavid du Colombier 
71d5099b52SDavid du Colombier 	name = "Ultraport16si";		/* 16L788 UARTs */
72d5099b52SDavid du Colombier 	io = p->mem[4].bar & ~1;
73d5099b52SDavid du Colombier 	if (ioalloc(io, p->mem[4].size, 0, name) < 0) {
74d5099b52SDavid du Colombier 		print("uartpci: can't get IO space to set %s to rs-232\n", name);
75d5099b52SDavid du Colombier 		return nil;
76d5099b52SDavid du Colombier 	}
77d5099b52SDavid du Colombier 	for (i = 0; i < 16; i++) {
78d5099b52SDavid du Colombier 		outb(io, i << 4);
79d5099b52SDavid du Colombier 		outb(io, (i << 4) + 1);	/* set to RS232 mode  (Don't ask!) */
80d5099b52SDavid du Colombier 	}
81d5099b52SDavid du Colombier 
82d5099b52SDavid du Colombier 	uart = uartpci(ctlrno, p, 2, 8, freq, name, 16);
83d5099b52SDavid du Colombier 	if(uart)
84d5099b52SDavid du Colombier 		uart = uartpci(ctlrno, p, 3, 8, freq, name, 16);
85d5099b52SDavid du Colombier 	return uart;
86d5099b52SDavid du Colombier }
87d5099b52SDavid du Colombier 
88d5099b52SDavid du Colombier static Uart*
uartpcipnp(void)899a747e4fSDavid du Colombier uartpcipnp(void)
909a747e4fSDavid du Colombier {
919a747e4fSDavid du Colombier 	Pcidev *p;
929a747e4fSDavid du Colombier 	char *name;
93d5099b52SDavid du Colombier 	int ctlrno, subid;
94d5099b52SDavid du Colombier 	ulong freq;
95d5099b52SDavid du Colombier 	Uart *uart;
969a747e4fSDavid du Colombier 
979a747e4fSDavid du Colombier 	/*
989a747e4fSDavid du Colombier 	 * Loop through all PCI devices looking for simple serial
9991157df7SDavid du Colombier 	 * controllers (ccrb == Pcibccomm (7)) and configure the ones which
1009a747e4fSDavid du Colombier 	 * are familiar. All suitable devices are configured to
1019a747e4fSDavid du Colombier 	 * simply point to the generic i8250 driver.
1029a747e4fSDavid du Colombier 	 */
103d5099b52SDavid du Colombier 	perlehead = perletail = nil;
1049a747e4fSDavid du Colombier 	ctlrno = 0;
1059a747e4fSDavid du Colombier 	for(p = pcimatch(nil, 0, 0); p != nil; p = pcimatch(p, 0, 0)){
106*6872394cSDavid du Colombier 		/* StarTech PCI8S9503V has ccru == 0x80 (other) */
107*6872394cSDavid du Colombier 		if(p->ccrb != Pcibccomm || p->ccru > 2 && p->ccru != 0x80)
1089a747e4fSDavid du Colombier 			continue;
1099a747e4fSDavid du Colombier 
110d5099b52SDavid du Colombier 		switch(p->did<<16 | p->vid){
1119a747e4fSDavid du Colombier 		default:
1129a747e4fSDavid du Colombier 			continue;
1135979f962SDavid du Colombier 		case (0x9835<<16)|0x9710:	/* StarTech PCI2S550 */
114d5099b52SDavid du Colombier 			uart = uartpci(ctlrno, p, 0, 1, 1843200, "PCI2S550-0", 8);
1155979f962SDavid du Colombier 			if(uart == nil)
1165979f962SDavid du Colombier 				continue;
117d5099b52SDavid du Colombier 			uart->next = uartpci(ctlrno, p, 1, 1, 1843200,
118d5099b52SDavid du Colombier 				"PCI2S550-1", 8);
119d5099b52SDavid du Colombier 			if(uart->next == nil)
120d5099b52SDavid du Colombier 				continue;
1215979f962SDavid du Colombier 			break;
122061a29d3SDavid du Colombier 		case (0x950A<<16)|0x1415:	/* Oxford Semi OX16PCI954 */
123bd8a67bbSDavid du Colombier 		case (0x9501<<16)|0x1415:
12491157df7SDavid du Colombier 		case (0x9521<<16)|0x1415:
125061a29d3SDavid du Colombier 			/*
126061a29d3SDavid du Colombier 			 * These are common devices used by 3rd-party
127061a29d3SDavid du Colombier 			 * manufacturers.
128061a29d3SDavid du Colombier 			 * Must check the subsystem VID and DID for correct
129061a29d3SDavid du Colombier 			 * match.
130061a29d3SDavid du Colombier 			 */
131061a29d3SDavid du Colombier 			subid = pcicfgr16(p, PciSVID);
132061a29d3SDavid du Colombier 			subid |= pcicfgr16(p, PciSID)<<16;
133061a29d3SDavid du Colombier 			switch(subid){
134061a29d3SDavid du Colombier 			default:
135bd8a67bbSDavid du Colombier 				print("oxsemi uart %.8#ux of vid %#ux did %#ux unknown\n",
136bd8a67bbSDavid du Colombier 					subid, p->vid, p->did);
137061a29d3SDavid du Colombier 				continue;
138bd8a67bbSDavid du Colombier 			case (0<<16)|0x1415:
139bd8a67bbSDavid du Colombier 				uart = uartpci(ctlrno, p, 0, 4, 1843200,
140bd8a67bbSDavid du Colombier 					"starport-pex4s", 8);
141bd8a67bbSDavid du Colombier 				break;
14291157df7SDavid du Colombier 			case (1<<16)|0x1415:
14391157df7SDavid du Colombier 				uart = uartpci(ctlrno, p, 0, 2, 14745600,
14491157df7SDavid du Colombier 					"starport-pex2s", 8);
14591157df7SDavid du Colombier 				break;
146061a29d3SDavid du Colombier 			case (0x2000<<16)|0x131F:/* SIIG CyberSerial PCIe */
147d5099b52SDavid du Colombier 				uart = uartpci(ctlrno, p, 0, 1, 18432000,
148d5099b52SDavid du Colombier 					"CyberSerial-1S", 8);
149061a29d3SDavid du Colombier 				break;
150061a29d3SDavid du Colombier 			}
151061a29d3SDavid du Colombier 			break;
152b39189fdSDavid du Colombier 		case (0x9505<<16)|0x1415:	/* Oxford Semi OXuPCI952 */
153b39189fdSDavid du Colombier 			name = "SATAGear-IOI-102";  /* PciSVID=1415, PciSID=0 */
154b39189fdSDavid du Colombier 			if (uartpci(ctlrno, p, 0, 1, 14745600, name, 8) != nil)
155b39189fdSDavid du Colombier 				ctlrno++;
156b39189fdSDavid du Colombier 			if (uartpci(ctlrno, p, 1, 1, 14745600, name, 8) != nil)
157b39189fdSDavid du Colombier 				ctlrno++;
158b39189fdSDavid du Colombier 			uart = nil;		/* don't ctlrno++ below */
159b39189fdSDavid du Colombier 			break;
160a30303efSDavid du Colombier 		case (0x9050<<16)|0x10B5:	/* Perle PCI-Fast4 series */
161a30303efSDavid du Colombier 		case (0x9030<<16)|0x10B5:	/* Perle Ultraport series */
1629a747e4fSDavid du Colombier 			/*
1639a747e4fSDavid du Colombier 			 * These devices consists of a PLX bridge (the above
1649a747e4fSDavid du Colombier 			 * PCI VID+DID) behind which are some 16C654 UARTs.
1659a747e4fSDavid du Colombier 			 * Must check the subsystem VID and DID for correct
1669a747e4fSDavid du Colombier 			 * match.
1679a747e4fSDavid du Colombier 			 */
1689a747e4fSDavid du Colombier 			subid = pcicfgr16(p, PciSVID);
1699a747e4fSDavid du Colombier 			subid |= pcicfgr16(p, PciSID)<<16;
170d5099b52SDavid du Colombier 			freq = 7372800;
1719a747e4fSDavid du Colombier 			switch(subid){
1729a747e4fSDavid du Colombier 			default:
173*6872394cSDavid du Colombier 				print("uartpci: unknown perle subid %#ux\n",
174*6872394cSDavid du Colombier 					subid);
1759a747e4fSDavid du Colombier 				continue;
176*6872394cSDavid du Colombier 			case (0x1588<<16)|0x10B5: /* StarTech PCI8S9503V (P588UG) */
177*6872394cSDavid du Colombier 				name = "P588UG";
178*6872394cSDavid du Colombier 				/* max. baud rate is 921,600 */
179*6872394cSDavid du Colombier 				freq = 1843200;
180*6872394cSDavid du Colombier 				uart = uartpci(ctlrno, p, 2, 8, freq, name, 8);
181*6872394cSDavid du Colombier 				break;
1829a747e4fSDavid du Colombier 			case (0x0011<<16)|0x12E0:	/* Perle PCI-Fast16 */
1839a747e4fSDavid du Colombier 				name = "PCI-Fast16";
184d5099b52SDavid du Colombier 				uart = uartpci(ctlrno, p, 2, 16, freq, name, 8);
1859a747e4fSDavid du Colombier 				break;
1869a747e4fSDavid du Colombier 			case (0x0021<<16)|0x12E0:	/* Perle PCI-Fast8 */
1879a747e4fSDavid du Colombier 				name = "PCI-Fast8";
188d5099b52SDavid du Colombier 				uart = uartpci(ctlrno, p, 2, 8, freq, name, 8);
1899a747e4fSDavid du Colombier 				break;
1909a747e4fSDavid du Colombier 			case (0x0031<<16)|0x12E0:	/* Perle PCI-Fast4 */
1919a747e4fSDavid du Colombier 				name = "PCI-Fast4";
192d5099b52SDavid du Colombier 				uart = uartpci(ctlrno, p, 2, 4, freq, name, 8);
1939a747e4fSDavid du Colombier 				break;
194a30303efSDavid du Colombier 			case (0x0021<<16)|0x155F:	/* Perle Ultraport8 */
195a30303efSDavid du Colombier 				name = "Ultraport8";	/* 16C754 UARTs */
196d5099b52SDavid du Colombier 				uart = uartpci(ctlrno, p, 2, 8, freq, name, 8);
197d5099b52SDavid du Colombier 				break;
19837a6523bSDavid du Colombier 			case (0x0041<<16)|0x155F:	/* Perle Ultraport16 */
19937a6523bSDavid du Colombier 				name = "Ultraport16";
20037a6523bSDavid du Colombier 				uart = uartpci(ctlrno, p, 2, 16, 2 * freq,
20137a6523bSDavid du Colombier 					name, 8);
20237a6523bSDavid du Colombier 				break;
203d5099b52SDavid du Colombier 			case (0x0241<<16)|0x155F:	/* Perle Ultraport16 */
20437a6523bSDavid du Colombier 				uart = ultraport16si(ctlrno, p, 4 * freq);
205a30303efSDavid du Colombier 				break;
2069a747e4fSDavid du Colombier 			}
2079a747e4fSDavid du Colombier 			break;
2089a747e4fSDavid du Colombier 		}
209d5099b52SDavid du Colombier 		if(uart)
2109a747e4fSDavid du Colombier 			ctlrno++;
2119a747e4fSDavid du Colombier 	}
2129a747e4fSDavid du Colombier 
213d5099b52SDavid du Colombier 	return perlehead;
2149a747e4fSDavid du Colombier }
2159a747e4fSDavid du Colombier 
2169a747e4fSDavid du Colombier PhysUart pciphysuart = {
2179a747e4fSDavid du Colombier 	.name		= "UartPCI",
2189a747e4fSDavid du Colombier 	.pnp		= uartpcipnp,
2199a747e4fSDavid du Colombier 	.enable		= nil,
2209a747e4fSDavid du Colombier 	.disable	= nil,
2219a747e4fSDavid du Colombier 	.kick		= nil,
2229a747e4fSDavid du Colombier 	.dobreak	= nil,
2239a747e4fSDavid du Colombier 	.baud		= nil,
2249a747e4fSDavid du Colombier 	.bits		= nil,
2259a747e4fSDavid du Colombier 	.stop		= nil,
2269a747e4fSDavid du Colombier 	.parity		= nil,
2279a747e4fSDavid du Colombier 	.modemctl	= nil,
2289a747e4fSDavid du Colombier 	.rts		= nil,
2299a747e4fSDavid du Colombier 	.dtr		= nil,
2309a747e4fSDavid du Colombier 	.status		= nil,
2319a747e4fSDavid du Colombier 	.fifo		= nil,
2329a747e4fSDavid du Colombier };
233