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