diff options
Diffstat (limited to 'os/pc/i8250.c')
| -rw-r--r-- | os/pc/i8250.c | 328 |
1 files changed, 328 insertions, 0 deletions
diff --git a/os/pc/i8250.c b/os/pc/i8250.c new file mode 100644 index 00000000..c625013e --- /dev/null +++ b/os/pc/i8250.c @@ -0,0 +1,328 @@ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "io.h" +#include "../port/error.h" + +/* + * INS8250 uart + */ +enum +{ + /* + * register numbers + */ + Data= 0, /* xmit/rcv buffer */ + Iena= 1, /* interrupt enable */ + Ircv= (1<<0), /* for char rcv'd */ + Ixmt= (1<<1), /* for xmit buffer empty */ + Irstat=(1<<2), /* for change in rcv'er status */ + Imstat=(1<<3), /* for change in modem status */ + Istat= 2, /* interrupt flag (read) */ + Fenabd=(3<<6), /* on if fifo's enabled */ + Fifoctl=2, /* fifo control (write) */ + Fena= (1<<0), /* enable xmit/rcv fifos */ + Ftrig= (1<<6), /* trigger after 4 input characters */ + Fclear=(3<<1), /* clear xmit & rcv fifos */ + Format= 3, /* byte format */ + Bits8= (3<<0), /* 8 bits/byte */ + Stop2= (1<<2), /* 2 stop bits */ + Pena= (1<<3), /* generate parity */ + Peven= (1<<4), /* even parity */ + Pforce=(1<<5), /* force parity */ + Break= (1<<6), /* generate a break */ + Dra= (1<<7), /* address the divisor */ + Mctl= 4, /* modem control */ + Dtr= (1<<0), /* data terminal ready */ + Rts= (1<<1), /* request to send */ + Ri= (1<<2), /* ring */ + Inton= (1<<3), /* turn on interrupts */ + Loop= (1<<4), /* loop back */ + Lstat= 5, /* line status */ + Inready=(1<<0), /* receive buffer full */ + Oerror=(1<<1), /* receiver overrun */ + Perror=(1<<2), /* receiver parity error */ + Ferror=(1<<3), /* rcv framing error */ + Outready=(1<<5), /* output buffer empty */ + Mstat= 6, /* modem status */ + Ctsc= (1<<0), /* clear to send changed */ + Dsrc= (1<<1), /* data set ready changed */ + Rire= (1<<2), /* rising edge of ring indicator */ + Dcdc= (1<<3), /* data carrier detect changed */ + Cts= (1<<4), /* complement of clear to send line */ + Dsr= (1<<5), /* complement of data set ready line */ + Ring= (1<<6), /* complement of ring indicator line */ + Dcd= (1<<7), /* complement of data carrier detect line */ + Scratch=7, /* scratchpad */ + Dlsb= 0, /* divisor lsb */ + Dmsb= 1, /* divisor msb */ + + Serial= 0, + Modem= 1, +}; + +typedef struct Uart Uart; +struct Uart +{ + int port; + uchar sticky[8]; /* sticky write register values */ + int nofifo; + + void (*rx)(int); /* routine to take a received character */ + int (*tx)(void); /* routine to get a character to transmit */ + + ulong frame; + ulong overrun; +}; + +static Uart i8250uart[1]; + +#define UartFREQ 1843200 + +#define i8250regw(u, r, v) outb((u)->port+(r), (u)->sticky[(r)]|(v)) +#define i8250regr(u, r) inb((u)->port+(r)) + +/* + * set the baud rate by calculating and setting the baudrate + * generator constant. This will work with fairly non-standard + * baud rates. + */ +static void +i8250setbaud(Uart* uart, int rate) +{ + ulong brconst; + + brconst = (UartFREQ+8*rate-1)/(16*rate); + + i8250regw(uart, Format, Dra); + outb(uart->port+Dmsb, (brconst>>8) & 0xff); + outb(uart->port+Dlsb, brconst & 0xff); + i8250regw(uart, Format, 0); +} + +/* + * toggle DTR + */ +static void +i8250dtr(Uart* uart, int n) +{ + if(n) + uart->sticky[Mctl] |= Dtr; + else + uart->sticky[Mctl] &= ~Dtr; + i8250regw(uart, Mctl, 0); +} + +/* + * toggle RTS + */ +static void +i8250rts(Uart* uart, int n) +{ + if(n) + uart->sticky[Mctl] |= Rts; + else + uart->sticky[Mctl] &= ~Rts; + i8250regw(uart, Mctl, 0); +} + +/* + * Enable/disable FIFOs (if possible). + */ +static void +i8250fifo(Uart* uart, int n) +{ + int i, s; + + if(uart->nofifo) + return; + + s = splhi(); + + /* reset fifos */ + i8250regw(uart, Fifoctl, Fclear); + + /* empty buffer and interrupt conditions */ + for(i = 0; i < 16; i++){ + if(i8250regr(uart, Istat)) + {} + if(i8250regr(uart, Data)) + {} + } + + /* turn on fifo */ + if(n){ + i8250regw(uart, Fifoctl, Fena|Ftrig); + + if((i8250regr(uart, Istat) & Fenabd) == 0){ + /* didn't work, must be an earlier chip type */ + uart->nofifo = 1; + } + } + + splx(s); +} + +#ifdef notdef +static void +i8250intr(Ureg*, void* arg) +{ + Uart *uart; + int ch; + int s, l, loops; + + uart = arg; + for(loops = 0; loops < 1024; loops++){ + s = i8250regr(uart, Istat); + switch(s & 0x3F){ + case 6: /* receiver line status */ + l = i8250regr(uart, Lstat); + if(l & Ferror) + uart->frame++; + if(l & Oerror) + uart->overrun++; + break; + + case 4: /* received data available */ + case 12: + ch = inb(uart->port+Data); + if(uart->rx) + (*uart->rx)(ch & 0x7F); + break; + + case 2: /* transmitter empty */ + ch = -1; + if(uart->tx) + ch = (*uart->tx)(); + if(ch != -1) + outb(uart->port+Data, ch); + break; + + case 0: /* modem status */ + i8250regr(uart, Mstat); + break; + + default: + if(s&1) + return; + print("weird modem interrupt #%2.2ux\n", s); + break; + } + } + panic("i8250intr: 0x%2.2ux\n", i8250regr(uart, Istat)); +} +#endif /* notdef */ + +/* + * turn on a port's interrupts. set DTR and RTS + */ +static void +i8250enable(Uart* uart) +{ + /* + * turn on interrupts + */ + uart->sticky[Iena] = 0; +#ifdef notdef + if(uart->tx) + uart->sticky[Iena] |= Ixmt; + if(uart->rx) + uart->sticky[Iena] |= Ircv|Irstat; +#endif /* notdef */ + + /* + * turn on DTR and RTS + */ + i8250dtr(uart, 1); + i8250rts(uart, 1); + i8250fifo(uart, 1); + + i8250regw(uart, Iena, 0); +} + +void +i8250special(int port, void (*rx)(int), int (*tx)(void), int baud) +{ + Uart *uart = &i8250uart[0]; + + if(uart->port) + return; + + switch(port){ + + case 0: + uart->port = 0x3F8; +#ifdef notdef + intrenable(VectorUART0, i8250intr, uart, BUSUNKNOWN); +#endif /* notdef */ + break; + + case 1: + uart->port = 0x2F8; +#ifdef notdef + intrenable(VectorUART1, i8250intr, uart, BUSUNKNOWN); +#endif /* notdef */ + break; + + default: + return; + } + + /* + * set rate to 9600 baud. + * 8 bits/character. + * 1 stop bit. + * interrupts enabled. + */ + i8250setbaud(uart, 9600); + uart->sticky[Format] = Bits8; + i8250regw(uart, Format, 0); + uart->sticky[Mctl] |= Inton; + i8250regw(uart, Mctl, 0x0); + + uart->rx = rx; + uart->tx = tx; + i8250enable(uart); + if(baud) + i8250setbaud(uart, baud); +} + +int +i8250getc(void) +{ + Uart *uart = &i8250uart[0]; + + if(i8250regr(uart, Lstat) & Inready) + return inb(uart->port+Data); + return 0; +} + +void +i8250putc(int c) +{ + Uart *uart = &i8250uart[0]; + int i; + + for(i = 0; i < 100; i++){ + if(i8250regr(uart, Lstat) & Outready) + break; + delay(1); + } + outb(uart->port+Data, c); +} + +void +i8250puts(char* s, int n) +{ + int x; + + x = splhi(); + while(n--){ + if(*s == '\n') + i8250putc('\r'); + i8250putc(*s++); + } + splx(x); +} |
