819 lines
16 KiB
C
819 lines
16 KiB
C
/*
|
|
* 8250-like UART
|
|
*/
|
|
|
|
#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,
|
|
.irq = Uartirq,
|
|
.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, },
|
|
};
|
|
|
|
#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");
|
|
serialoq = uart->oq;
|
|
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;
|
|
}
|