1257293Sneel/*-
2257293Sneel * Copyright (c) 2012 NetApp, Inc.
3257293Sneel * Copyright (c) 2013 Neel Natu <neel@freebsd.org>
4257293Sneel * All rights reserved.
5257293Sneel *
6257293Sneel * Redistribution and use in source and binary forms, with or without
7257293Sneel * modification, are permitted provided that the following conditions
8257293Sneel * are met:
9257293Sneel * 1. Redistributions of source code must retain the above copyright
10257293Sneel *    notice, this list of conditions and the following disclaimer.
11257293Sneel * 2. Redistributions in binary form must reproduce the above copyright
12257293Sneel *    notice, this list of conditions and the following disclaimer in the
13257293Sneel *    documentation and/or other materials provided with the distribution.
14257293Sneel *
15257293Sneel * THIS SOFTWARE IS PROVIDED BY NETAPP, INC ``AS IS'' AND
16257293Sneel * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17257293Sneel * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18257293Sneel * ARE DISCLAIMED.  IN NO EVENT SHALL NETAPP, INC OR CONTRIBUTORS BE LIABLE
19257293Sneel * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20257293Sneel * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21257293Sneel * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22257293Sneel * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23257293Sneel * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24257293Sneel * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25257293Sneel * SUCH DAMAGE.
26257293Sneel *
27257293Sneel * $FreeBSD$
28257293Sneel */
29257293Sneel
30257293Sneel#include <sys/cdefs.h>
31257293Sneel__FBSDID("$FreeBSD$");
32257293Sneel
33257293Sneel#include <sys/types.h>
34257293Sneel#include <dev/ic/ns16550.h>
35257293Sneel
36257293Sneel#include <stdio.h>
37257293Sneel#include <stdlib.h>
38257293Sneel#include <assert.h>
39259301Sgrehan#include <fcntl.h>
40257293Sneel#include <termios.h>
41257293Sneel#include <unistd.h>
42257293Sneel#include <stdbool.h>
43257293Sneel#include <string.h>
44257293Sneel#include <pthread.h>
45257293Sneel
46257293Sneel#include "mevent.h"
47257293Sneel#include "uart_emul.h"
48257293Sneel
49257293Sneel#define	COM1_BASE      	0x3F8
50257293Sneel#define COM1_IRQ	4
51257293Sneel#define	COM2_BASE      	0x2F8
52257293Sneel#define COM2_IRQ	3
53257293Sneel
54257293Sneel#define	DEFAULT_RCLK	1843200
55257293Sneel#define	DEFAULT_BAUD	9600
56257293Sneel
57257293Sneel#define	FCR_RX_MASK	0xC0
58257293Sneel
59257293Sneel#define	MCR_OUT1	0x04
60257293Sneel#define	MCR_OUT2	0x08
61257293Sneel
62257293Sneel#define	MSR_DELTA_MASK	0x0f
63257293Sneel
64257293Sneel#ifndef REG_SCR
65257293Sneel#define REG_SCR		com_scr
66257293Sneel#endif
67257293Sneel
68257293Sneel#define	FIFOSZ	16
69257293Sneel
70257293Sneelstatic bool uart_stdio;		/* stdio in use for i/o */
71259301Sgrehanstatic struct termios tio_stdio_orig;
72257293Sneel
73257293Sneelstatic struct {
74257293Sneel	int	baseaddr;
75257293Sneel	int	irq;
76257293Sneel	bool	inuse;
77257293Sneel} uart_lres[] = {
78257293Sneel	{ COM1_BASE, COM1_IRQ, false},
79257293Sneel	{ COM2_BASE, COM2_IRQ, false},
80257293Sneel};
81257293Sneel
82257293Sneel#define	UART_NLDEVS	(sizeof(uart_lres) / sizeof(uart_lres[0]))
83257293Sneel
84257293Sneelstruct fifo {
85257293Sneel	uint8_t	buf[FIFOSZ];
86257293Sneel	int	rindex;		/* index to read from */
87257293Sneel	int	windex;		/* index to write to */
88257293Sneel	int	num;		/* number of characters in the fifo */
89257293Sneel	int	size;		/* size of the fifo */
90257293Sneel};
91257293Sneel
92259301Sgrehanstruct ttyfd {
93259301Sgrehan	bool	opened;
94259301Sgrehan	int	fd;		/* tty device file descriptor */
95259301Sgrehan	struct termios tio_orig, tio_new;    /* I/O Terminals */
96259301Sgrehan};
97259301Sgrehan
98257293Sneelstruct uart_softc {
99257293Sneel	pthread_mutex_t mtx;	/* protects all softc elements */
100257293Sneel	uint8_t	data;		/* Data register (R/W) */
101257293Sneel	uint8_t ier;		/* Interrupt enable register (R/W) */
102257293Sneel	uint8_t lcr;		/* Line control register (R/W) */
103257293Sneel	uint8_t mcr;		/* Modem control register (R/W) */
104257293Sneel	uint8_t lsr;		/* Line status register (R/W) */
105257293Sneel	uint8_t msr;		/* Modem status register (R/W) */
106257293Sneel	uint8_t fcr;		/* FIFO control register (W) */
107257293Sneel	uint8_t scr;		/* Scratch register (R/W) */
108257293Sneel
109257293Sneel	uint8_t dll;		/* Baudrate divisor latch LSB */
110257293Sneel	uint8_t dlh;		/* Baudrate divisor latch MSB */
111257293Sneel
112257293Sneel	struct fifo rxfifo;
113268892Sjhb	struct mevent *mev;
114257293Sneel
115259301Sgrehan	struct ttyfd tty;
116257293Sneel	bool	thre_int_pending;	/* THRE interrupt pending */
117257293Sneel
118257293Sneel	void	*arg;
119257293Sneel	uart_intr_func_t intr_assert;
120257293Sneel	uart_intr_func_t intr_deassert;
121257293Sneel};
122257293Sneel
123257293Sneelstatic void uart_drain(int fd, enum ev_type ev, void *arg);
124257293Sneel
125257293Sneelstatic void
126257293Sneelttyclose(void)
127257293Sneel{
128257293Sneel
129259301Sgrehan	tcsetattr(STDIN_FILENO, TCSANOW, &tio_stdio_orig);
130257293Sneel}
131257293Sneel
132257293Sneelstatic void
133259301Sgrehanttyopen(struct ttyfd *tf)
134257293Sneel{
135257293Sneel
136259301Sgrehan	tcgetattr(tf->fd, &tf->tio_orig);
137257293Sneel
138259301Sgrehan	tf->tio_new = tf->tio_orig;
139259301Sgrehan	cfmakeraw(&tf->tio_new);
140259301Sgrehan	tf->tio_new.c_cflag |= CLOCAL;
141259301Sgrehan	tcsetattr(tf->fd, TCSANOW, &tf->tio_new);
142257293Sneel
143259301Sgrehan	if (tf->fd == STDIN_FILENO) {
144259301Sgrehan		tio_stdio_orig = tf->tio_orig;
145259301Sgrehan		atexit(ttyclose);
146259301Sgrehan	}
147257293Sneel}
148257293Sneel
149257293Sneelstatic int
150259301Sgrehanttyread(struct ttyfd *tf)
151257293Sneel{
152268892Sjhb	unsigned char rb;
153257293Sneel
154268892Sjhb	if (read(tf->fd, &rb, 1) == 1)
155268892Sjhb		return (rb);
156268892Sjhb	else
157257293Sneel		return (-1);
158257293Sneel}
159257293Sneel
160257293Sneelstatic void
161259301Sgrehanttywrite(struct ttyfd *tf, unsigned char wb)
162257293Sneel{
163257293Sneel
164259301Sgrehan	(void)write(tf->fd, &wb, 1);
165257293Sneel}
166257293Sneel
167257293Sneelstatic void
168268892Sjhbrxfifo_reset(struct uart_softc *sc, int size)
169257293Sneel{
170268892Sjhb	char flushbuf[32];
171268892Sjhb	struct fifo *fifo;
172268892Sjhb	ssize_t nread;
173268892Sjhb	int error;
174257293Sneel
175268892Sjhb	fifo = &sc->rxfifo;
176257293Sneel	bzero(fifo, sizeof(struct fifo));
177257293Sneel	fifo->size = size;
178268892Sjhb
179268892Sjhb	if (sc->tty.opened) {
180268892Sjhb		/*
181268892Sjhb		 * Flush any unread input from the tty buffer.
182268892Sjhb		 */
183268892Sjhb		while (1) {
184268892Sjhb			nread = read(sc->tty.fd, flushbuf, sizeof(flushbuf));
185268892Sjhb			if (nread != sizeof(flushbuf))
186268892Sjhb				break;
187268892Sjhb		}
188268892Sjhb
189268892Sjhb		/*
190268892Sjhb		 * Enable mevent to trigger when new characters are available
191268892Sjhb		 * on the tty fd.
192268892Sjhb		 */
193268892Sjhb		error = mevent_enable(sc->mev);
194268892Sjhb		assert(error == 0);
195268892Sjhb	}
196257293Sneel}
197257293Sneel
198257293Sneelstatic int
199268892Sjhbrxfifo_available(struct uart_softc *sc)
200257293Sneel{
201268892Sjhb	struct fifo *fifo;
202257293Sneel
203268892Sjhb	fifo = &sc->rxfifo;
204268892Sjhb	return (fifo->num < fifo->size);
205268892Sjhb}
206268892Sjhb
207268892Sjhbstatic int
208268892Sjhbrxfifo_putchar(struct uart_softc *sc, uint8_t ch)
209268892Sjhb{
210268892Sjhb	struct fifo *fifo;
211268892Sjhb	int error;
212268892Sjhb
213268892Sjhb	fifo = &sc->rxfifo;
214268892Sjhb
215257293Sneel	if (fifo->num < fifo->size) {
216257293Sneel		fifo->buf[fifo->windex] = ch;
217257293Sneel		fifo->windex = (fifo->windex + 1) % fifo->size;
218257293Sneel		fifo->num++;
219268892Sjhb		if (!rxfifo_available(sc)) {
220268892Sjhb			if (sc->tty.opened) {
221268892Sjhb				/*
222268892Sjhb				 * Disable mevent callback if the FIFO is full.
223268892Sjhb				 */
224268892Sjhb				error = mevent_disable(sc->mev);
225268892Sjhb				assert(error == 0);
226268892Sjhb			}
227268892Sjhb		}
228257293Sneel		return (0);
229257293Sneel	} else
230257293Sneel		return (-1);
231257293Sneel}
232257293Sneel
233257293Sneelstatic int
234268892Sjhbrxfifo_getchar(struct uart_softc *sc)
235257293Sneel{
236268892Sjhb	struct fifo *fifo;
237268892Sjhb	int c, error, wasfull;
238257293Sneel
239268892Sjhb	wasfull = 0;
240268892Sjhb	fifo = &sc->rxfifo;
241257293Sneel	if (fifo->num > 0) {
242268892Sjhb		if (!rxfifo_available(sc))
243268892Sjhb			wasfull = 1;
244257293Sneel		c = fifo->buf[fifo->rindex];
245257293Sneel		fifo->rindex = (fifo->rindex + 1) % fifo->size;
246257293Sneel		fifo->num--;
247268892Sjhb		if (wasfull) {
248268892Sjhb			if (sc->tty.opened) {
249268892Sjhb				error = mevent_enable(sc->mev);
250268892Sjhb				assert(error == 0);
251268892Sjhb			}
252268892Sjhb		}
253257293Sneel		return (c);
254257293Sneel	} else
255257293Sneel		return (-1);
256257293Sneel}
257257293Sneel
258257293Sneelstatic int
259268892Sjhbrxfifo_numchars(struct uart_softc *sc)
260257293Sneel{
261268892Sjhb	struct fifo *fifo = &sc->rxfifo;
262257293Sneel
263257293Sneel	return (fifo->num);
264257293Sneel}
265257293Sneel
266257293Sneelstatic void
267257293Sneeluart_opentty(struct uart_softc *sc)
268257293Sneel{
269257293Sneel
270259301Sgrehan	ttyopen(&sc->tty);
271268892Sjhb	sc->mev = mevent_add(sc->tty.fd, EVF_READ, uart_drain, sc);
272268892Sjhb	assert(sc->mev != NULL);
273257293Sneel}
274257293Sneel
275257293Sneel/*
276257293Sneel * The IIR returns a prioritized interrupt reason:
277257293Sneel * - receive data available
278257293Sneel * - transmit holding register empty
279257293Sneel * - modem status change
280257293Sneel *
281257293Sneel * Return an interrupt reason if one is available.
282257293Sneel */
283257293Sneelstatic int
284257293Sneeluart_intr_reason(struct uart_softc *sc)
285257293Sneel{
286257293Sneel
287257293Sneel	if ((sc->lsr & LSR_OE) != 0 && (sc->ier & IER_ERLS) != 0)
288257293Sneel		return (IIR_RLS);
289268892Sjhb	else if (rxfifo_numchars(sc) > 0 && (sc->ier & IER_ERXRDY) != 0)
290257293Sneel		return (IIR_RXTOUT);
291257293Sneel	else if (sc->thre_int_pending && (sc->ier & IER_ETXRDY) != 0)
292257293Sneel		return (IIR_TXRDY);
293257293Sneel	else if ((sc->msr & MSR_DELTA_MASK) != 0 && (sc->ier & IER_EMSC) != 0)
294257293Sneel		return (IIR_MLSC);
295257293Sneel	else
296257293Sneel		return (IIR_NOPEND);
297257293Sneel}
298257293Sneel
299257293Sneelstatic void
300257293Sneeluart_reset(struct uart_softc *sc)
301257293Sneel{
302257293Sneel	uint16_t divisor;
303257293Sneel
304257293Sneel	divisor = DEFAULT_RCLK / DEFAULT_BAUD / 16;
305257293Sneel	sc->dll = divisor;
306257293Sneel	sc->dlh = divisor >> 16;
307257293Sneel
308268892Sjhb	rxfifo_reset(sc, 1);	/* no fifo until enabled by software */
309257293Sneel}
310257293Sneel
311257293Sneel/*
312257293Sneel * Toggle the COM port's intr pin depending on whether or not we have an
313257293Sneel * interrupt condition to report to the processor.
314257293Sneel */
315257293Sneelstatic void
316257293Sneeluart_toggle_intr(struct uart_softc *sc)
317257293Sneel{
318257293Sneel	uint8_t intr_reason;
319257293Sneel
320257293Sneel	intr_reason = uart_intr_reason(sc);
321257293Sneel
322257293Sneel	if (intr_reason == IIR_NOPEND)
323257293Sneel		(*sc->intr_deassert)(sc->arg);
324257293Sneel	else
325257293Sneel		(*sc->intr_assert)(sc->arg);
326257293Sneel}
327257293Sneel
328257293Sneelstatic void
329257293Sneeluart_drain(int fd, enum ev_type ev, void *arg)
330257293Sneel{
331257293Sneel	struct uart_softc *sc;
332257293Sneel	int ch;
333257293Sneel
334257293Sneel	sc = arg;
335257293Sneel
336259301Sgrehan	assert(fd == sc->tty.fd);
337257293Sneel	assert(ev == EVF_READ);
338257293Sneel
339257293Sneel	/*
340257293Sneel	 * This routine is called in the context of the mevent thread
341257293Sneel	 * to take out the softc lock to protect against concurrent
342257293Sneel	 * access from a vCPU i/o exit
343257293Sneel	 */
344257293Sneel	pthread_mutex_lock(&sc->mtx);
345257293Sneel
346257293Sneel	if ((sc->mcr & MCR_LOOPBACK) != 0) {
347259301Sgrehan		(void) ttyread(&sc->tty);
348257293Sneel	} else {
349268892Sjhb		while (rxfifo_available(sc) &&
350259301Sgrehan		       ((ch = ttyread(&sc->tty)) != -1)) {
351268892Sjhb			rxfifo_putchar(sc, ch);
352257293Sneel		}
353257293Sneel		uart_toggle_intr(sc);
354257293Sneel	}
355257293Sneel
356257293Sneel	pthread_mutex_unlock(&sc->mtx);
357257293Sneel}
358257293Sneel
359257293Sneelvoid
360257293Sneeluart_write(struct uart_softc *sc, int offset, uint8_t value)
361257293Sneel{
362257293Sneel	int fifosz;
363257293Sneel	uint8_t msr;
364257293Sneel
365257293Sneel	pthread_mutex_lock(&sc->mtx);
366257293Sneel
367257293Sneel	/*
368257293Sneel	 * Take care of the special case DLAB accesses first
369257293Sneel	 */
370257293Sneel	if ((sc->lcr & LCR_DLAB) != 0) {
371257293Sneel		if (offset == REG_DLL) {
372257293Sneel			sc->dll = value;
373257293Sneel			goto done;
374257293Sneel		}
375257293Sneel
376257293Sneel		if (offset == REG_DLH) {
377257293Sneel			sc->dlh = value;
378257293Sneel			goto done;
379257293Sneel		}
380257293Sneel	}
381257293Sneel
382257293Sneel        switch (offset) {
383257293Sneel	case REG_DATA:
384257293Sneel		if (sc->mcr & MCR_LOOPBACK) {
385268892Sjhb			if (rxfifo_putchar(sc, value) != 0)
386257293Sneel				sc->lsr |= LSR_OE;
387259301Sgrehan		} else if (sc->tty.opened) {
388259301Sgrehan			ttywrite(&sc->tty, value);
389257293Sneel		} /* else drop on floor */
390257293Sneel		sc->thre_int_pending = true;
391257293Sneel		break;
392257293Sneel	case REG_IER:
393257293Sneel		/*
394257293Sneel		 * Apply mask so that bits 4-7 are 0
395257293Sneel		 * Also enables bits 0-3 only if they're 1
396257293Sneel		 */
397257293Sneel		sc->ier = value & 0x0F;
398257293Sneel		break;
399257293Sneel		case REG_FCR:
400257293Sneel			/*
401257293Sneel			 * When moving from FIFO and 16450 mode and vice versa,
402257293Sneel			 * the FIFO contents are reset.
403257293Sneel			 */
404257293Sneel			if ((sc->fcr & FCR_ENABLE) ^ (value & FCR_ENABLE)) {
405257293Sneel				fifosz = (value & FCR_ENABLE) ? FIFOSZ : 1;
406268892Sjhb				rxfifo_reset(sc, fifosz);
407257293Sneel			}
408257293Sneel
409257293Sneel			/*
410257293Sneel			 * The FCR_ENABLE bit must be '1' for the programming
411257293Sneel			 * of other FCR bits to be effective.
412257293Sneel			 */
413257293Sneel			if ((value & FCR_ENABLE) == 0) {
414257293Sneel				sc->fcr = 0;
415257293Sneel			} else {
416257293Sneel				if ((value & FCR_RCV_RST) != 0)
417268892Sjhb					rxfifo_reset(sc, FIFOSZ);
418257293Sneel
419257293Sneel				sc->fcr = value &
420257293Sneel					 (FCR_ENABLE | FCR_DMA | FCR_RX_MASK);
421257293Sneel			}
422257293Sneel			break;
423257293Sneel		case REG_LCR:
424257293Sneel			sc->lcr = value;
425257293Sneel			break;
426257293Sneel		case REG_MCR:
427257293Sneel			/* Apply mask so that bits 5-7 are 0 */
428257293Sneel			sc->mcr = value & 0x1F;
429257293Sneel
430257293Sneel			msr = 0;
431257293Sneel			if (sc->mcr & MCR_LOOPBACK) {
432257293Sneel				/*
433257293Sneel				 * In the loopback mode certain bits from the
434257293Sneel				 * MCR are reflected back into MSR
435257293Sneel				 */
436257293Sneel				if (sc->mcr & MCR_RTS)
437257293Sneel					msr |= MSR_CTS;
438257293Sneel				if (sc->mcr & MCR_DTR)
439257293Sneel					msr |= MSR_DSR;
440257293Sneel				if (sc->mcr & MCR_OUT1)
441257293Sneel					msr |= MSR_RI;
442257293Sneel				if (sc->mcr & MCR_OUT2)
443257293Sneel					msr |= MSR_DCD;
444257293Sneel			}
445257293Sneel
446257293Sneel			/*
447257293Sneel			 * Detect if there has been any change between the
448257293Sneel			 * previous and the new value of MSR. If there is
449257293Sneel			 * then assert the appropriate MSR delta bit.
450257293Sneel			 */
451257293Sneel			if ((msr & MSR_CTS) ^ (sc->msr & MSR_CTS))
452257293Sneel				sc->msr |= MSR_DCTS;
453257293Sneel			if ((msr & MSR_DSR) ^ (sc->msr & MSR_DSR))
454257293Sneel				sc->msr |= MSR_DDSR;
455257293Sneel			if ((msr & MSR_DCD) ^ (sc->msr & MSR_DCD))
456257293Sneel				sc->msr |= MSR_DDCD;
457257293Sneel			if ((sc->msr & MSR_RI) != 0 && (msr & MSR_RI) == 0)
458257293Sneel				sc->msr |= MSR_TERI;
459257293Sneel
460257293Sneel			/*
461257293Sneel			 * Update the value of MSR while retaining the delta
462257293Sneel			 * bits.
463257293Sneel			 */
464257293Sneel			sc->msr &= MSR_DELTA_MASK;
465257293Sneel			sc->msr |= msr;
466257293Sneel			break;
467257293Sneel		case REG_LSR:
468257293Sneel			/*
469257293Sneel			 * Line status register is not meant to be written to
470257293Sneel			 * during normal operation.
471257293Sneel			 */
472257293Sneel			break;
473257293Sneel		case REG_MSR:
474257293Sneel			/*
475257293Sneel			 * As far as I can tell MSR is a read-only register.
476257293Sneel			 */
477257293Sneel			break;
478257293Sneel		case REG_SCR:
479257293Sneel			sc->scr = value;
480257293Sneel			break;
481257293Sneel		default:
482257293Sneel			break;
483257293Sneel	}
484257293Sneel
485257293Sneeldone:
486257293Sneel	uart_toggle_intr(sc);
487257293Sneel	pthread_mutex_unlock(&sc->mtx);
488257293Sneel}
489257293Sneel
490257293Sneeluint8_t
491257293Sneeluart_read(struct uart_softc *sc, int offset)
492257293Sneel{
493257293Sneel	uint8_t iir, intr_reason, reg;
494257293Sneel
495257293Sneel	pthread_mutex_lock(&sc->mtx);
496257293Sneel
497257293Sneel	/*
498257293Sneel	 * Take care of the special case DLAB accesses first
499257293Sneel	 */
500257293Sneel	if ((sc->lcr & LCR_DLAB) != 0) {
501257293Sneel		if (offset == REG_DLL) {
502257293Sneel			reg = sc->dll;
503257293Sneel			goto done;
504257293Sneel		}
505257293Sneel
506257293Sneel		if (offset == REG_DLH) {
507257293Sneel			reg = sc->dlh;
508257293Sneel			goto done;
509257293Sneel		}
510257293Sneel	}
511257293Sneel
512257293Sneel	switch (offset) {
513257293Sneel	case REG_DATA:
514268892Sjhb		reg = rxfifo_getchar(sc);
515257293Sneel		break;
516257293Sneel	case REG_IER:
517257293Sneel		reg = sc->ier;
518257293Sneel		break;
519257293Sneel	case REG_IIR:
520257293Sneel		iir = (sc->fcr & FCR_ENABLE) ? IIR_FIFO_MASK : 0;
521257293Sneel
522257293Sneel		intr_reason = uart_intr_reason(sc);
523257293Sneel
524257293Sneel		/*
525257293Sneel		 * Deal with side effects of reading the IIR register
526257293Sneel		 */
527257293Sneel		if (intr_reason == IIR_TXRDY)
528257293Sneel			sc->thre_int_pending = false;
529257293Sneel
530257293Sneel		iir |= intr_reason;
531257293Sneel
532257293Sneel		reg = iir;
533257293Sneel		break;
534257293Sneel	case REG_LCR:
535257293Sneel		reg = sc->lcr;
536257293Sneel		break;
537257293Sneel	case REG_MCR:
538257293Sneel		reg = sc->mcr;
539257293Sneel		break;
540257293Sneel	case REG_LSR:
541257293Sneel		/* Transmitter is always ready for more data */
542257293Sneel		sc->lsr |= LSR_TEMT | LSR_THRE;
543257293Sneel
544257293Sneel		/* Check for new receive data */
545268892Sjhb		if (rxfifo_numchars(sc) > 0)
546257293Sneel			sc->lsr |= LSR_RXRDY;
547257293Sneel		else
548257293Sneel			sc->lsr &= ~LSR_RXRDY;
549257293Sneel
550257293Sneel		reg = sc->lsr;
551257293Sneel
552257293Sneel		/* The LSR_OE bit is cleared on LSR read */
553257293Sneel		sc->lsr &= ~LSR_OE;
554257293Sneel		break;
555257293Sneel	case REG_MSR:
556257293Sneel		/*
557257293Sneel		 * MSR delta bits are cleared on read
558257293Sneel		 */
559257293Sneel		reg = sc->msr;
560257293Sneel		sc->msr &= ~MSR_DELTA_MASK;
561257293Sneel		break;
562257293Sneel	case REG_SCR:
563257293Sneel		reg = sc->scr;
564257293Sneel		break;
565257293Sneel	default:
566257293Sneel		reg = 0xFF;
567257293Sneel		break;
568257293Sneel	}
569257293Sneel
570257293Sneeldone:
571257293Sneel	uart_toggle_intr(sc);
572257293Sneel	pthread_mutex_unlock(&sc->mtx);
573257293Sneel
574257293Sneel	return (reg);
575257293Sneel}
576257293Sneel
577257293Sneelint
578257293Sneeluart_legacy_alloc(int which, int *baseaddr, int *irq)
579257293Sneel{
580257293Sneel
581257293Sneel	if (which < 0 || which >= UART_NLDEVS || uart_lres[which].inuse)
582257293Sneel		return (-1);
583257293Sneel
584257293Sneel	uart_lres[which].inuse = true;
585257293Sneel	*baseaddr = uart_lres[which].baseaddr;
586257293Sneel	*irq = uart_lres[which].irq;
587257293Sneel
588257293Sneel	return (0);
589257293Sneel}
590257293Sneel
591257293Sneelstruct uart_softc *
592257293Sneeluart_init(uart_intr_func_t intr_assert, uart_intr_func_t intr_deassert,
593257293Sneel    void *arg)
594257293Sneel{
595257293Sneel	struct uart_softc *sc;
596257293Sneel
597268953Sjhb	sc = calloc(1, sizeof(struct uart_softc));
598257293Sneel
599257293Sneel	sc->arg = arg;
600257293Sneel	sc->intr_assert = intr_assert;
601257293Sneel	sc->intr_deassert = intr_deassert;
602257293Sneel
603257293Sneel	pthread_mutex_init(&sc->mtx, NULL);
604257293Sneel
605257293Sneel	uart_reset(sc);
606257293Sneel
607257293Sneel	return (sc);
608257293Sneel}
609257293Sneel
610259301Sgrehanstatic int
611259301Sgrehanuart_tty_backend(struct uart_softc *sc, const char *opts)
612259301Sgrehan{
613259301Sgrehan	int fd;
614259301Sgrehan	int retval;
615259301Sgrehan
616259301Sgrehan	retval = -1;
617259301Sgrehan
618268892Sjhb	fd = open(opts, O_RDWR | O_NONBLOCK);
619259301Sgrehan	if (fd > 0 && isatty(fd)) {
620259301Sgrehan		sc->tty.fd = fd;
621259301Sgrehan		sc->tty.opened = true;
622259301Sgrehan		retval = 0;
623259301Sgrehan	}
624259301Sgrehan
625259301Sgrehan	return (retval);
626259301Sgrehan}
627259301Sgrehan
628257293Sneelint
629257293Sneeluart_set_backend(struct uart_softc *sc, const char *opts)
630257293Sneel{
631259301Sgrehan	int retval;
632259301Sgrehan
633259301Sgrehan	retval = -1;
634259301Sgrehan
635257293Sneel	if (opts == NULL)
636257293Sneel		return (0);
637257293Sneel
638259301Sgrehan	if (strcmp("stdio", opts) == 0) {
639259301Sgrehan		if (!uart_stdio) {
640259301Sgrehan			sc->tty.fd = STDIN_FILENO;
641259301Sgrehan			sc->tty.opened = true;
642259301Sgrehan			uart_stdio = true;
643259301Sgrehan			retval = 0;
644259301Sgrehan		}
645259301Sgrehan	} else if (uart_tty_backend(sc, opts) == 0) {
646259301Sgrehan		retval = 0;
647259301Sgrehan	}
648259301Sgrehan
649268892Sjhb	/* Make the backend file descriptor non-blocking */
650259301Sgrehan	if (retval == 0)
651268892Sjhb		retval = fcntl(sc->tty.fd, F_SETFL, O_NONBLOCK);
652268892Sjhb
653268892Sjhb	if (retval == 0)
654259301Sgrehan		uart_opentty(sc);
655259301Sgrehan
656259301Sgrehan	return (retval);
657257293Sneel}
658