2011-03-30 12:46:40 +00:00
|
|
|
#include "u.h"
|
|
|
|
#include "../port/lib.h"
|
|
|
|
#include "mem.h"
|
|
|
|
#include "dat.h"
|
|
|
|
#include "fns.h"
|
|
|
|
#include "io.h"
|
|
|
|
#include "../port/error.h"
|
|
|
|
|
|
|
|
/*
|
|
|
|
* 8250 UART and compatibles.
|
|
|
|
*/
|
|
|
|
enum {
|
|
|
|
Uart0 = 0x3F8, /* COM1 */
|
|
|
|
Uart0IRQ = 4,
|
|
|
|
Uart1 = 0x2F8, /* COM2 */
|
|
|
|
Uart1IRQ = 3,
|
|
|
|
|
|
|
|
UartFREQ = 1843200,
|
|
|
|
};
|
|
|
|
|
|
|
|
enum { /* I/O ports */
|
|
|
|
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 */
|
|
|
|
Dll = 0, /* Divisor Latch LSB */
|
|
|
|
Dlm = 1, /* Divisor Latch MSB */
|
|
|
|
};
|
|
|
|
|
|
|
|
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 */
|
|
|
|
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 */
|
|
|
|
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 */
|
|
|
|
};
|
|
|
|
|
|
|
|
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 */
|
|
|
|
};
|
|
|
|
|
|
|
|
typedef struct Ctlr {
|
|
|
|
int io;
|
|
|
|
int irq;
|
|
|
|
int tbdf;
|
|
|
|
int iena;
|
|
|
|
|
|
|
|
uchar sticky[8];
|
|
|
|
|
|
|
|
Lock;
|
|
|
|
int hasfifo;
|
|
|
|
int checkfifo;
|
|
|
|
int fena;
|
|
|
|
} Ctlr;
|
|
|
|
|
|
|
|
extern PhysUart i8250physuart;
|
|
|
|
|
2013-01-13 09:23:31 +00:00
|
|
|
static Ctlr i8250ctlr[2] = {
|
2011-03-30 12:46:40 +00:00
|
|
|
{ .io = Uart0,
|
|
|
|
.irq = Uart0IRQ,
|
|
|
|
.tbdf = BUSUNKNOWN, },
|
|
|
|
|
|
|
|
{ .io = Uart1,
|
|
|
|
.irq = Uart1IRQ,
|
|
|
|
.tbdf = BUSUNKNOWN, },
|
|
|
|
};
|
|
|
|
|
2013-01-13 09:23:31 +00:00
|
|
|
static Uart i8250uart[2] = {
|
2011-03-30 12:46:40 +00:00
|
|
|
{ .regs = &i8250ctlr[0],
|
|
|
|
.name = "COM1",
|
|
|
|
.freq = UartFREQ,
|
|
|
|
.phys = &i8250physuart,
|
|
|
|
.special= 0,
|
|
|
|
.next = &i8250uart[1], },
|
|
|
|
|
|
|
|
{ .regs = &i8250ctlr[1],
|
|
|
|
.name = "COM2",
|
|
|
|
.freq = UartFREQ,
|
|
|
|
.phys = &i8250physuart,
|
|
|
|
.special= 0,
|
|
|
|
.next = nil, },
|
|
|
|
};
|
|
|
|
|
|
|
|
#define csr8r(c, r) inb((c)->io+(r))
|
|
|
|
#define csr8w(c, r, v) outb((c)->io+(r), (c)->sticky[(r)]|(v))
|
|
|
|
|
|
|
|
static long
|
|
|
|
i8250status(Uart* uart, void* buf, long n, long offset)
|
|
|
|
{
|
|
|
|
char *p;
|
|
|
|
Ctlr *ctlr;
|
|
|
|
uchar ier, lcr, mcr, msr;
|
|
|
|
|
|
|
|
ctlr = uart->regs;
|
2011-12-12 15:55:26 +00:00
|
|
|
p = smalloc(READSTR);
|
2011-03-30 12:46:40 +00:00
|
|
|
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, ctlr->sticky[Ier]);
|
|
|
|
uart->modem = 1;
|
|
|
|
uart->cts = csr8r(ctlr, Msr) & Cts;
|
|
|
|
}
|
|
|
|
else{
|
|
|
|
ctlr->sticky[Ier] &= ~Ems;
|
|
|
|
csr8w(ctlr, Ier, ctlr->sticky[Ier]);
|
|
|
|
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)
|
|
|
|
{
|
|
|
|
ulong bgc;
|
|
|
|
Ctlr *ctlr;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Set the Baud rate by calculating and setting the Baud rate
|
|
|
|
* Generator Constant. This will work with fairly non-standard
|
|
|
|
* Baud rates.
|
|
|
|
*/
|
|
|
|
if(uart->freq == 0 || baud <= 0)
|
|
|
|
return -1;
|
|
|
|
bgc = (uart->freq+8*baud-1)/(16*baud);
|
|
|
|
|
|
|
|
ctlr = uart->regs;
|
|
|
|
csr8w(ctlr, Lcr, Dlab);
|
|
|
|
outb(ctlr->io+Dlm, bgc>>8);
|
|
|
|
outb(ctlr->io+Dll, bgc);
|
|
|
|
csr8w(ctlr, Lcr, 0);
|
|
|
|
|
|
|
|
uart->baud = baud;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
i8250break(Uart* uart, int ms)
|
|
|
|
{
|
|
|
|
Ctlr *ctlr;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* 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
|
|
|
|
i8250kick(Uart* uart)
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
Ctlr *ctlr;
|
|
|
|
|
|
|
|
if(uart->cts == 0 || uart->blocked)
|
|
|
|
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
|
|
|
|
*/
|
|
|
|
ctlr = uart->regs;
|
|
|
|
for(i = 0; i < 128; i++){
|
|
|
|
if(!(csr8r(ctlr, Lsr) & Thre))
|
|
|
|
break;
|
|
|
|
if(uart->op >= uart->oe && uartstageoutput(uart) == 0)
|
|
|
|
break;
|
|
|
|
outb(ctlr->io+Thr, *(uart->op++));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
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 0x%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, ctlr->sticky[Ier]);
|
|
|
|
|
|
|
|
if(ctlr->iena != 0){
|
2014-12-22 15:56:04 +00:00
|
|
|
ctlr->iena = 0;
|
|
|
|
intrdisable(ctlr->irq, i8250interrupt, uart, ctlr->tbdf, uart->name);
|
2011-03-30 12:46:40 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
i8250enable(Uart* uart, int ie)
|
|
|
|
{
|
|
|
|
Ctlr *ctlr;
|
|
|
|
|
|
|
|
ctlr = uart->regs;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* 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){
|
|
|
|
intrenable(ctlr->irq, i8250interrupt, uart, ctlr->tbdf, uart->name);
|
|
|
|
ctlr->iena = 1;
|
|
|
|
}
|
|
|
|
ctlr->sticky[Ier] = Ethre|Erda;
|
|
|
|
ctlr->sticky[Mcr] |= Ie;
|
|
|
|
}
|
|
|
|
else{
|
|
|
|
ctlr->sticky[Ier] = 0;
|
|
|
|
ctlr->sticky[Mcr] = 0;
|
|
|
|
}
|
|
|
|
csr8w(ctlr, Ier, ctlr->sticky[Ier]);
|
|
|
|
csr8w(ctlr, Mcr, ctlr->sticky[Mcr]);
|
|
|
|
|
|
|
|
(*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);
|
|
|
|
}
|
|
|
|
|
|
|
|
void*
|
|
|
|
i8250alloc(int io, int irq, int tbdf)
|
|
|
|
{
|
|
|
|
Ctlr *ctlr;
|
|
|
|
|
2013-05-20 15:43:30 +00:00
|
|
|
ctlr = malloc(sizeof(Ctlr));
|
|
|
|
if(ctlr == nil){
|
|
|
|
print("i8250alloc: no memory for Ctlr\n");
|
|
|
|
return nil;
|
2011-03-30 12:46:40 +00:00
|
|
|
}
|
2013-05-20 15:43:30 +00:00
|
|
|
ctlr->io = io;
|
|
|
|
ctlr->irq = irq;
|
|
|
|
ctlr->tbdf = tbdf;
|
2011-03-30 12:46:40 +00:00
|
|
|
return ctlr;
|
|
|
|
}
|
|
|
|
|
|
|
|
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;
|
|
|
|
|
|
|
|
ctlr = uart->regs;
|
|
|
|
for(i = 0; !(csr8r(ctlr, Lsr)&Thre) && i < 128; i++)
|
|
|
|
delay(1);
|
|
|
|
outb(ctlr->io+Thr, c);
|
|
|
|
for(i = 0; !(csr8r(ctlr, Lsr)&Thre) && i < 128; i++)
|
|
|
|
delay(1);
|
|
|
|
}
|
|
|
|
|
|
|
|
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,
|
|
|
|
};
|
|
|
|
|
|
|
|
void
|
|
|
|
i8250console(void)
|
|
|
|
{
|
|
|
|
Uart *uart;
|
|
|
|
int n;
|
|
|
|
char *cmd, *p;
|
|
|
|
|
|
|
|
if((p = getconf("console")) == nil)
|
|
|
|
return;
|
|
|
|
n = strtoul(p, &cmd, 0);
|
2013-01-13 09:23:31 +00:00
|
|
|
if(p == cmd || n < 0 || n >= nelem(i8250uart))
|
2011-03-30 12:46:40 +00:00
|
|
|
return;
|
2013-01-13 09:23:31 +00:00
|
|
|
uart = &i8250uart[n];
|
2011-03-30 12:46:40 +00:00
|
|
|
|
|
|
|
(*uart->phys->enable)(uart, 0);
|
|
|
|
uartctl(uart, "b9600 l8 pn s1");
|
|
|
|
if(*cmd != '\0')
|
|
|
|
uartctl(uart, cmd);
|
|
|
|
|
|
|
|
consuart = uart;
|
|
|
|
uart->console = 1;
|
|
|
|
}
|