]> git.lizzy.rs Git - plan9front.git/blob - sys/src/boot/pc/8250.c
perms
[plan9front.git] / sys / src / boot / pc / 8250.c
1 #include "u.h"
2 #include "lib.h"
3 #include "mem.h"
4 #include "dat.h"
5 #include "fns.h"
6 #include "io.h"
7
8 /*
9  *  INS8250 uart
10  */
11 enum
12 {
13         /*
14          *  register numbers
15          */
16         Data=   0,              /* xmit/rcv buffer */
17         Iena=   1,              /* interrupt enable */
18          Ircv=  (1<<0),         /*  for char rcv'd */
19          Ixmt=  (1<<1),         /*  for xmit buffer empty */
20          Irstat=(1<<2),         /*  for change in rcv'er status */
21          Imstat=(1<<3),         /*  for change in modem status */
22         Istat=  2,              /* interrupt flag (read) */
23         Tctl=   2,              /* test control (write) */
24         Format= 3,              /* byte format */
25          Bits8= (3<<0),         /*  8 bits/byte */
26          Stop2= (1<<2),         /*  2 stop bits */
27          Pena=  (1<<3),         /*  generate parity */
28          Peven= (1<<4),         /*  even parity */
29          Pforce=(1<<5),         /*  force parity */
30          Break= (1<<6),         /*  generate a break */
31          Dra=   (1<<7),         /*  address the divisor */
32         Mctl=   4,              /* modem control */
33          Dtr=   (1<<0),         /*  data terminal ready */
34          Rts=   (1<<1),         /*  request to send */
35          Ri=    (1<<2),         /*  ring */
36          Inton= (1<<3),         /*  turn on interrupts */
37          Loop=  (1<<4),         /*  loop back */
38         Lstat=  5,              /* line status */
39          Inready=(1<<0),        /*  receive buffer full */
40          Oerror=(1<<1),         /*  receiver overrun */
41          Perror=(1<<2),         /*  receiver parity error */
42          Ferror=(1<<3),         /*  rcv framing error */
43          Outready=(1<<5),       /*  output buffer empty */
44         Mstat=  6,              /* modem status */
45          Ctsc=  (1<<0),         /*  clear to send changed */
46          Dsrc=  (1<<1),         /*  data set ready changed */
47          Rire=  (1<<2),         /*  rising edge of ring indicator */
48          Dcdc=  (1<<3),         /*  data carrier detect changed */
49          Cts=   (1<<4),         /*  complement of clear to send line */
50          Dsr=   (1<<5),         /*  complement of data set ready line */
51          Ring=  (1<<6),         /*  complement of ring indicator line */
52          Dcd=   (1<<7),         /*  complement of data carrier detect line */
53         Scratch=7,              /* scratchpad */
54         Dlsb=   0,              /* divisor lsb */
55         Dmsb=   1,              /* divisor msb */
56
57         Serial= 0,
58         Modem=  1,
59 };
60
61 typedef struct Uart     Uart;
62 struct Uart
63 {
64         int     port;
65         uchar   sticky[8];      /* sticky write register values */
66         uchar   txbusy;
67
68         void    (*rx)(int);     /* routine to take a received character */
69         int     (*tx)(void);    /* routine to get a character to transmit */
70
71         ulong   frame;
72         ulong   overrun;
73 };
74
75 static Uart com[2];
76 static Uart* uart;
77
78 #define UartFREQ 1843200
79
80 #define uartwrreg(u,r,v)        outb((u)->port + r, (u)->sticky[r] | (v))
81 #define uartrdreg(u,r)          inb((u)->port + r)
82
83 /*
84  *  set the baud rate by calculating and setting the baudrate
85  *  generator constant.  This will work with fairly non-standard
86  *  baud rates.
87  */
88 static void
89 uartsetbaud(Uart *up, int rate)
90 {
91         ulong brconst;
92
93         brconst = (UartFREQ+8*rate-1)/(16*rate);
94
95         uartwrreg(up, Format, Dra);
96         outb(up->port+Dmsb, (brconst>>8) & 0xff);
97         outb(up->port+Dlsb, brconst & 0xff);
98         uartwrreg(up, Format, 0);
99 }
100
101 /*
102  *  toggle DTR
103  */
104 static void
105 uartdtr(Uart *up, int n)
106 {
107         if(n)
108                 up->sticky[Mctl] |= Dtr;
109         else
110                 up->sticky[Mctl] &= ~Dtr;
111         uartwrreg(up, Mctl, 0);
112 }
113
114 /*
115  *  toggle RTS
116  */
117 static void
118 uartrts(Uart *up, int n)
119 {
120         if(n)
121                 up->sticky[Mctl] |= Rts;
122         else
123                 up->sticky[Mctl] &= ~Rts;
124         uartwrreg(up, Mctl, 0);
125 }
126
127 static void
128 uartintr(Ureg*, void *arg)
129 {
130         Uart *up;
131         int ch;
132         int s, l, loops;
133
134         up = arg;
135         for(loops = 0; loops < 1024; loops++){
136                 s = uartrdreg(up, Istat);
137                 switch(s & 0x3F){
138                 case 6: /* receiver line status */
139                         l = uartrdreg(up, Lstat);
140                         if(l & Ferror)
141                                 up->frame++;
142                         if(l & Oerror)
143                                 up->overrun++;
144                         break;
145         
146                 case 4: /* received data available */
147                 case 12:
148                         ch = inb(up->port+Data);
149                         if(up->rx)
150                                 (*up->rx)(ch);
151                         break;
152         
153                 case 2: /* transmitter empty */
154                         ch = -1;
155                         if(up->tx)
156                                 ch = (*up->tx)();
157                         if(ch != -1)
158                                 outb(up->port+Data, ch);
159                         else
160                                 up->txbusy = 0;
161                         break;
162         
163                 case 0: /* modem status */
164                         uartrdreg(up, Mstat);
165                         break;
166         
167                 default:
168                         if(s&1)
169                                 return;
170                         print("weird modem interrupt #%2.2ux\n", s);
171                         break;
172                 }
173         }
174         panic("uartintr: 0x%2.2ux", uartrdreg(up, Istat));
175 }
176
177 /*
178  *  turn on a port's interrupts.  set DTR and RTS
179  */
180 static void
181 uartenable(Uart *up)
182 {
183         /*
184          *  turn on interrupts
185          */
186         up->sticky[Iena] = 0;
187         if(up->tx)
188                 up->sticky[Iena] |= Ixmt;
189         if(up->rx)
190                 up->sticky[Iena] |= Ircv|Irstat;
191         uartwrreg(up, Iena, 0);
192
193         /*
194          *  turn on DTR and RTS
195          */
196         uartdtr(up, 1);
197         uartrts(up, 1);
198 }
199
200 static void
201 uartdisable(Uart* up)
202 {
203         /*
204          * Disable interrupts.
205          */
206         up->sticky[Iena] = 0;
207         uartwrreg(up, Iena, 0);
208         uartdtr(up, 0);
209         uartrts(up, 0);
210 }
211
212 void
213 uartspecial(int port, void (*rx)(int), int (*tx)(void), int baud)
214 {
215         Uart *up;
216         int vector;
217
218         switch(port){
219         case 0:
220                 port = 0x3F8;
221                 vector = VectorUART0;
222                 up = &com[0];
223                 break;
224         case 1:
225                 port = 0x2F8;
226                 vector = VectorUART1;
227                 up = &com[1];
228                 break;
229         default:
230                 return;
231         }
232
233         if(uart != nil && uart != up)
234                 uartdisable(uart);
235         uart = up;
236
237         if(up->port == 0){
238                 up->port = port;
239                 setvec(vector, uartintr, up);
240         }
241
242         /*
243          *  set rate to 9600 baud.
244          *  8 bits/character.
245          *  1 stop bit.
246          *  interrupts enabled.
247          */
248         uartsetbaud(up, 9600);
249         up->sticky[Format] = Bits8;
250         uartwrreg(up, Format, 0);
251         up->sticky[Mctl] |= Inton;
252         uartwrreg(up, Mctl, 0x0);
253
254         up->rx = rx;
255         up->tx = tx;
256         uartenable(up);
257         if(baud)
258                 uartsetbaud(up, baud);
259 }
260
261 void
262 uartputc(int c)
263 {
264         int i;
265         Uart *up;
266
267         if((up = uart) == nil)
268                 return;
269         for(i = 0; i < 100; i++){
270                 if(uartrdreg(up, Lstat) & Outready)
271                         break;
272                 delay(1);
273         }
274         outb(up->port+Data, c);
275 }
276
277 void
278 uartputs(IOQ *q, char *s, int n)
279 {
280         Uart *up;
281         int c, x;
282
283         if((up = uart) == nil)
284                 return;
285         while(n--){
286                 if(*s == '\n')
287                         q->putc(q, '\r');
288                 q->putc(q, *s++);
289         }
290         x = splhi();
291         if(up->txbusy == 0 && (c = q->getc(q)) != -1){
292                 uartputc(c & 0xFF);
293                 up->txbusy = 1;
294         }
295         splx(x);
296 }
297
298 void
299 uartdrain(void)
300 {
301         Uart *up;
302         int timeo;
303
304         if((up = uart) == nil)
305                 return;
306         for(timeo = 0; timeo < 2000 && up->txbusy; timeo++)
307                 delay(1);
308         up->txbusy = 0;
309 }