summaryrefslogtreecommitdiff
path: root/sys/src/9/omap/uarti8250.c
diff options
context:
space:
mode:
authorTaru Karttunen <taruti@taruti.net>2011-03-30 15:46:40 +0300
committerTaru Karttunen <taruti@taruti.net>2011-03-30 15:46:40 +0300
commite5888a1ffdae813d7575f5fb02275c6bb07e5199 (patch)
treed8d51eac403f07814b9e936eed0c9a79195e2450 /sys/src/9/omap/uarti8250.c
Import sources from 2011-03-30 iso image
Diffstat (limited to 'sys/src/9/omap/uarti8250.c')
-rwxr-xr-xsys/src/9/omap/uarti8250.c850
1 files changed, 850 insertions, 0 deletions
diff --git a/sys/src/9/omap/uarti8250.c b/sys/src/9/omap/uarti8250.c
new file mode 100755
index 000000000..7642a2685
--- /dev/null
+++ b/sys/src/9/omap/uarti8250.c
@@ -0,0 +1,850 @@
+/*
+ * omap35 8250-like UART
+ *
+ * we ignore the first 2 uarts on the omap35 (see below) and use the
+ * third one but call it 0.
+ */
+
+#include "u.h"
+#include "../port/lib.h"
+#include "mem.h"
+#include "dat.h"
+#include "fns.h"
+
+enum { /* registers */
+ Rbr = 0, /* Receiver Buffer (RO) */
+ Thr = 0, /* Transmitter Holding (WO) */
+ Ier = 1, /* Interrupt Enable */
+ Iir = 2, /* Interrupt Identification (RO) */
+ Fcr = 2, /* FIFO Control (WO) */
+ Lcr = 3, /* Line Control */
+ Mcr = 4, /* Modem Control */
+ Lsr = 5, /* Line Status */
+ Msr = 6, /* Modem Status */
+ Scr = 7, /* Scratch Pad */
+ Mdr = 8, /* Mode Def'n (omap rw) */
+// Usr = 31, /* Uart Status Register; missing in omap? */
+ Dll = 0, /* Divisor Latch LSB */
+ Dlm = 1, /* Divisor Latch MSB */
+};
+
+enum { /* Usr */
+ Busy = 0x01,
+};
+
+enum { /* Ier */
+ Erda = 0x01, /* Enable Received Data Available */
+ Ethre = 0x02, /* Enable Thr Empty */
+ Erls = 0x04, /* Enable Receiver Line Status */
+ Ems = 0x08, /* Enable Modem Status */
+};
+
+enum { /* Iir */
+ Ims = 0x00, /* Ms interrupt */
+ Ip = 0x01, /* Interrupt Pending (not) */
+ Ithre = 0x02, /* Thr Empty */
+ Irda = 0x04, /* Received Data Available */
+ Irls = 0x06, /* Receiver Line Status */
+ Ictoi = 0x0C, /* Character Time-out Indication */
+ IirMASK = 0x3F,
+ Ifena = 0xC0, /* FIFOs enabled */
+};
+
+enum { /* Fcr */
+ FIFOena = 0x01, /* FIFO enable */
+ FIFOrclr = 0x02, /* clear Rx FIFO */
+ FIFOtclr = 0x04, /* clear Tx FIFO */
+// FIFOdma = 0x08,
+ 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, /* no longer in use */
+// Ie = 0x08, /* IRQ Enable (cd_sts_ch on omap) */
+ 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, /* Transmitter Empty */
+ FIFOerr = 0x80, /* error in receiver FIFO */
+};
+
+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, /* Carrier Detect */
+};
+
+enum { /* Mdr */
+ Modemask = 7,
+ Modeuart = 0,
+};
+
+
+typedef struct Ctlr {
+ u32int* io;
+ int irq;
+ int tbdf;
+ int iena;
+ int poll;
+
+ uchar sticky[Scr+1];
+
+ Lock;
+ int hasfifo;
+ int checkfifo;
+ int fena;
+} Ctlr;
+
+extern PhysUart i8250physuart;
+
+static Ctlr i8250ctlr[] = {
+{ .io = (u32int*)PHYSCONS, /* UART3 in TI terminology */
+ .irq = 74,
+ .tbdf = -1,
+ .poll = 0, },
+
+/* these exist, but I don't think they're connected to anything external */
+//{ .io = (u32int*)PHYSUART0,
+// .irq = 72,
+// .tbdf = -1,
+// .poll = 0, },
+//
+//{ .io = (u32int*)PHYSUART1,
+// .irq = 73,
+// .tbdf = -1,
+// .poll = 0, },
+};
+
+static Uart i8250uart[] = {
+{ .regs = &i8250ctlr[0], /* not [2] */
+ .name = "COM3",
+ .freq = 3686000, /* Not used, we use the global i8250freq */
+ .phys = &i8250physuart,
+ .console= 1,
+ .next = nil, },
+
+/* these exist, but I don't think they're connected to anything external */
+//{ .regs = &i8250ctlr[0],
+// .name = "COM1",
+// .freq = 3686000, /* Not used, we use the global i8250freq */
+// .phys = &i8250physuart,
+// .special= 0,
+// .next = &i8250uart[1], },
+//
+//{ .regs = &i8250ctlr[1],
+// .name = "COM2",
+// .freq = 3686000, /* Not used, we use the global i8250freq */
+// .phys = &i8250physuart,
+// .special= 0,
+// .next = &i8250uart[2], },
+};
+
+#define csr8r(c, r) ((c)->io[r])
+#define csr8w(c, r, v) ((c)->io[r] = (c)->sticky[r] | (v), coherence())
+#define csr8o(c, r, v) ((c)->io[r] = (v), coherence())
+
+static long
+i8250status(Uart* uart, void* buf, long n, long offset)
+{
+ char *p;
+ Ctlr *ctlr;
+ uchar ier, lcr, mcr, msr;
+
+ ctlr = uart->regs;
+ p = malloc(READSTR);
+ mcr = ctlr->sticky[Mcr];
+ msr = csr8r(ctlr, Msr);
+ ier = ctlr->sticky[Ier];
+ lcr = ctlr->sticky[Lcr];
+ 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,
+ (msr & Dsr) != 0,
+ uart->hup_dsr,
+ (lcr & WlsMASK) + 5,
+ (ier & Ems) != 0,
+ (lcr & Pen) ? ((lcr & Eps) ? 'e': 'o'): 'n',
+ (mcr & Rts) != 0,
+ (lcr & Stb) ? 2: 1,
+ ctlr->fena,
+
+ uart->dev,
+ uart->type,
+ uart->ferr,
+ uart->oerr,
+ uart->berr,
+ uart->serr,
+ (msr & Cts) ? " cts": "",
+ (msr & Dsr) ? " dsr": "",
+ (msr & Dcd) ? " dcd": "",
+ (msr & Ri) ? " ring": ""
+ );
+ n = readstr(offset, buf, n, p);
+ free(p);
+
+ return n;
+}
+
+static void
+i8250fifo(Uart* uart, int level)
+{
+ Ctlr *ctlr;
+
+ ctlr = uart->regs;
+ if(ctlr->hasfifo == 0)
+ return;
+
+ /*
+ * 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.
+ * Some UARTs require FIFOena to be set before
+ * other bits can take effect, so set it twice.
+ */
+ 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);
+ csr8w(ctlr, Fcr, level);
+ iunlock(ctlr);
+}
+
+static void
+i8250dtr(Uart* uart, int on)
+{
+ Ctlr *ctlr;
+
+ /*
+ * Toggle DTR.
+ */
+ ctlr = uart->regs;
+ if(on)
+ ctlr->sticky[Mcr] |= Dtr;
+ else
+ ctlr->sticky[Mcr] &= ~Dtr;
+ csr8w(ctlr, Mcr, 0);
+}
+
+static void
+i8250rts(Uart* uart, int on)
+{
+ Ctlr *ctlr;
+
+ /*
+ * Toggle RTS.
+ */
+ ctlr = uart->regs;
+ if(on)
+ ctlr->sticky[Mcr] |= Rts;
+ else
+ ctlr->sticky[Mcr] &= ~Rts;
+ csr8w(ctlr, Mcr, 0);
+}
+
+static void
+i8250modemctl(Uart* uart, int on)
+{
+ Ctlr *ctlr;
+
+ ctlr = uart->regs;
+ ilock(&uart->tlock);
+ if(on){
+ ctlr->sticky[Ier] |= Ems;
+ csr8w(ctlr, Ier, 0);
+ uart->modem = 1;
+ uart->cts = csr8r(ctlr, Msr) & Cts;
+ }
+ else{
+ ctlr->sticky[Ier] &= ~Ems;
+ csr8w(ctlr, Ier, 0);
+ uart->modem = 0;
+ uart->cts = 1;
+ }
+ iunlock(&uart->tlock);
+
+ /* modem needs fifo */
+ (*uart->phys->fifo)(uart, on);
+}
+
+static int
+i8250parity(Uart* uart, int parity)
+{
+ int lcr;
+ Ctlr *ctlr;
+
+ ctlr = uart->regs;
+ lcr = ctlr->sticky[Lcr] & ~(Eps|Pen);
+
+ switch(parity){
+ case 'e':
+ lcr |= Eps|Pen;
+ break;
+ case 'o':
+ lcr |= Pen;
+ break;
+ case 'n':
+ break;
+ default:
+ return -1;
+ }
+ ctlr->sticky[Lcr] = lcr;
+ csr8w(ctlr, Lcr, 0);
+
+ uart->parity = parity;
+
+ return 0;
+}
+
+static int
+i8250stop(Uart* uart, int stop)
+{
+ int lcr;
+ Ctlr *ctlr;
+
+ ctlr = uart->regs;
+ lcr = ctlr->sticky[Lcr] & ~Stb;
+
+ switch(stop){
+ case 1:
+ break;
+ case 2:
+ lcr |= Stb;
+ break;
+ default:
+ return -1;
+ }
+ ctlr->sticky[Lcr] = lcr;
+ csr8w(ctlr, Lcr, 0);
+
+ uart->stop = stop;
+
+ return 0;
+}
+
+static int
+i8250bits(Uart* uart, int bits)
+{
+ int lcr;
+ Ctlr *ctlr;
+
+ ctlr = uart->regs;
+ lcr = ctlr->sticky[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;
+ }
+ ctlr->sticky[Lcr] = lcr;
+ csr8w(ctlr, Lcr, 0);
+
+ uart->bits = bits;
+
+ return 0;
+}
+
+static int
+i8250baud(Uart* uart, int baud)
+{
+#ifdef notdef /* don't change the speed */
+ ulong bgc;
+ Ctlr *ctlr;
+ extern int i8250freq; /* In the config file */
+
+ /*
+ * Set the Baud rate by calculating and setting the Baud rate
+ * Generator Constant. This will work with fairly non-standard
+ * Baud rates.
+ */
+ if(i8250freq == 0 || baud <= 0)
+ return -1;
+ bgc = (i8250freq+8*baud-1)/(16*baud);
+
+ ctlr = uart->regs;
+ while(csr8r(ctlr, Usr) & Busy)
+ delay(1);
+ csr8w(ctlr, Lcr, Dlab); /* begin kludge */
+ csr8o(ctlr, Dlm, bgc>>8);
+ csr8o(ctlr, Dll, bgc);
+ csr8w(ctlr, Lcr, 0);
+#endif
+ uart->baud = baud;
+ return 0;
+}
+
+static void
+i8250break(Uart* uart, int ms)
+{
+ Ctlr *ctlr;
+
+ if (up == nil)
+ panic("i8250break: nil up");
+ /*
+ * Send a break.
+ */
+ if(ms <= 0)
+ ms = 200;
+
+ ctlr = uart->regs;
+ csr8w(ctlr, Lcr, Brk);
+ tsleep(&up->sleep, return0, 0, ms);
+ csr8w(ctlr, Lcr, 0);
+}
+
+static void
+emptyoutstage(Uart *uart, int n)
+{
+ _uartputs((char *)uart->op, n);
+ uart->op = uart->oe = uart->ostage;
+}
+
+static void
+i8250kick(Uart* uart)
+{
+ int i;
+ Ctlr *ctlr;
+
+ if(/* uart->cts == 0 || */ uart->blocked)
+ return;
+
+ if(!normalprint) { /* early */
+ if (uart->op < uart->oe)
+ emptyoutstage(uart, uart->oe - uart->op);
+ while ((i = uartstageoutput(uart)) > 0)
+ emptyoutstage(uart, i);
+ return;
+ }
+
+ /* nothing more to send? then disable xmit intr */
+ ctlr = uart->regs;
+ if (uart->op >= uart->oe && qlen(uart->oq) == 0 &&
+ csr8r(ctlr, Lsr) & Temt) {
+ ctlr->sticky[Ier] &= ~Ethre;
+ csr8w(ctlr, Ier, 0);
+ return;
+ }
+
+ /*
+ * 128 here is an arbitrary limit to make sure
+ * we don't stay in this loop too long. If the
+ * chip's output queue is longer than 128, too
+ * bad -- presotto
+ */
+ for(i = 0; i < 128; i++){
+ if(!(csr8r(ctlr, Lsr) & Thre))
+ break;
+ if(uart->op >= uart->oe && uartstageoutput(uart) == 0)
+ break;
+ csr8o(ctlr, Thr, *uart->op++); /* start tx */
+ ctlr->sticky[Ier] |= Ethre;
+ csr8w(ctlr, Ier, 0); /* intr when done */
+ }
+}
+
+void
+serialkick(void)
+{
+ uartkick(&i8250uart[CONSOLE]);
+}
+
+static void
+i8250interrupt(Ureg*, void* arg)
+{
+ Ctlr *ctlr;
+ Uart *uart;
+ int iir, lsr, old, r;
+
+ uart = arg;
+ ctlr = uart->regs;
+ for(iir = csr8r(ctlr, Iir); !(iir & Ip); iir = csr8r(ctlr, Iir)){
+ switch(iir & IirMASK){
+ case Ims: /* Ms interrupt */
+ 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;
+ }
+ break;
+ case Ithre: /* Thr Empty */
+ uartkick(uart);
+ break;
+ case Irda: /* Received Data Available */
+ case Irls: /* Receiver Line Status */
+ case Ictoi: /* Character Time-out Indication */
+ /*
+ * 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.
+ */
+ while((lsr = csr8r(ctlr, Lsr)) & Dr){
+ if(lsr & (FIFOerr|Oe))
+ uart->oerr++;
+ if(lsr & Pe)
+ uart->perr++;
+ if(lsr & Fe)
+ uart->ferr++;
+ r = csr8r(ctlr, Rbr);
+ if(!(lsr & (Bi|Fe|Pe)))
+ uartrecv(uart, r);
+ }
+ break;
+
+ default:
+ iprint("weird uart interrupt type %#2.2uX\n", iir);
+ break;
+ }
+ }
+}
+
+static void
+i8250disable(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;
+ ctlr->sticky[Ier] = 0;
+ csr8w(ctlr, Ier, 0);
+
+ if(ctlr->iena != 0){
+ if(irqdisable(ctlr->irq, i8250interrupt, uart, uart->name) == 0)
+ ctlr->iena = 0;
+ }
+}
+
+static void
+i8250enable(Uart* uart, int ie)
+{
+ int mode;
+ Ctlr *ctlr;
+
+ if (up == nil)
+ return; /* too soon */
+
+ ctlr = uart->regs;
+
+ /* omap only: set uart/irda/cir mode to uart */
+ mode = csr8r(ctlr, Mdr);
+ csr8o(ctlr, Mdr, (mode & ~Modemask) | Modeuart);
+
+ ctlr->sticky[Lcr] = Wls8; /* no parity */
+ csr8w(ctlr, Lcr, 0);
+
+ /*
+ * Check if there is a FIFO.
+ * 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.
+ * Also, reading the Iir outwith i8250interrupt()
+ * can be dangerous, but this should only happen
+ * once, before interrupts are enabled.
+ */
+ ilock(ctlr);
+ if(!ctlr->checkfifo){
+ /*
+ * Wait until the transmitter is really empty.
+ */
+ while(!(csr8r(ctlr, Lsr) & Temt))
+ ;
+ csr8w(ctlr, Fcr, FIFOena);
+ if(csr8r(ctlr, Iir) & Ifena)
+ ctlr->hasfifo = 1;
+ csr8w(ctlr, Fcr, 0);
+ ctlr->checkfifo = 1;
+ }
+ iunlock(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){
+ if(ctlr->iena == 0 && !ctlr->poll){
+ irqenable(ctlr->irq, i8250interrupt, uart, uart->name);
+ ctlr->iena = 1;
+ }
+ ctlr->sticky[Ier] = Erda;
+// ctlr->sticky[Mcr] |= Ie; /* not on omap */
+ ctlr->sticky[Mcr] = 0;
+ }
+ else{
+ ctlr->sticky[Ier] = 0;
+ ctlr->sticky[Mcr] = 0;
+ }
+ csr8w(ctlr, Ier, 0);
+ csr8w(ctlr, Mcr, 0);
+
+ (*uart->phys->dtr)(uart, 1);
+ (*uart->phys->rts)(uart, 1);
+
+ /*
+ * During startup, the i8259 interrupt controller is reset.
+ * This may result in a lost interrupt from the i8250 uart.
+ * The i8250 thinks the interrupt is still outstanding and does not
+ * generate any further interrupts. The workaround is to call the
+ * interrupt handler to clear any pending interrupt events.
+ * Note: this must be done after setting Ier.
+ */
+ if(ie)
+ i8250interrupt(nil, uart);
+}
+
+static Uart*
+i8250pnp(void)
+{
+ return i8250uart;
+}
+
+static int
+i8250getc(Uart* uart)
+{
+ Ctlr *ctlr;
+
+ ctlr = uart->regs;
+ while(!(csr8r(ctlr, Lsr) & Dr))
+ delay(1);
+ return csr8r(ctlr, Rbr);
+}
+
+static void
+i8250putc(Uart* uart, int c)
+{
+ int i;
+ Ctlr *ctlr;
+
+ if (!normalprint) { /* too early; use brute force */
+ int s = splhi();
+
+ while (!(((ulong *)PHYSCONS)[Lsr] & Thre))
+ ;
+ ((ulong *)PHYSCONS)[Thr] = c;
+ coherence();
+ splx(s);
+ return;
+ }
+
+ ctlr = uart->regs;
+ for(i = 0; !(csr8r(ctlr, Lsr) & Thre) && i < 128; i++)
+ delay(1);
+ csr8o(ctlr, Thr, (uchar)c);
+ for(i = 0; !(csr8r(ctlr, Lsr) & Thre) && i < 128; i++)
+ delay(1);
+}
+
+void
+serialputc(int c)
+{
+ i8250putc(&i8250uart[CONSOLE], c);
+}
+
+void
+serialputs(char* s, int n)
+{
+ _uartputs(s, n);
+}
+
+#ifdef notdef
+static void
+i8250poll(Uart* uart)
+{
+ Ctlr *ctlr;
+
+ /*
+ * If PhysUart has a non-nil .poll member, this
+ * routine will be called from the uartclock timer.
+ * If the Ctlr .poll member is non-zero, when the
+ * Uart is enabled interrupts will not be enabled
+ * and the result is polled input and output.
+ * Not very useful here, but ports to new hardware
+ * or simulators can use this to get serial I/O
+ * without setting up the interrupt mechanism.
+ */
+ ctlr = uart->regs;
+ if(ctlr->iena || !ctlr->poll)
+ return;
+ i8250interrupt(nil, uart);
+}
+#endif
+
+PhysUart i8250physuart = {
+ .name = "i8250",
+ .pnp = i8250pnp,
+ .enable = i8250enable,
+ .disable = i8250disable,
+ .kick = i8250kick,
+ .dobreak = i8250break,
+ .baud = i8250baud,
+ .bits = i8250bits,
+ .stop = i8250stop,
+ .parity = i8250parity,
+ .modemctl = i8250modemctl,
+ .rts = i8250rts,
+ .dtr = i8250dtr,
+ .status = i8250status,
+ .fifo = i8250fifo,
+ .getc = i8250getc,
+ .putc = i8250putc,
+// .poll = i8250poll, /* only in 9k, not 9 */
+};
+
+static void
+i8250dumpregs(Ctlr* ctlr)
+{
+ int dlm, dll;
+ int _uartprint(char*, ...);
+
+ csr8w(ctlr, Lcr, Dlab);
+ dlm = csr8r(ctlr, Dlm);
+ dll = csr8r(ctlr, Dll);
+ csr8w(ctlr, Lcr, 0);
+
+ _uartprint("dlm %#ux dll %#ux\n", dlm, dll);
+}
+
+Uart* uartenable(Uart *p);
+
+/* must call this from a process's context */
+int
+i8250console(void)
+{
+ Uart *uart = &i8250uart[CONSOLE];
+
+ if (up == nil)
+ return -1; /* too early */
+
+ if(uartenable(uart) != nil /* && uart->console */){
+ // iprint("i8250console: enabling console uart\n");
+ kbdq = uart->iq;
+ serialoq = uart->oq;
+ uart->putc = kbdcr2nl;
+ uart->opens++;
+ consuart = uart;
+ }
+ uartctl(uart, "b115200 l8 pn r1 s1 i1");
+ return 0;
+}
+
+void
+_uartputs(char* s, int n)
+{
+ char *e;
+
+ for(e = s+n; s < e; s++){
+ if(*s == '\n')
+ i8250putc(&i8250uart[CONSOLE], '\r');
+ i8250putc(&i8250uart[CONSOLE], *s);
+ }
+}
+
+int
+_uartprint(char* fmt, ...)
+{
+ int n;
+ va_list arg;
+ char buf[PRINTSIZE];
+
+ va_start(arg, fmt);
+ n = vseprint(buf, buf+sizeof(buf), fmt, arg) - buf;
+ va_end(arg);
+ _uartputs(buf, n);
+
+ return n;
+}