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