241 lines · 6.1 KB · C
RawDownload
/*  * This file is part of the UCB release of Plan 9. It is subject to the license * terms in the LICENSE file found in the top-level directory of this * distribution and at http://akaros.cs.berkeley.edu/files/Plan9License. No * part of the UCB release of Plan 9, including this file, may be copied, * modified, propagated, or distributed except according to the terms contained * in the LICENSE file. */ #include "u.h"#include "../port/lib.h"#include "mem.h"#include "dat.h"#include "fns.h"#include "io.h"#include "../port/error.h" extern PhysUart i8250physuart;extern PhysUart pciphysuart;extern void* i8250alloc(int, int, int); static Uart *perlehead, *perletail; static Uart*uartpci(int ctlrno, Pcidev* p, int barno, int n, int freq, char* name,	int iosize){	int i, io;	void *ctlr;	char buf[64];	Uart *head, *uart; 	io = p->mem[barno].bar & ~0x01;	snprint(buf, sizeof(buf), "%s%d", pciphysuart.name, ctlrno);	if(ioalloc(io, p->mem[barno].size, 0, buf) < 0){		print("uartpci: I/O 0x%uX in use\n", io);		return nil;	} 	head = uart = malloc(sizeof(Uart)*n);	if(uart == nil)		error(Enomem);	for(i = 0; i < n; i++){		ctlr = i8250alloc(io, p->intl, p->tbdf);		io += iosize;		if(ctlr == nil)			continue; 		uart->regs = ctlr;		snprint(buf, sizeof(buf), "%s.%8.8uX", name, p->tbdf);		kstrdup(&uart->name, buf);		uart->freq = freq;		uart->phys = &i8250physuart;		/* print("#t: %s port %#x freq %,ldHz irq %d\n",			uart->name, io - iosize, uart->freq, p->intl); /**/		if(uart != head)			(uart-1)->next = uart;		uart++;	} 	if (head) {		if(perlehead != nil)			perletail->next = head;		else			perlehead = head;		for(perletail = head; perletail->next != nil;		    perletail = perletail->next)			;	}	return head;} static Uart *ultraport16si(int ctlrno, Pcidev *p, ulong freq){	int io, i;	char *name;	Uart *uart; 	name = "Ultraport16si";		/* 16L788 UARTs */	io = p->mem[4].bar & ~1;	if (ioalloc(io, p->mem[4].size, 0, name) < 0) {		print("uartpci: can't get IO space to set %s to rs-232\n", name);		return nil;	}	for (i = 0; i < 16; i++) {		outb(io, i << 4);		outb(io, (i << 4) + 1);	/* set to RS232 mode  (Don't ask!) */	} 	uart = uartpci(ctlrno, p, 2, 8, freq, name, 16);	if(uart)		uart = uartpci(ctlrno, p, 3, 8, freq, name, 16);	return uart;} static Uart*uartpcipnp(void){	Pcidev *p;	char *name;	int ctlrno, subid;	ulong freq;	Uart *uart; 	/*	 * Loop through all PCI devices looking for simple serial	 * controllers (ccrb == Pcibccomm (7)) and configure the ones which	 * are familiar. All suitable devices are configured to	 * simply point to the generic i8250 driver.	 */	perlehead = perletail = nil;	ctlrno = 0;	for(p = pcimatch(nil, 0, 0); p != nil; p = pcimatch(p, 0, 0)){		/* StarTech PCI8S9503V has ccru == 0x80 (other) */		if(p->ccrb != Pcibccomm || p->ccru > 2 && p->ccru != 0x80)			continue; 		switch(p->did<<16 | p->vid){		default:			continue;		case (0x9835<<16)|0x9710:	/* StarTech PCI2S550 */			uart = uartpci(ctlrno, p, 0, 1, 1843200, "PCI2S550-0", 8);			if(uart == nil)				continue;			uart->next = uartpci(ctlrno, p, 1, 1, 1843200,				"PCI2S550-1", 8);			if(uart->next == nil)				continue;			break;		case (0x950A<<16)|0x1415:	/* Oxford Semi OX16PCI954 */		case (0x9501<<16)|0x1415:		case (0x9521<<16)|0x1415:			/*			 * These are common devices used by 3rd-party			 * manufacturers.			 * Must check the subsystem VID and DID for correct			 * match.			 */			subid = pcicfgr16(p, PciSVID);			subid |= pcicfgr16(p, PciSID)<<16;			switch(subid){			default:				print("oxsemi uart %.8#ux of vid %#ux did %#ux unknown\n",					subid, p->vid, p->did);				continue;			case (0<<16)|0x1415:				uart = uartpci(ctlrno, p, 0, 4, 1843200,					"starport-pex4s", 8);				break;			case (1<<16)|0x1415:				uart = uartpci(ctlrno, p, 0, 2, 14745600,					"starport-pex2s", 8);				break;			case (0x2000<<16)|0x131F:/* SIIG CyberSerial PCIe */				uart = uartpci(ctlrno, p, 0, 1, 18432000,					"CyberSerial-1S", 8);				break;			}			break;		case (0x9505<<16)|0x1415:	/* Oxford Semi OXuPCI952 */			name = "SATAGear-IOI-102";  /* PciSVID=1415, PciSID=0 */			if (uartpci(ctlrno, p, 0, 1, 14745600, name, 8) != nil)				ctlrno++;			if (uartpci(ctlrno, p, 1, 1, 14745600, name, 8) != nil)				ctlrno++;			uart = nil;		/* don't ctlrno++ below */			break;		case (0x9050<<16)|0x10B5:	/* Perle PCI-Fast4 series */		case (0x9030<<16)|0x10B5:	/* Perle Ultraport series */			/*			 * These devices consists of a PLX bridge (the above			 * PCI VID+DID) behind which are some 16C654 UARTs.			 * Must check the subsystem VID and DID for correct			 * match.			 */			subid = pcicfgr16(p, PciSVID);			subid |= pcicfgr16(p, PciSID)<<16;			freq = 7372800;			switch(subid){			default:				print("uartpci: unknown perle subid %#ux\n",					subid);				continue;			case (0x1588<<16)|0x10B5: /* StarTech PCI8S9503V (P588UG) */				name = "P588UG";				/* max. baud rate is 921,600 */				freq = 1843200;				uart = uartpci(ctlrno, p, 2, 8, freq, name, 8);				break;			case (0x0011<<16)|0x12E0:	/* Perle PCI-Fast16 */				name = "PCI-Fast16";				uart = uartpci(ctlrno, p, 2, 16, freq, name, 8);				break;			case (0x0021<<16)|0x12E0:	/* Perle PCI-Fast8 */				name = "PCI-Fast8";				uart = uartpci(ctlrno, p, 2, 8, freq, name, 8);				break;			case (0x0031<<16)|0x12E0:	/* Perle PCI-Fast4 */				name = "PCI-Fast4";				uart = uartpci(ctlrno, p, 2, 4, freq, name, 8);				break;			case (0x0021<<16)|0x155F:	/* Perle Ultraport8 */				name = "Ultraport8";	/* 16C754 UARTs */				uart = uartpci(ctlrno, p, 2, 8, freq, name, 8);				break;			case (0x0041<<16)|0x155F:	/* Perle Ultraport16 */				name = "Ultraport16";				uart = uartpci(ctlrno, p, 2, 16, 2 * freq,					name, 8);				break;			case (0x0241<<16)|0x155F:	/* Perle Ultraport16 */				uart = ultraport16si(ctlrno, p, 4 * freq);				break;			}			break;		}		if(uart)			ctlrno++;	} 	return perlehead;} PhysUart pciphysuart = {	.name		= "UartPCI",	.pnp		= uartpcipnp,	.enable		= nil,	.disable	= nil,	.kick		= nil,	.dobreak	= nil,	.baud		= nil,	.bits		= nil,	.stop		= nil,	.parity		= nil,	.modemctl	= nil,	.rts		= nil,	.dtr		= nil,	.status		= nil,	.fifo		= nil,};