1 /*-
2 * SPDX-License-Identifier: BSD-2-Clause
3 *
4 * Copyright (c) 2012 NetApp, Inc.
5 * Copyright (c) 2013 Neel Natu <neel@freebsd.org>
6 * All rights reserved.
7 *
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
10 * are met:
11 * 1. Redistributions of source code must retain the above copyright
12 * notice, this list of conditions and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 * notice, this list of conditions and the following disclaimer in the
15 * documentation and/or other materials provided with the distribution.
16 *
17 * THIS SOFTWARE IS PROVIDED BY NETAPP, INC ``AS IS'' AND
18 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20 * ARE DISCLAIMED. IN NO EVENT SHALL NETAPP, INC OR CONTRIBUTORS BE LIABLE
21 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27 * SUCH DAMAGE.
28 */
29
30 #include <sys/types.h>
31 #include <dev/ic/ns16550.h>
32
33 #include <assert.h>
34 #include <stdio.h>
35 #include <stdlib.h>
36 #include <errno.h>
37 #include <unistd.h>
38 #include <stdbool.h>
39 #include <string.h>
40 #include <pthread.h>
41
42 #include "uart_backend.h"
43 #include "uart_emul.h"
44
45 #define COM1_BASE 0x3F8
46 #define COM1_IRQ 4
47 #define COM2_BASE 0x2F8
48 #define COM2_IRQ 3
49 #define COM3_BASE 0x3E8
50 #define COM3_IRQ 4
51 #define COM4_BASE 0x2E8
52 #define COM4_IRQ 3
53
54 #define DEFAULT_RCLK 1843200
55 #define DEFAULT_BAUD 115200
56
57 #define FCR_RX_MASK 0xC0
58
59 #define MCR_OUT1 0x04
60 #define MCR_OUT2 0x08
61
62 #define MSR_DELTA_MASK 0x0f
63
64 #ifndef REG_SCR
65 #define REG_SCR com_scr
66 #endif
67
68 static struct {
69 int baseaddr;
70 int irq;
71 bool inuse;
72 } uart_lres[] = {
73 { COM1_BASE, COM1_IRQ, false},
74 { COM2_BASE, COM2_IRQ, false},
75 { COM3_BASE, COM3_IRQ, false},
76 { COM4_BASE, COM4_IRQ, false},
77 };
78
79 #define UART_NLDEVS (sizeof(uart_lres) / sizeof(uart_lres[0]))
80
81 struct uart_ns16550_softc {
82 struct uart_softc *backend;
83
84 uint8_t data; /* Data register (R/W) */
85 uint8_t ier; /* Interrupt enable register (R/W) */
86 uint8_t lcr; /* Line control register (R/W) */
87 uint8_t mcr; /* Modem control register (R/W) */
88 uint8_t lsr; /* Line status register (R/W) */
89 uint8_t msr; /* Modem status register (R/W) */
90 uint8_t fcr; /* FIFO control register (W) */
91 uint8_t scr; /* Scratch register (R/W) */
92
93 uint8_t dll; /* Baudrate divisor latch LSB */
94 uint8_t dlh; /* Baudrate divisor latch MSB */
95
96 bool thre_int_pending; /* THRE interrupt pending */
97
98 void *arg;
99 uart_intr_func_t intr_assert;
100 uart_intr_func_t intr_deassert;
101 };
102
103 static uint8_t
modem_status(uint8_t mcr)104 modem_status(uint8_t mcr)
105 {
106 uint8_t msr;
107
108 if (mcr & MCR_LOOPBACK) {
109 /*
110 * In the loopback mode certain bits from the MCR are
111 * reflected back into MSR.
112 */
113 msr = 0;
114 if (mcr & MCR_RTS)
115 msr |= MSR_CTS;
116 if (mcr & MCR_DTR)
117 msr |= MSR_DSR;
118 if (mcr & MCR_OUT1)
119 msr |= MSR_RI;
120 if (mcr & MCR_OUT2)
121 msr |= MSR_DCD;
122 } else {
123 /*
124 * Always assert DCD and DSR so tty open doesn't block
125 * even if CLOCAL is turned off.
126 */
127 msr = MSR_DCD | MSR_DSR;
128 }
129 assert((msr & MSR_DELTA_MASK) == 0);
130
131 return (msr);
132 }
133
134 /*
135 * The IIR returns a prioritized interrupt reason:
136 * - receive data available
137 * - transmit holding register empty
138 * - modem status change
139 *
140 * Return an interrupt reason if one is available.
141 */
142 static int
uart_intr_reason(struct uart_ns16550_softc * sc)143 uart_intr_reason(struct uart_ns16550_softc *sc)
144 {
145
146 if ((sc->lsr & LSR_OE) != 0 && (sc->ier & IER_ERLS) != 0)
147 return (IIR_RLS);
148 else if (uart_rxfifo_numchars(sc->backend) > 0 &&
149 (sc->ier & IER_ERXRDY) != 0)
150 return (IIR_RXTOUT);
151 else if (sc->thre_int_pending && (sc->ier & IER_ETXRDY) != 0)
152 return (IIR_TXRDY);
153 else if ((sc->msr & MSR_DELTA_MASK) != 0 && (sc->ier & IER_EMSC) != 0)
154 return (IIR_MLSC);
155 else
156 return (IIR_NOPEND);
157 }
158
159 static void
uart_reset(struct uart_ns16550_softc * sc)160 uart_reset(struct uart_ns16550_softc *sc)
161 {
162 uint16_t divisor;
163
164 divisor = DEFAULT_RCLK / DEFAULT_BAUD / 16;
165 sc->dll = divisor;
166 #ifdef __FreeBSD__
167 sc->dlh = divisor >> 16;
168 #else
169 sc->dlh = 0;
170 #endif
171 sc->msr = modem_status(sc->mcr);
172
173 uart_rxfifo_reset(sc->backend, 1);
174 }
175
176 /*
177 * Toggle the COM port's intr pin depending on whether or not we have an
178 * interrupt condition to report to the processor.
179 */
180 static void
uart_toggle_intr(struct uart_ns16550_softc * sc)181 uart_toggle_intr(struct uart_ns16550_softc *sc)
182 {
183 uint8_t intr_reason;
184
185 intr_reason = uart_intr_reason(sc);
186
187 if (intr_reason == IIR_NOPEND)
188 (*sc->intr_deassert)(sc->arg);
189 else
190 (*sc->intr_assert)(sc->arg);
191 }
192
193 static void
uart_drain(int fd __unused,enum ev_type ev,void * arg)194 uart_drain(int fd __unused, enum ev_type ev, void *arg)
195 {
196 struct uart_ns16550_softc *sc;
197 bool loopback;
198
199 sc = arg;
200
201 assert(ev == EVF_READ);
202
203 /*
204 * This routine is called in the context of the mevent thread
205 * to take out the softc lock to protect against concurrent
206 * access from a vCPU i/o exit
207 */
208 uart_softc_lock(sc->backend);
209
210 loopback = (sc->mcr & MCR_LOOPBACK) != 0;
211 uart_rxfifo_drain(sc->backend, loopback);
212 if (!loopback)
213 uart_toggle_intr(sc);
214
215 uart_softc_unlock(sc->backend);
216 }
217
218 #ifndef __FreeBSD__
219 void
uart_sock_drain(int fd,enum ev_type ev,void * arg)220 uart_sock_drain(int fd, enum ev_type ev, void *arg)
221 {
222 struct uart_ns16550_softc *sc;
223 bool loopback;
224
225 sc = arg;
226
227 /*
228 * Take the softc lock to protect against concurrent
229 * access from a vCPU i/o exit
230 */
231 uart_softc_lock(sc->backend);
232
233 loopback = (sc->mcr & MCR_LOOPBACK) != 0;
234 uart_rxfifo_sock_drain(sc->backend, loopback);
235 if (!loopback)
236 uart_toggle_intr(sc);
237
238 uart_softc_unlock(sc->backend);
239 }
240 #endif /* !__FreeBSD__ */
241
242 void
uart_ns16550_write(struct uart_ns16550_softc * sc,int offset,uint8_t value)243 uart_ns16550_write(struct uart_ns16550_softc *sc, int offset, uint8_t value)
244 {
245 int fifosz;
246 uint8_t msr;
247
248 uart_softc_lock(sc->backend);
249
250 /*
251 * Take care of the special case DLAB accesses first
252 */
253 if ((sc->lcr & LCR_DLAB) != 0) {
254 if (offset == REG_DLL) {
255 sc->dll = value;
256 goto done;
257 }
258
259 if (offset == REG_DLH) {
260 sc->dlh = value;
261 goto done;
262 }
263 }
264
265 switch (offset) {
266 case REG_DATA:
267 if (uart_rxfifo_putchar(sc->backend, value,
268 (sc->mcr & MCR_LOOPBACK) != 0))
269 sc->lsr |= LSR_OE;
270 sc->thre_int_pending = true;
271 break;
272 case REG_IER:
273 /* Set pending when IER_ETXRDY is raised (edge-triggered). */
274 if ((sc->ier & IER_ETXRDY) == 0 && (value & IER_ETXRDY) != 0)
275 sc->thre_int_pending = true;
276 /*
277 * Apply mask so that bits 4-7 are 0
278 * Also enables bits 0-3 only if they're 1
279 */
280 sc->ier = value & 0x0F;
281 break;
282 case REG_FCR:
283 /*
284 * When moving from FIFO and 16450 mode and vice versa,
285 * the FIFO contents are reset.
286 */
287 if ((sc->fcr & FCR_ENABLE) ^ (value & FCR_ENABLE)) {
288 fifosz = (value & FCR_ENABLE) ?
289 uart_rxfifo_size(sc->backend) : 1;
290 uart_rxfifo_reset(sc->backend, fifosz);
291 }
292
293 /*
294 * The FCR_ENABLE bit must be '1' for the programming
295 * of other FCR bits to be effective.
296 */
297 if ((value & FCR_ENABLE) == 0) {
298 sc->fcr = 0;
299 } else {
300 if ((value & FCR_RCV_RST) != 0)
301 uart_rxfifo_reset(sc->backend,
302 uart_rxfifo_size(sc->backend));
303
304 sc->fcr = value &
305 (FCR_ENABLE | FCR_DMA | FCR_RX_MASK);
306 }
307 break;
308 case REG_LCR:
309 sc->lcr = value;
310 break;
311 case REG_MCR:
312 /* Apply mask so that bits 5-7 are 0 */
313 sc->mcr = value & 0x1F;
314 msr = modem_status(sc->mcr);
315
316 /*
317 * Detect if there has been any change between the
318 * previous and the new value of MSR. If there is
319 * then assert the appropriate MSR delta bit.
320 */
321 if ((msr & MSR_CTS) ^ (sc->msr & MSR_CTS))
322 sc->msr |= MSR_DCTS;
323 if ((msr & MSR_DSR) ^ (sc->msr & MSR_DSR))
324 sc->msr |= MSR_DDSR;
325 if ((msr & MSR_DCD) ^ (sc->msr & MSR_DCD))
326 sc->msr |= MSR_DDCD;
327 if ((sc->msr & MSR_RI) != 0 && (msr & MSR_RI) == 0)
328 sc->msr |= MSR_TERI;
329
330 /*
331 * Update the value of MSR while retaining the delta
332 * bits.
333 */
334 sc->msr &= MSR_DELTA_MASK;
335 sc->msr |= msr;
336 break;
337 case REG_LSR:
338 /*
339 * Line status register is not meant to be written to
340 * during normal operation.
341 */
342 break;
343 case REG_MSR:
344 /*
345 * As far as I can tell MSR is a read-only register.
346 */
347 break;
348 case REG_SCR:
349 sc->scr = value;
350 break;
351 default:
352 break;
353 }
354
355 done:
356 uart_toggle_intr(sc);
357 uart_softc_unlock(sc->backend);
358 }
359
360 uint8_t
uart_ns16550_read(struct uart_ns16550_softc * sc,int offset)361 uart_ns16550_read(struct uart_ns16550_softc *sc, int offset)
362 {
363 uint8_t iir, intr_reason, reg;
364
365 uart_softc_lock(sc->backend);
366
367 /*
368 * Take care of the special case DLAB accesses first
369 */
370 if ((sc->lcr & LCR_DLAB) != 0) {
371 if (offset == REG_DLL) {
372 reg = sc->dll;
373 goto done;
374 }
375
376 if (offset == REG_DLH) {
377 reg = sc->dlh;
378 goto done;
379 }
380 }
381
382 switch (offset) {
383 case REG_DATA:
384 reg = uart_rxfifo_getchar(sc->backend);
385 break;
386 case REG_IER:
387 reg = sc->ier;
388 break;
389 case REG_IIR:
390 iir = (sc->fcr & FCR_ENABLE) ? IIR_FIFO_MASK : 0;
391
392 intr_reason = uart_intr_reason(sc);
393
394 /*
395 * Deal with side effects of reading the IIR register
396 */
397 if (intr_reason == IIR_TXRDY)
398 sc->thre_int_pending = false;
399
400 iir |= intr_reason;
401
402 reg = iir;
403 break;
404 case REG_LCR:
405 reg = sc->lcr;
406 break;
407 case REG_MCR:
408 reg = sc->mcr;
409 break;
410 case REG_LSR:
411 /* Transmitter is always ready for more data */
412 sc->lsr |= LSR_TEMT | LSR_THRE;
413
414 /* Check for new receive data */
415 if (uart_rxfifo_numchars(sc->backend) > 0)
416 sc->lsr |= LSR_RXRDY;
417 else
418 sc->lsr &= ~LSR_RXRDY;
419
420 reg = sc->lsr;
421
422 /* The LSR_OE bit is cleared on LSR read */
423 sc->lsr &= ~LSR_OE;
424 break;
425 case REG_MSR:
426 /*
427 * MSR delta bits are cleared on read
428 */
429 reg = sc->msr;
430 sc->msr &= ~MSR_DELTA_MASK;
431 break;
432 case REG_SCR:
433 reg = sc->scr;
434 break;
435 default:
436 reg = 0xFF;
437 break;
438 }
439
440 done:
441 uart_toggle_intr(sc);
442 uart_softc_unlock(sc->backend);
443
444 return (reg);
445 }
446
447 int
uart_legacy_alloc(int which,int * baseaddr,int * irq)448 uart_legacy_alloc(int which, int *baseaddr, int *irq)
449 {
450
451 if (which < 0 || which >= (int)UART_NLDEVS || uart_lres[which].inuse)
452 return (-1);
453
454 uart_lres[which].inuse = true;
455 *baseaddr = uart_lres[which].baseaddr;
456 *irq = uart_lres[which].irq;
457
458 return (0);
459 }
460
461 struct uart_ns16550_softc *
uart_ns16550_init(uart_intr_func_t intr_assert,uart_intr_func_t intr_deassert,void * arg)462 uart_ns16550_init(uart_intr_func_t intr_assert, uart_intr_func_t intr_deassert,
463 void *arg)
464 {
465 struct uart_ns16550_softc *sc;
466
467 sc = calloc(1, sizeof(struct uart_ns16550_softc));
468
469 sc->arg = arg;
470 sc->intr_assert = intr_assert;
471 sc->intr_deassert = intr_deassert;
472 sc->backend = uart_init();
473
474 uart_reset(sc);
475
476 return (sc);
477 }
478
479 int
uart_ns16550_tty_open(struct uart_ns16550_softc * sc,const char * device)480 uart_ns16550_tty_open(struct uart_ns16550_softc *sc, const char *device)
481 {
482 #ifndef __FreeBSD__
483 if (strncmp("socket,", device, 7) == 0) {
484 return (uart_tty_open(sc->backend, device, uart_sock_drain,
485 sc));
486 }
487 #endif
488 return (uart_tty_open(sc->backend, device, uart_drain, sc));
489 }
490