963 lines · 19.0 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. */ /* * Avanstar Xp pci 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" #include "uartaxp.i" typedef struct Cc Cc;typedef struct Ccb Ccb;typedef struct Ctlr Ctlr;typedef struct Gcb Gcb; /* * Global Control Block. * Service Request fields must be accessed using XCHG. */struct Gcb {	u16int	gcw;				/* Global Command Word */	u16int	gsw;				/* Global Status Word */	u16int	gsr;				/* Global Service Request */	u16int	abs;				/* Available Buffer Space */	u16int	bt;				/* Board Type */	u16int	cpv;				/* Control Program Version */	u16int	ccbn;				/* Ccb count */	u16int	ccboff;				/* Ccb offset */	u16int	ccbsz;				/* Ccb size */	u16int	gcw2;				/* Global Command Word 2 */	u16int	gsw2;				/* Global Status Word 2 */	u16int	esr;				/* Error Service Request */	u16int	isr;				/* Input Service Request */	u16int	osr;				/* Output Service Request */	u16int	msr;				/* Modem Service Request */	u16int	csr;				/* Command Service Request */}; /* * Channel Control Block. */struct Ccb {	u16int	br;				/* Baud Rate */	u16int	df;				/* Data Format */	u16int	lp;				/* Line Protocol */	u16int	ibs;				/* Input Buffer Size */	u16int	obs;				/* Output Buffer Size */	u16int 	ibtr;				/* Ib Trigger Rate */	u16int	oblw;				/* Ob Low Watermark */	u8int	ixon[2];			/* IXON characters */	u16int	ibhw;				/* Ib High Watermark */	u16int	iblw;				/* Ib Low Watermark */	u16int	cc;				/* Channel Command */	u16int	cs;				/* Channel Status */	u16int	ibsa;				/* Ib Start Addr */	u16int 	ibea;				/* Ib Ending Addr */	u16int	obsa;				/* Ob Start Addr */	u16int 	obea;				/* Ob Ending Addr */	u16int	ibwp;				/* Ib write pointer (RO) */	u16int	ibrp;				/* Ib read pointer (R/W) */	u16int	obwp;				/* Ob write pointer (R/W) */	u16int	obrp;				/* Ob read pointer (RO) */	u16int	ces;				/* Communication Error Status */	u16int	bcp;				/* Bad Character Pointer */	u16int	mc;				/* Modem Control */	u16int	ms;				/* Modem Status */	u16int	bs;				/* Blocking Status */	u16int	crf;				/* Character Received Flag */	u8int	ixoff[2];			/* IXOFF characters */	u16int	cs2;				/* Channel Status 2 */	u8int	sec[2];				/* Strip/Error Characters */}; enum {						/* br */	Br76800		= 0xFF00,	Br115200	= 0xFF01,}; enum {						/* df */	Db5		= 0x0000,		/* Data Bits - 5 bits/byte */	Db6		= 0x0001,		/*	6 bits/byte */	Db7		= 0x0002,		/*	7 bits/byte */	Db8		= 0x0003,		/*	8 bits/byte */	DbMASK		= 0x0003,	Sb1		= 0x0000,		/* 1 Stop Bit */	Sb2		= 0x0004,		/* 2 Stop Bit */	SbMASK		= 0x0004,	Np		= 0x0000,		/* No Parity */	Op		= 0x0008,		/* Odd Parity */	Ep		= 0x0010,		/* Even Parity */	Mp		= 0x0020,		/* Mark Parity */	Sp		= 0x0030,		/* Space Parity */	PMASK		= 0x0038,	Cmn		= 0x0000,		/* Channel Mode Normal */	Cme		= 0x0040,		/* CM Echo */	Cmll		= 0x0080,		/* CM Local Loopback */	Cmrl		= 0x00C0,		/* CM Remote Loopback */}; enum {						/* lp */	Ixon		= 0x0001,		/* Obey IXON/IXOFF */	Ixany		= 0x0002,		/* Any character retarts Tx */	Ixgen		= 0x0004,		/* Generate IXON/IXOFF  */	Cts		= 0x0008,		/* CTS controls Tx */	Dtr		= 0x0010,		/* Rx controls DTR */	½d		= 0x0020,		/* RTS off during Tx */	Rts		= 0x0040,		/* generate RTS */	Emcs		= 0x0080,		/* Enable Modem Control */	Ecs		= 0x1000,		/* Enable Character Stripping */	Eia422		= 0x2000,		/* EIA422 */}; enum {						/* cc */	Ccu		= 0x0001,		/* Configure Channel and UART */	Cco		= 0x0002,		/* Configure Channel Only */	Fib		= 0x0004,		/* Flush Input Buffer */	Fob		= 0x0008,		/* Flush Output Buffer */	Er		= 0x0010,		/* Enable Receiver */	Dr		= 0x0020,		/* Disable Receiver */	Et		= 0x0040,		/* Enable Transmitter */	Dt		= 0x0080,		/* Disable Transmitter */}; enum {						/* ces */	Oe		= 0x0001,		/* Overrun Error */	Pe		= 0x0002,		/* Parity Error */	Fe		= 0x0004,		/* Framing Error */	Br		= 0x0008,		/* Break Received */}; enum {						/* mc */	Adtr		= 0x0001,		/* Assert DTR */	Arts		= 0x0002,		/* Assert RTS */	Ab		= 0x0010,		/* Assert BREAK */}; enum {						/* ms */	Scts		= 0x0001,		/* Status od CTS */	Sdsr		= 0x0002,		/* Status of DSR */	Sri		= 0x0004,		/* Status of RI */	Sdcd		= 0x0008,		/* Status of DCD */}; enum {						/* bs */	Rd		= 0x0001,		/* Receiver Disabled */	Td		= 0x0002,		/* Transmitter Disabled */	Tbxoff		= 0x0004,		/* Tx Blocked by XOFF */	Tbcts		= 0x0008,		/* Tx Blocked by CTS */	Rbxoff		= 0x0010,		/* Rx Blocked by XOFF */	Rbrts		= 0x0020,		/* Rx Blocked by RTS */}; enum {						/* Local Configuration */	Range		= 0x00,	Remap		= 0x04,	Region		= 0x18,	Mb0		= 0x40,			/* Mailbox 0 */	Ldb		= 0x60,			/* PCI to Local Doorbell */	Pdb		= 0x64,			/* Local to PCI Doorbell */	Ics		= 0x68,			/* Interrupt Control/Status */	Mcc		= 0x6C,			/* Misc. Command and Control */}; enum {						/* Mb0 */	Edcc		= 1,			/* exec. downloaded code cmd */	Aic		= 0x10,			/* adapter init'zed correctly */	Cpr		= 1ul << 31,		/* control program ready */}; enum {						/* Mcc */	Rcr		= 1ul << 29,		/* reload config. reg.s */	Asr		= 1ul << 30,		/* pci adapter sw reset */	Lis		= 1ul << 31,		/* local init status */}; typedef struct Cc Cc;typedef struct Ccb Ccb;typedef struct Ctlr Ctlr; /* * Channel Control, one per uart. * Devuart communicates via the PhysUart functions with * a Uart* argument. Uart.regs is filled in by this driver * to point to a Cc, and Cc.ctlr points to the Axp board * controller. */struct Cc {	int	uartno;	Ccb*	ccb;	Ctlr*	ctlr; 	Rendez; 	Uart;}; typedef struct Ctlr {	char*	name;	Pcidev*	pcidev;	int	ctlrno;	Ctlr*	next; 	u32int*	reg;	uchar*	mem;	Gcb*	gcb; 	int	im;		/* interrupt mask */	Cc	cc[16];} Ctlr; #define csr32r(c, r)	(*((c)->reg+((r)/4)))#define csr32w(c, r, v)	(*((c)->reg+((r)/4)) = (v)) static Ctlr* axpctlrhead;static Ctlr* axpctlrtail; extern PhysUart axpphysuart; static intaxpccdone(void* ccb){	return !((Ccb*)ccb)->cc;	/* hw sets ccb->cc to zero */} static voidaxpcc(Cc* cc, int cmd){	Ccb *ccb;	int timeo;	u16int cs; 	ccb = cc->ccb;	ccb->cc = cmd; 	if(!cc->ctlr->im)		for(timeo = 0; timeo < 1000000; timeo++){			if(!ccb->cc)				break;			microdelay(1);		}	else		tsleep(cc, axpccdone, ccb, 1000); 	cs = ccb->cs;	if(ccb->cc || cs){		print("%s: cmd %#ux didn't terminate: %#ux %#ux\n",			cc->name, cmd, ccb->cc, cs);		if(cc->ctlr->im)			error(Eio);	}} static longaxpstatus(Uart* uart, void* buf, long n, long offset){	char *p;	Ccb *ccb;	u16int bs, fstat, ms; 	p = malloc(READSTR);	if(p == nil)		error(Enomem);	ccb = ((Cc*)(uart->regs))->ccb;	bs = ccb->bs;	fstat = ccb->df;	ms = ccb->ms; 	snprint(p, READSTR,		"b%d c%d d%d e%d l%d m%d p%c r%d s%d i%d\n"		"dev(%d) type(%d) framing(%d) overruns(%d) "		"berr(%d) serr(%d)%s%s%s%s\n", 		uart->baud,		uart->hup_dcd,		ms & Sdsr,		uart->hup_dsr,		(fstat & DbMASK) + 5,		0,		(fstat & PMASK) ? ((fstat & Ep) == Ep? 'e': 'o'): 'n',		(bs & Rbrts) ? 1 : 0,		(fstat & Sb2) ? 2 : 1,		0, 		uart->dev,		uart->type,		uart->ferr,		uart->oerr,		uart->berr,		uart->serr,		(ms & Scts) ? " cts"  : "",		(ms & Sdsr) ? " dsr"  : "",		(ms & Sdcd) ? " dcd"  : "",		(ms & Sri) ? " ring" : ""	);	n = readstr(offset, buf, n, p);	free(p); 	return n;} static voidaxpfifo(Uart*, int){} static voidaxpdtr(Uart* uart, int on){	Ccb *ccb;	u16int mc; 	ccb = ((Cc*)(uart->regs))->ccb; 	mc = ccb->mc;	if(on)		mc |= Adtr;	else		mc &= ~Adtr;	ccb->mc = mc;} /* * can be called from uartstageinput() during an input interrupt, * with uart->rlock ilocked or the uart qlocked, sometimes both. */static voidaxprts(Uart* uart, int on){	Ccb *ccb;	u16int mc; 	ccb = ((Cc*)(uart->regs))->ccb; 	mc = ccb->mc;	if(on)		mc |= Arts;	else		mc &= ~Arts;	ccb->mc = mc;} static voidaxpmodemctl(Uart* uart, int on){	Ccb *ccb;	u16int lp; 	ccb = ((Cc*)(uart->regs))->ccb; 	ilock(&uart->tlock);	lp = ccb->lp;	if(on){		lp |= Cts|Rts;		lp &= ~Emcs;		uart->cts = ccb->ms & Scts;	}	else{		lp &= ~(Cts|Rts);		lp |= Emcs;		uart->cts = 1;	}	uart->modem = on;	iunlock(&uart->tlock); 	ccb->lp = lp;	axpcc(uart->regs, Ccu);} static intaxpparity(Uart* uart, int parity){	Ccb *ccb;	u16int df; 	switch(parity){	default:		return -1;	case 'e':		parity = Ep;		break;	case 'o':		parity = Op;		break;	case 'n':		parity = Np;		break;	} 	ccb = ((Cc*)(uart->regs))->ccb; 	df = ccb->df & ~PMASK;	ccb->df = df|parity;	axpcc(uart->regs, Ccu); 	return 0;} static intaxpstop(Uart* uart, int stop){	Ccb *ccb;	u16int df; 	switch(stop){	default:		return -1;	case 1:		stop = Sb1;		break;	case 2:		stop = Sb2;		break;	} 	ccb = ((Cc*)(uart->regs))->ccb; 	df = ccb->df & ~SbMASK;	ccb->df = df|stop;	axpcc(uart->regs, Ccu); 	return 0;} static intaxpbits(Uart* uart, int bits){	Ccb *ccb;	u16int df; 	bits -= 5;	if(bits < 0 || bits > 3)		return -1; 	ccb = ((Cc*)(uart->regs))->ccb; 	df = ccb->df & ~DbMASK;	ccb->df = df|bits;	axpcc(uart->regs, Ccu); 	return 0;} static intaxpbaud(Uart* uart, int baud){	Ccb *ccb;	int i, ibtr; 	/*	 * Set baud rate (high rates are special - only 16 bits).	 */	if(baud <= 0)		return -1;	uart->baud = baud; 	ccb = ((Cc*)(uart->regs))->ccb; 	switch(baud){	default:		ccb->br = baud;		break;	case 76800:		ccb->br = Br76800;		break;	case 115200:		ccb->br = Br115200;		break;	} 	/*	 * Set trigger level to about 50 per second.	 */	ibtr = baud/500;	i = (ccb->ibea - ccb->ibsa)/2;	if(ibtr > i)		ibtr = i;	ccb->ibtr = ibtr;	axpcc(uart->regs, Ccu); 	return 0;} static voidaxpbreak(Uart* uart, int ms){	Ccb *ccb;	u16int mc; 

Lines 1–500 of 963.