592 lines · 10.4 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. */ /* * Oxford Semiconductor OXPCIe95x UART driver */#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 oxphysuart; enum {	Ccr		= 0x0000/4,	/* Class Code and Revision ID */	Nuart		= 0x0004/4,	/* Decimal Number of UARTs */	Gis		= 0x0008/4,	/* Global UART IRQ Status */	Gie		= 0x000C/4,	/* Global UART IRQ Enable */	Gid		= 0x0010/4,	/* Global UART IRQ Disable */	Gwe		= 0x0014/4,	/* Global UART Wake Enable */	Gwd		= 0x0018/4,	/* Global UART Wake Disable */}; enum {	Thr		= 0x00,		/* Transmitter Holding */	Rhr		= 0x00,		/* Receiver Holding */	Ier		= 0x01,		/* Interrupt Enable */	Fcr		= 0x02,		/* FIFO Control */	Isr		= 0x02,		/* Interrupt Status */	Lcr		= 0x03,		/* Line Control */	Mcr		= 0x04,		/* Modem Control */	Lsr		= 0x05,		/* Line Status */	Msr		= 0x06,		/* Modem Status */	Spr		= 0x07,		/* Scratch Pad */	Dll		= 0x00,		/* Divisor Latch LSB */	Dlm		= 0x01,		/* Divisor Latch MSB */	Efr		= 0x02,		/* Enhanced Feature */}; typedef struct Port Port;typedef struct Ctlr Ctlr; struct Port {	Uart;	Ctlr	*ctlr;	u8int	*mem; 	int	level;	int	dtr, rts;	int	ri;}; struct Ctlr {	Lock;	char	*name;	Pcidev	*pcidev;	u32int	*mem; 	u32int	im; 	Port	port[0x10];	int	nport;}; static Uart *oxpnp(void){	Pcidev *p;	Ctlr *ctlr;	Port *port;	int i;	char *model;	char name[12+1];	Uart *head, *tail;	static int ctlrno; 	p = nil;	head = tail = nil;	while(p = pcimatch(p, 0x1415, 0)){		switch(p->did){		case 0xc101:		case 0xc105:		case 0xc11b:		case 0xc11f:		case 0xc120:		case 0xc124:		case 0xc138:		case 0xc13d:		case 0xc140:		case 0xc141:		case 0xc144:		case 0xc145:		case 0xc158:		case 0xc15d:			model = "OXPCIe952";			break;		case 0xc208:		case 0xc20d:			model = "OXPCIe954";			break;		case 0xc308:		case 0xc30d:			model = "OXPCIe958";			break;		default:			continue;		}		ctlr = malloc(sizeof *ctlr);		if(ctlr == nil){			print("oxpnp: out of memory\n");			continue;		}		ctlr->pcidev = p;		ctlr->mem = vmap(p->mem[0].bar & ~0xf, p->mem[0].size);		if(ctlr->mem == nil){			print("oxpnp: vmap failed\n");			free(ctlr);			continue;		}		snprint(name, sizeof name, "uartox%d", ctlrno);		kstrdup(&ctlr->name, name);		ctlr->nport = ctlr->mem[Nuart] & 0x1f;		for(i = 0; i < ctlr->nport; ++i){			port = &ctlr->port[i];			port->ctlr = ctlr;			port->mem = (u8int *)ctlr->mem + 0x1000 + 0x200*i;			port->regs = port;			snprint(name, sizeof name, "%s.%d", ctlr->name, i);			kstrdup(&port->name, name);			port->phys = &oxphysuart;			if(head == nil)				head = port;			else				tail->next = port;			tail = port;		}		print("%s: %s: %d ports irq %d\n",		    ctlr->name, model, ctlr->nport, p->intl);		ctlrno++;	}	return head;} static voidoxinterrupt(Ureg *, void *arg){	Ctlr *ctlr;	Port *port;	Uart *uart;	int i, old;	u8int val;	char ch; 	ctlr = arg; 	ilock(ctlr);	if(!(ctlr->im & ctlr->mem[Gis])){		iunlock(ctlr);		return;	}	for(i = 0; i < ctlr->nport; ++i){		if(!(ctlr->im & 1<<i))			continue;		port = &ctlr->port[i];		uart = port;	/* "Come Clarity" */		switch(port->mem[Isr] & 0x3f){		case 0x06:	/* Receiver status error */		case 0x04:	/* Receiver data available */		case 0x0c:	/* Receiver time-out */			for(;;){				val = port->mem[Lsr];				if(!(val & 1<<0))	/* RxRDY */					break;				if(val & 1<<1)		/* Overrun Error */					uart->oerr++;				if(val & 1<<2)		/* Parity Error */					uart->perr++;				if(val & 1<<3)		/* Framing Error */					uart->ferr++;				ch = port->mem[Rhr];				if(!(val & 1<<7))	/* Data Error */					uartrecv(uart, ch);			}			break;		case 0x02:	/* Transmitter THR empty */			uartkick(uart);			break;		case 0x00:	/* Modem status change */			val = port->mem[Msr];			if(val & 1<<0){			/* Delta nCTS */				ilock(&uart->tlock);				old = uart->cts;				uart->cts = val & 1<<4;	/* CTS */				if(!old && uart->cts)					uart->ctsbackoff = 2;				iunlock(&uart->tlock);			}			if(val & 1<<1){			/* Delta nDSR */				old = val & 1<<5;	/* DSR */				if(!old && uart->dsr && uart->hup_dsr)					uart->dohup = 1;				uart->dsr = old;			}			port->ri = val & 1<<6;		/* RI */			if(val & 1<<3){			/* Delta nDCD */				old = val & 1<<7;	/* DCD */				if(!old && uart->dcd && uart->hup_dcd)					uart->dohup = 1;				uart->dcd = old;			}			break;		}	}	iunlock(ctlr);} #define MASK(p)	(1UL<<((p)-(p)->ctlr->port)) static voidoxenable(Uart *uart, int){	Ctlr *ctlr;	Port *port; 	port = uart->regs;	ctlr = port->ctlr; 	ilock(ctlr);	if(ctlr->im == 0)		intrenable(ctlr->pcidev->intl, oxinterrupt, ctlr,		    ctlr->pcidev->tbdf, ctlr->name);	ctlr->im |= MASK(port);	iunlock(ctlr); 	/* Enable 950 Mode */	port->mem[Lcr] |= 1<<7;			/* Divisor latch access */	port->mem[Efr] = 1<<4;			/* Enhanced mode */	port->mem[Lcr] &= ~(1<<7); 	port->mem[Ier] = 1<<2|1<<1|1<<0;	/* Rx Stat, THRE, RxRDY */ 	(*uart->phys->dtr)(uart, 1);	(*uart->phys->rts)(uart, 1); 	/* Enable FIFO */	(*uart->phys->fifo)(uart, ~0);} static voidoxdisable(Uart *uart){	Ctlr *ctlr;	Port *port; 	port = uart->regs;	ctlr = port->ctlr; 	(*uart->phys->dtr)(uart, 0);	(*uart->phys->rts)(uart, 0);	(*uart->phys->fifo)(uart, 0); 	port->mem[Ier] = 0; 	ilock(ctlr);	ctlr->im &= ~MASK(port);	if(ctlr->im == 0)		intrdisable(ctlr->pcidev->intl, oxinterrupt, ctlr,		    ctlr->pcidev->tbdf, ctlr->name);	iunlock(ctlr);} static voidoxkick(Uart *uart){	Port *port; 	if(uart->cts == 0 || uart->blocked)		return; 	port = uart->regs; 	for(;;){		if(!(port->mem[Lsr] & 1<<5))	/* THR Empty */			break;		if(uart->op >= uart->oe && uartstageoutput(uart) == 0)			break;		port->mem[Thr] = *(uart->op++);	}} static voidoxdobreak(Uart *uart, int ms){	Port *port; 	if(ms <= 0)		ms = 200; 	port = uart->regs; 	port->mem[Lcr] |= 1<<6;			/* Transmission break */	if(!waserror()){		tsleep(&up->sleep, return0, nil, ms);		poperror();	}	port->mem[Lcr] &= ~(1<<6);} static intoxbaud(Uart *uart, int baud){	Port *port;	u16int val; 	if(baud <= 0)		return -1; 	port = uart->regs; 	/*	 * We aren't terribly interested in non-standard baud rates.	 * Rather than mess about with generator constants, we instead	 * program DLM and DLL according to Table 37 in the datasheet.	 */	switch(baud){	case 1200:		val = 0x0cb6;		break;	case 2400:		val = 0x065b;		break;	case 4800:		val = 0x032d;		break;	case 9600:		val = 0x0196;		break;	case 19200:		val = 0x00cb;		break;	case 38400:		val = 0x0066;		break;	case 57600:		val = 0x0044;		break;	case 115200:		val = 0x0022;		break;	default:		return -1;	}	port->mem[Lcr] |= 1<<7;			/* Divisor latch access */	port->mem[Dlm] = val>>8;	port->mem[Dll] = val;	port->mem[Lcr] &= ~(1<<7);	uart->baud = baud;	return 0;} static intoxbits(Uart *uart, int bits){	Port *port;	u8int val; 	port = uart->regs; 	val = port->mem[Lcr] & 0x7c;	switch(bits){	case 8:		val |= 0x3;			/* Data length */		break;	case 7:		val |= 0x2;		break;	case 6:		val |= 0x1;		break;	case 5:		break;	default:		return -1;	}	port->mem[Lcr] = val;	uart->bits = bits;	return 0;} static intoxstop(Uart *uart, int stop){	Port *port;	u8int val; 	port = uart->regs; 	val = port->mem[Lcr] & 0x7b;	switch(stop){	case 2:		val |= 1<<2;			/* Number of Stop Bits */		break;	case 1:		break;	default:		return -1;	}	port->mem[Lcr] = val;	uart->stop = stop;	return 0;} static intoxparity(Uart *uart, int parity){	Port *port;	u8int val; 	port = uart->regs; 	val = port->mem[Lcr] & 0x67;	switch(parity){	case 'e':		val |= 1<<4;			/* Even/Odd Parity */	case 'o':		val |= 1<<3;			/* Parity Enable */		break;	case 'n':		break;	default:		return -1;	}	port->mem[Lcr] = val;	uart->parity = parity;	return 0;} static voidoxmodemctl(Uart *uart, int on){	Ctlr *ctlr;	Port *port; 	port = uart->regs;	ctlr = port->ctlr; 	ilock(ctlr);	ilock(&uart->tlock);	if(on){		port->mem[Ier] |= 1<<3;			/* Modem */		uart->cts = port->mem[Msr] & 1<<4;	/* CTS */	}else{		port->mem[Ier] &= ~(1<<3);		uart->cts = 1;	}	uart->modem = on;	iunlock(&uart->tlock);	iunlock(ctlr);} static voidoxrts(Uart *uart, int on){	Port *port; 	port = uart->regs; 	if(on)		port->mem[Mcr] |= 1<<1;		/* RTS */	else		port->mem[Mcr] &= ~(1<<1);	port->rts = on;} static voidoxdtr(Uart *uart, int on){	Port *port; 	port = uart->regs; 	if(on)		port->mem[Mcr] |= 1<<0;		/* DTR */	else		port->mem[Mcr] &= ~(1<<0);	port->dtr = on;} static longoxstatus(Uart *uart, void *buf, long n, long offset){

Lines 1–500 of 592.