| plan 9 kernel history: overview | file list | diff list |
1995/0108/power/devduart.c (diff list | history)
| power/devduart.c on 1991/0607 | ||
| 1991/0607 | #include "u.h" | |
| 1992/0321 | #include "../port/lib.h" | |
| 1991/0607 | #include "mem.h" #include "dat.h" #include "fns.h" #include "io.h" | |
| 1992/0111 | #include "../port/error.h" | |
| 1991/0607 | ||
| 1993/0806 | #include "devtab.h" #include "../port/netif.h" | |
| 1991/0607 | /* | |
| 1993/0806 | * Register set for half the duart. * There are really two sets in adjacent memory locations. | |
| 1991/0607 | */ | |
| 1992/0520 | struct Duartreg { | |
| 1993/0806 | uchar mr12; /* Mode Register Channels 1 & 2 */ uchar pad0[15]; uchar srcsr; /* Status Register/Clock Select Register */ uchar pad1[15]; uchar cmnd; /* Command Register */ uchar pad2[15]; uchar data; /* RX Holding / TX Holding Register */ uchar pad3[15]; uchar ipcacr; /* Input Uart Change/Aux. Control Register */ uchar pad4[15]; uchar isimr; /* Interrupt Status/Interrupt Mask Register */ uchar pad5[15]; uchar ctur; /* Counter/Timer Upper Register */ uchar pad6[15]; uchar ctlr; /* Counter/Timer Lower Register */ uchar pad7[15]; | |
| 1991/0607 | }; | |
| 1993/0806 | #define ppcr isimr /* in the second register set */ | |
| 1991/0607 | ||
| 1993/0806 | enum { DBD75 = 0, DBD110 = 1, DBD38400 = 2, DBD150 = 3, DBD300 = 4, DBD600 = 5, DBD1200 = 6, DBD2000 = 7, DBD2400 = 8, DBD4800 = 9, DBD1800 = 10, DBD9600 = 11, DBD19200 = 12, CHARERR = 0x00, /* MR1x - Mode Register 1 */ EVENPAR = 0x00, ODDPAR = 0x04, NOPAR = 0x10, CBITS8 = 0x03, CBITS7 = 0x02, CBITS6 = 0x01, CBITS5 = 0x00, NORMOP = 0x00, /* MR2x - Mode Register 2 */ TWOSTOPB = 0x0F, ONESTOPB = 0x07, ENBRX = 0x01, /* CRx - Command Register */ DISRX = 0x02, ENBTX = 0x04, DISTX = 0x08, RESETMR = 0x10, RESETRCV = 0x20, RESETTRANS = 0x30, RESETERR = 0x40, RESETBCH = 0x50, STRTBRK = 0x60, STOPBRK = 0x70, RCVRDY = 0x01, /* SRx - Channel Status Register */ FIFOFULL = 0x02, XMTRDY = 0x04, XMTEMT = 0x08, OVRERR = 0x10, PARERR = 0x20, FRMERR = 0x40, RCVDBRK = 0x80, IMIPC = 0x80, /* IMRx/ISRx - Int Mask/Interrupt Status */ IMDBB = 0x40, IMRRDYB = 0x20, IMXRDYB = 0x10, IMCRDY = 0x08, IMDBA = 0x04, IMRRDYA = 0x02, IMXRDYA = 0x01, BD38400 = 0xCC|0x0000, BD19200 = 0xCC|0x0100, BD9600 = 0xBB|0x0000, BD4800 = 0x99|0x0000, BD2400 = 0x88|0x0000, BD1200 = 0x66|0x0000, BD300 = 0x44|0x0000, | |
| 1991/0607 | ||
| 1993/0806 | Maxduart = 8, | |
| 1991/0607 | }; /* * requests to perform on a duart */ | |
| 1993/0806 | enum { | |
| 1991/0607 | Dnone= 0, Dbaud, Dbreak, Ddtr, Dprint, Dena, Dstate, }; /* | |
| 1992/0520 | * a duart | |
| 1991/0607 | */ | |
| 1992/0520 | typedef struct Duart Duart; struct Duart | |
| 1991/0607 | { QLock; | |
| 1992/0520 | Duartreg *reg; /* duart registers */ uchar imr; /* sticky interrupt mask reg bits */ uchar acr; /* sticky auxiliary reg bits */ int inited; }; Duart duart[Maxduart]; | |
| 1991/0607 | ||
| 1992/0520 | /* * values specific to a single duart port */ | |
| 1993/0806 | typedef struct Uart Uart; struct Uart | |
| 1992/0520 | { QLock; Duart *d; /* device */ Duartreg *reg; /* duart registers (for this port) */ int c; /* character to restart output */ int op; /* operation requested */ int val; /* value of operation */ Rendez opr; /* waiot here for op to complete */ | |
| 1993/0806 | int printing; /* need kick */ int opens; Rendez r; | |
| 1991/0607 | ||
| 1993/0806 | /* buffers */ int (*putc)(Queue*, int); Queue *iq; Queue *oq; | |
| 1991/0607 | }; | |
| 1993/0806 | Uart uart[2*Maxduart]; | |
| 1991/0607 | ||
| 1993/0806 | void duartkick(Uart*); | |
| 1991/0607 | /* * configure a duart port, default is 9600 baud, 8 bits/char, 1 stop bit, * no parity */ void | |
| 1993/0806 | duartsetup(Uart *p, Duart *d, int devno) | |
| 1991/0607 | { | |
| 1992/0520 | Duartreg *reg; | |
| 1991/0607 | ||
| 1992/0520 | p->d = d; reg = &d->reg[devno]; p->reg = reg; | |
| 1991/0607 | ||
| 1993/0806 | reg->cmnd = RESETRCV|DISTX|DISRX; reg->cmnd = RESETTRANS; reg->cmnd = RESETERR; reg->cmnd = STOPBRK; | |
| 1991/0607 | ||
| 1993/0806 | reg->cmnd = RESETMR; reg->mr12 = NOPAR|CBITS8; reg->mr12 = ONESTOPB; reg->srcsr = (DBD9600<<4)|DBD9600; reg->cmnd = ENBTX|ENBRX; p->iq = qopen(4*1024, 0, 0, 0); p->oq = qopen(4*1024, 0, duartkick, p); | |
| 1991/0607 | } /* * init the duart on the current processor */ void duartinit(void) { | |
| 1993/0806 | Uart *p; | |
| 1992/0520 | Duart *d; | |
| 1991/0607 | ||
| 1992/0520 | d = &duart[m->machno]; if(d->inited) | |
| 1991/0607 | return; | |
| 1993/0806 | ||
| 1992/0520 | d->reg = DUARTREG; | |
| 1993/0806 | d->imr = IMRRDYA|IMXRDYA|IMRRDYB|IMXRDYB; d->reg->isimr = d->imr; | |
| 1992/0520 | d->acr = 0x80; /* baud rate set 2 */ | |
| 1993/0806 | d->reg->ipcacr = d->acr; | |
| 1991/0607 | ||
| 1993/0806 | p = &uart[2*m->machno]; | |
| 1992/0520 | duartsetup(p, d, 0); p++; duartsetup(p, d, 1); | |
| 1992/0608 | d->inited = 1; | |
| 1991/0607 | } /* * enable a duart port */ void | |
| 1993/0806 | duartenable(Uart *p) | |
| 1991/0607 | { | |
| 1993/0806 | p->reg->cmnd = ENBTX|ENBRX; | |
| 1991/0607 | } void duartenable0(void) { | |
| 1993/0806 | DUARTREG->cmnd = ENBTX|ENBRX; | |
| 1991/0607 | } void | |
| 1993/0806 | duartbaud(Uart *p, int b) | |
| 1991/0607 | { | |
| 1992/0519 | int x; | |
| 1991/0607 | switch(b){ case 38400: x = BD38400; break; case 19200: x = BD19200; break; case 9600: x = BD9600; break; case 4800: x = BD4800; break; case 2400: x = BD2400; break; case 1200: x = BD1200; break; case 300: x = BD300; break; default: | |
| 1992/0516 | return; | |
| 1991/0607 | } if(x & 0x0100) | |
| 1992/0520 | p->d->acr |= 0x80; | |
| 1991/0607 | else | |
| 1992/0520 | p->d->acr &= ~0x80; | |
| 1993/0806 | p->d->reg->ipcacr = p->d->acr; p->reg->srcsr = x; | |
| 1991/0607 | } void | |
| 1993/0806 | duartdtr(Uart *p, int val) | |
| 1991/0607 | { if (val) | |
| 1992/0520 | p->reg->ctlr = 0x01; | |
| 1991/0607 | else | |
| 1992/0520 | p->reg->ctur = 0x01; | |
| 1991/0607 | } void | |
| 1993/0806 | duartbreak(Uart *p, int val) | |
| 1991/0607 | { | |
| 1992/0520 | Duartreg *reg; | |
| 1991/0607 | ||
| 1992/0520 | reg = p->reg; | |
| 1991/0607 | if (val){ | |
| 1993/0806 | p->d->imr &= ~IMXRDYB; p->d->reg->isimr = p->d->imr; reg->cmnd = STRTBRK|ENBTX; | |
| 1991/0607 | } else { | |
| 1993/0806 | reg->cmnd = STOPBRK|ENBTX; p->d->imr |= IMXRDYB; p->d->reg->isimr = p->d->imr; | |
| 1991/0607 | } } /* * do anything requested for this CPU's duarts */ void | |
| 1993/0806 | duartslave0(Uart *p) | |
| 1991/0607 | { | |
| 1992/0520 | switch(p->op){ | |
| 1991/0607 | case Ddtr: | |
| 1992/0520 | duartdtr(p, p->val); | |
| 1991/0607 | break; case Dbaud: | |
| 1992/0520 | duartbaud(p, p->val); | |
| 1991/0607 | break; case Dbreak: | |
| 1992/0520 | duartbreak(p, p->val); | |
| 1991/0607 | break; case Dprint: | |
| 1993/0806 | p->reg->cmnd = ENBTX; | |
| 1992/0520 | p->reg->data = p->val; | |
| 1991/0607 | break; case Dena: | |
| 1992/0520 | duartenable(p); | |
| 1991/0607 | break; case Dstate: | |
| 1992/0520 | p->val = p->reg->ppcr; | |
| 1991/0607 | break; } | |
| 1992/0520 | p->op = Dnone; wakeup(&p->opr); | |
| 1991/0607 | } void duartslave(void) { | |
| 1993/0806 | Uart *p; | |
| 1991/0607 | ||
| 1993/0806 | p = &uart[2*m->machno]; | |
| 1992/0520 | if(p->op != Dnone) duartslave0(p); p++; if(p->op != Dnone) duartslave0(p); | |
| 1991/0607 | } | |
| 1992/0519 | void | |
| 1993/0806 | duartrintr(Uart *p) | |
| 1991/0607 | { char ch; | |
| 1993/0806 | int status; Duartreg *reg; | |
| 1991/0607 | ||
| 1992/0520 | reg = p->reg; | |
| 1993/0806 | status = reg->srcsr; | |
| 1992/0520 | ch = reg->data; | |
| 1993/0806 | if(status & (FRMERR|OVRERR|PARERR)) reg->cmnd = RESETERR; | |
| 1991/0607 | ||
| 1993/0806 | if(p->putc) (*p->putc)(p->iq, ch); | |
| 1992/0519 | else | |
| 1993/0806 | qproduce(p->iq, &ch, 1); | |
| 1991/0607 | } | |
| 1993/0806 | /* * (re)start output */ | |
| 1992/0519 | void | |
| 1993/0806 | duartkick(Uart *p) | |
| 1991/0607 | { | |
| 1993/0806 | char ch; int n, x; x = splhi(); if(p->printing) { splx(x); return; } n = qconsume(p->oq, &ch, 1); if(n <= 0){ splx(x); return; } p->printing = 1; p->val = ch; p->op = Dprint; splx(x); } void duartxintr(Uart *p) { | |
| 1993/0807 | char ch; | |
| 1993/0806 | Duartreg *reg; | |
| 1991/0607 | ||
| 1992/0520 | reg = p->reg; | |
| 1993/0806 | if(qconsume(p->oq, &ch, 1) <= 0) { | |
| 1992/0520 | p->printing = 0; | |
| 1993/0806 | reg->cmnd = DISTX; } else | |
| 1992/0520 | reg->data = ch; | |
| 1991/0607 | } void duartintr(void) { | |
| 1992/0711 | int cause; | |
| 1992/0520 | Duartreg *reg; | |
| 1993/0806 | Uart *p; | |
| 1991/0607 | ||
| 1993/0806 | p = &uart[2*m->machno]; | |
| 1992/0520 | reg = p->reg; | |
| 1993/0806 | cause = reg->isimr; | |
| 1992/0520 | ||
| 1993/0806 | if(cause & IMRRDYA) | |
| 1992/0520 | duartrintr(p); | |
| 1993/0806 | if(cause & IMXRDYA) | |
| 1992/0520 | duartxintr(p); | |
| 1993/0806 | if(cause & IMRRDYB) | |
| 1992/0520 | duartrintr(p+1); | |
| 1993/0806 | if(cause & IMXRDYB) | |
| 1992/0520 | duartxintr(p+1); | |
| 1991/0607 | } /* * processor 0 only */ int duartrawputc(int c) { int i; | |
| 1993/0806 | Duartreg *reg; | |
| 1991/0607 | ||
| 1992/0520 | reg = DUARTREG; | |
| 1993/0806 | if(c == '\n') { | |
| 1991/0607 | duartrawputc('\r'); | |
| 1993/0806 | delay(100); } reg->cmnd = ENBTX; | |
| 1991/0607 | i = 0; | |
| 1993/0806 | while((reg->srcsr&XMTRDY) == 0 && i++ < 100000) ; | |
| 1992/0520 | reg->data = c; | |
| 1991/0607 | return c; } | |
| 1993/0501 | ||
| 1993/0806 | int | |
| 1991/0607 | iprint(char *fmt, ...) { | |
| 1993/0806 | int n, i; | |
| 1993/0501 | char buf[512]; | |
| 1991/0607 | ||
| 1993/0806 | n = doprint(buf, buf+sizeof(buf), fmt, (&fmt+1)) - buf; for(i = 0; i < n; i++) duartrawputc(buf[i]); return n; | |
| 1991/0607 | } void | |
| 1993/0806 | duartspecial(int port, int s, Queue **in, Queue **out, int (*putc)(Queue*, int)) | |
| 1991/0607 | { | |
| 1993/0806 | Uart *p; | |
| 1991/0607 | ||
| 1993/0806 | p = &uart[port]; | |
| 1991/0607 | ||
| 1993/0806 | duartenable(p); if(s) duartbaud(p, s); | |
| 1991/0607 | ||
| 1993/0806 | p->putc = putc; | |
| 1993/0807 | if(in != 0) | |
| 1993/0806 | *in = p->iq; | |
| 1993/0807 | if(out != 0) | |
| 1993/0806 | *out = p->oq; | |
| 1991/0727 | ||
| 1993/0806 | p->opens++; | |
| 1991/0607 | } static int | |
| 1992/0519 | opdone(void *x) | |
| 1991/0607 | { | |
| 1993/0806 | Uart *p = x; | |
| 1991/0607 | ||
| 1992/0520 | return p->op == Dnone; | |
| 1991/0607 | } Dirtab *duartdir; | |
| 1993/0806 | int nuart; | |
| 1991/0607 | void duartreset(void) { int i; | |
| 1993/0806 | Dirtab *dp; | |
| 1991/0607 | ||
| 1993/0806 | nuart = 2*conf.nmach; duartdir = xalloc(2 * nuart * sizeof(Dirtab)); dp = duartdir; for(i = 0; i < nuart; i++){ /* 2 directory entries per port */ print(dp->name, "eia%d", i); dp->qid.path = NETQID(i, Ndataqid); dp->perm = 0666; dp++; | |
| 1991/0607 | ||
| 1993/0806 | print(dp->name, "eia%dctl", i); dp->qid.path = NETQID(i, Nctlqid); dp->perm = 0666; dp++; | |
| 1991/0607 | } } Chan* duartattach(char *spec) { return devattach('t', spec); } Chan* duartclone(Chan *c, Chan *nc) { return devclone(c, nc); } int duartwalk(Chan *c, char *name) { | |
| 1993/0806 | return devwalk(c, name, duartdir, 2*nuart, devgen); | |
| 1991/0607 | } void | |
| 1993/0806 | duartstat(Chan *c, char *dp) | |
| 1991/0607 | { | |
| 1993/0806 | int i; Uart *p; Dir dir; i = NETID(c->qid.path); switch(NETTYPE(c->qid.path)){ case Ndataqid: p = &uart[i]; devdir(c, c->qid, duartdir[2*i].name, qlen(p->iq), eve, 0660, &dir); convD2M(&dir, dp); | |
| 1991/0607 | break; default: | |
| 1993/0806 | devstat(c, dp, duartdir, 2*nuart, devgen); | |
| 1991/0607 | break; } } Chan* duartopen(Chan *c, int omode) { | |
| 1993/0806 | Uart *p; | |
| 1991/0607 | ||
| 1993/0806 | if(c->qid.path & CHDIR){ if(omode != OREAD) error(Ebadarg); } else { p = &uart[NETID(c->qid.path)]; qlock(p); p->opens++; if(p->opens == 1) { /* enable the port */ p->op = Dena; sleep(&p->opr, opdone, p); qreopen(p->iq); qreopen(p->oq); } qunlock(p); | |
| 1991/0607 | } | |
| 1993/0806 | c->mode = omode&~OTRUNC; c->flag |= COPEN; c->offset = 0; return c; | |
| 1991/0607 | } void duartcreate(Chan *c, char *name, int omode, ulong perm) { | |
| 1993/0806 | USED(c, name, omode, perm); | |
| 1991/1120 | error(Eperm); | |
| 1991/0607 | } void duartclose(Chan *c) { | |
| 1993/0806 | Uart *p; if(c->qid.path & CHDIR) return; p = &uart[NETID(c->qid.path)]; qlock(p); p->opens++; if(p->opens == 0){ qclose(p->iq); qclose(p->oq); } qunlock(p); | |
| 1991/0607 | } long duartread(Chan *c, void *buf, long n, ulong offset) { | |
| 1993/0806 | Uart *p; | |
| 1991/0607 | ||
| 1993/0806 | if(c->qid.path & CHDIR) return devdirread(c, buf, n, duartdir, 2*nuart, devgen); | |
| 1991/0607 | ||
| 1993/0806 | p = &uart[NETID(c->qid.path)]; switch(NETTYPE(c->qid.path)){ case Ndataqid: return qread(p->iq, buf, n); case Nctlqid: return readnum(offset, buf, n, NETID(c->qid.path), NUMSIZE); | |
| 1991/0607 | } | |
| 1993/0806 | return 0; | |
| 1991/0607 | } | |
| 1995/0108 | Block* duartbread(Chan *c, long n, ulong offset) { return devbread(c, n, offset); } | |
| 1993/0806 | static void duartctl(Uart *p, char *cmd) { int n, i; /* let output drain for a while */ for(i = 0; i < 16 && qlen(p->oq); i++) tsleep(&p->r, qlen, p->oq, 125); n = atoi(cmd+1); switch(cmd[0]){ case 'B': case 'b': p->val = n; p->op = Dbaud; sleep(&p->opr, opdone, p); break; case 'D': case 'd': p->val = n; p->op = Ddtr; sleep(&p->opr, opdone, p); break; case 'K': case 'k': p->val = 1; p->op = Dbreak; if(!waserror()){ sleep(&p->opr, opdone, p); tsleep(&p->opr, return0, 0, n); poperror(); } p->val = 0; p->op = Dbreak; sleep(&p->opr, opdone, p); break; case 'R': case 'r': /* can't control? */ break; } } | |
| 1991/0607 | long duartwrite(Chan *c, void *va, long n, ulong offset) { | |
| 1993/0806 | Uart *p; char cmd[32]; | |
| 1992/0711 | USED(offset); | |
| 1993/0806 | if(c->qid.path & CHDIR) error(Eperm); p = &uart[NETID(c->qid.path)]; switch(NETTYPE(c->qid.path)){ case Ndataqid: | |
| 1994/0902 | return qwrite(p->oq, va, n); | |
| 1993/0806 | case Nctlqid: if(n >= sizeof(cmd)) n = sizeof(cmd)-1; memmove(cmd, va, n); cmd[n] = 0; duartctl(p, cmd); return n; } | |
| 1995/0108 | } long duartbwrite(Chan *c, Block *bp, ulong offset) { return devbwrite(c, bp, offset); | |
| 1991/0607 | } void duartremove(Chan *c) { | |
| 1991/1214 | USED(c); | |
| 1992/0114 | error(Eperm); | |
| 1991/0607 | } void | |
| 1992/0520 | duartwstat(Chan *c, char *p) | |
| 1991/0607 | { | |
| 1993/0806 | USED(c, p); | |
| 1992/0114 | error(Eperm); | |
| 1991/1214 | } | |