xref: /illumos-gate/usr/src/cmd/bhyve/common/uart_emul.c (revision 5c4a5fe16715fb423db76577a6883b5bbecdbe45)
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