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