19a747e4fSDavid du Colombier #include "u.h"
29a747e4fSDavid du Colombier #include "../port/lib.h"
39a747e4fSDavid du Colombier #include "mem.h"
49a747e4fSDavid du Colombier #include "dat.h"
59a747e4fSDavid du Colombier #include "fns.h"
69a747e4fSDavid du Colombier #include "io.h"
79a747e4fSDavid du Colombier #include "../port/error.h"
89a747e4fSDavid du Colombier
99a747e4fSDavid du Colombier /*
109a747e4fSDavid du Colombier * 8250 UART and compatibles.
119a747e4fSDavid du Colombier */
129a747e4fSDavid du Colombier enum {
139a747e4fSDavid du Colombier Uart0 = 0x3F8, /* COM1 */
149a747e4fSDavid du Colombier Uart0IRQ = 4,
159a747e4fSDavid du Colombier Uart1 = 0x2F8, /* COM2 */
169a747e4fSDavid du Colombier Uart1IRQ = 3,
179a747e4fSDavid du Colombier
189a747e4fSDavid du Colombier UartFREQ = 1843200,
199a747e4fSDavid du Colombier };
209a747e4fSDavid du Colombier
219a747e4fSDavid du Colombier enum { /* I/O ports */
229a747e4fSDavid du Colombier Rbr = 0, /* Receiver Buffer (RO) */
239a747e4fSDavid du Colombier Thr = 0, /* Transmitter Holding (WO) */
249a747e4fSDavid du Colombier Ier = 1, /* Interrupt Enable */
259a747e4fSDavid du Colombier Iir = 2, /* Interrupt Identification (RO) */
269a747e4fSDavid du Colombier Fcr = 2, /* FIFO Control (WO) */
279a747e4fSDavid du Colombier Lcr = 3, /* Line Control */
289a747e4fSDavid du Colombier Mcr = 4, /* Modem Control */
299a747e4fSDavid du Colombier Lsr = 5, /* Line Status */
309a747e4fSDavid du Colombier Msr = 6, /* Modem Status */
319a747e4fSDavid du Colombier Scr = 7, /* Scratch Pad */
329a747e4fSDavid du Colombier Dll = 0, /* Divisor Latch LSB */
339a747e4fSDavid du Colombier Dlm = 1, /* Divisor Latch MSB */
349a747e4fSDavid du Colombier };
359a747e4fSDavid du Colombier
369a747e4fSDavid du Colombier enum { /* Ier */
379a747e4fSDavid du Colombier Erda = 0x01, /* Enable Received Data Available */
389a747e4fSDavid du Colombier Ethre = 0x02, /* Enable Thr Empty */
399a747e4fSDavid du Colombier Erls = 0x04, /* Enable Receiver Line Status */
409a747e4fSDavid du Colombier Ems = 0x08, /* Enable Modem Status */
419a747e4fSDavid du Colombier };
429a747e4fSDavid du Colombier
439a747e4fSDavid du Colombier enum { /* Iir */
449a747e4fSDavid du Colombier Ims = 0x00, /* Ms interrupt */
459a747e4fSDavid du Colombier Ip = 0x01, /* Interrupt Pending (not) */
469a747e4fSDavid du Colombier Ithre = 0x02, /* Thr Empty */
479a747e4fSDavid du Colombier Irda = 0x04, /* Received Data Available */
489a747e4fSDavid du Colombier Irls = 0x06, /* Receiver Line Status */
499a747e4fSDavid du Colombier Ictoi = 0x0C, /* Character Time-out Indication */
509a747e4fSDavid du Colombier IirMASK = 0x3F,
51e288d156SDavid du Colombier Ifena = 0xC0, /* FIFOs enabled */
529a747e4fSDavid du Colombier };
539a747e4fSDavid du Colombier
549a747e4fSDavid du Colombier enum { /* Fcr */
559a747e4fSDavid du Colombier FIFOena = 0x01, /* FIFO enable */
569a747e4fSDavid du Colombier FIFOrclr = 0x02, /* clear Rx FIFO */
579a747e4fSDavid du Colombier FIFOtclr = 0x04, /* clear Tx FIFO */
589a747e4fSDavid du Colombier FIFO1 = 0x00, /* Rx FIFO trigger level 1 byte */
599a747e4fSDavid du Colombier FIFO4 = 0x40, /* 4 bytes */
609a747e4fSDavid du Colombier FIFO8 = 0x80, /* 8 bytes */
619a747e4fSDavid du Colombier FIFO14 = 0xC0, /* 14 bytes */
629a747e4fSDavid du Colombier };
639a747e4fSDavid du Colombier
649a747e4fSDavid du Colombier enum { /* Lcr */
659a747e4fSDavid du Colombier Wls5 = 0x00, /* Word Length Select 5 bits/byte */
669a747e4fSDavid du Colombier Wls6 = 0x01, /* 6 bits/byte */
679a747e4fSDavid du Colombier Wls7 = 0x02, /* 7 bits/byte */
689a747e4fSDavid du Colombier Wls8 = 0x03, /* 8 bits/byte */
699a747e4fSDavid du Colombier WlsMASK = 0x03,
709a747e4fSDavid du Colombier Stb = 0x04, /* 2 stop bits */
719a747e4fSDavid du Colombier Pen = 0x08, /* Parity Enable */
729a747e4fSDavid du Colombier Eps = 0x10, /* Even Parity Select */
739a747e4fSDavid du Colombier Stp = 0x20, /* Stick Parity */
749a747e4fSDavid du Colombier Brk = 0x40, /* Break */
759a747e4fSDavid du Colombier Dlab = 0x80, /* Divisor Latch Access Bit */
769a747e4fSDavid du Colombier };
779a747e4fSDavid du Colombier
789a747e4fSDavid du Colombier enum { /* Mcr */
799a747e4fSDavid du Colombier Dtr = 0x01, /* Data Terminal Ready */
809a747e4fSDavid du Colombier Rts = 0x02, /* Ready To Send */
819a747e4fSDavid du Colombier Out1 = 0x04, /* no longer in use */
829a747e4fSDavid du Colombier Ie = 0x08, /* IRQ Enable */
839a747e4fSDavid du Colombier Dm = 0x10, /* Diagnostic Mode loopback */
849a747e4fSDavid du Colombier };
859a747e4fSDavid du Colombier
869a747e4fSDavid du Colombier enum { /* Lsr */
879a747e4fSDavid du Colombier Dr = 0x01, /* Data Ready */
889a747e4fSDavid du Colombier Oe = 0x02, /* Overrun Error */
899a747e4fSDavid du Colombier Pe = 0x04, /* Parity Error */
909a747e4fSDavid du Colombier Fe = 0x08, /* Framing Error */
919a747e4fSDavid du Colombier Bi = 0x10, /* Break Interrupt */
929a747e4fSDavid du Colombier Thre = 0x20, /* Thr Empty */
939a747e4fSDavid du Colombier Temt = 0x40, /* Tramsmitter Empty */
949a747e4fSDavid du Colombier FIFOerr = 0x80, /* error in receiver FIFO */
959a747e4fSDavid du Colombier };
969a747e4fSDavid du Colombier
979a747e4fSDavid du Colombier enum { /* Msr */
989a747e4fSDavid du Colombier Dcts = 0x01, /* Delta Cts */
999a747e4fSDavid du Colombier Ddsr = 0x02, /* Delta Dsr */
1009a747e4fSDavid du Colombier Teri = 0x04, /* Trailing Edge of Ri */
1019a747e4fSDavid du Colombier Ddcd = 0x08, /* Delta Dcd */
1029a747e4fSDavid du Colombier Cts = 0x10, /* Clear To Send */
1039a747e4fSDavid du Colombier Dsr = 0x20, /* Data Set Ready */
1049a747e4fSDavid du Colombier Ri = 0x40, /* Ring Indicator */
1059a747e4fSDavid du Colombier Dcd = 0x80, /* Data Set Ready */
1069a747e4fSDavid du Colombier };
1079a747e4fSDavid du Colombier
1089a747e4fSDavid du Colombier typedef struct Ctlr {
1099a747e4fSDavid du Colombier int io;
1109a747e4fSDavid du Colombier int irq;
1119a747e4fSDavid du Colombier int tbdf;
1129a747e4fSDavid du Colombier int iena;
1139a747e4fSDavid du Colombier
1149a747e4fSDavid du Colombier uchar sticky[8];
1159a747e4fSDavid du Colombier
1169a747e4fSDavid du Colombier Lock;
117e288d156SDavid du Colombier int hasfifo;
118e288d156SDavid du Colombier int checkfifo;
1199a747e4fSDavid du Colombier int fena;
1209a747e4fSDavid du Colombier } Ctlr;
1219a747e4fSDavid du Colombier
1229a747e4fSDavid du Colombier extern PhysUart i8250physuart;
1239a747e4fSDavid du Colombier
1249a747e4fSDavid du Colombier static Ctlr i8250ctlr[2] = {
1259a747e4fSDavid du Colombier { .io = Uart0,
1269a747e4fSDavid du Colombier .irq = Uart0IRQ,
1279a747e4fSDavid du Colombier .tbdf = BUSUNKNOWN, },
1289a747e4fSDavid du Colombier
1299a747e4fSDavid du Colombier { .io = Uart1,
1309a747e4fSDavid du Colombier .irq = Uart1IRQ,
1319a747e4fSDavid du Colombier .tbdf = BUSUNKNOWN, },
1329a747e4fSDavid du Colombier };
1339a747e4fSDavid du Colombier
1349a747e4fSDavid du Colombier static Uart i8250uart[2] = {
1359a747e4fSDavid du Colombier { .regs = &i8250ctlr[0],
1369a747e4fSDavid du Colombier .name = "COM1",
1379a747e4fSDavid du Colombier .freq = UartFREQ,
1389a747e4fSDavid du Colombier .phys = &i8250physuart,
1399a747e4fSDavid du Colombier .special= 0,
1409a747e4fSDavid du Colombier .next = &i8250uart[1], },
1419a747e4fSDavid du Colombier
1429a747e4fSDavid du Colombier { .regs = &i8250ctlr[1],
1439a747e4fSDavid du Colombier .name = "COM2",
1449a747e4fSDavid du Colombier .freq = UartFREQ,
1459a747e4fSDavid du Colombier .phys = &i8250physuart,
1469a747e4fSDavid du Colombier .special= 0,
1479a747e4fSDavid du Colombier .next = nil, },
1489a747e4fSDavid du Colombier };
1499a747e4fSDavid du Colombier
1509a747e4fSDavid du Colombier #define csr8r(c, r) inb((c)->io+(r))
1519a747e4fSDavid du Colombier #define csr8w(c, r, v) outb((c)->io+(r), (c)->sticky[(r)]|(v))
1529a747e4fSDavid du Colombier
1539a747e4fSDavid du Colombier static long
i8250status(Uart * uart,void * buf,long n,long offset)1549a747e4fSDavid du Colombier i8250status(Uart* uart, void* buf, long n, long offset)
1559a747e4fSDavid du Colombier {
1569a747e4fSDavid du Colombier char *p;
1579a747e4fSDavid du Colombier Ctlr *ctlr;
1589a747e4fSDavid du Colombier uchar ier, lcr, mcr, msr;
1599a747e4fSDavid du Colombier
1609a747e4fSDavid du Colombier p = malloc(READSTR);
161aa72973aSDavid du Colombier if(p == nil)
162aa72973aSDavid du Colombier error(Enomem);
163aa72973aSDavid du Colombier ctlr = uart->regs;
1649a747e4fSDavid du Colombier mcr = ctlr->sticky[Mcr];
1659a747e4fSDavid du Colombier msr = csr8r(ctlr, Msr);
1669a747e4fSDavid du Colombier ier = ctlr->sticky[Ier];
1679a747e4fSDavid du Colombier lcr = ctlr->sticky[Lcr];
1689a747e4fSDavid du Colombier snprint(p, READSTR,
1695243b8d1SDavid du Colombier "b%d c%d d%d e%d l%d m%d p%c r%d s%d i%d\n"
1705243b8d1SDavid du Colombier "dev(%d) type(%d) framing(%d) overruns(%d) "
1715243b8d1SDavid du Colombier "berr(%d) serr(%d)%s%s%s%s\n",
1729a747e4fSDavid du Colombier
1739a747e4fSDavid du Colombier uart->baud,
1749a747e4fSDavid du Colombier uart->hup_dcd,
1759a747e4fSDavid du Colombier (msr & Dsr) != 0,
1769a747e4fSDavid du Colombier uart->hup_dsr,
1779a747e4fSDavid du Colombier (lcr & WlsMASK) + 5,
1789a747e4fSDavid du Colombier (ier & Ems) != 0,
1799a747e4fSDavid du Colombier (lcr & Pen) ? ((lcr & Eps) ? 'e': 'o'): 'n',
1809a747e4fSDavid du Colombier (mcr & Rts) != 0,
1819a747e4fSDavid du Colombier (lcr & Stb) ? 2: 1,
1829a747e4fSDavid du Colombier ctlr->fena,
1839a747e4fSDavid du Colombier
1849a747e4fSDavid du Colombier uart->dev,
1859a747e4fSDavid du Colombier uart->type,
1869a747e4fSDavid du Colombier uart->ferr,
1879a747e4fSDavid du Colombier uart->oerr,
1885243b8d1SDavid du Colombier uart->berr,
1895243b8d1SDavid du Colombier uart->serr,
1909a747e4fSDavid du Colombier (msr & Cts) ? " cts": "",
1919a747e4fSDavid du Colombier (msr & Dsr) ? " dsr": "",
1929a747e4fSDavid du Colombier (msr & Dcd) ? " dcd": "",
1939a747e4fSDavid du Colombier (msr & Ri) ? " ring": ""
1949a747e4fSDavid du Colombier );
1959a747e4fSDavid du Colombier n = readstr(offset, buf, n, p);
1969a747e4fSDavid du Colombier free(p);
1979a747e4fSDavid du Colombier
1989a747e4fSDavid du Colombier return n;
1999a747e4fSDavid du Colombier }
2009a747e4fSDavid du Colombier
2019a747e4fSDavid du Colombier static void
i8250fifo(Uart * uart,int level)202e288d156SDavid du Colombier i8250fifo(Uart* uart, int level)
2039a747e4fSDavid du Colombier {
2049a747e4fSDavid du Colombier Ctlr *ctlr;
2059a747e4fSDavid du Colombier
2069a747e4fSDavid du Colombier ctlr = uart->regs;
207e288d156SDavid du Colombier if(ctlr->hasfifo == 0)
208e288d156SDavid du Colombier return;
2099a747e4fSDavid du Colombier
210e288d156SDavid du Colombier /*
211e288d156SDavid du Colombier * Changing the FIFOena bit in Fcr flushes data
212e288d156SDavid du Colombier * from both receive and transmit FIFOs; there's
213e288d156SDavid du Colombier * no easy way to guarantee not losing data on
214e288d156SDavid du Colombier * the receive side, but it's possible to wait until
215e288d156SDavid du Colombier * the transmitter is really empty.
216e288d156SDavid du Colombier */
2179a747e4fSDavid du Colombier ilock(ctlr);
218e288d156SDavid du Colombier while(!(csr8r(ctlr, Lsr) & Temt))
219e288d156SDavid du Colombier ;
2209a747e4fSDavid du Colombier
221e288d156SDavid du Colombier /*
222e288d156SDavid du Colombier * Set the trigger level, default is the max.
223e288d156SDavid du Colombier * value.
224e288d156SDavid du Colombier * Some UARTs require FIFOena to be set before
225e288d156SDavid du Colombier * other bits can take effect, so set it twice.
226e288d156SDavid du Colombier */
227e288d156SDavid du Colombier ctlr->fena = level;
228e288d156SDavid du Colombier switch(level){
229e288d156SDavid du Colombier case 0:
230e288d156SDavid du Colombier break;
231e288d156SDavid du Colombier case 1:
232e288d156SDavid du Colombier level = FIFO1|FIFOena;
233e288d156SDavid du Colombier break;
234e288d156SDavid du Colombier case 4:
235e288d156SDavid du Colombier level = FIFO4|FIFOena;
236e288d156SDavid du Colombier break;
237e288d156SDavid du Colombier case 8:
238e288d156SDavid du Colombier level = FIFO8|FIFOena;
239e288d156SDavid du Colombier break;
240e288d156SDavid du Colombier default:
241e288d156SDavid du Colombier level = FIFO14|FIFOena;
242e288d156SDavid du Colombier break;
2439a747e4fSDavid du Colombier }
244e288d156SDavid du Colombier csr8w(ctlr, Fcr, level);
245e288d156SDavid du Colombier csr8w(ctlr, Fcr, level);
2469a747e4fSDavid du Colombier iunlock(ctlr);
2479a747e4fSDavid du Colombier }
2489a747e4fSDavid du Colombier
2499a747e4fSDavid du Colombier static void
i8250dtr(Uart * uart,int on)2509a747e4fSDavid du Colombier i8250dtr(Uart* uart, int on)
2519a747e4fSDavid du Colombier {
2529a747e4fSDavid du Colombier Ctlr *ctlr;
2539a747e4fSDavid du Colombier
2549a747e4fSDavid du Colombier /*
2559a747e4fSDavid du Colombier * Toggle DTR.
2569a747e4fSDavid du Colombier */
2579a747e4fSDavid du Colombier ctlr = uart->regs;
2589a747e4fSDavid du Colombier if(on)
2599a747e4fSDavid du Colombier ctlr->sticky[Mcr] |= Dtr;
2609a747e4fSDavid du Colombier else
2619a747e4fSDavid du Colombier ctlr->sticky[Mcr] &= ~Dtr;
2629a747e4fSDavid du Colombier csr8w(ctlr, Mcr, 0);
2639a747e4fSDavid du Colombier }
2649a747e4fSDavid du Colombier
2659a747e4fSDavid du Colombier static void
i8250rts(Uart * uart,int on)2669a747e4fSDavid du Colombier i8250rts(Uart* uart, int on)
2679a747e4fSDavid du Colombier {
2689a747e4fSDavid du Colombier Ctlr *ctlr;
2699a747e4fSDavid du Colombier
2709a747e4fSDavid du Colombier /*
2719a747e4fSDavid du Colombier * Toggle RTS.
2729a747e4fSDavid du Colombier */
2739a747e4fSDavid du Colombier ctlr = uart->regs;
2749a747e4fSDavid du Colombier if(on)
2759a747e4fSDavid du Colombier ctlr->sticky[Mcr] |= Rts;
2769a747e4fSDavid du Colombier else
2779a747e4fSDavid du Colombier ctlr->sticky[Mcr] &= ~Rts;
2789a747e4fSDavid du Colombier csr8w(ctlr, Mcr, 0);
2799a747e4fSDavid du Colombier }
2809a747e4fSDavid du Colombier
2819a747e4fSDavid du Colombier static void
i8250modemctl(Uart * uart,int on)2829a747e4fSDavid du Colombier i8250modemctl(Uart* uart, int on)
2839a747e4fSDavid du Colombier {
2849a747e4fSDavid du Colombier Ctlr *ctlr;
2859a747e4fSDavid du Colombier
2869a747e4fSDavid du Colombier ctlr = uart->regs;
2879a747e4fSDavid du Colombier ilock(&uart->tlock);
2889a747e4fSDavid du Colombier if(on){
2899a747e4fSDavid du Colombier ctlr->sticky[Ier] |= Ems;
2909a747e4fSDavid du Colombier csr8w(ctlr, Ier, ctlr->sticky[Ier]);
2919a747e4fSDavid du Colombier uart->modem = 1;
2929a747e4fSDavid du Colombier uart->cts = csr8r(ctlr, Msr) & Cts;
2939a747e4fSDavid du Colombier }
2949a747e4fSDavid du Colombier else{
2959a747e4fSDavid du Colombier ctlr->sticky[Ier] &= ~Ems;
2969a747e4fSDavid du Colombier csr8w(ctlr, Ier, ctlr->sticky[Ier]);
2979a747e4fSDavid du Colombier uart->modem = 0;
2989a747e4fSDavid du Colombier uart->cts = 1;
2999a747e4fSDavid du Colombier }
3009a747e4fSDavid du Colombier iunlock(&uart->tlock);
3019a747e4fSDavid du Colombier
3029a747e4fSDavid du Colombier /* modem needs fifo */
3039a747e4fSDavid du Colombier (*uart->phys->fifo)(uart, on);
3049a747e4fSDavid du Colombier }
3059a747e4fSDavid du Colombier
3069a747e4fSDavid du Colombier static int
i8250parity(Uart * uart,int parity)3079a747e4fSDavid du Colombier i8250parity(Uart* uart, int parity)
3089a747e4fSDavid du Colombier {
3099a747e4fSDavid du Colombier int lcr;
3109a747e4fSDavid du Colombier Ctlr *ctlr;
3119a747e4fSDavid du Colombier
3129a747e4fSDavid du Colombier ctlr = uart->regs;
3139a747e4fSDavid du Colombier lcr = ctlr->sticky[Lcr] & ~(Eps|Pen);
3149a747e4fSDavid du Colombier
3159a747e4fSDavid du Colombier switch(parity){
3169a747e4fSDavid du Colombier case 'e':
3179a747e4fSDavid du Colombier lcr |= Eps|Pen;
3189a747e4fSDavid du Colombier break;
3199a747e4fSDavid du Colombier case 'o':
3209a747e4fSDavid du Colombier lcr |= Pen;
3219a747e4fSDavid du Colombier break;
3229a747e4fSDavid du Colombier case 'n':
3239a747e4fSDavid du Colombier break;
3245243b8d1SDavid du Colombier default:
3255243b8d1SDavid du Colombier return -1;
3269a747e4fSDavid du Colombier }
3279a747e4fSDavid du Colombier ctlr->sticky[Lcr] = lcr;
3289a747e4fSDavid du Colombier csr8w(ctlr, Lcr, 0);
3299a747e4fSDavid du Colombier
3309a747e4fSDavid du Colombier uart->parity = parity;
3319a747e4fSDavid du Colombier
3329a747e4fSDavid du Colombier return 0;
3339a747e4fSDavid du Colombier }
3349a747e4fSDavid du Colombier
3359a747e4fSDavid du Colombier static int
i8250stop(Uart * uart,int stop)3369a747e4fSDavid du Colombier i8250stop(Uart* uart, int stop)
3379a747e4fSDavid du Colombier {
3389a747e4fSDavid du Colombier int lcr;
3399a747e4fSDavid du Colombier Ctlr *ctlr;
3409a747e4fSDavid du Colombier
3419a747e4fSDavid du Colombier ctlr = uart->regs;
3429a747e4fSDavid du Colombier lcr = ctlr->sticky[Lcr] & ~Stb;
3439a747e4fSDavid du Colombier
3449a747e4fSDavid du Colombier switch(stop){
3459a747e4fSDavid du Colombier case 1:
3469a747e4fSDavid du Colombier break;
3479a747e4fSDavid du Colombier case 2:
3489a747e4fSDavid du Colombier lcr |= Stb;
3499a747e4fSDavid du Colombier break;
3509a747e4fSDavid du Colombier default:
3519a747e4fSDavid du Colombier return -1;
3529a747e4fSDavid du Colombier }
3539a747e4fSDavid du Colombier ctlr->sticky[Lcr] = lcr;
3549a747e4fSDavid du Colombier csr8w(ctlr, Lcr, 0);
3559a747e4fSDavid du Colombier
3569a747e4fSDavid du Colombier uart->stop = stop;
3579a747e4fSDavid du Colombier
3589a747e4fSDavid du Colombier return 0;
3599a747e4fSDavid du Colombier }
3609a747e4fSDavid du Colombier
3619a747e4fSDavid du Colombier static int
i8250bits(Uart * uart,int bits)3629a747e4fSDavid du Colombier i8250bits(Uart* uart, int bits)
3639a747e4fSDavid du Colombier {
3649a747e4fSDavid du Colombier int lcr;
3659a747e4fSDavid du Colombier Ctlr *ctlr;
3669a747e4fSDavid du Colombier
3679a747e4fSDavid du Colombier ctlr = uart->regs;
3689a747e4fSDavid du Colombier lcr = ctlr->sticky[Lcr] & ~WlsMASK;
3699a747e4fSDavid du Colombier
3709a747e4fSDavid du Colombier switch(bits){
3719a747e4fSDavid du Colombier case 5:
3729a747e4fSDavid du Colombier lcr |= Wls5;
3739a747e4fSDavid du Colombier break;
3749a747e4fSDavid du Colombier case 6:
3759a747e4fSDavid du Colombier lcr |= Wls6;
3769a747e4fSDavid du Colombier break;
3779a747e4fSDavid du Colombier case 7:
3789a747e4fSDavid du Colombier lcr |= Wls7;
3799a747e4fSDavid du Colombier break;
3809a747e4fSDavid du Colombier case 8:
3819a747e4fSDavid du Colombier lcr |= Wls8;
3829a747e4fSDavid du Colombier break;
3839a747e4fSDavid du Colombier default:
3849a747e4fSDavid du Colombier return -1;
3859a747e4fSDavid du Colombier }
3869a747e4fSDavid du Colombier ctlr->sticky[Lcr] = lcr;
3879a747e4fSDavid du Colombier csr8w(ctlr, Lcr, 0);
3889a747e4fSDavid du Colombier
3899a747e4fSDavid du Colombier uart->bits = bits;
3909a747e4fSDavid du Colombier
3919a747e4fSDavid du Colombier return 0;
3929a747e4fSDavid du Colombier }
3939a747e4fSDavid du Colombier
3949a747e4fSDavid du Colombier static int
i8250baud(Uart * uart,int baud)3959a747e4fSDavid du Colombier i8250baud(Uart* uart, int baud)
3969a747e4fSDavid du Colombier {
3979a747e4fSDavid du Colombier ulong bgc;
3989a747e4fSDavid du Colombier Ctlr *ctlr;
3999a747e4fSDavid du Colombier
4009a747e4fSDavid du Colombier /*
4019a747e4fSDavid du Colombier * Set the Baud rate by calculating and setting the Baud rate
4029a747e4fSDavid du Colombier * Generator Constant. This will work with fairly non-standard
4039a747e4fSDavid du Colombier * Baud rates.
4049a747e4fSDavid du Colombier */
4059a747e4fSDavid du Colombier if(uart->freq == 0 || baud <= 0)
4069a747e4fSDavid du Colombier return -1;
4079a747e4fSDavid du Colombier bgc = (uart->freq+8*baud-1)/(16*baud);
4089a747e4fSDavid du Colombier
4099a747e4fSDavid du Colombier ctlr = uart->regs;
4109a747e4fSDavid du Colombier csr8w(ctlr, Lcr, Dlab);
4119a747e4fSDavid du Colombier outb(ctlr->io+Dlm, bgc>>8);
4129a747e4fSDavid du Colombier outb(ctlr->io+Dll, bgc);
4139a747e4fSDavid du Colombier csr8w(ctlr, Lcr, 0);
4149a747e4fSDavid du Colombier
4159a747e4fSDavid du Colombier uart->baud = baud;
4169a747e4fSDavid du Colombier
4179a747e4fSDavid du Colombier return 0;
4189a747e4fSDavid du Colombier }
4199a747e4fSDavid du Colombier
4209a747e4fSDavid du Colombier static void
i8250break(Uart * uart,int ms)4219a747e4fSDavid du Colombier i8250break(Uart* uart, int ms)
4229a747e4fSDavid du Colombier {
4239a747e4fSDavid du Colombier Ctlr *ctlr;
4249a747e4fSDavid du Colombier
4259a747e4fSDavid du Colombier /*
4269a747e4fSDavid du Colombier * Send a break.
4279a747e4fSDavid du Colombier */
4285243b8d1SDavid du Colombier if(ms <= 0)
4299a747e4fSDavid du Colombier ms = 200;
4309a747e4fSDavid du Colombier
4319a747e4fSDavid du Colombier ctlr = uart->regs;
4329a747e4fSDavid du Colombier csr8w(ctlr, Lcr, Brk);
4339a747e4fSDavid du Colombier tsleep(&up->sleep, return0, 0, ms);
4349a747e4fSDavid du Colombier csr8w(ctlr, Lcr, 0);
4359a747e4fSDavid du Colombier }
4369a747e4fSDavid du Colombier
4379a747e4fSDavid du Colombier static void
i8250kick(Uart * uart)4389a747e4fSDavid du Colombier i8250kick(Uart* uart)
4399a747e4fSDavid du Colombier {
4409a747e4fSDavid du Colombier int i;
4419a747e4fSDavid du Colombier Ctlr *ctlr;
4429a747e4fSDavid du Colombier
4439a747e4fSDavid du Colombier if(uart->cts == 0 || uart->blocked)
4449a747e4fSDavid du Colombier return;
4459a747e4fSDavid du Colombier
4469a747e4fSDavid du Colombier /*
4479a747e4fSDavid du Colombier * 128 here is an arbitrary limit to make sure
4489a747e4fSDavid du Colombier * we don't stay in this loop too long. If the
4499a747e4fSDavid du Colombier * chip's output queue is longer than 128, too
4509a747e4fSDavid du Colombier * bad -- presotto
4519a747e4fSDavid du Colombier */
4529a747e4fSDavid du Colombier ctlr = uart->regs;
4539a747e4fSDavid du Colombier for(i = 0; i < 128; i++){
4549a747e4fSDavid du Colombier if(!(csr8r(ctlr, Lsr) & Thre))
4559a747e4fSDavid du Colombier break;
4569a747e4fSDavid du Colombier if(uart->op >= uart->oe && uartstageoutput(uart) == 0)
4579a747e4fSDavid du Colombier break;
4589a747e4fSDavid du Colombier outb(ctlr->io+Thr, *(uart->op++));
4599a747e4fSDavid du Colombier }
4609a747e4fSDavid du Colombier }
4619a747e4fSDavid du Colombier
4629a747e4fSDavid du Colombier static void
i8250interrupt(Ureg *,void * arg)4639a747e4fSDavid du Colombier i8250interrupt(Ureg*, void* arg)
4649a747e4fSDavid du Colombier {
4659a747e4fSDavid du Colombier Ctlr *ctlr;
4669a747e4fSDavid du Colombier Uart *uart;
4679a747e4fSDavid du Colombier int iir, lsr, old, r;
4689a747e4fSDavid du Colombier
4699a747e4fSDavid du Colombier uart = arg;
4709a747e4fSDavid du Colombier
4719a747e4fSDavid du Colombier ctlr = uart->regs;
4729a747e4fSDavid du Colombier for(iir = csr8r(ctlr, Iir); !(iir & Ip); iir = csr8r(ctlr, Iir)){
4739a747e4fSDavid du Colombier switch(iir & IirMASK){
4749a747e4fSDavid du Colombier case Ims: /* Ms interrupt */
4759a747e4fSDavid du Colombier r = csr8r(ctlr, Msr);
4769a747e4fSDavid du Colombier if(r & Dcts){
4779a747e4fSDavid du Colombier ilock(&uart->tlock);
4789a747e4fSDavid du Colombier old = uart->cts;
4799a747e4fSDavid du Colombier uart->cts = r & Cts;
4809a747e4fSDavid du Colombier if(old == 0 && uart->cts)
4819a747e4fSDavid du Colombier uart->ctsbackoff = 2;
4829a747e4fSDavid du Colombier iunlock(&uart->tlock);
4839a747e4fSDavid du Colombier }
4849a747e4fSDavid du Colombier if(r & Ddsr){
4859a747e4fSDavid du Colombier old = r & Dsr;
4869a747e4fSDavid du Colombier if(uart->hup_dsr && uart->dsr && !old)
4879a747e4fSDavid du Colombier uart->dohup = 1;
4889a747e4fSDavid du Colombier uart->dsr = old;
4899a747e4fSDavid du Colombier }
4909a747e4fSDavid du Colombier if(r & Ddcd){
4919a747e4fSDavid du Colombier old = r & Dcd;
4929a747e4fSDavid du Colombier if(uart->hup_dcd && uart->dcd && !old)
4939a747e4fSDavid du Colombier uart->dohup = 1;
4949a747e4fSDavid du Colombier uart->dcd = old;
4959a747e4fSDavid du Colombier }
4969a747e4fSDavid du Colombier break;
4979a747e4fSDavid du Colombier case Ithre: /* Thr Empty */
4989a747e4fSDavid du Colombier uartkick(uart);
4999a747e4fSDavid du Colombier break;
5009a747e4fSDavid du Colombier case Irda: /* Received Data Available */
501b5630d1bSDavid du Colombier case Irls: /* Receiver Line Status */
5029a747e4fSDavid du Colombier case Ictoi: /* Character Time-out Indication */
5039a747e4fSDavid du Colombier /*
5049a747e4fSDavid du Colombier * Consume any received data.
5059a747e4fSDavid du Colombier * If the received byte came in with a break,
5069a747e4fSDavid du Colombier * parity or framing error, throw it away;
5079a747e4fSDavid du Colombier * overrun is an indication that something has
5089a747e4fSDavid du Colombier * already been tossed.
5099a747e4fSDavid du Colombier */
5109a747e4fSDavid du Colombier while((lsr = csr8r(ctlr, Lsr)) & Dr){
5115243b8d1SDavid du Colombier if(lsr & (FIFOerr|Oe))
5129a747e4fSDavid du Colombier uart->oerr++;
5139a747e4fSDavid du Colombier if(lsr & Pe)
5149a747e4fSDavid du Colombier uart->perr++;
5159a747e4fSDavid du Colombier if(lsr & Fe)
5169a747e4fSDavid du Colombier uart->ferr++;
5179a747e4fSDavid du Colombier r = csr8r(ctlr, Rbr);
5189a747e4fSDavid du Colombier if(!(lsr & (Bi|Fe|Pe)))
5199a747e4fSDavid du Colombier uartrecv(uart, r);
5209a747e4fSDavid du Colombier }
5219a747e4fSDavid du Colombier break;
5229a747e4fSDavid du Colombier
5239a747e4fSDavid du Colombier default:
5249a747e4fSDavid du Colombier iprint("weird uart interrupt 0x%2.2uX\n", iir);
5259a747e4fSDavid du Colombier break;
5269a747e4fSDavid du Colombier }
5279a747e4fSDavid du Colombier }
5289a747e4fSDavid du Colombier }
5299a747e4fSDavid du Colombier
5309a747e4fSDavid du Colombier static void
i8250disable(Uart * uart)5319a747e4fSDavid du Colombier i8250disable(Uart* uart)
5329a747e4fSDavid du Colombier {
5339a747e4fSDavid du Colombier Ctlr *ctlr;
5349a747e4fSDavid du Colombier
5359a747e4fSDavid du Colombier /*
5369a747e4fSDavid du Colombier * Turn off DTR and RTS, disable interrupts and fifos.
5379a747e4fSDavid du Colombier */
5389a747e4fSDavid du Colombier (*uart->phys->dtr)(uart, 0);
5399a747e4fSDavid du Colombier (*uart->phys->rts)(uart, 0);
5409a747e4fSDavid du Colombier (*uart->phys->fifo)(uart, 0);
5419a747e4fSDavid du Colombier
5429a747e4fSDavid du Colombier ctlr = uart->regs;
5439a747e4fSDavid du Colombier ctlr->sticky[Ier] = 0;
5449a747e4fSDavid du Colombier csr8w(ctlr, Ier, ctlr->sticky[Ier]);
5459a747e4fSDavid du Colombier
5469a747e4fSDavid du Colombier if(ctlr->iena != 0){
54707a38badSDavid du Colombier if(intrdisable(ctlr->irq, i8250interrupt, uart, ctlr->tbdf, uart->name) == 0)
5489a747e4fSDavid du Colombier ctlr->iena = 0;
5499a747e4fSDavid du Colombier }
5509a747e4fSDavid du Colombier }
5519a747e4fSDavid du Colombier
5529a747e4fSDavid du Colombier static void
i8250enable(Uart * uart,int ie)5539a747e4fSDavid du Colombier i8250enable(Uart* uart, int ie)
5549a747e4fSDavid du Colombier {
5559a747e4fSDavid du Colombier Ctlr *ctlr;
5569a747e4fSDavid du Colombier
557e288d156SDavid du Colombier ctlr = uart->regs;
558e288d156SDavid du Colombier
559e288d156SDavid du Colombier /*
560e288d156SDavid du Colombier * Check if there is a FIFO.
561e288d156SDavid du Colombier * Changing the FIFOena bit in Fcr flushes data
562e288d156SDavid du Colombier * from both receive and transmit FIFOs; there's
563e288d156SDavid du Colombier * no easy way to guarantee not losing data on
564e288d156SDavid du Colombier * the receive side, but it's possible to wait until
565e288d156SDavid du Colombier * the transmitter is really empty.
566e288d156SDavid du Colombier * Also, reading the Iir outwith i8250interrupt()
567e288d156SDavid du Colombier * can be dangerous, but this should only happen
568e29d4813SDavid du Colombier * once before interrupts are enabled.
569e288d156SDavid du Colombier */
570e288d156SDavid du Colombier ilock(ctlr);
571e288d156SDavid du Colombier if(!ctlr->checkfifo){
572e288d156SDavid du Colombier /*
573e288d156SDavid du Colombier * Wait until the transmitter is really empty.
574e288d156SDavid du Colombier */
575e288d156SDavid du Colombier while(!(csr8r(ctlr, Lsr) & Temt))
576e288d156SDavid du Colombier ;
577e288d156SDavid du Colombier csr8w(ctlr, Fcr, FIFOena);
578e288d156SDavid du Colombier if(csr8r(ctlr, Iir) & Ifena)
579e288d156SDavid du Colombier ctlr->hasfifo = 1;
580e288d156SDavid du Colombier csr8w(ctlr, Fcr, 0);
581e288d156SDavid du Colombier ctlr->checkfifo = 1;
582e288d156SDavid du Colombier }
583e288d156SDavid du Colombier iunlock(ctlr);
584e288d156SDavid du Colombier
5859a747e4fSDavid du Colombier /*
5869a747e4fSDavid du Colombier * Enable interrupts and turn on DTR and RTS.
5879a747e4fSDavid du Colombier * Be careful if this is called to set up a polled serial line
5889a747e4fSDavid du Colombier * early on not to try to enable interrupts as interrupt-
5899a747e4fSDavid du Colombier * -enabling mechanisms might not be set up yet.
5909a747e4fSDavid du Colombier */
5919a747e4fSDavid du Colombier if(ie){
5929a747e4fSDavid du Colombier if(ctlr->iena == 0){
5939a747e4fSDavid du Colombier intrenable(ctlr->irq, i8250interrupt, uart, ctlr->tbdf, uart->name);
5949a747e4fSDavid du Colombier ctlr->iena = 1;
5959a747e4fSDavid du Colombier }
5969a747e4fSDavid du Colombier ctlr->sticky[Ier] = Ethre|Erda;
5979a747e4fSDavid du Colombier ctlr->sticky[Mcr] |= Ie;
5989a747e4fSDavid du Colombier }
5999a747e4fSDavid du Colombier else{
6009a747e4fSDavid du Colombier ctlr->sticky[Ier] = 0;
6019a747e4fSDavid du Colombier ctlr->sticky[Mcr] = 0;
6029a747e4fSDavid du Colombier }
6039a747e4fSDavid du Colombier csr8w(ctlr, Ier, ctlr->sticky[Ier]);
6049a747e4fSDavid du Colombier csr8w(ctlr, Mcr, ctlr->sticky[Mcr]);
6059a747e4fSDavid du Colombier
6069a747e4fSDavid du Colombier (*uart->phys->dtr)(uart, 1);
6079a747e4fSDavid du Colombier (*uart->phys->rts)(uart, 1);
6089a747e4fSDavid du Colombier
6099a747e4fSDavid du Colombier /*
610b7b24591SDavid du Colombier * During startup, the i8259 interrupt controller is reset.
6119a747e4fSDavid du Colombier * This may result in a lost interrupt from the i8250 uart.
6129a747e4fSDavid du Colombier * The i8250 thinks the interrupt is still outstanding and does not
613b7b24591SDavid du Colombier * generate any further interrupts. The workaround is to call the
614b7b24591SDavid du Colombier * interrupt handler to clear any pending interrupt events.
615b7b24591SDavid du Colombier * Note: this must be done after setting Ier.
6169a747e4fSDavid du Colombier */
6179a747e4fSDavid du Colombier if(ie)
6189a747e4fSDavid du Colombier i8250interrupt(nil, uart);
6199a747e4fSDavid du Colombier }
6209a747e4fSDavid du Colombier
6219a747e4fSDavid du Colombier void*
i8250alloc(int io,int irq,int tbdf)6229a747e4fSDavid du Colombier i8250alloc(int io, int irq, int tbdf)
6239a747e4fSDavid du Colombier {
6249a747e4fSDavid du Colombier Ctlr *ctlr;
6259a747e4fSDavid du Colombier
6269a747e4fSDavid du Colombier if((ctlr = malloc(sizeof(Ctlr))) != nil){
6279a747e4fSDavid du Colombier ctlr->io = io;
6289a747e4fSDavid du Colombier ctlr->irq = irq;
6299a747e4fSDavid du Colombier ctlr->tbdf = tbdf;
6309a747e4fSDavid du Colombier }
6319a747e4fSDavid du Colombier
6329a747e4fSDavid du Colombier return ctlr;
6339a747e4fSDavid du Colombier }
6349a747e4fSDavid du Colombier
6359a747e4fSDavid du Colombier static Uart*
i8250pnp(void)6369a747e4fSDavid du Colombier i8250pnp(void)
6379a747e4fSDavid du Colombier {
6389a747e4fSDavid du Colombier return i8250uart;
6399a747e4fSDavid du Colombier }
6409a747e4fSDavid du Colombier
6419a747e4fSDavid du Colombier static int
i8250getc(Uart * uart)6429a747e4fSDavid du Colombier i8250getc(Uart *uart)
6439a747e4fSDavid du Colombier {
6449a747e4fSDavid du Colombier Ctlr *ctlr;
6459a747e4fSDavid du Colombier
6469a747e4fSDavid du Colombier ctlr = uart->regs;
6479a747e4fSDavid du Colombier while(!(csr8r(ctlr, Lsr)&Dr))
6489a747e4fSDavid du Colombier delay(1);
6499a747e4fSDavid du Colombier return csr8r(ctlr, Rbr);
6509a747e4fSDavid du Colombier }
6519a747e4fSDavid du Colombier
6529a747e4fSDavid du Colombier static void
i8250putc(Uart * uart,int c)6539a747e4fSDavid du Colombier i8250putc(Uart *uart, int c)
6549a747e4fSDavid du Colombier {
6559a747e4fSDavid du Colombier int i;
6569a747e4fSDavid du Colombier Ctlr *ctlr;
6579a747e4fSDavid du Colombier
6589a747e4fSDavid du Colombier ctlr = uart->regs;
6599a747e4fSDavid du Colombier for(i = 0; !(csr8r(ctlr, Lsr)&Thre) && i < 128; i++)
6609a747e4fSDavid du Colombier delay(1);
6619a747e4fSDavid du Colombier outb(ctlr->io+Thr, c);
6629a747e4fSDavid du Colombier for(i = 0; !(csr8r(ctlr, Lsr)&Thre) && i < 128; i++)
6639a747e4fSDavid du Colombier delay(1);
6649a747e4fSDavid du Colombier }
6659a747e4fSDavid du Colombier
6669a747e4fSDavid du Colombier PhysUart i8250physuart = {
6679a747e4fSDavid du Colombier .name = "i8250",
6689a747e4fSDavid du Colombier .pnp = i8250pnp,
6699a747e4fSDavid du Colombier .enable = i8250enable,
6709a747e4fSDavid du Colombier .disable = i8250disable,
6719a747e4fSDavid du Colombier .kick = i8250kick,
6729a747e4fSDavid du Colombier .dobreak = i8250break,
6739a747e4fSDavid du Colombier .baud = i8250baud,
6749a747e4fSDavid du Colombier .bits = i8250bits,
6759a747e4fSDavid du Colombier .stop = i8250stop,
6769a747e4fSDavid du Colombier .parity = i8250parity,
6779a747e4fSDavid du Colombier .modemctl = i8250modemctl,
6789a747e4fSDavid du Colombier .rts = i8250rts,
6799a747e4fSDavid du Colombier .dtr = i8250dtr,
6809a747e4fSDavid du Colombier .status = i8250status,
6819a747e4fSDavid du Colombier .fifo = i8250fifo,
6829a747e4fSDavid du Colombier .getc = i8250getc,
6839a747e4fSDavid du Colombier .putc = i8250putc,
6849a747e4fSDavid du Colombier };
6859a747e4fSDavid du Colombier
6869a747e4fSDavid du Colombier void
i8250config(char * p)687*3d8d68ddSDavid du Colombier i8250config(char *p)
6889a747e4fSDavid du Colombier {
6899a747e4fSDavid du Colombier Uart *uart;
6909a747e4fSDavid du Colombier int n;
691*3d8d68ddSDavid du Colombier char *cmd;
6929a747e4fSDavid du Colombier
693*3d8d68ddSDavid du Colombier if(p == nil)
6949a747e4fSDavid du Colombier return;
6959a747e4fSDavid du Colombier n = strtoul(p, &cmd, 0);
6969a747e4fSDavid du Colombier if(p == cmd)
6979a747e4fSDavid du Colombier return;
6989a747e4fSDavid du Colombier switch(n){
6999a747e4fSDavid du Colombier default:
7009a747e4fSDavid du Colombier return;
7019a747e4fSDavid du Colombier case 0:
7029a747e4fSDavid du Colombier uart = &i8250uart[0];
7039a747e4fSDavid du Colombier break;
7049a747e4fSDavid du Colombier case 1:
7059a747e4fSDavid du Colombier uart = &i8250uart[1];
7069a747e4fSDavid du Colombier break;
7079a747e4fSDavid du Colombier }
7089a747e4fSDavid du Colombier
709*3d8d68ddSDavid du Colombier if(!uart->enabled)
710e288d156SDavid du Colombier (*uart->phys->enable)(uart, 0);
7119a747e4fSDavid du Colombier uartctl(uart, "b9600 l8 pn s1");
7129a747e4fSDavid du Colombier if(*cmd != '\0')
7139a747e4fSDavid du Colombier uartctl(uart, cmd);
7149a747e4fSDavid du Colombier
7159a747e4fSDavid du Colombier consuart = uart;
7169a747e4fSDavid du Colombier uart->console = 1;
7179a747e4fSDavid du Colombier }
718b7b24591SDavid du Colombier
719b7b24591SDavid du Colombier void
i8250console(void)720*3d8d68ddSDavid du Colombier i8250console(void)
721*3d8d68ddSDavid du Colombier {
722*3d8d68ddSDavid du Colombier i8250config(getconf("console"));
723*3d8d68ddSDavid du Colombier }
724*3d8d68ddSDavid du Colombier
725*3d8d68ddSDavid du Colombier void
i8250mouse(char * which,int (* putc)(Queue *,int),int setb1200)726b7b24591SDavid du Colombier i8250mouse(char* which, int (*putc)(Queue*, int), int setb1200)
727b7b24591SDavid du Colombier {
728b7b24591SDavid du Colombier char *p;
729b7b24591SDavid du Colombier int port;
730b7b24591SDavid du Colombier
731b7b24591SDavid du Colombier port = strtol(which, &p, 0);
732b7b24591SDavid du Colombier if(p == which || port < 0 || port > 1)
733b7b24591SDavid du Colombier error(Ebadarg);
734b7b24591SDavid du Colombier uartmouse(&i8250uart[port], putc, setb1200);
735b7b24591SDavid du Colombier }
736aa46331bSDavid du Colombier
737aa46331bSDavid du Colombier void
i8250setmouseputc(char * which,int (* putc)(Queue *,int))738aa46331bSDavid du Colombier i8250setmouseputc(char* which, int (*putc)(Queue*, int))
739aa46331bSDavid du Colombier {
740aa46331bSDavid du Colombier char *p;
741aa46331bSDavid du Colombier int port;
742aa46331bSDavid du Colombier
743aa46331bSDavid du Colombier port = strtol(which, &p, 0);
744aa46331bSDavid du Colombier if(p == which || port < 0 || port > 1)
745aa46331bSDavid du Colombier error(Ebadarg);
746aa46331bSDavid du Colombier uartsetmouseputc(&i8250uart[port], putc);
747aa46331bSDavid du Colombier
748aa46331bSDavid du Colombier }
749