xref: /plan9/sys/src/9/pc/uartaxp.c (revision aa72973a2891ccbd3fb042462446761159389e19)
14df489c4SDavid du Colombier /*
24df489c4SDavid du Colombier  * Avanstar Xp pci uart driver
34df489c4SDavid du Colombier  */
44df489c4SDavid du Colombier #include "u.h"
54df489c4SDavid du Colombier #include "../port/lib.h"
64df489c4SDavid du Colombier #include "mem.h"
74df489c4SDavid du Colombier #include "dat.h"
84df489c4SDavid du Colombier #include "fns.h"
94df489c4SDavid du Colombier #include "io.h"
104df489c4SDavid du Colombier #include "../port/error.h"
114df489c4SDavid du Colombier 
124df489c4SDavid du Colombier #include "uartaxp.i"
134df489c4SDavid du Colombier 
144df489c4SDavid du Colombier typedef struct Cc Cc;
154df489c4SDavid du Colombier typedef struct Ccb Ccb;
164df489c4SDavid du Colombier typedef struct Ctlr Ctlr;
174df489c4SDavid du Colombier typedef struct Gcb Gcb;
184df489c4SDavid du Colombier 
194df489c4SDavid du Colombier /*
204df489c4SDavid du Colombier  * Global Control Block.
214df489c4SDavid du Colombier  * Service Request fields must be accessed using XCHG.
224df489c4SDavid du Colombier  */
234df489c4SDavid du Colombier struct Gcb {
244df489c4SDavid du Colombier 	u16int	gcw;				/* Global Command Word */
254df489c4SDavid du Colombier 	u16int	gsw;				/* Global Status Word */
264df489c4SDavid du Colombier 	u16int	gsr;				/* Global Service Request */
274df489c4SDavid du Colombier 	u16int	abs;				/* Available Buffer Space */
284df489c4SDavid du Colombier 	u16int	bt;				/* Board Type */
294df489c4SDavid du Colombier 	u16int	cpv;				/* Control Program Version */
304df489c4SDavid du Colombier 	u16int	ccbn;				/* Ccb count */
314df489c4SDavid du Colombier 	u16int	ccboff;				/* Ccb offset */
324df489c4SDavid du Colombier 	u16int	ccbsz;				/* Ccb size */
334df489c4SDavid du Colombier 	u16int	gcw2;				/* Global Command Word 2 */
344df489c4SDavid du Colombier 	u16int	gsw2;				/* Global Status Word 2 */
354df489c4SDavid du Colombier 	u16int	esr;				/* Error Service Request */
364df489c4SDavid du Colombier 	u16int	isr;				/* Input Service Request */
374df489c4SDavid du Colombier 	u16int	osr;				/* Output Service Request */
384df489c4SDavid du Colombier 	u16int	msr;				/* Modem Service Request */
394df489c4SDavid du Colombier 	u16int	csr;				/* Command Service Request */
404df489c4SDavid du Colombier };
414df489c4SDavid du Colombier 
424df489c4SDavid du Colombier /*
434df489c4SDavid du Colombier  * Channel Control Block.
444df489c4SDavid du Colombier  */
454df489c4SDavid du Colombier struct Ccb {
464df489c4SDavid du Colombier 	u16int	br;				/* Baud Rate */
474df489c4SDavid du Colombier 	u16int	df;				/* Data Format */
484df489c4SDavid du Colombier 	u16int	lp;				/* Line Protocol */
494df489c4SDavid du Colombier 	u16int	ibs;				/* Input Buffer Size */
504df489c4SDavid du Colombier 	u16int	obs;				/* Output Buffer Size */
514df489c4SDavid du Colombier 	u16int 	ibtr;				/* Ib Trigger Rate */
524df489c4SDavid du Colombier 	u16int	oblw;				/* Ob Low Watermark */
534df489c4SDavid du Colombier 	u8int	ixon[2];			/* IXON characters */
544df489c4SDavid du Colombier 	u16int	ibhw;				/* Ib High Watermark */
554df489c4SDavid du Colombier 	u16int	iblw;				/* Ib Low Watermark */
564df489c4SDavid du Colombier 	u16int	cc;				/* Channel Command */
574df489c4SDavid du Colombier 	u16int	cs;				/* Channel Status */
584df489c4SDavid du Colombier 	u16int	ibsa;				/* Ib Start Addr */
594df489c4SDavid du Colombier 	u16int 	ibea;				/* Ib Ending Addr */
604df489c4SDavid du Colombier 	u16int	obsa;				/* Ob Start Addr */
614df489c4SDavid du Colombier 	u16int 	obea;				/* Ob Ending Addr */
624df489c4SDavid du Colombier 	u16int	ibwp;				/* Ib write pointer (RO) */
634df489c4SDavid du Colombier 	u16int	ibrp;				/* Ib read pointer (R/W) */
644df489c4SDavid du Colombier 	u16int	obwp;				/* Ob write pointer (R/W) */
654df489c4SDavid du Colombier 	u16int	obrp;				/* Ob read pointer (RO) */
664df489c4SDavid du Colombier 	u16int	ces;				/* Communication Error Status */
674df489c4SDavid du Colombier 	u16int	bcp;				/* Bad Character Pointer */
684df489c4SDavid du Colombier 	u16int	mc;				/* Modem Control */
694df489c4SDavid du Colombier 	u16int	ms;				/* Modem Status */
704df489c4SDavid du Colombier 	u16int	bs;				/* Blocking Status */
714df489c4SDavid du Colombier 	u16int	crf;				/* Character Received Flag */
724df489c4SDavid du Colombier 	u8int	ixoff[2];			/* IXOFF characters */
734df489c4SDavid du Colombier 	u16int	cs2;				/* Channel Status 2 */
744df489c4SDavid du Colombier 	u8int	sec[2];				/* Strip/Error Characters */
754df489c4SDavid du Colombier };
764df489c4SDavid du Colombier 
774df489c4SDavid du Colombier enum {						/* br */
784df489c4SDavid du Colombier 	Br76800		= 0xFF00,
794df489c4SDavid du Colombier 	Br115200	= 0xFF01,
804df489c4SDavid du Colombier };
814df489c4SDavid du Colombier 
824df489c4SDavid du Colombier enum {						/* df */
834df489c4SDavid du Colombier 	Db5		= 0x0000,		/* Data Bits - 5 bits/byte */
844df489c4SDavid du Colombier 	Db6		= 0x0001,		/*	6 bits/byte */
854df489c4SDavid du Colombier 	Db7		= 0x0002,		/*	7 bits/byte */
864df489c4SDavid du Colombier 	Db8		= 0x0003,		/*	8 bits/byte */
874df489c4SDavid du Colombier 	DbMASK		= 0x0003,
884df489c4SDavid du Colombier 	Sb1		= 0x0000,		/* 1 Stop Bit */
894df489c4SDavid du Colombier 	Sb2		= 0x0004,		/* 2 Stop Bit */
904df489c4SDavid du Colombier 	SbMASK		= 0x0004,
914df489c4SDavid du Colombier 	Np		= 0x0000,		/* No Parity */
924df489c4SDavid du Colombier 	Op		= 0x0008,		/* Odd Parity */
934df489c4SDavid du Colombier 	Ep		= 0x0010,		/* Even Parity */
944df489c4SDavid du Colombier 	Mp		= 0x0020,		/* Mark Parity */
954df489c4SDavid du Colombier 	Sp		= 0x0030,		/* Space Parity */
964df489c4SDavid du Colombier 	PMASK		= 0x0038,
974df489c4SDavid du Colombier 	Cmn		= 0x0000,		/* Channel Mode Normal */
984df489c4SDavid du Colombier 	Cme		= 0x0040,		/* CM Echo */
994df489c4SDavid du Colombier 	Cmll		= 0x0080,		/* CM Local Loopback */
1004df489c4SDavid du Colombier 	Cmrl		= 0x00C0,		/* CM Remote Loopback */
1014df489c4SDavid du Colombier };
1024df489c4SDavid du Colombier 
1034df489c4SDavid du Colombier enum {						/* lp */
1044df489c4SDavid du Colombier 	Ixon		= 0x0001,		/* Obey IXON/IXOFF */
1054df489c4SDavid du Colombier 	Ixany		= 0x0002,		/* Any character retarts Tx */
1064df489c4SDavid du Colombier 	Ixgen		= 0x0004,		/* Generate IXON/IXOFF  */
1074df489c4SDavid du Colombier 	Cts		= 0x0008,		/* CTS controls Tx */
1084df489c4SDavid du Colombier 	Dtr		= 0x0010,		/* Rx controls DTR */
1094df489c4SDavid du Colombier 	½d		= 0x0020,		/* RTS off during Tx */
1104df489c4SDavid du Colombier 	Rts		= 0x0040,		/* generate RTS */
1114df489c4SDavid du Colombier 	Emcs		= 0x0080,		/* Enable Modem Control */
1124df489c4SDavid du Colombier 	Ecs		= 0x1000,		/* Enable Character Stripping */
1134df489c4SDavid du Colombier 	Eia422		= 0x2000,		/* EIA422 */
1144df489c4SDavid du Colombier };
1154df489c4SDavid du Colombier 
1164df489c4SDavid du Colombier enum {						/* cc */
1174df489c4SDavid du Colombier 	Ccu		= 0x0001,		/* Configure Channel and UART */
1184df489c4SDavid du Colombier 	Cco		= 0x0002,		/* Configure Channel Only */
1194df489c4SDavid du Colombier 	Fib		= 0x0004,		/* Flush Input Buffer */
1204df489c4SDavid du Colombier 	Fob		= 0x0008,		/* Flush Output Buffer */
1214df489c4SDavid du Colombier 	Er		= 0x0010,		/* Enable Receiver */
1224df489c4SDavid du Colombier 	Dr		= 0x0020,		/* Disable Receiver */
1234df489c4SDavid du Colombier 	Et		= 0x0040,		/* Enable Transmitter */
1244df489c4SDavid du Colombier 	Dt		= 0x0080,		/* Disable Transmitter */
1254df489c4SDavid du Colombier };
1264df489c4SDavid du Colombier 
1274df489c4SDavid du Colombier enum {						/* ces */
1284df489c4SDavid du Colombier 	Oe		= 0x0001,		/* Overrun Error */
1294df489c4SDavid du Colombier 	Pe		= 0x0002,		/* Parity Error */
1304df489c4SDavid du Colombier 	Fe		= 0x0004,		/* Framing Error */
1314df489c4SDavid du Colombier 	Br		= 0x0008,		/* Break Received */
1324df489c4SDavid du Colombier };
1334df489c4SDavid du Colombier 
1344df489c4SDavid du Colombier enum {						/* mc */
1354df489c4SDavid du Colombier 	Adtr		= 0x0001,		/* Assert DTR */
1364df489c4SDavid du Colombier 	Arts		= 0x0002,		/* Assert RTS */
1374df489c4SDavid du Colombier 	Ab		= 0x0010,		/* Assert BREAK */
1384df489c4SDavid du Colombier };
1394df489c4SDavid du Colombier 
1404df489c4SDavid du Colombier enum {						/* ms */
1414df489c4SDavid du Colombier 	Scts		= 0x0001,		/* Status od CTS */
1424df489c4SDavid du Colombier 	Sdsr		= 0x0002,		/* Status of DSR */
1434df489c4SDavid du Colombier 	Sri		= 0x0004,		/* Status of RI */
1444df489c4SDavid du Colombier 	Sdcd		= 0x0008,		/* Status of DCD */
1454df489c4SDavid du Colombier };
1464df489c4SDavid du Colombier 
1474df489c4SDavid du Colombier enum {						/* bs */
1484df489c4SDavid du Colombier 	Rd		= 0x0001,		/* Receiver Disabled */
1494df489c4SDavid du Colombier 	Td		= 0x0002,		/* Transmitter Disabled */
1504df489c4SDavid du Colombier 	Tbxoff		= 0x0004,		/* Tx Blocked by XOFF */
1514df489c4SDavid du Colombier 	Tbcts		= 0x0008,		/* Tx Blocked by CTS */
1524df489c4SDavid du Colombier 	Rbxoff		= 0x0010,		/* Rx Blocked by XOFF */
1534df489c4SDavid du Colombier 	Rbrts		= 0x0020,		/* Rx Blocked by RTS */
1544df489c4SDavid du Colombier };
1554df489c4SDavid du Colombier 
1564df489c4SDavid du Colombier enum {						/* Local Configuration */
1574df489c4SDavid du Colombier 	Range		= 0x00,
1584df489c4SDavid du Colombier 	Remap		= 0x04,
1594df489c4SDavid du Colombier 	Region		= 0x18,
1604df489c4SDavid du Colombier 	Mb0		= 0x40,			/* Mailbox 0 */
1614df489c4SDavid du Colombier 	Ldb		= 0x60,			/* PCI to Local Doorbell */
1624df489c4SDavid du Colombier 	Pdb		= 0x64,			/* Local to PCI Doorbell */
1634df489c4SDavid du Colombier 	Ics		= 0x68,			/* Interrupt Control/Status */
1644df489c4SDavid du Colombier 	Mcc		= 0x6C,			/* Misc. Command and Control */
1654df489c4SDavid du Colombier };
1664df489c4SDavid du Colombier 
1674df489c4SDavid du Colombier enum {						/* Mb0 */
1684df489c4SDavid du Colombier 	Edcc		= 1,			/* exec. downloaded code cmd */
1694df489c4SDavid du Colombier 	Aic		= 0x10,			/* adapter init'zed correctly */
1704df489c4SDavid du Colombier 	Cpr		= 1ul << 31,		/* control program ready */
1714df489c4SDavid du Colombier };
1724df489c4SDavid du Colombier 
1734df489c4SDavid du Colombier enum {						/* Mcc */
1744df489c4SDavid du Colombier 	Rcr		= 1ul << 29,		/* reload config. reg.s */
1754df489c4SDavid du Colombier 	Asr		= 1ul << 30,		/* pci adapter sw reset */
1764df489c4SDavid du Colombier 	Lis		= 1ul << 31,		/* local init status */
1774df489c4SDavid du Colombier };
1784df489c4SDavid du Colombier 
1794df489c4SDavid du Colombier typedef struct Cc Cc;
1804df489c4SDavid du Colombier typedef struct Ccb Ccb;
1814df489c4SDavid du Colombier typedef struct Ctlr Ctlr;
1824df489c4SDavid du Colombier 
1834df489c4SDavid du Colombier /*
1844df489c4SDavid du Colombier  * Channel Control, one per uart.
1854df489c4SDavid du Colombier  * Devuart communicates via the PhysUart functions with
1864df489c4SDavid du Colombier  * a Uart* argument. Uart.regs is filled in by this driver
1874df489c4SDavid du Colombier  * to point to a Cc, and Cc.ctlr points to the Axp board
1884df489c4SDavid du Colombier  * controller.
1894df489c4SDavid du Colombier  */
1904df489c4SDavid du Colombier struct Cc {
1914df489c4SDavid du Colombier 	int	uartno;
1924df489c4SDavid du Colombier 	Ccb*	ccb;
1934df489c4SDavid du Colombier 	Ctlr*	ctlr;
1944df489c4SDavid du Colombier 
1954df489c4SDavid du Colombier 	Rendez;
1964df489c4SDavid du Colombier 
1974df489c4SDavid du Colombier 	Uart;
1984df489c4SDavid du Colombier };
1994df489c4SDavid du Colombier 
2004df489c4SDavid du Colombier typedef struct Ctlr {
2014df489c4SDavid du Colombier 	char*	name;
2024df489c4SDavid du Colombier 	Pcidev*	pcidev;
2034df489c4SDavid du Colombier 	int	ctlrno;
2044df489c4SDavid du Colombier 	Ctlr*	next;
2054df489c4SDavid du Colombier 
2064df489c4SDavid du Colombier 	u32int*	reg;
2074df489c4SDavid du Colombier 	uchar*	mem;
2084df489c4SDavid du Colombier 	Gcb*	gcb;
2094df489c4SDavid du Colombier 
210c65c114fSDavid du Colombier 	int	im;		/* interrupt mask */
2114df489c4SDavid du Colombier 	Cc	cc[16];
2124df489c4SDavid du Colombier } Ctlr;
2134df489c4SDavid du Colombier 
2144df489c4SDavid du Colombier #define csr32r(c, r)	(*((c)->reg+((r)/4)))
2154df489c4SDavid du Colombier #define csr32w(c, r, v)	(*((c)->reg+((r)/4)) = (v))
2164df489c4SDavid du Colombier 
2174df489c4SDavid du Colombier static Ctlr* axpctlrhead;
2184df489c4SDavid du Colombier static Ctlr* axpctlrtail;
2194df489c4SDavid du Colombier 
2204df489c4SDavid du Colombier extern PhysUart axpphysuart;
2214df489c4SDavid du Colombier 
2224df489c4SDavid du Colombier static int
axpccdone(void * ccb)2234df489c4SDavid du Colombier axpccdone(void* ccb)
2244df489c4SDavid du Colombier {
225c65c114fSDavid du Colombier 	return !((Ccb*)ccb)->cc;	/* hw sets ccb->cc to zero */
2264df489c4SDavid du Colombier }
2274df489c4SDavid du Colombier 
2284df489c4SDavid du Colombier static void
axpcc(Cc * cc,int cmd)2294df489c4SDavid du Colombier axpcc(Cc* cc, int cmd)
2304df489c4SDavid du Colombier {
2314df489c4SDavid du Colombier 	Ccb *ccb;
2324df489c4SDavid du Colombier 	int timeo;
2334df489c4SDavid du Colombier 	u16int cs;
2344df489c4SDavid du Colombier 
2354df489c4SDavid du Colombier 	ccb = cc->ccb;
2364df489c4SDavid du Colombier 	ccb->cc = cmd;
2374df489c4SDavid du Colombier 
238c65c114fSDavid du Colombier 	if(!cc->ctlr->im)
2394df489c4SDavid du Colombier 		for(timeo = 0; timeo < 1000000; timeo++){
2404df489c4SDavid du Colombier 			if(!ccb->cc)
2414df489c4SDavid du Colombier 				break;
2424df489c4SDavid du Colombier 			microdelay(1);
2434df489c4SDavid du Colombier 		}
2444df489c4SDavid du Colombier 	else
2454df489c4SDavid du Colombier 		tsleep(cc, axpccdone, ccb, 1000);
2464df489c4SDavid du Colombier 
2474df489c4SDavid du Colombier 	cs = ccb->cs;
2484df489c4SDavid du Colombier 	if(ccb->cc || cs){
2494df489c4SDavid du Colombier 		print("%s: cmd %#ux didn't terminate: %#ux %#ux\n",
2504df489c4SDavid du Colombier 			cc->name, cmd, ccb->cc, cs);
2514df489c4SDavid du Colombier 		if(cc->ctlr->im)
2524df489c4SDavid du Colombier 			error(Eio);
2534df489c4SDavid du Colombier 	}
2544df489c4SDavid du Colombier }
2554df489c4SDavid du Colombier 
2564df489c4SDavid du Colombier static long
axpstatus(Uart * uart,void * buf,long n,long offset)2574df489c4SDavid du Colombier axpstatus(Uart* uart, void* buf, long n, long offset)
2584df489c4SDavid du Colombier {
2594df489c4SDavid du Colombier 	char *p;
2604df489c4SDavid du Colombier 	Ccb *ccb;
2614df489c4SDavid du Colombier 	u16int bs, fstat, ms;
2624df489c4SDavid du Colombier 
2634df489c4SDavid du Colombier 	p = malloc(READSTR);
264*aa72973aSDavid du Colombier 	if(p == nil)
265*aa72973aSDavid du Colombier 		error(Enomem);
266*aa72973aSDavid du Colombier 	ccb = ((Cc*)(uart->regs))->ccb;
2674df489c4SDavid du Colombier 	bs = ccb->bs;
2684df489c4SDavid du Colombier 	fstat = ccb->df;
2694df489c4SDavid du Colombier 	ms = ccb->ms;
2704df489c4SDavid du Colombier 
2714df489c4SDavid du Colombier 	snprint(p, READSTR,
2724df489c4SDavid du Colombier 		"b%d c%d d%d e%d l%d m%d p%c r%d s%d i%d\n"
2734df489c4SDavid du Colombier 		"dev(%d) type(%d) framing(%d) overruns(%d) "
2744df489c4SDavid du Colombier 		"berr(%d) serr(%d)%s%s%s%s\n",
2754df489c4SDavid du Colombier 
2764df489c4SDavid du Colombier 		uart->baud,
2774df489c4SDavid du Colombier 		uart->hup_dcd,
2784df489c4SDavid du Colombier 		ms & Sdsr,
2794df489c4SDavid du Colombier 		uart->hup_dsr,
2804df489c4SDavid du Colombier 		(fstat & DbMASK) + 5,
2814df489c4SDavid du Colombier 		0,
2824df489c4SDavid du Colombier 		(fstat & PMASK) ? ((fstat & Ep) == Ep? 'e': 'o'): 'n',
2834df489c4SDavid du Colombier 		(bs & Rbrts) ? 1 : 0,
2844df489c4SDavid du Colombier 		(fstat & Sb2) ? 2 : 1,
2854df489c4SDavid du Colombier 		0,
2864df489c4SDavid du Colombier 
2874df489c4SDavid du Colombier 		uart->dev,
2884df489c4SDavid du Colombier 		uart->type,
2894df489c4SDavid du Colombier 		uart->ferr,
2904df489c4SDavid du Colombier 		uart->oerr,
2914df489c4SDavid du Colombier 		uart->berr,
2924df489c4SDavid du Colombier 		uart->serr,
2934df489c4SDavid du Colombier 		(ms & Scts) ? " cts"  : "",
2944df489c4SDavid du Colombier 		(ms & Sdsr) ? " dsr"  : "",
2954df489c4SDavid du Colombier 		(ms & Sdcd) ? " dcd"  : "",
2964df489c4SDavid du Colombier 		(ms & Sri) ? " ring" : ""
2974df489c4SDavid du Colombier 	);
2984df489c4SDavid du Colombier 	n = readstr(offset, buf, n, p);
2994df489c4SDavid du Colombier 	free(p);
3004df489c4SDavid du Colombier 
3014df489c4SDavid du Colombier 	return n;
3024df489c4SDavid du Colombier }
3034df489c4SDavid du Colombier 
3044df489c4SDavid du Colombier static void
axpfifo(Uart *,int)3054df489c4SDavid du Colombier axpfifo(Uart*, int)
3064df489c4SDavid du Colombier {
3074df489c4SDavid du Colombier }
3084df489c4SDavid du Colombier 
3094df489c4SDavid du Colombier static void
axpdtr(Uart * uart,int on)3104df489c4SDavid du Colombier axpdtr(Uart* uart, int on)
3114df489c4SDavid du Colombier {
3124df489c4SDavid du Colombier 	Ccb *ccb;
3134df489c4SDavid du Colombier 	u16int mc;
3144df489c4SDavid du Colombier 
3154df489c4SDavid du Colombier 	ccb = ((Cc*)(uart->regs))->ccb;
3164df489c4SDavid du Colombier 
3174df489c4SDavid du Colombier 	mc = ccb->mc;
3184df489c4SDavid du Colombier 	if(on)
3194df489c4SDavid du Colombier 		mc |= Adtr;
3204df489c4SDavid du Colombier 	else
3214df489c4SDavid du Colombier 		mc &= ~Adtr;
3224df489c4SDavid du Colombier 	ccb->mc = mc;
3234df489c4SDavid du Colombier }
3244df489c4SDavid du Colombier 
325c65c114fSDavid du Colombier /*
326c65c114fSDavid du Colombier  * can be called from uartstageinput() during an input interrupt,
327c65c114fSDavid du Colombier  * with uart->rlock ilocked or the uart qlocked, sometimes both.
328c65c114fSDavid du Colombier  */
3294df489c4SDavid du Colombier static void
axprts(Uart * uart,int on)3304df489c4SDavid du Colombier axprts(Uart* uart, int on)
3314df489c4SDavid du Colombier {
3324df489c4SDavid du Colombier 	Ccb *ccb;
3334df489c4SDavid du Colombier 	u16int mc;
3344df489c4SDavid du Colombier 
3354df489c4SDavid du Colombier 	ccb = ((Cc*)(uart->regs))->ccb;
3364df489c4SDavid du Colombier 
3374df489c4SDavid du Colombier 	mc = ccb->mc;
3384df489c4SDavid du Colombier 	if(on)
3394df489c4SDavid du Colombier 		mc |= Arts;
3404df489c4SDavid du Colombier 	else
3414df489c4SDavid du Colombier 		mc &= ~Arts;
3424df489c4SDavid du Colombier 	ccb->mc = mc;
3434df489c4SDavid du Colombier }
3444df489c4SDavid du Colombier 
3454df489c4SDavid du Colombier static void
axpmodemctl(Uart * uart,int on)3464df489c4SDavid du Colombier axpmodemctl(Uart* uart, int on)
3474df489c4SDavid du Colombier {
3484df489c4SDavid du Colombier 	Ccb *ccb;
3494df489c4SDavid du Colombier 	u16int lp;
3504df489c4SDavid du Colombier 
3514df489c4SDavid du Colombier 	ccb = ((Cc*)(uart->regs))->ccb;
3524df489c4SDavid du Colombier 
3534df489c4SDavid du Colombier 	ilock(&uart->tlock);
3544df489c4SDavid du Colombier 	lp = ccb->lp;
3554df489c4SDavid du Colombier 	if(on){
3564df489c4SDavid du Colombier 		lp |= Cts|Rts;
3574df489c4SDavid du Colombier 		lp &= ~Emcs;
3584df489c4SDavid du Colombier 		uart->cts = ccb->ms & Scts;
3594df489c4SDavid du Colombier 	}
3604df489c4SDavid du Colombier 	else{
3614df489c4SDavid du Colombier 		lp &= ~(Cts|Rts);
3624df489c4SDavid du Colombier 		lp |= Emcs;
3634df489c4SDavid du Colombier 		uart->cts = 1;
3644df489c4SDavid du Colombier 	}
3654df489c4SDavid du Colombier 	uart->modem = on;
3664df489c4SDavid du Colombier 	iunlock(&uart->tlock);
3674df489c4SDavid du Colombier 
3684df489c4SDavid du Colombier 	ccb->lp = lp;
3694df489c4SDavid du Colombier 	axpcc(uart->regs, Ccu);
3704df489c4SDavid du Colombier }
3714df489c4SDavid du Colombier 
3724df489c4SDavid du Colombier static int
axpparity(Uart * uart,int parity)3734df489c4SDavid du Colombier axpparity(Uart* uart, int parity)
3744df489c4SDavid du Colombier {
3754df489c4SDavid du Colombier 	Ccb *ccb;
3764df489c4SDavid du Colombier 	u16int df;
3774df489c4SDavid du Colombier 
3784df489c4SDavid du Colombier 	switch(parity){
3794df489c4SDavid du Colombier 	default:
3804df489c4SDavid du Colombier 		return -1;
3814df489c4SDavid du Colombier 	case 'e':
3824df489c4SDavid du Colombier 		parity = Ep;
3834df489c4SDavid du Colombier 		break;
3844df489c4SDavid du Colombier 	case 'o':
3854df489c4SDavid du Colombier 		parity = Op;
3864df489c4SDavid du Colombier 		break;
3874df489c4SDavid du Colombier 	case 'n':
3884df489c4SDavid du Colombier 		parity = Np;
3894df489c4SDavid du Colombier 		break;
3904df489c4SDavid du Colombier 	}
3914df489c4SDavid du Colombier 
3924df489c4SDavid du Colombier 	ccb = ((Cc*)(uart->regs))->ccb;
3934df489c4SDavid du Colombier 
3944df489c4SDavid du Colombier 	df = ccb->df & ~PMASK;
3954df489c4SDavid du Colombier 	ccb->df = df|parity;
3964df489c4SDavid du Colombier 	axpcc(uart->regs, Ccu);
3974df489c4SDavid du Colombier 
3984df489c4SDavid du Colombier 	return 0;
3994df489c4SDavid du Colombier }
4004df489c4SDavid du Colombier 
4014df489c4SDavid du Colombier static int
axpstop(Uart * uart,int stop)4024df489c4SDavid du Colombier axpstop(Uart* uart, int stop)
4034df489c4SDavid du Colombier {
4044df489c4SDavid du Colombier 	Ccb *ccb;
4054df489c4SDavid du Colombier 	u16int df;
4064df489c4SDavid du Colombier 
4074df489c4SDavid du Colombier 	switch(stop){
4084df489c4SDavid du Colombier 	default:
4094df489c4SDavid du Colombier 		return -1;
4104df489c4SDavid du Colombier 	case 1:
4114df489c4SDavid du Colombier 		stop = Sb1;
4124df489c4SDavid du Colombier 		break;
4134df489c4SDavid du Colombier 	case 2:
4144df489c4SDavid du Colombier 		stop = Sb2;
4154df489c4SDavid du Colombier 		break;
4164df489c4SDavid du Colombier 	}
4174df489c4SDavid du Colombier 
4184df489c4SDavid du Colombier 	ccb = ((Cc*)(uart->regs))->ccb;
4194df489c4SDavid du Colombier 
4204df489c4SDavid du Colombier 	df = ccb->df & ~SbMASK;
4214df489c4SDavid du Colombier 	ccb->df = df|stop;
4224df489c4SDavid du Colombier 	axpcc(uart->regs, Ccu);
4234df489c4SDavid du Colombier 
4244df489c4SDavid du Colombier 	return 0;
4254df489c4SDavid du Colombier }
4264df489c4SDavid du Colombier 
4274df489c4SDavid du Colombier static int
axpbits(Uart * uart,int bits)4284df489c4SDavid du Colombier axpbits(Uart* uart, int bits)
4294df489c4SDavid du Colombier {
4304df489c4SDavid du Colombier 	Ccb *ccb;
4314df489c4SDavid du Colombier 	u16int df;
4324df489c4SDavid du Colombier 
4334df489c4SDavid du Colombier 	bits -= 5;
4344df489c4SDavid du Colombier 	if(bits < 0 || bits > 3)
4354df489c4SDavid du Colombier 		return -1;
4364df489c4SDavid du Colombier 
4374df489c4SDavid du Colombier 	ccb = ((Cc*)(uart->regs))->ccb;
4384df489c4SDavid du Colombier 
4394df489c4SDavid du Colombier 	df = ccb->df & ~DbMASK;
4404df489c4SDavid du Colombier 	ccb->df = df|bits;
4414df489c4SDavid du Colombier 	axpcc(uart->regs, Ccu);
4424df489c4SDavid du Colombier 
4434df489c4SDavid du Colombier 	return 0;
4444df489c4SDavid du Colombier }
4454df489c4SDavid du Colombier 
4464df489c4SDavid du Colombier static int
axpbaud(Uart * uart,int baud)4474df489c4SDavid du Colombier axpbaud(Uart* uart, int baud)
4484df489c4SDavid du Colombier {
4494df489c4SDavid du Colombier 	Ccb *ccb;
4504df489c4SDavid du Colombier 	int i, ibtr;
4514df489c4SDavid du Colombier 
4524df489c4SDavid du Colombier 	/*
4534df489c4SDavid du Colombier 	 * Set baud rate (high rates are special - only 16 bits).
4544df489c4SDavid du Colombier 	 */
4554df489c4SDavid du Colombier 	if(baud <= 0)
4564df489c4SDavid du Colombier 		return -1;
4574df489c4SDavid du Colombier 	uart->baud = baud;
4584df489c4SDavid du Colombier 
4594df489c4SDavid du Colombier 	ccb = ((Cc*)(uart->regs))->ccb;
4604df489c4SDavid du Colombier 
4614df489c4SDavid du Colombier 	switch(baud){
4624df489c4SDavid du Colombier 	default:
4634df489c4SDavid du Colombier 		ccb->br = baud;
4644df489c4SDavid du Colombier 		break;
4654df489c4SDavid du Colombier 	case 76800:
4664df489c4SDavid du Colombier 		ccb->br = Br76800;
4674df489c4SDavid du Colombier 		break;
4684df489c4SDavid du Colombier 	case 115200:
4694df489c4SDavid du Colombier 		ccb->br = Br115200;
4704df489c4SDavid du Colombier 		break;
4714df489c4SDavid du Colombier 	}
4724df489c4SDavid du Colombier 
4734df489c4SDavid du Colombier 	/*
4744df489c4SDavid du Colombier 	 * Set trigger level to about 50 per second.
4754df489c4SDavid du Colombier 	 */
4764df489c4SDavid du Colombier 	ibtr = baud/500;
4774df489c4SDavid du Colombier 	i = (ccb->ibea - ccb->ibsa)/2;
4784df489c4SDavid du Colombier 	if(ibtr > i)
4794df489c4SDavid du Colombier 		ibtr = i;
4804df489c4SDavid du Colombier 	ccb->ibtr = ibtr;
4814df489c4SDavid du Colombier 	axpcc(uart->regs, Ccu);
4824df489c4SDavid du Colombier 
4834df489c4SDavid du Colombier 	return 0;
4844df489c4SDavid du Colombier }
4854df489c4SDavid du Colombier 
4864df489c4SDavid du Colombier static void
axpbreak(Uart * uart,int ms)4874df489c4SDavid du Colombier axpbreak(Uart* uart, int ms)
4884df489c4SDavid du Colombier {
4894df489c4SDavid du Colombier 	Ccb *ccb;
4904df489c4SDavid du Colombier 	u16int mc;
4914df489c4SDavid du Colombier 
4924df489c4SDavid du Colombier 	/*
4934df489c4SDavid du Colombier 	 * Send a break.
4944df489c4SDavid du Colombier 	 */
4954df489c4SDavid du Colombier 	if(ms <= 0)
4964df489c4SDavid du Colombier 		ms = 200;
4974df489c4SDavid du Colombier 
4984df489c4SDavid du Colombier 	ccb = ((Cc*)(uart->regs))->ccb;
4994df489c4SDavid du Colombier 
5004df489c4SDavid du Colombier 	mc = ccb->mc;
5014df489c4SDavid du Colombier 	ccb->mc = Ab|mc;
5024df489c4SDavid du Colombier 	tsleep(&up->sleep, return0, 0, ms);
5034df489c4SDavid du Colombier 	ccb->mc = mc & ~Ab;
5044df489c4SDavid du Colombier }
5054df489c4SDavid du Colombier 
506c65c114fSDavid du Colombier /* only called from interrupt service */
5074df489c4SDavid du Colombier static void
axpmc(Cc * cc)5084df489c4SDavid du Colombier axpmc(Cc* cc)
5094df489c4SDavid du Colombier {
5104df489c4SDavid du Colombier 	int old;
5114df489c4SDavid du Colombier 	Ccb *ccb;
5124df489c4SDavid du Colombier 	u16int ms;
5134df489c4SDavid du Colombier 
5144df489c4SDavid du Colombier 	ccb = cc->ccb;
5154df489c4SDavid du Colombier 
5164df489c4SDavid du Colombier 	ms = ccb->ms;
5174df489c4SDavid du Colombier 
5184df489c4SDavid du Colombier 	if(ms & Scts){
5194df489c4SDavid du Colombier 		ilock(&cc->tlock);
5204df489c4SDavid du Colombier 		old = cc->cts;
5214df489c4SDavid du Colombier 		cc->cts = ms & Scts;
5224df489c4SDavid du Colombier 		if(old == 0 && cc->cts)
5234df489c4SDavid du Colombier 			cc->ctsbackoff = 2;
5244df489c4SDavid du Colombier 		iunlock(&cc->tlock);
5254df489c4SDavid du Colombier 	}
5264df489c4SDavid du Colombier 	if(ms & Sdsr){
5274df489c4SDavid du Colombier 		old = ms & Sdsr;
5284df489c4SDavid du Colombier 		if(cc->hup_dsr && cc->dsr && !old)
5294df489c4SDavid du Colombier 			cc->dohup = 1;
5304df489c4SDavid du Colombier 		cc->dsr = old;
5314df489c4SDavid du Colombier 	}
5324df489c4SDavid du Colombier 	if(ms & Sdcd){
5334df489c4SDavid du Colombier 		old = ms & Sdcd;
5344df489c4SDavid du Colombier 		if(cc->hup_dcd && cc->dcd && !old)
5354df489c4SDavid du Colombier 			cc->dohup = 1;
5364df489c4SDavid du Colombier 		cc->dcd = old;
5374df489c4SDavid du Colombier 	}
5384df489c4SDavid du Colombier }
5394df489c4SDavid du Colombier 
540c65c114fSDavid du Colombier /* called from uartkick() with uart->tlock ilocked */
5414df489c4SDavid du Colombier static void
axpkick(Uart * uart)5424df489c4SDavid du Colombier axpkick(Uart* uart)
5434df489c4SDavid du Colombier {
5444df489c4SDavid du Colombier 	Cc *cc;
5454df489c4SDavid du Colombier 	Ccb *ccb;
546c65c114fSDavid du Colombier 	uchar *ep, *mem, *rp, *wp, *bp;
5474df489c4SDavid du Colombier 
5484df489c4SDavid du Colombier 	if(uart->cts == 0 || uart->blocked)
5494df489c4SDavid du Colombier 		return;
5504df489c4SDavid du Colombier 
5514df489c4SDavid du Colombier 	cc = uart->regs;
5524df489c4SDavid du Colombier 	ccb = cc->ccb;
5534df489c4SDavid du Colombier 
5544df489c4SDavid du Colombier 	mem = (uchar*)cc->ctlr->gcb;
555c65c114fSDavid du Colombier 	bp = mem + ccb->obsa;
5564df489c4SDavid du Colombier 	rp = mem + ccb->obrp;
5574df489c4SDavid du Colombier 	wp = mem + ccb->obwp;
5584df489c4SDavid du Colombier 	ep = mem + ccb->obea;
559c65c114fSDavid du Colombier 	while(wp != rp-1 && (rp != bp || wp != ep)){
560c65c114fSDavid du Colombier 		/*
561c65c114fSDavid du Colombier 		 * if we've exhausted the uart's output buffer,
562c65c114fSDavid du Colombier 		 * ask for more from the output queue, and quit if there
563c65c114fSDavid du Colombier 		 * isn't any.
564c65c114fSDavid du Colombier 		 */
5654df489c4SDavid du Colombier 		if(uart->op >= uart->oe && uartstageoutput(uart) == 0)
5664df489c4SDavid du Colombier 			break;
5674df489c4SDavid du Colombier 		*wp++ = *(uart->op++);
568c65c114fSDavid du Colombier 		if(wp > ep)
569c65c114fSDavid du Colombier 			wp = bp;
5704df489c4SDavid du Colombier 		ccb->obwp = wp - mem;
5714df489c4SDavid du Colombier 	}
5724df489c4SDavid du Colombier }
5734df489c4SDavid du Colombier 
574c65c114fSDavid du Colombier /* only called from interrupt service */
5754df489c4SDavid du Colombier static void
axprecv(Cc * cc)5764df489c4SDavid du Colombier axprecv(Cc* cc)
5774df489c4SDavid du Colombier {
5784df489c4SDavid du Colombier 	Ccb *ccb;
5794df489c4SDavid du Colombier 	uchar *ep, *mem, *rp, *wp;
5804df489c4SDavid du Colombier 
5814df489c4SDavid du Colombier 	ccb = cc->ccb;
5824df489c4SDavid du Colombier 
5834df489c4SDavid du Colombier 	mem = (uchar*)cc->ctlr->gcb;
5844df489c4SDavid du Colombier 	rp = mem + ccb->ibrp;
5854df489c4SDavid du Colombier 	wp = mem + ccb->ibwp;
5864df489c4SDavid du Colombier 	ep = mem + ccb->ibea;
5874df489c4SDavid du Colombier 
5884df489c4SDavid du Colombier 	while(rp != wp){
589c65c114fSDavid du Colombier 		uartrecv(cc, *rp++);		/* ilocks cc->tlock */
5904df489c4SDavid du Colombier 		if(rp > ep)
5914df489c4SDavid du Colombier 			rp = mem + ccb->ibsa;
5924df489c4SDavid du Colombier 		ccb->ibrp = rp - mem;
5934df489c4SDavid du Colombier 	}
5944df489c4SDavid du Colombier }
5954df489c4SDavid du Colombier 
5964df489c4SDavid du Colombier static void
axpinterrupt(Ureg *,void * arg)5974df489c4SDavid du Colombier axpinterrupt(Ureg*, void* arg)
5984df489c4SDavid du Colombier {
599c65c114fSDavid du Colombier 	int work;
6004df489c4SDavid du Colombier 	Cc *cc;
6014df489c4SDavid du Colombier 	Ctlr *ctlr;
6024df489c4SDavid du Colombier 	u32int ics;
6034df489c4SDavid du Colombier 	u16int r, sr;
6044df489c4SDavid du Colombier 
605c65c114fSDavid du Colombier 	work = 0;
6064df489c4SDavid du Colombier 	ctlr = arg;
6074df489c4SDavid du Colombier 	ics = csr32r(ctlr, Ics);
6084df489c4SDavid du Colombier 	if(ics & 0x0810C000)
6094df489c4SDavid du Colombier 		print("%s: unexpected interrupt %#ux\n", ctlr->name, ics);
610c65c114fSDavid du Colombier 	if(!(ics & 0x00002000)) {
611e47b3901SDavid du Colombier 		/* we get a steady stream of these on consoles */
612e47b3901SDavid du Colombier 		// print("%s: non-doorbell interrupt\n", ctlr->name);
613e47b3901SDavid du Colombier 		ctlr->gcb->gcw2 = 0x0001;	/* set Gintack */
6144df489c4SDavid du Colombier 		return;
615c65c114fSDavid du Colombier 	}
6164df489c4SDavid du Colombier 
6174df489c4SDavid du Colombier //	while(work to do){
6184df489c4SDavid du Colombier 		cc = ctlr->cc;
6194df489c4SDavid du Colombier 		for(sr = xchgw(&ctlr->gcb->isr, 0); sr != 0; sr >>= 1){
6204df489c4SDavid du Colombier 			if(sr & 0x0001)
621c65c114fSDavid du Colombier 				work++, axprecv(cc);
6224df489c4SDavid du Colombier 			cc++;
6234df489c4SDavid du Colombier 		}
6244df489c4SDavid du Colombier 		cc = ctlr->cc;
6254df489c4SDavid du Colombier 		for(sr = xchgw(&ctlr->gcb->osr, 0); sr != 0; sr >>= 1){
6264df489c4SDavid du Colombier 			if(sr & 0x0001)
627c65c114fSDavid du Colombier 				work++, uartkick(&cc->Uart);
6284df489c4SDavid du Colombier 			cc++;
6294df489c4SDavid du Colombier 		}
6304df489c4SDavid du Colombier 		cc = ctlr->cc;
6314df489c4SDavid du Colombier 		for(sr = xchgw(&ctlr->gcb->csr, 0); sr != 0; sr >>= 1){
6324df489c4SDavid du Colombier 			if(sr & 0x0001)
633c65c114fSDavid du Colombier 				work++, wakeup(cc);
6344df489c4SDavid du Colombier 			cc++;
6354df489c4SDavid du Colombier 		}
6364df489c4SDavid du Colombier 		cc = ctlr->cc;
6374df489c4SDavid du Colombier 		for(sr = xchgw(&ctlr->gcb->msr, 0); sr != 0; sr >>= 1){
6384df489c4SDavid du Colombier 			if(sr & 0x0001)
639c65c114fSDavid du Colombier 				work++, axpmc(cc);
6404df489c4SDavid du Colombier 			cc++;
6414df489c4SDavid du Colombier 		}
6424df489c4SDavid du Colombier 		cc = ctlr->cc;
6434df489c4SDavid du Colombier 		for(sr = xchgw(&ctlr->gcb->esr, 0); sr != 0; sr >>= 1){
6444df489c4SDavid du Colombier 			if(sr & 0x0001){
6454df489c4SDavid du Colombier 				r = cc->ccb->ms;
6464df489c4SDavid du Colombier 				if(r & Oe)
6474df489c4SDavid du Colombier 					cc->oerr++;
6484df489c4SDavid du Colombier 				if(r & Pe)
6494df489c4SDavid du Colombier 					cc->perr++;
6504df489c4SDavid du Colombier 				if(r & Fe)
6514df489c4SDavid du Colombier 					cc->ferr++;
652c65c114fSDavid du Colombier 				if (r & (Oe|Pe|Fe))
653c65c114fSDavid du Colombier 					work++;
6544df489c4SDavid du Colombier 			}
6554df489c4SDavid du Colombier 			cc++;
6564df489c4SDavid du Colombier 		}
6574df489c4SDavid du Colombier //	}
658c65c114fSDavid du Colombier 	/* only meaningful if we don't share the irq */
659c65c114fSDavid du Colombier 	if (0 && !work)
660c65c114fSDavid du Colombier 		print("%s: interrupt with no work\n", ctlr->name);
661c65c114fSDavid du Colombier 	csr32w(ctlr, Pdb, 1);		/* clear doorbell interrupt */
662c65c114fSDavid du Colombier 	ctlr->gcb->gcw2 = 0x0001;	/* set Gintack */
6634df489c4SDavid du Colombier }
6644df489c4SDavid du Colombier 
6654df489c4SDavid du Colombier static void
axpdisable(Uart * uart)6664df489c4SDavid du Colombier axpdisable(Uart* uart)
6674df489c4SDavid du Colombier {
6684df489c4SDavid du Colombier 	Cc *cc;
6694df489c4SDavid du Colombier 	u16int lp;
6704df489c4SDavid du Colombier 	Ctlr *ctlr;
6714df489c4SDavid du Colombier 
6724df489c4SDavid du Colombier 	/*
6734df489c4SDavid du Colombier  	 * Turn off DTR and RTS, disable interrupts.
6744df489c4SDavid du Colombier 	 */
6754df489c4SDavid du Colombier 	(*uart->phys->dtr)(uart, 0);
6764df489c4SDavid du Colombier 	(*uart->phys->rts)(uart, 0);
6774df489c4SDavid du Colombier 
6784df489c4SDavid du Colombier 	cc = uart->regs;
6794df489c4SDavid du Colombier 	lp = cc->ccb->lp;
6804df489c4SDavid du Colombier 	cc->ccb->lp = Emcs|lp;
6814df489c4SDavid du Colombier 	axpcc(cc, Dt|Dr|Fob|Fib|Ccu);
6824df489c4SDavid du Colombier 
6834df489c4SDavid du Colombier 	/*
6844df489c4SDavid du Colombier 	 * The Uart is qlocked.
6854df489c4SDavid du Colombier 	 */
6864df489c4SDavid du Colombier 	ctlr = cc->ctlr;
6874df489c4SDavid du Colombier 	ctlr->im &= ~(1<<cc->uartno);
6884df489c4SDavid du Colombier 	if(ctlr->im == 0)
6894df489c4SDavid du Colombier 		intrdisable(ctlr->pcidev->intl, axpinterrupt, ctlr,
6904df489c4SDavid du Colombier 			ctlr->pcidev->tbdf, ctlr->name);
6914df489c4SDavid du Colombier }
6924df489c4SDavid du Colombier 
6934df489c4SDavid du Colombier static void
axpenable(Uart * uart,int ie)6944df489c4SDavid du Colombier axpenable(Uart* uart, int ie)
6954df489c4SDavid du Colombier {
6964df489c4SDavid du Colombier 	Cc *cc;
6974df489c4SDavid du Colombier 	Ctlr *ctlr;
6984df489c4SDavid du Colombier 	u16int lp;
6994df489c4SDavid du Colombier 
7004df489c4SDavid du Colombier 	cc = uart->regs;
7014df489c4SDavid du Colombier 	ctlr = cc->ctlr;
7024df489c4SDavid du Colombier 
7034df489c4SDavid du Colombier 	/*
7044df489c4SDavid du Colombier  	 * Enable interrupts and turn on DTR and RTS.
7054df489c4SDavid du Colombier 	 * Be careful if this is called to set up a polled serial line
7064df489c4SDavid du Colombier 	 * early on not to try to enable interrupts as interrupt-
7074df489c4SDavid du Colombier 	 * -enabling mechanisms might not be set up yet.
7084df489c4SDavid du Colombier 	 */
7094df489c4SDavid du Colombier 	if(ie){
7104df489c4SDavid du Colombier 		/*
7114df489c4SDavid du Colombier 		 * The Uart is qlocked.
7124df489c4SDavid du Colombier 		 */
7134df489c4SDavid du Colombier 		if(ctlr->im == 0){
7144df489c4SDavid du Colombier 			intrenable(ctlr->pcidev->intl, axpinterrupt, ctlr,
7154df489c4SDavid du Colombier 				ctlr->pcidev->tbdf, ctlr->name);
7164df489c4SDavid du Colombier 			csr32w(ctlr, Ics, 0x00031F00);
7174df489c4SDavid du Colombier 			csr32w(ctlr, Pdb, 1);
7184df489c4SDavid du Colombier 			ctlr->gcb->gcw2 = 1;
7194df489c4SDavid du Colombier 		}
7204df489c4SDavid du Colombier 		ctlr->im |= 1<<cc->uartno;
7214df489c4SDavid du Colombier 	}
7224df489c4SDavid du Colombier 
7234df489c4SDavid du Colombier 	(*uart->phys->dtr)(uart, 1);
7244df489c4SDavid du Colombier 	(*uart->phys->rts)(uart, 1);
7254df489c4SDavid du Colombier 
7264df489c4SDavid du Colombier 	/*
7274df489c4SDavid du Colombier 	 * Make sure we control RTS, DTR and break.
7284df489c4SDavid du Colombier 	 */
7294df489c4SDavid du Colombier 	lp = cc->ccb->lp;
7304df489c4SDavid du Colombier 	cc->ccb->lp = Emcs|lp;
7314df489c4SDavid du Colombier 	cc->ccb->oblw = 64;
7324df489c4SDavid du Colombier 	axpcc(cc, Et|Er|Ccu);
7334df489c4SDavid du Colombier }
7344df489c4SDavid du Colombier 
7354df489c4SDavid du Colombier static void*
axpdealloc(Ctlr * ctlr)7364df489c4SDavid du Colombier axpdealloc(Ctlr* ctlr)
7374df489c4SDavid du Colombier {
7384df489c4SDavid du Colombier 	int i;
7394df489c4SDavid du Colombier 
7404df489c4SDavid du Colombier 	for(i = 0; i < 16; i++){
7414df489c4SDavid du Colombier 		if(ctlr->cc[i].name != nil)
7424df489c4SDavid du Colombier 			free(ctlr->cc[i].name);
7434df489c4SDavid du Colombier 	}
7444df489c4SDavid du Colombier 	if(ctlr->reg != nil)
7454df489c4SDavid du Colombier 		vunmap(ctlr->reg, ctlr->pcidev->mem[0].size);
7464df489c4SDavid du Colombier 	if(ctlr->mem != nil)
7474df489c4SDavid du Colombier 		vunmap(ctlr->mem, ctlr->pcidev->mem[2].size);
7484df489c4SDavid du Colombier 	if(ctlr->name != nil)
7494df489c4SDavid du Colombier 		free(ctlr->name);
7504df489c4SDavid du Colombier 	free(ctlr);
7514df489c4SDavid du Colombier 
7524df489c4SDavid du Colombier 	return nil;
7534df489c4SDavid du Colombier }
7544df489c4SDavid du Colombier 
7554df489c4SDavid du Colombier static Uart*
axpalloc(int ctlrno,Pcidev * pcidev)7564df489c4SDavid du Colombier axpalloc(int ctlrno, Pcidev* pcidev)
7574df489c4SDavid du Colombier {
7584df489c4SDavid du Colombier 	Cc *cc;
7594df489c4SDavid du Colombier 	uchar *p;
7604df489c4SDavid du Colombier 	Ctlr *ctlr;
7614df489c4SDavid du Colombier 	void *addr;
7624df489c4SDavid du Colombier 	char name[64];
7634df489c4SDavid du Colombier 	u32int bar, r;
7644df489c4SDavid du Colombier 	int i, n, timeo;
7654df489c4SDavid du Colombier 
7664df489c4SDavid du Colombier 	ctlr = malloc(sizeof(Ctlr));
767*aa72973aSDavid du Colombier 	if(ctlr == nil)
768*aa72973aSDavid du Colombier 		error(Enomem);
7694df489c4SDavid du Colombier 	seprint(name, name+sizeof(name), "uartaxp%d", ctlrno);
7704df489c4SDavid du Colombier 	kstrdup(&ctlr->name, name);
7714df489c4SDavid du Colombier 	ctlr->pcidev = pcidev;
7724df489c4SDavid du Colombier 	ctlr->ctlrno = ctlrno;
7734df489c4SDavid du Colombier 
7744df489c4SDavid du Colombier 	/*
7754df489c4SDavid du Colombier 	 * Access to runtime registers.
7764df489c4SDavid du Colombier 	 */
7774df489c4SDavid du Colombier 	bar = pcidev->mem[0].bar;
7784df489c4SDavid du Colombier 	if((addr = vmap(bar & ~0x0F, pcidev->mem[0].size)) == 0){
7794df489c4SDavid du Colombier 		print("%s: can't map registers at %#ux\n", ctlr->name, bar);
7804df489c4SDavid du Colombier 		return axpdealloc(ctlr);
7814df489c4SDavid du Colombier 	}
7824df489c4SDavid du Colombier 	ctlr->reg = addr;
7834df489c4SDavid du Colombier 	print("%s: port 0x%ux irq %d ", ctlr->name, bar, pcidev->intl);
7844df489c4SDavid du Colombier 
7854df489c4SDavid du Colombier 	/*
7864df489c4SDavid du Colombier 	 * Local address space 0.
7874df489c4SDavid du Colombier 	 */
7884df489c4SDavid du Colombier 	bar = pcidev->mem[2].bar;
7894df489c4SDavid du Colombier 	if((addr = vmap(bar & ~0x0F, pcidev->mem[2].size)) == 0){
7904df489c4SDavid du Colombier 		print("%s: can't map memory at %#ux\n", ctlr->name, bar);
7914df489c4SDavid du Colombier 		return axpdealloc(ctlr);
7924df489c4SDavid du Colombier 	}
7934df489c4SDavid du Colombier 	ctlr->mem = addr;
7944df489c4SDavid du Colombier 	ctlr->gcb = (Gcb*)(ctlr->mem+0x10000);
7954df489c4SDavid du Colombier 	print("mem 0x%ux size %d: ", bar, pcidev->mem[2].size);
7964df489c4SDavid du Colombier 
7974df489c4SDavid du Colombier 	/*
7984df489c4SDavid du Colombier 	 * Toggle the software reset and wait for
7994df489c4SDavid du Colombier 	 * the adapter local init status to indicate done.
8004df489c4SDavid du Colombier 	 *
8014df489c4SDavid du Colombier 	 * The two 'delay(100)'s below are important,
8024df489c4SDavid du Colombier 	 * without them the board seems to become confused
8034df489c4SDavid du Colombier 	 * (perhaps it needs some 'quiet time' because the
8044df489c4SDavid du Colombier 	 * timeout loops are not sufficient in themselves).
8054df489c4SDavid du Colombier 	 */
8064df489c4SDavid du Colombier 	r = csr32r(ctlr, Mcc);
8074df489c4SDavid du Colombier 	csr32w(ctlr, Mcc, r|Asr);
8084df489c4SDavid du Colombier 	microdelay(1);
8094df489c4SDavid du Colombier 	csr32w(ctlr, Mcc, r&~Asr);
8104df489c4SDavid du Colombier 	delay(100);
8114df489c4SDavid du Colombier 
8124df489c4SDavid du Colombier 	for(timeo = 0; timeo < 100000; timeo++){
8134df489c4SDavid du Colombier 		if(csr32r(ctlr, Mcc) & Lis)
8144df489c4SDavid du Colombier 			break;
8154df489c4SDavid du Colombier 		microdelay(1);
8164df489c4SDavid du Colombier 	}
8174df489c4SDavid du Colombier 	if(!(csr32r(ctlr, Mcc) & Lis)){
8184df489c4SDavid du Colombier 		print("%s: couldn't reset\n", ctlr->name);
8194df489c4SDavid du Colombier 		return axpdealloc(ctlr);
8204df489c4SDavid du Colombier 	}
8214df489c4SDavid du Colombier 	print("downloading...");
8224df489c4SDavid du Colombier 	/*
8234df489c4SDavid du Colombier 	 * Copy the control programme to the card memory.
8244df489c4SDavid du Colombier 	 * The card's i960 control structures live at 0xD000.
8254df489c4SDavid du Colombier 	 */
8264df489c4SDavid du Colombier 	if(sizeof(uartaxpcp) > 0xD000){
8274df489c4SDavid du Colombier 		print("%s: control programme too big\n", ctlr->name);
8284df489c4SDavid du Colombier 		return axpdealloc(ctlr);
8294df489c4SDavid du Colombier 	}
830c65c114fSDavid du Colombier 	/* TODO: is this right for more than 1 card? devastar does the same */
8314df489c4SDavid du Colombier 	csr32w(ctlr, Remap, 0xA0000001);
8324df489c4SDavid du Colombier 	for(i = 0; i < sizeof(uartaxpcp); i++)
8334df489c4SDavid du Colombier 		ctlr->mem[i] = uartaxpcp[i];
8344df489c4SDavid du Colombier 	/*
8354df489c4SDavid du Colombier 	 * Execute downloaded code and wait for it
8364df489c4SDavid du Colombier 	 * to signal ready.
8374df489c4SDavid du Colombier 	 */
8384df489c4SDavid du Colombier 	csr32w(ctlr, Mb0, Edcc);
8394df489c4SDavid du Colombier 	delay(100);
8404df489c4SDavid du Colombier 	/* the manual says to wait for Cpr for 1 second */
8414df489c4SDavid du Colombier 	for(timeo = 0; timeo < 10000; timeo++){
8424df489c4SDavid du Colombier 		if(csr32r(ctlr, Mb0) & Cpr)
8434df489c4SDavid du Colombier 			break;
8444df489c4SDavid du Colombier 		microdelay(100);
8454df489c4SDavid du Colombier 	}
8464df489c4SDavid du Colombier 	if(!(csr32r(ctlr, Mb0) & Cpr)){
8474df489c4SDavid du Colombier 		print("control programme not ready; Mb0 %#ux\n",
8484df489c4SDavid du Colombier 			csr32r(ctlr, Mb0));
849f7c4f893SDavid du Colombier 		print("%s: distribution panel not connected or card not fully seated?\n",
8504df489c4SDavid du Colombier 			ctlr->name);
8514df489c4SDavid du Colombier 
8524df489c4SDavid du Colombier 		return axpdealloc(ctlr);
8534df489c4SDavid du Colombier 	}
8544df489c4SDavid du Colombier 	print("\n");
8554df489c4SDavid du Colombier 
8564df489c4SDavid du Colombier 	n = ctlr->gcb->ccbn;
8574df489c4SDavid du Colombier 	if(ctlr->gcb->bt != 0x12 || n > 16){
8584df489c4SDavid du Colombier 		print("%s: wrong board type %#ux, %d channels\n",
8594df489c4SDavid du Colombier 			ctlr->name, ctlr->gcb->bt, ctlr->gcb->ccbn);
8604df489c4SDavid du Colombier 		return axpdealloc(ctlr);
8614df489c4SDavid du Colombier 	}
8624df489c4SDavid du Colombier 
8634df489c4SDavid du Colombier 	p = ((uchar*)ctlr->gcb) + ctlr->gcb->ccboff;
8644df489c4SDavid du Colombier 	for(i = 0; i < n; i++){
8654df489c4SDavid du Colombier 		cc = &ctlr->cc[i];
8664df489c4SDavid du Colombier 		cc->ccb = (Ccb*)p;
8674df489c4SDavid du Colombier 		p += ctlr->gcb->ccbsz;
8684df489c4SDavid du Colombier 		cc->uartno = i;
8694df489c4SDavid du Colombier 		cc->ctlr = ctlr;
8704df489c4SDavid du Colombier 
871c65c114fSDavid du Colombier 		cc->regs = cc;		/* actually Uart->regs */
8724df489c4SDavid du Colombier 		seprint(name, name+sizeof(name), "uartaxp%d%2.2d", ctlrno, i);
8734df489c4SDavid du Colombier 		kstrdup(&cc->name, name);
8744df489c4SDavid du Colombier 		cc->freq = 0;
8754df489c4SDavid du Colombier 		cc->bits = 8;
8764df489c4SDavid du Colombier 		cc->stop = 1;
8774df489c4SDavid du Colombier 		cc->parity = 'n';
8784df489c4SDavid du Colombier 		cc->baud = 9600;
8794df489c4SDavid du Colombier 		cc->phys = &axpphysuart;
8804df489c4SDavid du Colombier 		cc->console = 0;
8814df489c4SDavid du Colombier 		cc->special = 0;
8824df489c4SDavid du Colombier 
8834df489c4SDavid du Colombier 		cc->next = &ctlr->cc[i+1];
8844df489c4SDavid du Colombier 	}
8854df489c4SDavid du Colombier 	ctlr->cc[n-1].next = nil;
8864df489c4SDavid du Colombier 
887c65c114fSDavid du Colombier 	ctlr->next = nil;
8884df489c4SDavid du Colombier 	if(axpctlrhead != nil)
8894df489c4SDavid du Colombier 		axpctlrtail->next = ctlr;
8904df489c4SDavid du Colombier 	else
8914df489c4SDavid du Colombier 		axpctlrhead = ctlr;
8924df489c4SDavid du Colombier 	axpctlrtail = ctlr;
8934df489c4SDavid du Colombier 
8944df489c4SDavid du Colombier 	return ctlr->cc;
8954df489c4SDavid du Colombier }
8964df489c4SDavid du Colombier 
8974df489c4SDavid du Colombier static Uart*
axppnp(void)8984df489c4SDavid du Colombier axppnp(void)
8994df489c4SDavid du Colombier {
9004df489c4SDavid du Colombier 	Pcidev *p;
9014df489c4SDavid du Colombier 	int ctlrno;
9024df489c4SDavid du Colombier 	Uart *head, *tail, *uart;
9034df489c4SDavid du Colombier 
9044df489c4SDavid du Colombier 	/*
9054df489c4SDavid du Colombier 	 * Loop through all PCI devices looking for simple serial
9064df489c4SDavid du Colombier 	 * controllers (ccrb == 0x07) and configure the ones which
9074df489c4SDavid du Colombier 	 * are familiar.
9084df489c4SDavid du Colombier 	 */
9094df489c4SDavid du Colombier 	head = tail = nil;
9104df489c4SDavid du Colombier 	ctlrno = 0;
9114df489c4SDavid du Colombier 	for(p = pcimatch(nil, 0, 0); p != nil; p = pcimatch(p, 0, 0)){
9124df489c4SDavid du Colombier 		if(p->ccrb != 0x07)
9134df489c4SDavid du Colombier 			continue;
9144df489c4SDavid du Colombier 
9154df489c4SDavid du Colombier 		switch((p->did<<16)|p->vid){
9164df489c4SDavid du Colombier 		default:
9174df489c4SDavid du Colombier 			continue;
9184df489c4SDavid du Colombier 		case (0x6001<<16)|0x114F:	/* AvanstarXp */
9194df489c4SDavid du Colombier 			if((uart = axpalloc(ctlrno, p)) == nil)
9204df489c4SDavid du Colombier 				continue;
9214df489c4SDavid du Colombier 			break;
9224df489c4SDavid du Colombier 		}
9234df489c4SDavid du Colombier 
9244df489c4SDavid du Colombier 		if(head != nil)
9254df489c4SDavid du Colombier 			tail->next = uart;
9264df489c4SDavid du Colombier 		else
9274df489c4SDavid du Colombier 			head = uart;
9284df489c4SDavid du Colombier 		for(tail = uart; tail->next != nil; tail = tail->next)
9294df489c4SDavid du Colombier 			;
9304df489c4SDavid du Colombier 		ctlrno++;
9314df489c4SDavid du Colombier 	}
9324df489c4SDavid du Colombier 
9334df489c4SDavid du Colombier 	return head;
9344df489c4SDavid du Colombier }
9354df489c4SDavid du Colombier 
9364df489c4SDavid du Colombier PhysUart axpphysuart = {
9374df489c4SDavid du Colombier 	.name		= "AvanstarXp",
9384df489c4SDavid du Colombier 	.pnp		= axppnp,
9394df489c4SDavid du Colombier 	.enable		= axpenable,
9404df489c4SDavid du Colombier 	.disable	= axpdisable,
9414df489c4SDavid du Colombier 	.kick		= axpkick,
9424df489c4SDavid du Colombier 	.dobreak	= axpbreak,
9434df489c4SDavid du Colombier 	.baud		= axpbaud,
9444df489c4SDavid du Colombier 	.bits		= axpbits,
9454df489c4SDavid du Colombier 	.stop		= axpstop,
9464df489c4SDavid du Colombier 	.parity		= axpparity,
9474df489c4SDavid du Colombier 	.modemctl	= axpmodemctl,
9484df489c4SDavid du Colombier 	.rts		= axprts,
9494df489c4SDavid du Colombier 	.dtr		= axpdtr,
9504df489c4SDavid du Colombier 	.status		= axpstatus,
9514df489c4SDavid du Colombier 	.fifo		= axpfifo,
9524df489c4SDavid du Colombier 	.getc		= nil,
9534df489c4SDavid du Colombier 	.putc		= nil,
9544df489c4SDavid du Colombier };
955