summaryrefslogtreecommitdiff
path: root/os/manga/uartks8695.c
diff options
context:
space:
mode:
Diffstat (limited to 'os/manga/uartks8695.c')
-rw-r--r--os/manga/uartks8695.c629
1 files changed, 629 insertions, 0 deletions
diff --git a/os/manga/uartks8695.c b/os/manga/uartks8695.c
new file mode 100644
index 00000000..dd04f02d
--- /dev/null
+++ b/os/manga/uartks8695.c
@@ -0,0 +1,629 @@
+#include "u.h"
+#include "../port/lib.h"
+#include "mem.h"
+#include "dat.h"
+#include "fns.h"
+#include "io.h"
+#include "../port/error.h"
+
+#include "../port/uart.h"
+
+/*
+ * KS8695 uart; similar to 8250 etc but registers are slightly different,
+ * and interrupt control is quite different
+ */
+enum {
+ UartFREQ = CLOCKFREQ,
+};
+
+/*
+ * similar to i8250/16450/16550 (slight differences)
+ */
+
+enum { /* I/O ports */
+ Rbr = 0, /* Receiver Buffer (RO) */
+ Thr = 1, /* Transmitter Holding (WO) */
+ Fcr = 2, /* FIFO Control */
+ Lcr = 3, /* Line Control */
+ Mcr = 4, /* Modem Control */
+ Lsr = 5, /* Line Status */
+ Msr = 6, /* Modem Status */
+ Div = 7, /* Divisor */
+ Usr = 8, /* Status */
+};
+
+enum { /* Fcr */
+ FIFOena = 0x01, /* FIFO enable */
+ FIFOrclr = 0x02, /* clear Rx FIFO */
+ FIFOtclr = 0x04, /* clear Tx FIFO */
+ FIFO1 = 0x00, /* Rx FIFO trigger level 1 byte */
+ FIFO4 = 0x40, /* 4 bytes */
+ FIFO8 = 0x80, /* 8 bytes */
+ FIFO14 = 0xC0, /* 14 bytes */
+};
+
+enum { /* Lcr */
+ Wls5 = 0x00, /* Word Length Select 5 bits/byte */
+ Wls6 = 0x01, /* 6 bits/byte */
+ Wls7 = 0x02, /* 7 bits/byte */
+ Wls8 = 0x03, /* 8 bits/byte */
+ WlsMASK = 0x03,
+ Stb = 0x04, /* 2 stop bits */
+ Pen = 0x08, /* Parity Enable */
+ Eps = 0x10, /* Even Parity Select */
+ Stp = 0x20, /* Stick Parity */
+ Brk = 0x40, /* Break */
+ Dlab = 0x80, /* Divisor Latch Access Bit */
+};
+
+enum { /* Mcr */
+ Dtr = 0x01, /* Data Terminal Ready */
+ Rts = 0x02, /* Ready To Send */
+ Out1 = 0x04, /* UART OUT1 asserted */
+ Out2 = 0x08, /* UART OUT2 asserted */
+ Dm = 0x10, /* Diagnostic Mode loopback */
+};
+
+enum { /* Lsr */
+ Dr = 0x01, /* Data Ready */
+ Oe = 0x02, /* Overrun Error */
+ Pe = 0x04, /* Parity Error */
+ Fe = 0x08, /* Framing Error */
+ Bi = 0x10, /* Break Interrupt */
+ Thre = 0x20, /* Thr Empty */
+ Temt = 0x40, /* Tramsmitter Empty */
+ FIFOerr = 0x80, /* error in receiver FIFO */
+ LsrInput = FIFOerr|Oe|Pe|Fe|Dr|Bi, /* input status only */
+};
+
+enum { /* Msr */
+ Dcts = 0x01, /* Delta Cts */
+ Ddsr = 0x02, /* Delta Dsr */
+ Teri = 0x04, /* Trailing Edge of Ri */
+ Ddcd = 0x08, /* Delta Dcd */
+ Cts = 0x10, /* Clear To Send */
+ Dsr = 0x20, /* Data Set Ready */
+ Ri = 0x40, /* Ring Indicator */
+ Dcd = 0x80, /* Data Set Ready */
+};
+
+enum { /* Usr */
+ Uti = 0x01, /* INTST[9]=1=> =1, interrupt is timeout; =0, receive FIFO trigger */
+};
+
+typedef struct Ctlr {
+ ulong* regs;
+ int irq;
+ int iena;
+
+ Lock;
+ int fena;
+} Ctlr;
+
+extern PhysUart ks8695physuart;
+
+
+static Ctlr ks8695_ctlr[1] = {
+{ .regs = (ulong*)PHYSUART,
+ .irq = IRQuts, /* base: ts then rs, ls, ms */
+},
+};
+
+static Uart ks8695_uart[1] = {
+{ .regs = &ks8695_ctlr[0],
+ .name = "eia0",
+ .freq = UartFREQ,
+ .phys = &ks8695physuart,
+ .special= 0,
+ .next = nil, },
+};
+
+#define csr8r(c, r) ((c)->regs[(r)])
+#define csr8w(c, r, v) ((c)->regs[(r)] = (v))
+
+static long
+ks8695_status(Uart* uart, void* buf, long n, long offset)
+{
+ char *p;
+ Ctlr *ctlr;
+ uchar ier, lcr, mcr, msr;
+
+ ctlr = uart->regs;
+ p = malloc(READSTR);
+ mcr = csr8r(ctlr, Mcr);
+ msr = csr8r(ctlr, Msr);
+ ier = INTRREG->en;
+ lcr = csr8r(ctlr, Lcr);
+ snprint(p, READSTR,
+ "b%d c%d d%d e%d l%d m%d p%c r%d s%d i%d ier=%ux\n"
+ "dev(%d) type(%d) framing(%d) overruns(%d)%s%s%s%s\n",
+
+ uart->baud,
+ uart->hup_dcd,
+ (msr & Dsr) != 0,
+ uart->hup_dsr,
+ (lcr & WlsMASK) + 5,
+ (ier & (1<<IRQums)) != 0,
+ (lcr & Pen) ? ((lcr & Eps) ? 'e': 'o'): 'n',
+ (mcr & Rts) != 0,
+ (lcr & Stb) ? 2: 1,
+ ctlr->fena,
+ ier,
+
+ uart->dev,
+ uart->type,
+ uart->ferr,
+ uart->oerr,
+ (msr & Cts) ? " cts": "",
+ (msr & Dsr) ? " dsr": "",
+ (msr & Dcd) ? " dcd": "",
+ (msr & Ri) ? " ring": ""
+ );
+ n = readstr(offset, buf, n, p);
+ free(p);
+
+ return n;
+}
+
+static void
+ks8695_fifo(Uart* uart, int level)
+{
+ Ctlr *ctlr;
+
+ ctlr = uart->regs;
+
+ /*
+ * Changing the FIFOena bit in Fcr flushes data
+ * from both receive and transmit FIFOs; there's
+ * no easy way to guarantee not losing data on
+ * the receive side, but it's possible to wait until
+ * the transmitter is really empty.
+ */
+ ilock(ctlr);
+ while(!(csr8r(ctlr, Lsr) & Temt))
+ ;
+
+ /*
+ * Set the trigger level, default is the max.
+ * value.
+ */
+ ctlr->fena = level;
+ switch(level){
+ case 0:
+ break;
+ case 1:
+ level = FIFO1|FIFOena;
+ break;
+ case 4:
+ level = FIFO4|FIFOena;
+ break;
+ case 8:
+ level = FIFO8|FIFOena;
+ break;
+ default:
+ level = FIFO14|FIFOena;
+ break;
+ }
+ csr8w(ctlr, Fcr, level);
+ iunlock(ctlr);
+}
+
+static void
+ks8695_dtr(Uart* uart, int on)
+{
+ Ctlr *ctlr;
+ int r;
+
+ /*
+ * Toggle DTR.
+ */
+ ctlr = uart->regs;
+ r = csr8r(ctlr, Mcr);
+ if(on)
+ r |= Dtr;
+ else
+ r &= ~Dtr;
+ csr8w(ctlr, Mcr, r);
+}
+
+static void
+ks8695_rts(Uart* uart, int on)
+{
+ Ctlr *ctlr;
+ int r;
+
+ /*
+ * Toggle RTS.
+ */
+ ctlr = uart->regs;
+ r = csr8r(ctlr, Mcr);
+ if(on)
+ r |= Rts;
+ else
+ r &= ~Rts;
+ csr8w(ctlr, Mcr, r);
+}
+
+static void
+ks8695_modemctl(Uart* uart, int on)
+{
+ Ctlr *ctlr;
+
+ ctlr = uart->regs;
+ ilock(&uart->tlock);
+ if(on){
+ INTRREG->en |= 1<<IRQums; /* TO DO */
+ uart->modem = 1;
+ uart->cts = csr8r(ctlr, Msr) & Cts;
+ }
+ else{
+ INTRREG->en &= ~(1<<IRQums);
+ uart->modem = 0;
+ uart->cts = 1;
+ }
+ iunlock(&uart->tlock);
+
+ /* modem needs fifo */
+ (*uart->phys->fifo)(uart, on);
+}
+
+static int
+ks8695_parity(Uart* uart, int parity)
+{
+ int lcr;
+ Ctlr *ctlr;
+
+ ctlr = uart->regs;
+ lcr = csr8r(ctlr, Lcr) & ~(Eps|Pen);
+
+ switch(parity){
+ case 'e':
+ lcr |= Eps|Pen;
+ break;
+ case 'o':
+ lcr |= Pen;
+ break;
+ case 'n':
+ default:
+ break;
+ }
+ csr8w(ctlr, Lcr, lcr);
+
+ uart->parity = parity;
+
+ return 0;
+}
+
+static int
+ks8695_stop(Uart* uart, int stop)
+{
+ int lcr;
+ Ctlr *ctlr;
+
+ ctlr = uart->regs;
+ lcr = csr8r(ctlr, Lcr);
+ switch(stop){
+ case 1:
+ lcr &= ~Stb;
+ break;
+ case 2:
+ lcr |= Stb;
+ break;
+ default:
+ return -1;
+ }
+ csr8w(ctlr, Lcr, lcr);
+ uart->stop = stop;
+ return 0;
+}
+
+static int
+ks8695_bits(Uart* uart, int bits)
+{
+ int lcr;
+ Ctlr *ctlr;
+
+ ctlr = uart->regs;
+ lcr = csr8r(ctlr, Lcr) & ~WlsMASK;
+
+ switch(bits){
+ case 5:
+ lcr |= Wls5;
+ break;
+ case 6:
+ lcr |= Wls6;
+ break;
+ case 7:
+ lcr |= Wls7;
+ break;
+ case 8:
+ lcr |= Wls8;
+ break;
+ default:
+ return -1;
+ }
+ csr8w(ctlr, Lcr, lcr);
+
+ uart->bits = bits;
+
+ return 0;
+}
+
+static int
+ks8695_baud(Uart* uart, int baud)
+{
+ ulong bgc;
+ Ctlr *ctlr;
+
+ if(uart->freq == 0 || baud <= 0)
+ return -1;
+ ctlr = uart->regs;
+ bgc = (uart->freq+baud-1)/baud;
+ csr8w(ctlr, Div, bgc);
+ uart->baud = baud;
+ return 0;
+}
+
+static void
+ks8695_break(Uart* uart, int ms)
+{
+ Ctlr *ctlr;
+ int lcr;
+
+ /*
+ * Send a break.
+ */
+ if(ms == 0)
+ ms = 200;
+
+ ctlr = uart->regs;
+ lcr = csr8r(ctlr, Lcr);
+ csr8w(ctlr, Lcr, lcr|Brk);
+ tsleep(&up->sleep, return0, 0, ms);
+ csr8w(ctlr, Lcr, lcr);
+}
+
+static void
+ks8695_kick(Uart* uart)
+{
+ int i;
+ Ctlr *ctlr;
+
+ if(uart->cts == 0 || uart->blocked)
+ return;
+
+ ctlr = uart->regs;
+ for(i = 0; i < 16; i++){
+ if(!(csr8r(ctlr, Lsr) & Thre))
+ break;
+ if(uart->op >= uart->oe && uartstageoutput(uart) == 0)
+ break;
+ csr8w(ctlr, Thr, *uart->op++);
+ }
+}
+
+static void
+ks8695_modemintr(Ureg*, void *arg)
+{
+ Ctlr *ctlr;
+ Uart *uart;
+ int old, r;
+
+ uart = arg;
+ ctlr = uart->regs;
+ r = csr8r(ctlr, Msr);
+ if(r & Dcts){
+ ilock(&uart->tlock);
+ old = uart->cts;
+ uart->cts = r & Cts;
+ if(old == 0 && uart->cts)
+ uart->ctsbackoff = 2;
+ iunlock(&uart->tlock);
+ }
+ if(r & Ddsr){
+ old = r & Dsr;
+ if(uart->hup_dsr && uart->dsr && !old)
+ uart->dohup = 1;
+ uart->dsr = old;
+ }
+ if(r & Ddcd){
+ old = r & Dcd;
+ if(uart->hup_dcd && uart->dcd && !old)
+ uart->dohup = 1;
+ uart->dcd = old;
+ }
+}
+
+static void
+ks8695_rxintr(Ureg*, void* arg)
+{
+ Ctlr *ctlr;
+ Uart *uart;
+ int lsr, r;
+
+ /* handle line error status here as well */
+ uart = arg;
+ ctlr = uart->regs;
+ while((lsr = csr8r(ctlr, Lsr) & LsrInput) != 0){
+ /*
+ * Consume any received data.
+ * If the received byte came in with a break,
+ * parity or framing error, throw it away;
+ * overrun is an indication that something has
+ * already been tossed.
+ */
+ if(lsr & (FIFOerr|Oe))
+ uart->oerr++;
+ if(lsr & Pe)
+ uart->perr++;
+ if(lsr & Fe)
+ uart->ferr++;
+ if(lsr & Dr){
+ r = csr8r(ctlr, Rbr);
+ if(!(lsr & (Bi|Fe|Pe)))
+ uartrecv(uart, r);
+ }
+ }
+}
+
+static void
+ks8695_txintr(Ureg*, void* arg)
+{
+ uartkick(arg);
+}
+
+static void
+ks8695_disable(Uart* uart)
+{
+ Ctlr *ctlr;
+
+ /*
+ * Turn off DTR and RTS, disable interrupts and fifos.
+ */
+ (*uart->phys->dtr)(uart, 0);
+ (*uart->phys->rts)(uart, 0);
+ (*uart->phys->fifo)(uart, 0);
+
+ ctlr = uart->regs;
+
+ if(ctlr->iena != 0){
+ intrdisable(IRQ, ctlr->irq, ks8695_txintr, uart, uart->name);
+ intrdisable(IRQ, ctlr->irq+1, ks8695_rxintr, uart, uart->name);
+ intrdisable(IRQ, ctlr->irq+2, ks8695_rxintr, uart, uart->name);
+ intrdisable(IRQ, ctlr->irq+3, ks8695_modemintr, uart, uart->name);
+ ctlr->iena = 0;
+ }
+}
+
+static void
+ks8695_enable(Uart* uart, int ie)
+{
+ Ctlr *ctlr;
+
+ ctlr = uart->regs;
+
+ /*
+ * 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(ctlr->iena == 0 && ie){
+ intrenable(IRQ, ctlr->irq, ks8695_txintr, uart, uart->name);
+ intrenable(IRQ, ctlr->irq+1, ks8695_rxintr, uart, uart->name);
+ intrenable(IRQ, ctlr->irq+2, ks8695_rxintr, uart, uart->name);
+ intrenable(IRQ, ctlr->irq+3, ks8695_modemintr, uart, uart->name);
+ ctlr->iena = 1;
+ }
+
+ (*uart->phys->dtr)(uart, 1);
+ (*uart->phys->rts)(uart, 1);
+}
+
+static Uart*
+ks8695_pnp(void)
+{
+ return ks8695_uart;
+}
+
+static int
+ks8695_getc(Uart *uart)
+{
+ Ctlr *ctlr;
+
+ ctlr = uart->regs;
+ while(!(csr8r(ctlr, Lsr)&Dr))
+ delay(1);
+ return csr8r(ctlr, Rbr);
+}
+
+static void
+ks8695_putc(Uart *uart, int c)
+{
+ serialputc(c);
+#ifdef ROT
+ int i;
+ Ctlr *ctlr;
+
+ ctlr = uart->regs;
+ for(i = 0; !(csr8r(ctlr, Lsr)&Thre) && i < 256; i++)
+ delay(1);
+ csr8w(ctlr, Thr, c);
+ if(c == '\n')
+ while((csr8r(ctlr, Lsr) & Temt) == 0){ /* let fifo drain */
+ /* skip */
+ }
+#endif
+}
+
+PhysUart ks8695physuart = {
+ .name = "ks8695",
+ .pnp = ks8695_pnp,
+ .enable = ks8695_enable,
+ .disable = ks8695_disable,
+ .kick = ks8695_kick,
+ .dobreak = ks8695_break,
+ .baud = ks8695_baud,
+ .bits = ks8695_bits,
+ .stop = ks8695_stop,
+ .parity = ks8695_parity,
+ .modemctl = ks8695_modemctl,
+ .rts = ks8695_rts,
+ .dtr = ks8695_dtr,
+ .status = ks8695_status,
+ .fifo = ks8695_fifo,
+ .getc = ks8695_getc,
+ .putc = ks8695_putc,
+};
+
+void
+uartconsole(void)
+{
+ Uart *uart;
+
+ uart = &ks8695_uart[0];
+ (*uart->phys->enable)(uart, 0);
+ uartctl(uart, "b38400 l8 pn s1");
+ consuart = uart;
+ uart->console = 1;
+}
+
+#define UR(p,r) ((ulong*)(p))[r]
+
+void
+serialputc(int c)
+{
+ ulong *p;
+
+ if(c == 0)
+ return;
+ p = (ulong*)PHYSUART;
+ while((UR(p,Lsr) & Thre) == 0){
+ /* skip */
+ }
+ UR(p,Thr) = c;
+ if(c == '\n')
+ while((UR(p,Lsr) & Temt) == 0){ /* let fifo drain */
+ /* skip */
+ }
+}
+
+/*
+ * for iprint, just write it
+ */
+void
+serialputs(char *data, int len)
+{
+ ulong *p;
+
+ p = (ulong*)PHYSUART;
+ while(--len >= 0){
+ if(*data == '\n')
+ serialputc('\r');
+ serialputc(*data++);
+ }
+ while((UR(p,Lsr) & Temt) == 0){ /* let fifo drain */
+ /* skip */
+ }
+}
+void (*serwrite)(char*, int) = serialputs;