summaryrefslogtreecommitdiff
path: root/os/boot/rpcg/devuart.c
diff options
context:
space:
mode:
authorCharles.Forsyth <devnull@localhost>2006-12-22 21:39:35 +0000
committerCharles.Forsyth <devnull@localhost>2006-12-22 21:39:35 +0000
commit74a4d8c26dd3c1e9febcb717cfd6cb6512991a7a (patch)
treec6e220ba61db3a6ea4052e6841296d829654e664 /os/boot/rpcg/devuart.c
parent46439007cf417cbd9ac8049bb4122c890097a0fa (diff)
20060303
Diffstat (limited to 'os/boot/rpcg/devuart.c')
-rw-r--r--os/boot/rpcg/devuart.c230
1 files changed, 230 insertions, 0 deletions
diff --git a/os/boot/rpcg/devuart.c b/os/boot/rpcg/devuart.c
new file mode 100644
index 00000000..14e38592
--- /dev/null
+++ b/os/boot/rpcg/devuart.c
@@ -0,0 +1,230 @@
+#include "u.h"
+#include "lib.h"
+#include "mem.h"
+#include "dat.h"
+#include "fns.h"
+#include "io.h"
+
+/*
+ * SMC1 in UART mode
+ */
+
+typedef struct Uartsmc Uartsmc;
+struct Uartsmc {
+ IOCparam;
+ ushort maxidl;
+ ushort idlc;
+ ushort brkln;
+ ushort brkec;
+ ushort brkcr;
+ ushort rmask;
+};
+
+typedef struct Uart Uart;
+struct Uart
+{
+ int port;
+ int setup;
+ uchar txbusy;
+
+ Queue* iq;
+ Queue* oq;
+ void (*rx)(Queue*, int);
+ void (*boot)(uchar*, int);
+
+ ulong frame;
+ ulong overrun;
+ uchar rxbuf[128];
+ char txbuf[16];
+ BD* rxb;
+ BD* txb;
+};
+
+Uart uart[1];
+int predawn = 1;
+
+static void uartintr(Ureg*, void*);
+static void uartkick(void*);
+
+static int
+baudgen(int baud)
+{
+ int d;
+
+ d = ((m->cpuhz/baud)+8)>>4;
+ if(d >= (1<<12))
+ return ((d+15)>>3)|1;
+ return d<<1;
+}
+
+static void
+smcsetup(Uart *up, int baud)
+{
+ IMM *io;
+ Uartsmc *p;
+ BD *bd;
+ SMC *smc;
+
+ archenableuart(SMC1ID, 0);
+ io = m->iomem;
+ io->pbpar |= IBIT(24)|IBIT(25); /* enable SMC1 TX/RX */
+ io->pbdir &= ~(IBIT(24)|IBIT(25));
+ io->brgc1 = baudgen(baud) | BaudEnable;
+ io->simode &= ~0xF000; /* SMC1 to NMSI mode, Tx/Rx clocks are BRG1 */
+
+ bd = bdalloc(1);
+ p = (Uartsmc*)KADDR(SMC1P);
+ p->rbase = (ushort)bd;
+ up->rxb = bd;
+ bd->status = BDEmpty|BDWrap|BDInt;
+ bd->length = 0;
+ bd->addr = PADDR(up->rxbuf);
+ bd = bdalloc(1);
+ p->tbase = (ushort)bd;
+ up->txb = bd;
+ bd->status = BDWrap|BDInt;
+ bd->length = 0;
+ bd->addr = PADDR(up->txbuf);
+
+ cpmop(InitRxTx, SMC1ID, 0);
+
+ /* protocol parameters */
+ p->rfcr = 0x18;
+ p->tfcr = 0x18;
+ p->mrblr = 1;
+ p->maxidl = 1;
+ p->brkln = 0;
+ p->brkec = 0;
+ p->brkcr = 1;
+ smc = IOREGS(0xA80, SMC);
+ smc->smce = 0xff; /* clear events */
+ smc->smcm = 0x17; /* enable all possible interrupts */
+ setvec(VectorCPIC+4, uartintr, up);
+ smc->smcmr = 0x4820; /* 8-bit mode, no parity, 1 stop bit, UART mode, ... */
+ smc->smcmr |= 3; /* enable rx/tx */
+}
+
+static void
+uartintr(Ureg*, void *arg)
+{
+ Uart *up;
+ int ch, i;
+ BD *bd;
+ SMC *smc;
+ Block *b;
+
+ up = arg;
+ smc = IOREGS(0xA80, SMC);
+ smc->smce = 0xff; /* clear all events */
+ if((bd = up->rxb) != nil && (bd->status & BDEmpty) == 0){
+ if(up->iq != nil && bd->length > 0){
+ if(up->boot != nil){
+ up->boot(up->rxbuf, bd->length);
+ }else if(up->rx != nil){
+ for(i=0; i<bd->length; i++){
+ ch = up->rxbuf[i];
+ up->rx(up->iq, ch);
+ }
+ }else{
+ b = iallocb(bd->length);
+ memmove(b->wp, up->rxbuf, bd->length);
+ b->wp += bd->length;
+ qbwrite(up->iq, b);
+ }
+ }
+ bd->status |= BDEmpty|BDInt;
+ } else if((bd = up->txb) != nil && (bd->status & BDReady) == 0){
+ ch = -1;
+ if(up->oq)
+ ch = qbgetc(up->oq);
+ if(ch != -1){
+ up->txbuf[0] = ch;
+ bd->length = 1;
+ bd->status |= BDReady;
+ }else
+ up->txbusy = 0;
+ }
+ /* TO DO: modem status, errors, etc */
+}
+
+static void
+uartkick(void *arg)
+{
+ Uart *up = arg;
+ int s, c, i;
+
+ s = splhi();
+ while(up->txbusy == 0 && (c = qbgetc(up->oq)) != -1){
+ if(predawn){
+ while(up->txb->status & BDReady)
+ ;
+ } else {
+ for(i = 0; i < 100; i++){
+ if((up->txb->status & BDReady) == 0)
+ break;
+ delay(1);
+ }
+ }
+ up->txbuf[0] = c;
+ up->txb->length = 1;
+ up->txb->status |= BDReady;
+ up->txbusy = !predawn;
+ }
+ splx(s);
+}
+
+void
+uartspecial(int port, int baud, Queue **iq, Queue **oq, void (*rx)(Queue*,int))
+{
+ Uart *up = &uart[0];
+
+ if(up->setup)
+ return;
+ up->setup = 1;
+
+ *iq = up->iq = qopen(4*1024, 0, 0, 0);
+ *oq = up->oq = qopen(16*1024, 0, uartkick, up);
+ up->rx = rx;
+ USED(port);
+ up->port = SMC1ID;
+ if(baud == 0)
+ baud = 9600;
+ smcsetup(up, baud);
+ /* if using SCCn's UART, would also set DTR and RTS, but SMC doesn't use them */
+}
+
+void
+uartsetboot(void (*f)(uchar*, int))
+{
+ uart[0].boot = f;
+}
+
+void
+uartputs(char *s, int n)
+{
+ Uart *up = &uart[0];
+ Block *b;
+ int nl;
+ char *p;
+
+ nl = 0;
+ for(p = s; p < s+n; p++)
+ if(*p == '\n')
+ nl++;
+ b = iallocb(n+nl);
+ while(n--){
+ if(*s == '\n')
+ *b->wp++ = '\r';
+ *b->wp++ = *s++;
+ }
+ qbwrite(up->oq, b);
+}
+
+void
+uartwait(void)
+{
+ Uart *up = &uart[0];
+
+ while(up->txbusy)
+ ;
+}