diff options
author | Taru Karttunen <taruti@taruti.net> | 2011-03-30 15:46:40 +0300 |
---|---|---|
committer | Taru Karttunen <taruti@taruti.net> | 2011-03-30 15:46:40 +0300 |
commit | e5888a1ffdae813d7575f5fb02275c6bb07e5199 (patch) | |
tree | d8d51eac403f07814b9e936eed0c9a79195e2450 /sys/src/9/pc/uartaxp.c |
Import sources from 2011-03-30 iso image
Diffstat (limited to 'sys/src/9/pc/uartaxp.c')
-rwxr-xr-x | sys/src/9/pc/uartaxp.c | 951 |
1 files changed, 951 insertions, 0 deletions
diff --git a/sys/src/9/pc/uartaxp.c b/sys/src/9/pc/uartaxp.c new file mode 100755 index 000000000..af928a1e2 --- /dev/null +++ b/sys/src/9/pc/uartaxp.c @@ -0,0 +1,951 @@ +/* + * Avanstar Xp pci uart driver + */ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "io.h" +#include "../port/error.h" + +#include "uartaxp.i" + +typedef struct Cc Cc; +typedef struct Ccb Ccb; +typedef struct Ctlr Ctlr; +typedef struct Gcb Gcb; + +/* + * Global Control Block. + * Service Request fields must be accessed using XCHG. + */ +struct Gcb { + u16int gcw; /* Global Command Word */ + u16int gsw; /* Global Status Word */ + u16int gsr; /* Global Service Request */ + u16int abs; /* Available Buffer Space */ + u16int bt; /* Board Type */ + u16int cpv; /* Control Program Version */ + u16int ccbn; /* Ccb count */ + u16int ccboff; /* Ccb offset */ + u16int ccbsz; /* Ccb size */ + u16int gcw2; /* Global Command Word 2 */ + u16int gsw2; /* Global Status Word 2 */ + u16int esr; /* Error Service Request */ + u16int isr; /* Input Service Request */ + u16int osr; /* Output Service Request */ + u16int msr; /* Modem Service Request */ + u16int csr; /* Command Service Request */ +}; + +/* + * Channel Control Block. + */ +struct Ccb { + u16int br; /* Baud Rate */ + u16int df; /* Data Format */ + u16int lp; /* Line Protocol */ + u16int ibs; /* Input Buffer Size */ + u16int obs; /* Output Buffer Size */ + u16int ibtr; /* Ib Trigger Rate */ + u16int oblw; /* Ob Low Watermark */ + u8int ixon[2]; /* IXON characters */ + u16int ibhw; /* Ib High Watermark */ + u16int iblw; /* Ib Low Watermark */ + u16int cc; /* Channel Command */ + u16int cs; /* Channel Status */ + u16int ibsa; /* Ib Start Addr */ + u16int ibea; /* Ib Ending Addr */ + u16int obsa; /* Ob Start Addr */ + u16int obea; /* Ob Ending Addr */ + u16int ibwp; /* Ib write pointer (RO) */ + u16int ibrp; /* Ib read pointer (R/W) */ + u16int obwp; /* Ob write pointer (R/W) */ + u16int obrp; /* Ob read pointer (RO) */ + u16int ces; /* Communication Error Status */ + u16int bcp; /* Bad Character Pointer */ + u16int mc; /* Modem Control */ + u16int ms; /* Modem Status */ + u16int bs; /* Blocking Status */ + u16int crf; /* Character Received Flag */ + u8int ixoff[2]; /* IXOFF characters */ + u16int cs2; /* Channel Status 2 */ + u8int sec[2]; /* Strip/Error Characters */ +}; + +enum { /* br */ + Br76800 = 0xFF00, + Br115200 = 0xFF01, +}; + +enum { /* df */ + Db5 = 0x0000, /* Data Bits - 5 bits/byte */ + Db6 = 0x0001, /* 6 bits/byte */ + Db7 = 0x0002, /* 7 bits/byte */ + Db8 = 0x0003, /* 8 bits/byte */ + DbMASK = 0x0003, + Sb1 = 0x0000, /* 1 Stop Bit */ + Sb2 = 0x0004, /* 2 Stop Bit */ + SbMASK = 0x0004, + Np = 0x0000, /* No Parity */ + Op = 0x0008, /* Odd Parity */ + Ep = 0x0010, /* Even Parity */ + Mp = 0x0020, /* Mark Parity */ + Sp = 0x0030, /* Space Parity */ + PMASK = 0x0038, + Cmn = 0x0000, /* Channel Mode Normal */ + Cme = 0x0040, /* CM Echo */ + Cmll = 0x0080, /* CM Local Loopback */ + Cmrl = 0x00C0, /* CM Remote Loopback */ +}; + +enum { /* lp */ + Ixon = 0x0001, /* Obey IXON/IXOFF */ + Ixany = 0x0002, /* Any character retarts Tx */ + Ixgen = 0x0004, /* Generate IXON/IXOFF */ + Cts = 0x0008, /* CTS controls Tx */ + Dtr = 0x0010, /* Rx controls DTR */ + ½d = 0x0020, /* RTS off during Tx */ + Rts = 0x0040, /* generate RTS */ + Emcs = 0x0080, /* Enable Modem Control */ + Ecs = 0x1000, /* Enable Character Stripping */ + Eia422 = 0x2000, /* EIA422 */ +}; + +enum { /* cc */ + Ccu = 0x0001, /* Configure Channel and UART */ + Cco = 0x0002, /* Configure Channel Only */ + Fib = 0x0004, /* Flush Input Buffer */ + Fob = 0x0008, /* Flush Output Buffer */ + Er = 0x0010, /* Enable Receiver */ + Dr = 0x0020, /* Disable Receiver */ + Et = 0x0040, /* Enable Transmitter */ + Dt = 0x0080, /* Disable Transmitter */ +}; + +enum { /* ces */ + Oe = 0x0001, /* Overrun Error */ + Pe = 0x0002, /* Parity Error */ + Fe = 0x0004, /* Framing Error */ + Br = 0x0008, /* Break Received */ +}; + +enum { /* mc */ + Adtr = 0x0001, /* Assert DTR */ + Arts = 0x0002, /* Assert RTS */ + Ab = 0x0010, /* Assert BREAK */ +}; + +enum { /* ms */ + Scts = 0x0001, /* Status od CTS */ + Sdsr = 0x0002, /* Status of DSR */ + Sri = 0x0004, /* Status of RI */ + Sdcd = 0x0008, /* Status of DCD */ +}; + +enum { /* bs */ + Rd = 0x0001, /* Receiver Disabled */ + Td = 0x0002, /* Transmitter Disabled */ + Tbxoff = 0x0004, /* Tx Blocked by XOFF */ + Tbcts = 0x0008, /* Tx Blocked by CTS */ + Rbxoff = 0x0010, /* Rx Blocked by XOFF */ + Rbrts = 0x0020, /* Rx Blocked by RTS */ +}; + +enum { /* Local Configuration */ + Range = 0x00, + Remap = 0x04, + Region = 0x18, + Mb0 = 0x40, /* Mailbox 0 */ + Ldb = 0x60, /* PCI to Local Doorbell */ + Pdb = 0x64, /* Local to PCI Doorbell */ + Ics = 0x68, /* Interrupt Control/Status */ + Mcc = 0x6C, /* Misc. Command and Control */ +}; + +enum { /* Mb0 */ + Edcc = 1, /* exec. downloaded code cmd */ + Aic = 0x10, /* adapter init'zed correctly */ + Cpr = 1ul << 31, /* control program ready */ +}; + +enum { /* Mcc */ + Rcr = 1ul << 29, /* reload config. reg.s */ + Asr = 1ul << 30, /* pci adapter sw reset */ + Lis = 1ul << 31, /* local init status */ +}; + +typedef struct Cc Cc; +typedef struct Ccb Ccb; +typedef struct Ctlr Ctlr; + +/* + * Channel Control, one per uart. + * Devuart communicates via the PhysUart functions with + * a Uart* argument. Uart.regs is filled in by this driver + * to point to a Cc, and Cc.ctlr points to the Axp board + * controller. + */ +struct Cc { + int uartno; + Ccb* ccb; + Ctlr* ctlr; + + Rendez; + + Uart; +}; + +typedef struct Ctlr { + char* name; + Pcidev* pcidev; + int ctlrno; + Ctlr* next; + + u32int* reg; + uchar* mem; + Gcb* gcb; + + int im; /* interrupt mask */ + Cc cc[16]; +} Ctlr; + +#define csr32r(c, r) (*((c)->reg+((r)/4))) +#define csr32w(c, r, v) (*((c)->reg+((r)/4)) = (v)) + +static Ctlr* axpctlrhead; +static Ctlr* axpctlrtail; + +extern PhysUart axpphysuart; + +static int +axpccdone(void* ccb) +{ + return !((Ccb*)ccb)->cc; /* hw sets ccb->cc to zero */ +} + +static void +axpcc(Cc* cc, int cmd) +{ + Ccb *ccb; + int timeo; + u16int cs; + + ccb = cc->ccb; + ccb->cc = cmd; + + if(!cc->ctlr->im) + for(timeo = 0; timeo < 1000000; timeo++){ + if(!ccb->cc) + break; + microdelay(1); + } + else + tsleep(cc, axpccdone, ccb, 1000); + + cs = ccb->cs; + if(ccb->cc || cs){ + print("%s: cmd %#ux didn't terminate: %#ux %#ux\n", + cc->name, cmd, ccb->cc, cs); + if(cc->ctlr->im) + error(Eio); + } +} + +static long +axpstatus(Uart* uart, void* buf, long n, long offset) +{ + char *p; + Ccb *ccb; + u16int bs, fstat, ms; + + ccb = ((Cc*)(uart->regs))->ccb; + + p = malloc(READSTR); + bs = ccb->bs; + fstat = ccb->df; + ms = ccb->ms; + + snprint(p, READSTR, + "b%d c%d d%d e%d l%d m%d p%c r%d s%d i%d\n" + "dev(%d) type(%d) framing(%d) overruns(%d) " + "berr(%d) serr(%d)%s%s%s%s\n", + + uart->baud, + uart->hup_dcd, + ms & Sdsr, + uart->hup_dsr, + (fstat & DbMASK) + 5, + 0, + (fstat & PMASK) ? ((fstat & Ep) == Ep? 'e': 'o'): 'n', + (bs & Rbrts) ? 1 : 0, + (fstat & Sb2) ? 2 : 1, + 0, + + uart->dev, + uart->type, + uart->ferr, + uart->oerr, + uart->berr, + uart->serr, + (ms & Scts) ? " cts" : "", + (ms & Sdsr) ? " dsr" : "", + (ms & Sdcd) ? " dcd" : "", + (ms & Sri) ? " ring" : "" + ); + n = readstr(offset, buf, n, p); + free(p); + + return n; +} + +static void +axpfifo(Uart*, int) +{ +} + +static void +axpdtr(Uart* uart, int on) +{ + Ccb *ccb; + u16int mc; + + ccb = ((Cc*)(uart->regs))->ccb; + + mc = ccb->mc; + if(on) + mc |= Adtr; + else + mc &= ~Adtr; + ccb->mc = mc; +} + +/* + * can be called from uartstageinput() during an input interrupt, + * with uart->rlock ilocked or the uart qlocked, sometimes both. + */ +static void +axprts(Uart* uart, int on) +{ + Ccb *ccb; + u16int mc; + + ccb = ((Cc*)(uart->regs))->ccb; + + mc = ccb->mc; + if(on) + mc |= Arts; + else + mc &= ~Arts; + ccb->mc = mc; +} + +static void +axpmodemctl(Uart* uart, int on) +{ + Ccb *ccb; + u16int lp; + + ccb = ((Cc*)(uart->regs))->ccb; + + ilock(&uart->tlock); + lp = ccb->lp; + if(on){ + lp |= Cts|Rts; + lp &= ~Emcs; + uart->cts = ccb->ms & Scts; + } + else{ + lp &= ~(Cts|Rts); + lp |= Emcs; + uart->cts = 1; + } + uart->modem = on; + iunlock(&uart->tlock); + + ccb->lp = lp; + axpcc(uart->regs, Ccu); +} + +static int +axpparity(Uart* uart, int parity) +{ + Ccb *ccb; + u16int df; + + switch(parity){ + default: + return -1; + case 'e': + parity = Ep; + break; + case 'o': + parity = Op; + break; + case 'n': + parity = Np; + break; + } + + ccb = ((Cc*)(uart->regs))->ccb; + + df = ccb->df & ~PMASK; + ccb->df = df|parity; + axpcc(uart->regs, Ccu); + + return 0; +} + +static int +axpstop(Uart* uart, int stop) +{ + Ccb *ccb; + u16int df; + + switch(stop){ + default: + return -1; + case 1: + stop = Sb1; + break; + case 2: + stop = Sb2; + break; + } + + ccb = ((Cc*)(uart->regs))->ccb; + + df = ccb->df & ~SbMASK; + ccb->df = df|stop; + axpcc(uart->regs, Ccu); + + return 0; +} + +static int +axpbits(Uart* uart, int bits) +{ + Ccb *ccb; + u16int df; + + bits -= 5; + if(bits < 0 || bits > 3) + return -1; + + ccb = ((Cc*)(uart->regs))->ccb; + + df = ccb->df & ~DbMASK; + ccb->df = df|bits; + axpcc(uart->regs, Ccu); + + return 0; +} + +static int +axpbaud(Uart* uart, int baud) +{ + Ccb *ccb; + int i, ibtr; + + /* + * Set baud rate (high rates are special - only 16 bits). + */ + if(baud <= 0) + return -1; + uart->baud = baud; + + ccb = ((Cc*)(uart->regs))->ccb; + + switch(baud){ + default: + ccb->br = baud; + break; + case 76800: + ccb->br = Br76800; + break; + case 115200: + ccb->br = Br115200; + break; + } + + /* + * Set trigger level to about 50 per second. + */ + ibtr = baud/500; + i = (ccb->ibea - ccb->ibsa)/2; + if(ibtr > i) + ibtr = i; + ccb->ibtr = ibtr; + axpcc(uart->regs, Ccu); + + return 0; +} + +static void +axpbreak(Uart* uart, int ms) +{ + Ccb *ccb; + u16int mc; + + /* + * Send a break. + */ + if(ms <= 0) + ms = 200; + + ccb = ((Cc*)(uart->regs))->ccb; + + mc = ccb->mc; + ccb->mc = Ab|mc; + tsleep(&up->sleep, return0, 0, ms); + ccb->mc = mc & ~Ab; +} + +/* only called from interrupt service */ +static void +axpmc(Cc* cc) +{ + int old; + Ccb *ccb; + u16int ms; + + ccb = cc->ccb; + + ms = ccb->ms; + + if(ms & Scts){ + ilock(&cc->tlock); + old = cc->cts; + cc->cts = ms & Scts; + if(old == 0 && cc->cts) + cc->ctsbackoff = 2; + iunlock(&cc->tlock); + } + if(ms & Sdsr){ + old = ms & Sdsr; + if(cc->hup_dsr && cc->dsr && !old) + cc->dohup = 1; + cc->dsr = old; + } + if(ms & Sdcd){ + old = ms & Sdcd; + if(cc->hup_dcd && cc->dcd && !old) + cc->dohup = 1; + cc->dcd = old; + } +} + +/* called from uartkick() with uart->tlock ilocked */ +static void +axpkick(Uart* uart) +{ + Cc *cc; + Ccb *ccb; + uchar *ep, *mem, *rp, *wp, *bp; + + if(uart->cts == 0 || uart->blocked) + return; + + cc = uart->regs; + ccb = cc->ccb; + + mem = (uchar*)cc->ctlr->gcb; + bp = mem + ccb->obsa; + rp = mem + ccb->obrp; + wp = mem + ccb->obwp; + ep = mem + ccb->obea; + while(wp != rp-1 && (rp != bp || wp != ep)){ + /* + * if we've exhausted the uart's output buffer, + * ask for more from the output queue, and quit if there + * isn't any. + */ + if(uart->op >= uart->oe && uartstageoutput(uart) == 0) + break; + *wp++ = *(uart->op++); + if(wp > ep) + wp = bp; + ccb->obwp = wp - mem; + } +} + +/* only called from interrupt service */ +static void +axprecv(Cc* cc) +{ + Ccb *ccb; + uchar *ep, *mem, *rp, *wp; + + ccb = cc->ccb; + + mem = (uchar*)cc->ctlr->gcb; + rp = mem + ccb->ibrp; + wp = mem + ccb->ibwp; + ep = mem + ccb->ibea; + + while(rp != wp){ + uartrecv(cc, *rp++); /* ilocks cc->tlock */ + if(rp > ep) + rp = mem + ccb->ibsa; + ccb->ibrp = rp - mem; + } +} + +static void +axpinterrupt(Ureg*, void* arg) +{ + int work; + Cc *cc; + Ctlr *ctlr; + u32int ics; + u16int r, sr; + + work = 0; + ctlr = arg; + ics = csr32r(ctlr, Ics); + if(ics & 0x0810C000) + print("%s: unexpected interrupt %#ux\n", ctlr->name, ics); + if(!(ics & 0x00002000)) { + /* we get a steady stream of these on consoles */ + // print("%s: non-doorbell interrupt\n", ctlr->name); + ctlr->gcb->gcw2 = 0x0001; /* set Gintack */ + return; + } + +// while(work to do){ + cc = ctlr->cc; + for(sr = xchgw(&ctlr->gcb->isr, 0); sr != 0; sr >>= 1){ + if(sr & 0x0001) + work++, axprecv(cc); + cc++; + } + cc = ctlr->cc; + for(sr = xchgw(&ctlr->gcb->osr, 0); sr != 0; sr >>= 1){ + if(sr & 0x0001) + work++, uartkick(&cc->Uart); + cc++; + } + cc = ctlr->cc; + for(sr = xchgw(&ctlr->gcb->csr, 0); sr != 0; sr >>= 1){ + if(sr & 0x0001) + work++, wakeup(cc); + cc++; + } + cc = ctlr->cc; + for(sr = xchgw(&ctlr->gcb->msr, 0); sr != 0; sr >>= 1){ + if(sr & 0x0001) + work++, axpmc(cc); + cc++; + } + cc = ctlr->cc; + for(sr = xchgw(&ctlr->gcb->esr, 0); sr != 0; sr >>= 1){ + if(sr & 0x0001){ + r = cc->ccb->ms; + if(r & Oe) + cc->oerr++; + if(r & Pe) + cc->perr++; + if(r & Fe) + cc->ferr++; + if (r & (Oe|Pe|Fe)) + work++; + } + cc++; + } +// } + /* only meaningful if we don't share the irq */ + if (0 && !work) + print("%s: interrupt with no work\n", ctlr->name); + csr32w(ctlr, Pdb, 1); /* clear doorbell interrupt */ + ctlr->gcb->gcw2 = 0x0001; /* set Gintack */ +} + +static void +axpdisable(Uart* uart) +{ + Cc *cc; + u16int lp; + Ctlr *ctlr; + + /* + * Turn off DTR and RTS, disable interrupts. + */ + (*uart->phys->dtr)(uart, 0); + (*uart->phys->rts)(uart, 0); + + cc = uart->regs; + lp = cc->ccb->lp; + cc->ccb->lp = Emcs|lp; + axpcc(cc, Dt|Dr|Fob|Fib|Ccu); + + /* + * The Uart is qlocked. + */ + ctlr = cc->ctlr; + ctlr->im &= ~(1<<cc->uartno); + if(ctlr->im == 0) + intrdisable(ctlr->pcidev->intl, axpinterrupt, ctlr, + ctlr->pcidev->tbdf, ctlr->name); +} + +static void +axpenable(Uart* uart, int ie) +{ + Cc *cc; + Ctlr *ctlr; + u16int lp; + + cc = uart->regs; + ctlr = cc->ctlr; + + /* + * Enable interrupts and turn on DTR and RTS. + * Be careful if this is called to set up a polled serial line + * early on not to try to enable interrupts as interrupt- + * -enabling mechanisms might not be set up yet. + */ + if(ie){ + /* + * The Uart is qlocked. + */ + if(ctlr->im == 0){ + intrenable(ctlr->pcidev->intl, axpinterrupt, ctlr, + ctlr->pcidev->tbdf, ctlr->name); + csr32w(ctlr, Ics, 0x00031F00); + csr32w(ctlr, Pdb, 1); + ctlr->gcb->gcw2 = 1; + } + ctlr->im |= 1<<cc->uartno; + } + + (*uart->phys->dtr)(uart, 1); + (*uart->phys->rts)(uart, 1); + + /* + * Make sure we control RTS, DTR and break. + */ + lp = cc->ccb->lp; + cc->ccb->lp = Emcs|lp; + cc->ccb->oblw = 64; + axpcc(cc, Et|Er|Ccu); +} + +static void* +axpdealloc(Ctlr* ctlr) +{ + int i; + + for(i = 0; i < 16; i++){ + if(ctlr->cc[i].name != nil) + free(ctlr->cc[i].name); + } + if(ctlr->reg != nil) + vunmap(ctlr->reg, ctlr->pcidev->mem[0].size); + if(ctlr->mem != nil) + vunmap(ctlr->mem, ctlr->pcidev->mem[2].size); + if(ctlr->name != nil) + free(ctlr->name); + free(ctlr); + + return nil; +} + +static Uart* +axpalloc(int ctlrno, Pcidev* pcidev) +{ + Cc *cc; + uchar *p; + Ctlr *ctlr; + void *addr; + char name[64]; + u32int bar, r; + int i, n, timeo; + + ctlr = malloc(sizeof(Ctlr)); + seprint(name, name+sizeof(name), "uartaxp%d", ctlrno); + kstrdup(&ctlr->name, name); + ctlr->pcidev = pcidev; + ctlr->ctlrno = ctlrno; + + /* + * Access to runtime registers. + */ + bar = pcidev->mem[0].bar; + if((addr = vmap(bar & ~0x0F, pcidev->mem[0].size)) == 0){ + print("%s: can't map registers at %#ux\n", ctlr->name, bar); + return axpdealloc(ctlr); + } + ctlr->reg = addr; + print("%s: port 0x%ux irq %d ", ctlr->name, bar, pcidev->intl); + + /* + * Local address space 0. + */ + bar = pcidev->mem[2].bar; + if((addr = vmap(bar & ~0x0F, pcidev->mem[2].size)) == 0){ + print("%s: can't map memory at %#ux\n", ctlr->name, bar); + return axpdealloc(ctlr); + } + ctlr->mem = addr; + ctlr->gcb = (Gcb*)(ctlr->mem+0x10000); + print("mem 0x%ux size %d: ", bar, pcidev->mem[2].size); + + /* + * Toggle the software reset and wait for + * the adapter local init status to indicate done. + * + * The two 'delay(100)'s below are important, + * without them the board seems to become confused + * (perhaps it needs some 'quiet time' because the + * timeout loops are not sufficient in themselves). + */ + r = csr32r(ctlr, Mcc); + csr32w(ctlr, Mcc, r|Asr); + microdelay(1); + csr32w(ctlr, Mcc, r&~Asr); + delay(100); + + for(timeo = 0; timeo < 100000; timeo++){ + if(csr32r(ctlr, Mcc) & Lis) + break; + microdelay(1); + } + if(!(csr32r(ctlr, Mcc) & Lis)){ + print("%s: couldn't reset\n", ctlr->name); + return axpdealloc(ctlr); + } + print("downloading..."); + /* + * Copy the control programme to the card memory. + * The card's i960 control structures live at 0xD000. + */ + if(sizeof(uartaxpcp) > 0xD000){ + print("%s: control programme too big\n", ctlr->name); + return axpdealloc(ctlr); + } + /* TODO: is this right for more than 1 card? devastar does the same */ + csr32w(ctlr, Remap, 0xA0000001); + for(i = 0; i < sizeof(uartaxpcp); i++) + ctlr->mem[i] = uartaxpcp[i]; + /* + * Execute downloaded code and wait for it + * to signal ready. + */ + csr32w(ctlr, Mb0, Edcc); + delay(100); + /* the manual says to wait for Cpr for 1 second */ + for(timeo = 0; timeo < 10000; timeo++){ + if(csr32r(ctlr, Mb0) & Cpr) + break; + microdelay(100); + } + if(!(csr32r(ctlr, Mb0) & Cpr)){ + print("control programme not ready; Mb0 %#ux\n", + csr32r(ctlr, Mb0)); + print("%s: distribution panel not connected or card not fully seated?\n", + ctlr->name); + + return axpdealloc(ctlr); + } + print("\n"); + + n = ctlr->gcb->ccbn; + if(ctlr->gcb->bt != 0x12 || n > 16){ + print("%s: wrong board type %#ux, %d channels\n", + ctlr->name, ctlr->gcb->bt, ctlr->gcb->ccbn); + return axpdealloc(ctlr); + } + + p = ((uchar*)ctlr->gcb) + ctlr->gcb->ccboff; + for(i = 0; i < n; i++){ + cc = &ctlr->cc[i]; + cc->ccb = (Ccb*)p; + p += ctlr->gcb->ccbsz; + cc->uartno = i; + cc->ctlr = ctlr; + + cc->regs = cc; /* actually Uart->regs */ + seprint(name, name+sizeof(name), "uartaxp%d%2.2d", ctlrno, i); + kstrdup(&cc->name, name); + cc->freq = 0; + cc->bits = 8; + cc->stop = 1; + cc->parity = 'n'; + cc->baud = 9600; + cc->phys = &axpphysuart; + cc->console = 0; + cc->special = 0; + + cc->next = &ctlr->cc[i+1]; + } + ctlr->cc[n-1].next = nil; + + ctlr->next = nil; + if(axpctlrhead != nil) + axpctlrtail->next = ctlr; + else + axpctlrhead = ctlr; + axpctlrtail = ctlr; + + return ctlr->cc; +} + +static Uart* +axppnp(void) +{ + Pcidev *p; + int ctlrno; + Uart *head, *tail, *uart; + + /* + * Loop through all PCI devices looking for simple serial + * controllers (ccrb == 0x07) and configure the ones which + * are familiar. + */ + head = tail = nil; + ctlrno = 0; + for(p = pcimatch(nil, 0, 0); p != nil; p = pcimatch(p, 0, 0)){ + if(p->ccrb != 0x07) + continue; + + switch((p->did<<16)|p->vid){ + default: + continue; + case (0x6001<<16)|0x114F: /* AvanstarXp */ + if((uart = axpalloc(ctlrno, p)) == nil) + continue; + break; + } + + if(head != nil) + tail->next = uart; + else + head = uart; + for(tail = uart; tail->next != nil; tail = tail->next) + ; + ctlrno++; + } + + return head; +} + +PhysUart axpphysuart = { + .name = "AvanstarXp", + .pnp = axppnp, + .enable = axpenable, + .disable = axpdisable, + .kick = axpkick, + .dobreak = axpbreak, + .baud = axpbaud, + .bits = axpbits, + .stop = axpstop, + .parity = axpparity, + .modemctl = axpmodemctl, + .rts = axprts, + .dtr = axpdtr, + .status = axpstatus, + .fifo = axpfifo, + .getc = nil, + .putc = nil, +}; |