xref: /freebsd/usr.sbin/bhyve/uart_emul.c (revision ea7f1c8cd270e452eef2f9cf071932c4424f8d40)
1*ea7f1c8cSNeel Natu /*-
2*ea7f1c8cSNeel Natu  * Copyright (c) 2012 NetApp, Inc.
3*ea7f1c8cSNeel Natu  * Copyright (c) 2013 Neel Natu <neel@freebsd.org>
4*ea7f1c8cSNeel Natu  * All rights reserved.
5*ea7f1c8cSNeel Natu  *
6*ea7f1c8cSNeel Natu  * Redistribution and use in source and binary forms, with or without
7*ea7f1c8cSNeel Natu  * modification, are permitted provided that the following conditions
8*ea7f1c8cSNeel Natu  * are met:
9*ea7f1c8cSNeel Natu  * 1. Redistributions of source code must retain the above copyright
10*ea7f1c8cSNeel Natu  *    notice, this list of conditions and the following disclaimer.
11*ea7f1c8cSNeel Natu  * 2. Redistributions in binary form must reproduce the above copyright
12*ea7f1c8cSNeel Natu  *    notice, this list of conditions and the following disclaimer in the
13*ea7f1c8cSNeel Natu  *    documentation and/or other materials provided with the distribution.
14*ea7f1c8cSNeel Natu  *
15*ea7f1c8cSNeel Natu  * THIS SOFTWARE IS PROVIDED BY NETAPP, INC ``AS IS'' AND
16*ea7f1c8cSNeel Natu  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17*ea7f1c8cSNeel Natu  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18*ea7f1c8cSNeel Natu  * ARE DISCLAIMED.  IN NO EVENT SHALL NETAPP, INC OR CONTRIBUTORS BE LIABLE
19*ea7f1c8cSNeel Natu  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20*ea7f1c8cSNeel Natu  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21*ea7f1c8cSNeel Natu  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22*ea7f1c8cSNeel Natu  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23*ea7f1c8cSNeel Natu  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24*ea7f1c8cSNeel Natu  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25*ea7f1c8cSNeel Natu  * SUCH DAMAGE.
26*ea7f1c8cSNeel Natu  *
27*ea7f1c8cSNeel Natu  * $FreeBSD$
28*ea7f1c8cSNeel Natu  */
29*ea7f1c8cSNeel Natu 
30*ea7f1c8cSNeel Natu #include <sys/cdefs.h>
31*ea7f1c8cSNeel Natu __FBSDID("$FreeBSD$");
32*ea7f1c8cSNeel Natu 
33*ea7f1c8cSNeel Natu #include <sys/types.h>
34*ea7f1c8cSNeel Natu #include <dev/ic/ns16550.h>
35*ea7f1c8cSNeel Natu 
36*ea7f1c8cSNeel Natu #include <stdio.h>
37*ea7f1c8cSNeel Natu #include <stdlib.h>
38*ea7f1c8cSNeel Natu #include <assert.h>
39*ea7f1c8cSNeel Natu #include <termios.h>
40*ea7f1c8cSNeel Natu #include <unistd.h>
41*ea7f1c8cSNeel Natu #include <stdbool.h>
42*ea7f1c8cSNeel Natu #include <string.h>
43*ea7f1c8cSNeel Natu #include <pthread.h>
44*ea7f1c8cSNeel Natu 
45*ea7f1c8cSNeel Natu #include "mevent.h"
46*ea7f1c8cSNeel Natu #include "uart_emul.h"
47*ea7f1c8cSNeel Natu 
48*ea7f1c8cSNeel Natu #define	COM1_BASE      	0x3F8
49*ea7f1c8cSNeel Natu #define COM1_IRQ	4
50*ea7f1c8cSNeel Natu #define	COM2_BASE      	0x2F8
51*ea7f1c8cSNeel Natu #define COM2_IRQ	3
52*ea7f1c8cSNeel Natu 
53*ea7f1c8cSNeel Natu #define	DEFAULT_RCLK	1843200
54*ea7f1c8cSNeel Natu #define	DEFAULT_BAUD	9600
55*ea7f1c8cSNeel Natu 
56*ea7f1c8cSNeel Natu #define	FCR_RX_MASK	0xC0
57*ea7f1c8cSNeel Natu 
58*ea7f1c8cSNeel Natu #define	MCR_OUT1	0x04
59*ea7f1c8cSNeel Natu #define	MCR_OUT2	0x08
60*ea7f1c8cSNeel Natu 
61*ea7f1c8cSNeel Natu #define	MSR_DELTA_MASK	0x0f
62*ea7f1c8cSNeel Natu 
63*ea7f1c8cSNeel Natu #ifndef REG_SCR
64*ea7f1c8cSNeel Natu #define REG_SCR		com_scr
65*ea7f1c8cSNeel Natu #endif
66*ea7f1c8cSNeel Natu 
67*ea7f1c8cSNeel Natu #define	FIFOSZ	16
68*ea7f1c8cSNeel Natu 
69*ea7f1c8cSNeel Natu static bool uart_stdio;		/* stdio in use for i/o */
70*ea7f1c8cSNeel Natu 
71*ea7f1c8cSNeel Natu static struct {
72*ea7f1c8cSNeel Natu 	int	baseaddr;
73*ea7f1c8cSNeel Natu 	int	irq;
74*ea7f1c8cSNeel Natu 	bool	inuse;
75*ea7f1c8cSNeel Natu } uart_lres[] = {
76*ea7f1c8cSNeel Natu 	{ COM1_BASE, COM1_IRQ, false},
77*ea7f1c8cSNeel Natu 	{ COM2_BASE, COM2_IRQ, false},
78*ea7f1c8cSNeel Natu };
79*ea7f1c8cSNeel Natu 
80*ea7f1c8cSNeel Natu #define	UART_NLDEVS	(sizeof(uart_lres) / sizeof(uart_lres[0]))
81*ea7f1c8cSNeel Natu 
82*ea7f1c8cSNeel Natu struct fifo {
83*ea7f1c8cSNeel Natu 	uint8_t	buf[FIFOSZ];
84*ea7f1c8cSNeel Natu 	int	rindex;		/* index to read from */
85*ea7f1c8cSNeel Natu 	int	windex;		/* index to write to */
86*ea7f1c8cSNeel Natu 	int	num;		/* number of characters in the fifo */
87*ea7f1c8cSNeel Natu 	int	size;		/* size of the fifo */
88*ea7f1c8cSNeel Natu };
89*ea7f1c8cSNeel Natu 
90*ea7f1c8cSNeel Natu struct uart_softc {
91*ea7f1c8cSNeel Natu 	pthread_mutex_t mtx;	/* protects all softc elements */
92*ea7f1c8cSNeel Natu 	uint8_t	data;		/* Data register (R/W) */
93*ea7f1c8cSNeel Natu 	uint8_t ier;		/* Interrupt enable register (R/W) */
94*ea7f1c8cSNeel Natu 	uint8_t lcr;		/* Line control register (R/W) */
95*ea7f1c8cSNeel Natu 	uint8_t mcr;		/* Modem control register (R/W) */
96*ea7f1c8cSNeel Natu 	uint8_t lsr;		/* Line status register (R/W) */
97*ea7f1c8cSNeel Natu 	uint8_t msr;		/* Modem status register (R/W) */
98*ea7f1c8cSNeel Natu 	uint8_t fcr;		/* FIFO control register (W) */
99*ea7f1c8cSNeel Natu 	uint8_t scr;		/* Scratch register (R/W) */
100*ea7f1c8cSNeel Natu 
101*ea7f1c8cSNeel Natu 	uint8_t dll;		/* Baudrate divisor latch LSB */
102*ea7f1c8cSNeel Natu 	uint8_t dlh;		/* Baudrate divisor latch MSB */
103*ea7f1c8cSNeel Natu 
104*ea7f1c8cSNeel Natu 	struct fifo rxfifo;
105*ea7f1c8cSNeel Natu 
106*ea7f1c8cSNeel Natu 	bool	opened;
107*ea7f1c8cSNeel Natu 	bool	stdio;
108*ea7f1c8cSNeel Natu 	bool	thre_int_pending;	/* THRE interrupt pending */
109*ea7f1c8cSNeel Natu 
110*ea7f1c8cSNeel Natu 	void	*arg;
111*ea7f1c8cSNeel Natu 	uart_intr_func_t intr_assert;
112*ea7f1c8cSNeel Natu 	uart_intr_func_t intr_deassert;
113*ea7f1c8cSNeel Natu };
114*ea7f1c8cSNeel Natu 
115*ea7f1c8cSNeel Natu static void uart_drain(int fd, enum ev_type ev, void *arg);
116*ea7f1c8cSNeel Natu 
117*ea7f1c8cSNeel Natu static struct termios tio_orig, tio_new;	/* I/O Terminals */
118*ea7f1c8cSNeel Natu 
119*ea7f1c8cSNeel Natu static void
120*ea7f1c8cSNeel Natu ttyclose(void)
121*ea7f1c8cSNeel Natu {
122*ea7f1c8cSNeel Natu 
123*ea7f1c8cSNeel Natu 	tcsetattr(STDIN_FILENO, TCSANOW, &tio_orig);
124*ea7f1c8cSNeel Natu }
125*ea7f1c8cSNeel Natu 
126*ea7f1c8cSNeel Natu static void
127*ea7f1c8cSNeel Natu ttyopen(void)
128*ea7f1c8cSNeel Natu {
129*ea7f1c8cSNeel Natu 
130*ea7f1c8cSNeel Natu 	tcgetattr(STDIN_FILENO, &tio_orig);
131*ea7f1c8cSNeel Natu 
132*ea7f1c8cSNeel Natu 	cfmakeraw(&tio_new);
133*ea7f1c8cSNeel Natu 	tcsetattr(STDIN_FILENO, TCSANOW, &tio_new);
134*ea7f1c8cSNeel Natu 
135*ea7f1c8cSNeel Natu 	atexit(ttyclose);
136*ea7f1c8cSNeel Natu }
137*ea7f1c8cSNeel Natu 
138*ea7f1c8cSNeel Natu static bool
139*ea7f1c8cSNeel Natu tty_char_available(void)
140*ea7f1c8cSNeel Natu {
141*ea7f1c8cSNeel Natu 	fd_set rfds;
142*ea7f1c8cSNeel Natu 	struct timeval tv;
143*ea7f1c8cSNeel Natu 
144*ea7f1c8cSNeel Natu 	FD_ZERO(&rfds);
145*ea7f1c8cSNeel Natu 	FD_SET(STDIN_FILENO, &rfds);
146*ea7f1c8cSNeel Natu 	tv.tv_sec = 0;
147*ea7f1c8cSNeel Natu 	tv.tv_usec = 0;
148*ea7f1c8cSNeel Natu 	if (select(STDIN_FILENO + 1, &rfds, NULL, NULL, &tv) > 0 ) {
149*ea7f1c8cSNeel Natu 		return (true);
150*ea7f1c8cSNeel Natu 	} else {
151*ea7f1c8cSNeel Natu 		return (false);
152*ea7f1c8cSNeel Natu 	}
153*ea7f1c8cSNeel Natu }
154*ea7f1c8cSNeel Natu 
155*ea7f1c8cSNeel Natu static int
156*ea7f1c8cSNeel Natu ttyread(void)
157*ea7f1c8cSNeel Natu {
158*ea7f1c8cSNeel Natu 	char rb;
159*ea7f1c8cSNeel Natu 
160*ea7f1c8cSNeel Natu 	if (tty_char_available()) {
161*ea7f1c8cSNeel Natu 		read(STDIN_FILENO, &rb, 1);
162*ea7f1c8cSNeel Natu 		return (rb & 0xff);
163*ea7f1c8cSNeel Natu 	} else {
164*ea7f1c8cSNeel Natu 		return (-1);
165*ea7f1c8cSNeel Natu 	}
166*ea7f1c8cSNeel Natu }
167*ea7f1c8cSNeel Natu 
168*ea7f1c8cSNeel Natu static void
169*ea7f1c8cSNeel Natu ttywrite(unsigned char wb)
170*ea7f1c8cSNeel Natu {
171*ea7f1c8cSNeel Natu 
172*ea7f1c8cSNeel Natu 	(void)write(STDIN_FILENO, &wb, 1);
173*ea7f1c8cSNeel Natu }
174*ea7f1c8cSNeel Natu 
175*ea7f1c8cSNeel Natu static void
176*ea7f1c8cSNeel Natu fifo_reset(struct fifo *fifo, int size)
177*ea7f1c8cSNeel Natu {
178*ea7f1c8cSNeel Natu 
179*ea7f1c8cSNeel Natu 	bzero(fifo, sizeof(struct fifo));
180*ea7f1c8cSNeel Natu 	fifo->size = size;
181*ea7f1c8cSNeel Natu }
182*ea7f1c8cSNeel Natu 
183*ea7f1c8cSNeel Natu static int
184*ea7f1c8cSNeel Natu fifo_putchar(struct fifo *fifo, uint8_t ch)
185*ea7f1c8cSNeel Natu {
186*ea7f1c8cSNeel Natu 
187*ea7f1c8cSNeel Natu 	if (fifo->num < fifo->size) {
188*ea7f1c8cSNeel Natu 		fifo->buf[fifo->windex] = ch;
189*ea7f1c8cSNeel Natu 		fifo->windex = (fifo->windex + 1) % fifo->size;
190*ea7f1c8cSNeel Natu 		fifo->num++;
191*ea7f1c8cSNeel Natu 		return (0);
192*ea7f1c8cSNeel Natu 	} else
193*ea7f1c8cSNeel Natu 		return (-1);
194*ea7f1c8cSNeel Natu }
195*ea7f1c8cSNeel Natu 
196*ea7f1c8cSNeel Natu static int
197*ea7f1c8cSNeel Natu fifo_getchar(struct fifo *fifo)
198*ea7f1c8cSNeel Natu {
199*ea7f1c8cSNeel Natu 	int c;
200*ea7f1c8cSNeel Natu 
201*ea7f1c8cSNeel Natu 	if (fifo->num > 0) {
202*ea7f1c8cSNeel Natu 		c = fifo->buf[fifo->rindex];
203*ea7f1c8cSNeel Natu 		fifo->rindex = (fifo->rindex + 1) % fifo->size;
204*ea7f1c8cSNeel Natu 		fifo->num--;
205*ea7f1c8cSNeel Natu 		return (c);
206*ea7f1c8cSNeel Natu 	} else
207*ea7f1c8cSNeel Natu 		return (-1);
208*ea7f1c8cSNeel Natu }
209*ea7f1c8cSNeel Natu 
210*ea7f1c8cSNeel Natu static int
211*ea7f1c8cSNeel Natu fifo_numchars(struct fifo *fifo)
212*ea7f1c8cSNeel Natu {
213*ea7f1c8cSNeel Natu 
214*ea7f1c8cSNeel Natu 	return (fifo->num);
215*ea7f1c8cSNeel Natu }
216*ea7f1c8cSNeel Natu 
217*ea7f1c8cSNeel Natu static int
218*ea7f1c8cSNeel Natu fifo_available(struct fifo *fifo)
219*ea7f1c8cSNeel Natu {
220*ea7f1c8cSNeel Natu 
221*ea7f1c8cSNeel Natu 	return (fifo->num < fifo->size);
222*ea7f1c8cSNeel Natu }
223*ea7f1c8cSNeel Natu 
224*ea7f1c8cSNeel Natu static void
225*ea7f1c8cSNeel Natu uart_opentty(struct uart_softc *sc)
226*ea7f1c8cSNeel Natu {
227*ea7f1c8cSNeel Natu 	struct mevent *mev;
228*ea7f1c8cSNeel Natu 
229*ea7f1c8cSNeel Natu 	assert(!sc->opened && sc->stdio);
230*ea7f1c8cSNeel Natu 
231*ea7f1c8cSNeel Natu 	ttyopen();
232*ea7f1c8cSNeel Natu 	mev = mevent_add(STDIN_FILENO, EVF_READ, uart_drain, sc);
233*ea7f1c8cSNeel Natu 	assert(mev);
234*ea7f1c8cSNeel Natu }
235*ea7f1c8cSNeel Natu 
236*ea7f1c8cSNeel Natu /*
237*ea7f1c8cSNeel Natu  * The IIR returns a prioritized interrupt reason:
238*ea7f1c8cSNeel Natu  * - receive data available
239*ea7f1c8cSNeel Natu  * - transmit holding register empty
240*ea7f1c8cSNeel Natu  * - modem status change
241*ea7f1c8cSNeel Natu  *
242*ea7f1c8cSNeel Natu  * Return an interrupt reason if one is available.
243*ea7f1c8cSNeel Natu  */
244*ea7f1c8cSNeel Natu static int
245*ea7f1c8cSNeel Natu uart_intr_reason(struct uart_softc *sc)
246*ea7f1c8cSNeel Natu {
247*ea7f1c8cSNeel Natu 
248*ea7f1c8cSNeel Natu 	if ((sc->lsr & LSR_OE) != 0 && (sc->ier & IER_ERLS) != 0)
249*ea7f1c8cSNeel Natu 		return (IIR_RLS);
250*ea7f1c8cSNeel Natu 	else if (fifo_numchars(&sc->rxfifo) > 0 && (sc->ier & IER_ERXRDY) != 0)
251*ea7f1c8cSNeel Natu 		return (IIR_RXTOUT);
252*ea7f1c8cSNeel Natu 	else if (sc->thre_int_pending && (sc->ier & IER_ETXRDY) != 0)
253*ea7f1c8cSNeel Natu 		return (IIR_TXRDY);
254*ea7f1c8cSNeel Natu 	else if ((sc->msr & MSR_DELTA_MASK) != 0 && (sc->ier & IER_EMSC) != 0)
255*ea7f1c8cSNeel Natu 		return (IIR_MLSC);
256*ea7f1c8cSNeel Natu 	else
257*ea7f1c8cSNeel Natu 		return (IIR_NOPEND);
258*ea7f1c8cSNeel Natu }
259*ea7f1c8cSNeel Natu 
260*ea7f1c8cSNeel Natu static void
261*ea7f1c8cSNeel Natu uart_reset(struct uart_softc *sc)
262*ea7f1c8cSNeel Natu {
263*ea7f1c8cSNeel Natu 	uint16_t divisor;
264*ea7f1c8cSNeel Natu 
265*ea7f1c8cSNeel Natu 	divisor = DEFAULT_RCLK / DEFAULT_BAUD / 16;
266*ea7f1c8cSNeel Natu 	sc->dll = divisor;
267*ea7f1c8cSNeel Natu 	sc->dlh = divisor >> 16;
268*ea7f1c8cSNeel Natu 
269*ea7f1c8cSNeel Natu 	fifo_reset(&sc->rxfifo, 1);	/* no fifo until enabled by software */
270*ea7f1c8cSNeel Natu }
271*ea7f1c8cSNeel Natu 
272*ea7f1c8cSNeel Natu /*
273*ea7f1c8cSNeel Natu  * Toggle the COM port's intr pin depending on whether or not we have an
274*ea7f1c8cSNeel Natu  * interrupt condition to report to the processor.
275*ea7f1c8cSNeel Natu  */
276*ea7f1c8cSNeel Natu static void
277*ea7f1c8cSNeel Natu uart_toggle_intr(struct uart_softc *sc)
278*ea7f1c8cSNeel Natu {
279*ea7f1c8cSNeel Natu 	uint8_t intr_reason;
280*ea7f1c8cSNeel Natu 
281*ea7f1c8cSNeel Natu 	intr_reason = uart_intr_reason(sc);
282*ea7f1c8cSNeel Natu 
283*ea7f1c8cSNeel Natu 	if (intr_reason == IIR_NOPEND)
284*ea7f1c8cSNeel Natu 		(*sc->intr_deassert)(sc->arg);
285*ea7f1c8cSNeel Natu 	else
286*ea7f1c8cSNeel Natu 		(*sc->intr_assert)(sc->arg);
287*ea7f1c8cSNeel Natu }
288*ea7f1c8cSNeel Natu 
289*ea7f1c8cSNeel Natu static void
290*ea7f1c8cSNeel Natu uart_drain(int fd, enum ev_type ev, void *arg)
291*ea7f1c8cSNeel Natu {
292*ea7f1c8cSNeel Natu 	struct uart_softc *sc;
293*ea7f1c8cSNeel Natu 	int ch;
294*ea7f1c8cSNeel Natu 
295*ea7f1c8cSNeel Natu 	sc = arg;
296*ea7f1c8cSNeel Natu 
297*ea7f1c8cSNeel Natu 	assert(fd == STDIN_FILENO);
298*ea7f1c8cSNeel Natu 	assert(ev == EVF_READ);
299*ea7f1c8cSNeel Natu 
300*ea7f1c8cSNeel Natu 	/*
301*ea7f1c8cSNeel Natu 	 * This routine is called in the context of the mevent thread
302*ea7f1c8cSNeel Natu 	 * to take out the softc lock to protect against concurrent
303*ea7f1c8cSNeel Natu 	 * access from a vCPU i/o exit
304*ea7f1c8cSNeel Natu 	 */
305*ea7f1c8cSNeel Natu 	pthread_mutex_lock(&sc->mtx);
306*ea7f1c8cSNeel Natu 
307*ea7f1c8cSNeel Natu 	if ((sc->mcr & MCR_LOOPBACK) != 0) {
308*ea7f1c8cSNeel Natu 		(void) ttyread();
309*ea7f1c8cSNeel Natu 	} else {
310*ea7f1c8cSNeel Natu 		while (fifo_available(&sc->rxfifo) &&
311*ea7f1c8cSNeel Natu 		       ((ch = ttyread()) != -1)) {
312*ea7f1c8cSNeel Natu 			fifo_putchar(&sc->rxfifo, ch);
313*ea7f1c8cSNeel Natu 		}
314*ea7f1c8cSNeel Natu 		uart_toggle_intr(sc);
315*ea7f1c8cSNeel Natu 	}
316*ea7f1c8cSNeel Natu 
317*ea7f1c8cSNeel Natu 	pthread_mutex_unlock(&sc->mtx);
318*ea7f1c8cSNeel Natu }
319*ea7f1c8cSNeel Natu 
320*ea7f1c8cSNeel Natu void
321*ea7f1c8cSNeel Natu uart_write(struct uart_softc *sc, int offset, uint8_t value)
322*ea7f1c8cSNeel Natu {
323*ea7f1c8cSNeel Natu 	int fifosz;
324*ea7f1c8cSNeel Natu 	uint8_t msr;
325*ea7f1c8cSNeel Natu 
326*ea7f1c8cSNeel Natu 	/* Open terminal */
327*ea7f1c8cSNeel Natu 	if (!sc->opened && sc->stdio) {
328*ea7f1c8cSNeel Natu 		uart_opentty(sc);
329*ea7f1c8cSNeel Natu 		sc->opened = true;
330*ea7f1c8cSNeel Natu 	}
331*ea7f1c8cSNeel Natu 
332*ea7f1c8cSNeel Natu 	pthread_mutex_lock(&sc->mtx);
333*ea7f1c8cSNeel Natu 
334*ea7f1c8cSNeel Natu 	/*
335*ea7f1c8cSNeel Natu 	 * Take care of the special case DLAB accesses first
336*ea7f1c8cSNeel Natu 	 */
337*ea7f1c8cSNeel Natu 	if ((sc->lcr & LCR_DLAB) != 0) {
338*ea7f1c8cSNeel Natu 		if (offset == REG_DLL) {
339*ea7f1c8cSNeel Natu 			sc->dll = value;
340*ea7f1c8cSNeel Natu 			goto done;
341*ea7f1c8cSNeel Natu 		}
342*ea7f1c8cSNeel Natu 
343*ea7f1c8cSNeel Natu 		if (offset == REG_DLH) {
344*ea7f1c8cSNeel Natu 			sc->dlh = value;
345*ea7f1c8cSNeel Natu 			goto done;
346*ea7f1c8cSNeel Natu 		}
347*ea7f1c8cSNeel Natu 	}
348*ea7f1c8cSNeel Natu 
349*ea7f1c8cSNeel Natu         switch (offset) {
350*ea7f1c8cSNeel Natu 	case REG_DATA:
351*ea7f1c8cSNeel Natu 		if (sc->mcr & MCR_LOOPBACK) {
352*ea7f1c8cSNeel Natu 			if (fifo_putchar(&sc->rxfifo, value) != 0)
353*ea7f1c8cSNeel Natu 				sc->lsr |= LSR_OE;
354*ea7f1c8cSNeel Natu 		} else if (sc->stdio) {
355*ea7f1c8cSNeel Natu 			ttywrite(value);
356*ea7f1c8cSNeel Natu 		} /* else drop on floor */
357*ea7f1c8cSNeel Natu 		sc->thre_int_pending = true;
358*ea7f1c8cSNeel Natu 		break;
359*ea7f1c8cSNeel Natu 	case REG_IER:
360*ea7f1c8cSNeel Natu 		/*
361*ea7f1c8cSNeel Natu 		 * Apply mask so that bits 4-7 are 0
362*ea7f1c8cSNeel Natu 		 * Also enables bits 0-3 only if they're 1
363*ea7f1c8cSNeel Natu 		 */
364*ea7f1c8cSNeel Natu 		sc->ier = value & 0x0F;
365*ea7f1c8cSNeel Natu 		break;
366*ea7f1c8cSNeel Natu 		case REG_FCR:
367*ea7f1c8cSNeel Natu 			/*
368*ea7f1c8cSNeel Natu 			 * When moving from FIFO and 16450 mode and vice versa,
369*ea7f1c8cSNeel Natu 			 * the FIFO contents are reset.
370*ea7f1c8cSNeel Natu 			 */
371*ea7f1c8cSNeel Natu 			if ((sc->fcr & FCR_ENABLE) ^ (value & FCR_ENABLE)) {
372*ea7f1c8cSNeel Natu 				fifosz = (value & FCR_ENABLE) ? FIFOSZ : 1;
373*ea7f1c8cSNeel Natu 				fifo_reset(&sc->rxfifo, fifosz);
374*ea7f1c8cSNeel Natu 			}
375*ea7f1c8cSNeel Natu 
376*ea7f1c8cSNeel Natu 			/*
377*ea7f1c8cSNeel Natu 			 * The FCR_ENABLE bit must be '1' for the programming
378*ea7f1c8cSNeel Natu 			 * of other FCR bits to be effective.
379*ea7f1c8cSNeel Natu 			 */
380*ea7f1c8cSNeel Natu 			if ((value & FCR_ENABLE) == 0) {
381*ea7f1c8cSNeel Natu 				sc->fcr = 0;
382*ea7f1c8cSNeel Natu 			} else {
383*ea7f1c8cSNeel Natu 				if ((value & FCR_RCV_RST) != 0)
384*ea7f1c8cSNeel Natu 					fifo_reset(&sc->rxfifo, FIFOSZ);
385*ea7f1c8cSNeel Natu 
386*ea7f1c8cSNeel Natu 				sc->fcr = value &
387*ea7f1c8cSNeel Natu 					 (FCR_ENABLE | FCR_DMA | FCR_RX_MASK);
388*ea7f1c8cSNeel Natu 			}
389*ea7f1c8cSNeel Natu 			break;
390*ea7f1c8cSNeel Natu 		case REG_LCR:
391*ea7f1c8cSNeel Natu 			sc->lcr = value;
392*ea7f1c8cSNeel Natu 			break;
393*ea7f1c8cSNeel Natu 		case REG_MCR:
394*ea7f1c8cSNeel Natu 			/* Apply mask so that bits 5-7 are 0 */
395*ea7f1c8cSNeel Natu 			sc->mcr = value & 0x1F;
396*ea7f1c8cSNeel Natu 
397*ea7f1c8cSNeel Natu 			msr = 0;
398*ea7f1c8cSNeel Natu 			if (sc->mcr & MCR_LOOPBACK) {
399*ea7f1c8cSNeel Natu 				/*
400*ea7f1c8cSNeel Natu 				 * In the loopback mode certain bits from the
401*ea7f1c8cSNeel Natu 				 * MCR are reflected back into MSR
402*ea7f1c8cSNeel Natu 				 */
403*ea7f1c8cSNeel Natu 				if (sc->mcr & MCR_RTS)
404*ea7f1c8cSNeel Natu 					msr |= MSR_CTS;
405*ea7f1c8cSNeel Natu 				if (sc->mcr & MCR_DTR)
406*ea7f1c8cSNeel Natu 					msr |= MSR_DSR;
407*ea7f1c8cSNeel Natu 				if (sc->mcr & MCR_OUT1)
408*ea7f1c8cSNeel Natu 					msr |= MSR_RI;
409*ea7f1c8cSNeel Natu 				if (sc->mcr & MCR_OUT2)
410*ea7f1c8cSNeel Natu 					msr |= MSR_DCD;
411*ea7f1c8cSNeel Natu 			}
412*ea7f1c8cSNeel Natu 
413*ea7f1c8cSNeel Natu 			/*
414*ea7f1c8cSNeel Natu 			 * Detect if there has been any change between the
415*ea7f1c8cSNeel Natu 			 * previous and the new value of MSR. If there is
416*ea7f1c8cSNeel Natu 			 * then assert the appropriate MSR delta bit.
417*ea7f1c8cSNeel Natu 			 */
418*ea7f1c8cSNeel Natu 			if ((msr & MSR_CTS) ^ (sc->msr & MSR_CTS))
419*ea7f1c8cSNeel Natu 				sc->msr |= MSR_DCTS;
420*ea7f1c8cSNeel Natu 			if ((msr & MSR_DSR) ^ (sc->msr & MSR_DSR))
421*ea7f1c8cSNeel Natu 				sc->msr |= MSR_DDSR;
422*ea7f1c8cSNeel Natu 			if ((msr & MSR_DCD) ^ (sc->msr & MSR_DCD))
423*ea7f1c8cSNeel Natu 				sc->msr |= MSR_DDCD;
424*ea7f1c8cSNeel Natu 			if ((sc->msr & MSR_RI) != 0 && (msr & MSR_RI) == 0)
425*ea7f1c8cSNeel Natu 				sc->msr |= MSR_TERI;
426*ea7f1c8cSNeel Natu 
427*ea7f1c8cSNeel Natu 			/*
428*ea7f1c8cSNeel Natu 			 * Update the value of MSR while retaining the delta
429*ea7f1c8cSNeel Natu 			 * bits.
430*ea7f1c8cSNeel Natu 			 */
431*ea7f1c8cSNeel Natu 			sc->msr &= MSR_DELTA_MASK;
432*ea7f1c8cSNeel Natu 			sc->msr |= msr;
433*ea7f1c8cSNeel Natu 			break;
434*ea7f1c8cSNeel Natu 		case REG_LSR:
435*ea7f1c8cSNeel Natu 			/*
436*ea7f1c8cSNeel Natu 			 * Line status register is not meant to be written to
437*ea7f1c8cSNeel Natu 			 * during normal operation.
438*ea7f1c8cSNeel Natu 			 */
439*ea7f1c8cSNeel Natu 			break;
440*ea7f1c8cSNeel Natu 		case REG_MSR:
441*ea7f1c8cSNeel Natu 			/*
442*ea7f1c8cSNeel Natu 			 * As far as I can tell MSR is a read-only register.
443*ea7f1c8cSNeel Natu 			 */
444*ea7f1c8cSNeel Natu 			break;
445*ea7f1c8cSNeel Natu 		case REG_SCR:
446*ea7f1c8cSNeel Natu 			sc->scr = value;
447*ea7f1c8cSNeel Natu 			break;
448*ea7f1c8cSNeel Natu 		default:
449*ea7f1c8cSNeel Natu 			break;
450*ea7f1c8cSNeel Natu 	}
451*ea7f1c8cSNeel Natu 
452*ea7f1c8cSNeel Natu done:
453*ea7f1c8cSNeel Natu 	uart_toggle_intr(sc);
454*ea7f1c8cSNeel Natu 	pthread_mutex_unlock(&sc->mtx);
455*ea7f1c8cSNeel Natu }
456*ea7f1c8cSNeel Natu 
457*ea7f1c8cSNeel Natu uint8_t
458*ea7f1c8cSNeel Natu uart_read(struct uart_softc *sc, int offset)
459*ea7f1c8cSNeel Natu {
460*ea7f1c8cSNeel Natu 	uint8_t iir, intr_reason, reg;
461*ea7f1c8cSNeel Natu 
462*ea7f1c8cSNeel Natu 	/* Open terminal */
463*ea7f1c8cSNeel Natu 	if (!sc->opened && sc->stdio) {
464*ea7f1c8cSNeel Natu 		uart_opentty(sc);
465*ea7f1c8cSNeel Natu 		sc->opened = true;
466*ea7f1c8cSNeel Natu 	}
467*ea7f1c8cSNeel Natu 
468*ea7f1c8cSNeel Natu 	pthread_mutex_lock(&sc->mtx);
469*ea7f1c8cSNeel Natu 
470*ea7f1c8cSNeel Natu 	/*
471*ea7f1c8cSNeel Natu 	 * Take care of the special case DLAB accesses first
472*ea7f1c8cSNeel Natu 	 */
473*ea7f1c8cSNeel Natu 	if ((sc->lcr & LCR_DLAB) != 0) {
474*ea7f1c8cSNeel Natu 		if (offset == REG_DLL) {
475*ea7f1c8cSNeel Natu 			reg = sc->dll;
476*ea7f1c8cSNeel Natu 			goto done;
477*ea7f1c8cSNeel Natu 		}
478*ea7f1c8cSNeel Natu 
479*ea7f1c8cSNeel Natu 		if (offset == REG_DLH) {
480*ea7f1c8cSNeel Natu 			reg = sc->dlh;
481*ea7f1c8cSNeel Natu 			goto done;
482*ea7f1c8cSNeel Natu 		}
483*ea7f1c8cSNeel Natu 	}
484*ea7f1c8cSNeel Natu 
485*ea7f1c8cSNeel Natu 	switch (offset) {
486*ea7f1c8cSNeel Natu 	case REG_DATA:
487*ea7f1c8cSNeel Natu 		reg = fifo_getchar(&sc->rxfifo);
488*ea7f1c8cSNeel Natu 		break;
489*ea7f1c8cSNeel Natu 	case REG_IER:
490*ea7f1c8cSNeel Natu 		reg = sc->ier;
491*ea7f1c8cSNeel Natu 		break;
492*ea7f1c8cSNeel Natu 	case REG_IIR:
493*ea7f1c8cSNeel Natu 		iir = (sc->fcr & FCR_ENABLE) ? IIR_FIFO_MASK : 0;
494*ea7f1c8cSNeel Natu 
495*ea7f1c8cSNeel Natu 		intr_reason = uart_intr_reason(sc);
496*ea7f1c8cSNeel Natu 
497*ea7f1c8cSNeel Natu 		/*
498*ea7f1c8cSNeel Natu 		 * Deal with side effects of reading the IIR register
499*ea7f1c8cSNeel Natu 		 */
500*ea7f1c8cSNeel Natu 		if (intr_reason == IIR_TXRDY)
501*ea7f1c8cSNeel Natu 			sc->thre_int_pending = false;
502*ea7f1c8cSNeel Natu 
503*ea7f1c8cSNeel Natu 		iir |= intr_reason;
504*ea7f1c8cSNeel Natu 
505*ea7f1c8cSNeel Natu 		reg = iir;
506*ea7f1c8cSNeel Natu 		break;
507*ea7f1c8cSNeel Natu 	case REG_LCR:
508*ea7f1c8cSNeel Natu 		reg = sc->lcr;
509*ea7f1c8cSNeel Natu 		break;
510*ea7f1c8cSNeel Natu 	case REG_MCR:
511*ea7f1c8cSNeel Natu 		reg = sc->mcr;
512*ea7f1c8cSNeel Natu 		break;
513*ea7f1c8cSNeel Natu 	case REG_LSR:
514*ea7f1c8cSNeel Natu 		/* Transmitter is always ready for more data */
515*ea7f1c8cSNeel Natu 		sc->lsr |= LSR_TEMT | LSR_THRE;
516*ea7f1c8cSNeel Natu 
517*ea7f1c8cSNeel Natu 		/* Check for new receive data */
518*ea7f1c8cSNeel Natu 		if (fifo_numchars(&sc->rxfifo) > 0)
519*ea7f1c8cSNeel Natu 			sc->lsr |= LSR_RXRDY;
520*ea7f1c8cSNeel Natu 		else
521*ea7f1c8cSNeel Natu 			sc->lsr &= ~LSR_RXRDY;
522*ea7f1c8cSNeel Natu 
523*ea7f1c8cSNeel Natu 		reg = sc->lsr;
524*ea7f1c8cSNeel Natu 
525*ea7f1c8cSNeel Natu 		/* The LSR_OE bit is cleared on LSR read */
526*ea7f1c8cSNeel Natu 		sc->lsr &= ~LSR_OE;
527*ea7f1c8cSNeel Natu 		break;
528*ea7f1c8cSNeel Natu 	case REG_MSR:
529*ea7f1c8cSNeel Natu 		/*
530*ea7f1c8cSNeel Natu 		 * MSR delta bits are cleared on read
531*ea7f1c8cSNeel Natu 		 */
532*ea7f1c8cSNeel Natu 		reg = sc->msr;
533*ea7f1c8cSNeel Natu 		sc->msr &= ~MSR_DELTA_MASK;
534*ea7f1c8cSNeel Natu 		break;
535*ea7f1c8cSNeel Natu 	case REG_SCR:
536*ea7f1c8cSNeel Natu 		reg = sc->scr;
537*ea7f1c8cSNeel Natu 		break;
538*ea7f1c8cSNeel Natu 	default:
539*ea7f1c8cSNeel Natu 		reg = 0xFF;
540*ea7f1c8cSNeel Natu 		break;
541*ea7f1c8cSNeel Natu 	}
542*ea7f1c8cSNeel Natu 
543*ea7f1c8cSNeel Natu done:
544*ea7f1c8cSNeel Natu 	uart_toggle_intr(sc);
545*ea7f1c8cSNeel Natu 	pthread_mutex_unlock(&sc->mtx);
546*ea7f1c8cSNeel Natu 
547*ea7f1c8cSNeel Natu 	return (reg);
548*ea7f1c8cSNeel Natu }
549*ea7f1c8cSNeel Natu 
550*ea7f1c8cSNeel Natu int
551*ea7f1c8cSNeel Natu uart_legacy_alloc(int which, int *baseaddr, int *irq)
552*ea7f1c8cSNeel Natu {
553*ea7f1c8cSNeel Natu 
554*ea7f1c8cSNeel Natu 	if (which < 0 || which >= UART_NLDEVS || uart_lres[which].inuse)
555*ea7f1c8cSNeel Natu 		return (-1);
556*ea7f1c8cSNeel Natu 
557*ea7f1c8cSNeel Natu 	uart_lres[which].inuse = true;
558*ea7f1c8cSNeel Natu 	*baseaddr = uart_lres[which].baseaddr;
559*ea7f1c8cSNeel Natu 	*irq = uart_lres[which].irq;
560*ea7f1c8cSNeel Natu 
561*ea7f1c8cSNeel Natu 	return (0);
562*ea7f1c8cSNeel Natu }
563*ea7f1c8cSNeel Natu 
564*ea7f1c8cSNeel Natu struct uart_softc *
565*ea7f1c8cSNeel Natu uart_init(uart_intr_func_t intr_assert, uart_intr_func_t intr_deassert,
566*ea7f1c8cSNeel Natu     void *arg)
567*ea7f1c8cSNeel Natu {
568*ea7f1c8cSNeel Natu 	struct uart_softc *sc;
569*ea7f1c8cSNeel Natu 
570*ea7f1c8cSNeel Natu 	sc = malloc(sizeof(struct uart_softc));
571*ea7f1c8cSNeel Natu 	bzero(sc, sizeof(struct uart_softc));
572*ea7f1c8cSNeel Natu 
573*ea7f1c8cSNeel Natu 	sc->arg = arg;
574*ea7f1c8cSNeel Natu 	sc->intr_assert = intr_assert;
575*ea7f1c8cSNeel Natu 	sc->intr_deassert = intr_deassert;
576*ea7f1c8cSNeel Natu 
577*ea7f1c8cSNeel Natu 	pthread_mutex_init(&sc->mtx, NULL);
578*ea7f1c8cSNeel Natu 
579*ea7f1c8cSNeel Natu 	uart_reset(sc);
580*ea7f1c8cSNeel Natu 
581*ea7f1c8cSNeel Natu 	return (sc);
582*ea7f1c8cSNeel Natu }
583*ea7f1c8cSNeel Natu 
584*ea7f1c8cSNeel Natu int
585*ea7f1c8cSNeel Natu uart_set_backend(struct uart_softc *sc, const char *opts)
586*ea7f1c8cSNeel Natu {
587*ea7f1c8cSNeel Natu 	/*
588*ea7f1c8cSNeel Natu 	 * XXX one stdio backend supported at this time.
589*ea7f1c8cSNeel Natu 	 */
590*ea7f1c8cSNeel Natu 	if (opts == NULL)
591*ea7f1c8cSNeel Natu 		return (0);
592*ea7f1c8cSNeel Natu 
593*ea7f1c8cSNeel Natu 	if (strcmp("stdio", opts) == 0 && !uart_stdio) {
594*ea7f1c8cSNeel Natu 		sc->stdio = true;
595*ea7f1c8cSNeel Natu 		uart_stdio = true;
596*ea7f1c8cSNeel Natu 		return (0);
597*ea7f1c8cSNeel Natu 	} else
598*ea7f1c8cSNeel Natu 		return (-1);
599*ea7f1c8cSNeel Natu }
600