1221828Sgrehan/*-
2221828Sgrehan * SPDX-License-Identifier: BSD-2-Clause
3221828Sgrehan *
4221828Sgrehan * Copyright (c) 2012 NetApp, Inc.
5221828Sgrehan * Copyright (c) 2013 Neel Natu <neel@freebsd.org>
6221828Sgrehan * All rights reserved.
7221828Sgrehan *
8221828Sgrehan * Redistribution and use in source and binary forms, with or without
9221828Sgrehan * modification, are permitted provided that the following conditions
10221828Sgrehan * are met:
11221828Sgrehan * 1. Redistributions of source code must retain the above copyright
12221828Sgrehan *    notice, this list of conditions and the following disclaimer.
13221828Sgrehan * 2. Redistributions in binary form must reproduce the above copyright
14221828Sgrehan *    notice, this list of conditions and the following disclaimer in the
15221828Sgrehan *    documentation and/or other materials provided with the distribution.
16221828Sgrehan *
17221828Sgrehan * THIS SOFTWARE IS PROVIDED BY NETAPP, INC ``AS IS'' AND
18221828Sgrehan * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19221828Sgrehan * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20221828Sgrehan * ARE DISCLAIMED.  IN NO EVENT SHALL NETAPP, INC OR CONTRIBUTORS BE LIABLE
21221828Sgrehan * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22221828Sgrehan * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23221828Sgrehan * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24221828Sgrehan * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25221828Sgrehan * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26221828Sgrehan * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27221828Sgrehan * SUCH DAMAGE.
28221828Sgrehan */
29221828Sgrehan
30221828Sgrehan#include <sys/types.h>
31221828Sgrehan#include <dev/ic/ns16550.h>
32221828Sgrehan
33221828Sgrehan#include <machine/vmm_snapshot.h>
34221828Sgrehan
35221828Sgrehan#include <assert.h>
36221828Sgrehan#include <stdio.h>
37221828Sgrehan#include <stdlib.h>
38221828Sgrehan#include <errno.h>
39221828Sgrehan#include <unistd.h>
40221828Sgrehan#include <stdbool.h>
41221828Sgrehan#include <string.h>
42221828Sgrehan#include <pthread.h>
43221828Sgrehan
44221828Sgrehan#include "uart_backend.h"
45221828Sgrehan#include "uart_emul.h"
46221828Sgrehan
47256072Sneel#define	COM1_BASE      	0x3F8
48221828Sgrehan#define	COM1_IRQ	4
49221828Sgrehan#define	COM2_BASE      	0x2F8
50221828Sgrehan#define	COM2_IRQ	3
51261088Sjhb#define	COM3_BASE	0x3E8
52261088Sjhb#define	COM3_IRQ	4
53221828Sgrehan#define	COM4_BASE	0x2E8
54221828Sgrehan#define	COM4_IRQ	3
55221828Sgrehan
56239700Sgrehan#define	DEFAULT_RCLK	1843200
57221828Sgrehan#define	DEFAULT_BAUD	115200
58261088Sjhb
59261088Sjhb#define	FCR_RX_MASK	0xC0
60221828Sgrehan
61221828Sgrehan#define	MCR_OUT1	0x04
62221828Sgrehan#define	MCR_OUT2	0x08
63221828Sgrehan
64221828Sgrehan#define	MSR_DELTA_MASK	0x0f
65256651Sneel
66221828Sgrehan#ifndef REG_SCR
67256651Sneel#define	REG_SCR		com_scr
68256651Sneel#endif
69221828Sgrehan
70221828Sgrehanstatic struct {
71221828Sgrehan	int	baseaddr;
72221828Sgrehan	int	irq;
73221828Sgrehan	bool	inuse;
74221828Sgrehan} uart_lres[] = {
75221828Sgrehan	{ COM1_BASE, COM1_IRQ, false},
76221828Sgrehan	{ COM2_BASE, COM2_IRQ, false},
77221828Sgrehan	{ COM3_BASE, COM3_IRQ, false},
78221828Sgrehan	{ COM4_BASE, COM4_IRQ, false},
79221828Sgrehan};
80221828Sgrehan
81221828Sgrehan#define	UART_NLDEVS	(sizeof(uart_lres) / sizeof(uart_lres[0]))
82221828Sgrehan
83221828Sgrehanstruct uart_ns16550_softc {
84221828Sgrehan	struct uart_softc *backend;
85221828Sgrehan
86221828Sgrehan	uint8_t	data;		/* Data register (R/W) */
87221828Sgrehan	uint8_t ier;		/* Interrupt enable register (R/W) */
88221828Sgrehan	uint8_t lcr;		/* Line control register (R/W) */
89221828Sgrehan	uint8_t mcr;		/* Modem control register (R/W) */
90221828Sgrehan	uint8_t lsr;		/* Line status register (R/W) */
91221828Sgrehan	uint8_t msr;		/* Modem status register (R/W) */
92221828Sgrehan	uint8_t fcr;		/* FIFO control register (W) */
93221828Sgrehan	uint8_t scr;		/* Scratch register (R/W) */
94221828Sgrehan
95221828Sgrehan	uint8_t dll;		/* Baudrate divisor latch LSB */
96221828Sgrehan	uint8_t dlh;		/* Baudrate divisor latch MSB */
97221828Sgrehan
98241454Sneel	bool	thre_int_pending;	/* THRE interrupt pending */
99221828Sgrehan
100221828Sgrehan	void	*arg;
101221828Sgrehan	uart_intr_func_t intr_assert;
102221828Sgrehan	uart_intr_func_t intr_deassert;
103221828Sgrehan};
104256072Sneel
105256072Sneelstatic uint8_t
106256072Sneelmodem_status(uint8_t mcr)
107221828Sgrehan{
108221828Sgrehan	uint8_t msr;
109221828Sgrehan
110221828Sgrehan	if (mcr & MCR_LOOPBACK) {
111221828Sgrehan		/*
112221828Sgrehan		 * In the loopback mode certain bits from the MCR are
113241454Sneel		 * reflected back into MSR.
114241454Sneel		 */
115221828Sgrehan		msr = 0;
116256072Sneel		if (mcr & MCR_RTS)
117221828Sgrehan			msr |= MSR_CTS;
118221828Sgrehan		if (mcr & MCR_DTR)
119221828Sgrehan			msr |= MSR_DSR;
120221828Sgrehan		if (mcr & MCR_OUT1)
121221828Sgrehan			msr |= MSR_RI;
122221828Sgrehan		if (mcr & MCR_OUT2)
123221828Sgrehan			msr |= MSR_DCD;
124221828Sgrehan	} else {
125221828Sgrehan		/*
126221828Sgrehan		 * Always assert DCD and DSR so tty open doesn't block
127221828Sgrehan		 * even if CLOCAL is turned off.
128221828Sgrehan		 */
129221828Sgrehan		msr = MSR_DCD | MSR_DSR;
130256072Sneel	}
131256072Sneel	assert((msr & MSR_DELTA_MASK) == 0);
132221828Sgrehan
133221828Sgrehan	return (msr);
134221828Sgrehan}
135221828Sgrehan
136256072Sneel/*
137256072Sneel * The IIR returns a prioritized interrupt reason:
138256072Sneel * - receive data available
139256072Sneel * - transmit holding register empty
140221828Sgrehan * - modem status change
141221828Sgrehan *
142221828Sgrehan * Return an interrupt reason if one is available.
143221828Sgrehan */
144221828Sgrehanstatic int
145221828Sgrehanuart_intr_reason(struct uart_ns16550_softc *sc)
146221828Sgrehan{
147221828Sgrehan
148241489Sneel	if ((sc->lsr & LSR_OE) != 0 && (sc->ier & IER_ERLS) != 0)
149221828Sgrehan		return (IIR_RLS);
150221828Sgrehan	else if (uart_rxfifo_numchars(sc->backend) > 0 &&
151221828Sgrehan	    (sc->ier & IER_ERXRDY) != 0)
152261088Sjhb		return (IIR_RXTOUT);
153221828Sgrehan	else if (sc->thre_int_pending && (sc->ier & IER_ETXRDY) != 0)
154221828Sgrehan		return (IIR_TXRDY);
155221828Sgrehan	else if ((sc->msr & MSR_DELTA_MASK) != 0 && (sc->ier & IER_EMSC) != 0)
156261088Sjhb		return (IIR_MLSC);
157221828Sgrehan	else
158221828Sgrehan		return (IIR_NOPEND);
159221828Sgrehan}
160221828Sgrehan
161234761Sgrehanstatic void
162221828Sgrehanuart_reset(struct uart_ns16550_softc *sc)
163221828Sgrehan{
164221828Sgrehan	uint16_t divisor;
165240922Sneel
166256072Sneel	divisor = DEFAULT_RCLK / DEFAULT_BAUD / 16;
167221828Sgrehan	sc->dll = divisor;
168221828Sgrehan	sc->dlh = divisor >> 16;
169241489Sneel	sc->msr = modem_status(sc->mcr);
170221828Sgrehan
171221828Sgrehan	uart_rxfifo_reset(sc->backend, 1);
172241489Sneel}
173241489Sneel
174241489Sneel/*
175221828Sgrehan * Toggle the COM port's intr pin depending on whether or not we have an
176221828Sgrehan * interrupt condition to report to the processor.
177221828Sgrehan */
178221828Sgrehanstatic void
179221828Sgrehanuart_toggle_intr(struct uart_ns16550_softc *sc)
180221828Sgrehan{
181221828Sgrehan	uint8_t intr_reason;
182221828Sgrehan
183221828Sgrehan	intr_reason = uart_intr_reason(sc);
184221828Sgrehan
185221828Sgrehan	if (intr_reason == IIR_NOPEND)
186221828Sgrehan		(*sc->intr_deassert)(sc->arg);
187221828Sgrehan	else
188241489Sneel		(*sc->intr_assert)(sc->arg);
189240922Sneel}
190221828Sgrehan
191221828Sgrehanstatic void
192221828Sgrehanuart_drain(int fd __unused, enum ev_type ev, void *arg)
193221828Sgrehan{
194221828Sgrehan	struct uart_ns16550_softc *sc;
195221828Sgrehan	bool loopback;
196221828Sgrehan
197221828Sgrehan	sc = arg;
198221828Sgrehan
199221828Sgrehan	assert(ev == EVF_READ);
200256072Sneel
201241489Sneel	/*
202221828Sgrehan	 * This routine is called in the context of the mevent thread
203241489Sneel	 * to take out the softc lock to protect against concurrent
204241489Sneel	 * access from a vCPU i/o exit
205241489Sneel	 */
206241489Sneel	uart_softc_lock(sc->backend);
207241489Sneel
208241489Sneel	loopback = (sc->mcr & MCR_LOOPBACK) != 0;
209241489Sneel	uart_rxfifo_drain(sc->backend, loopback);
210241489Sneel	if (!loopback)
211241489Sneel		uart_toggle_intr(sc);
212241489Sneel
213241489Sneel	uart_softc_unlock(sc->backend);
214241489Sneel}
215241489Sneel
216241489Sneelvoid
217256072Sneeluart_ns16550_write(struct uart_ns16550_softc *sc, int offset, uint8_t value)
218241489Sneel{
219241489Sneel	int fifosz;
220221828Sgrehan	uint8_t msr;
221241489Sneel
222241489Sneel	uart_softc_lock(sc->backend);
223241489Sneel
224241489Sneel	/*
225241489Sneel	 * Take care of the special case DLAB accesses first
226241489Sneel	 */
227241489Sneel	if ((sc->lcr & LCR_DLAB) != 0) {
228241489Sneel		if (offset == REG_DLL) {
229221828Sgrehan			sc->dll = value;
230241489Sneel			goto done;
231221828Sgrehan		}
232221828Sgrehan
233221828Sgrehan		if (offset == REG_DLH) {
234221828Sgrehan			sc->dlh = value;
235221828Sgrehan			goto done;
236221828Sgrehan		}
237221828Sgrehan	}
238221828Sgrehan
239221828Sgrehan        switch (offset) {
240221828Sgrehan	case REG_DATA:
241221828Sgrehan		if (uart_rxfifo_putchar(sc->backend, value,
242250427Sneel		    (sc->mcr & MCR_LOOPBACK) != 0))
243250427Sneel			sc->lsr |= LSR_OE;
244221828Sgrehan		sc->thre_int_pending = true;
245221828Sgrehan		break;
246221828Sgrehan	case REG_IER:
247250427Sneel		/* Set pending when IER_ETXRDY is raised (edge-triggered). */
248221828Sgrehan		if ((sc->ier & IER_ETXRDY) == 0 && (value & IER_ETXRDY) != 0)
249221828Sgrehan			sc->thre_int_pending = true;
250221828Sgrehan		/*
251221828Sgrehan		 * Apply mask so that bits 4-7 are 0
252221828Sgrehan		 * Also enables bits 0-3 only if they're 1
253221828Sgrehan		 */
254221828Sgrehan		sc->ier = value & 0x0F;
255221828Sgrehan		break;
256221828Sgrehan	case REG_FCR:
257221828Sgrehan		/*
258221828Sgrehan		 * When moving from FIFO and 16450 mode and vice versa,
259221828Sgrehan		 * the FIFO contents are reset.
260221828Sgrehan		 */
261234761Sgrehan		if ((sc->fcr & FCR_ENABLE) ^ (value & FCR_ENABLE)) {
262234761Sgrehan			fifosz = (value & FCR_ENABLE) ?
263234761Sgrehan			    uart_rxfifo_size(sc->backend) : 1;
264234761Sgrehan			uart_rxfifo_reset(sc->backend, fifosz);
265234761Sgrehan		}
266234761Sgrehan
267234761Sgrehan		/*
268234761Sgrehan		 * The FCR_ENABLE bit must be '1' for the programming
269221828Sgrehan		 * of other FCR bits to be effective.
270221828Sgrehan		 */
271221828Sgrehan		if ((value & FCR_ENABLE) == 0) {
272221828Sgrehan			sc->fcr = 0;
273221828Sgrehan		} else {
274221828Sgrehan			if ((value & FCR_RCV_RST) != 0)
275221828Sgrehan				uart_rxfifo_reset(sc->backend,
276221828Sgrehan				    uart_rxfifo_size(sc->backend));
277256072Sneel
278256072Sneel			sc->fcr = value &
279221828Sgrehan				 (FCR_ENABLE | FCR_DMA | FCR_RX_MASK);
280221828Sgrehan		}
281221828Sgrehan		break;
282256072Sneel	case REG_LCR:
283256072Sneel		sc->lcr = value;
284221828Sgrehan		break;
285221828Sgrehan	case REG_MCR:
286221828Sgrehan		/* Apply mask so that bits 5-7 are 0 */
287221828Sgrehan		sc->mcr = value & 0x1F;
288221828Sgrehan		msr = modem_status(sc->mcr);
289221828Sgrehan
290221828Sgrehan		/*
291221828Sgrehan		 * Detect if there has been any change between the
292221828Sgrehan		 * previous and the new value of MSR. If there is
293221828Sgrehan		 * then assert the appropriate MSR delta bit.
294221828Sgrehan		 */
295221828Sgrehan		if ((msr & MSR_CTS) ^ (sc->msr & MSR_CTS))
296221828Sgrehan			sc->msr |= MSR_DCTS;
297221828Sgrehan		if ((msr & MSR_DSR) ^ (sc->msr & MSR_DSR))
298261088Sjhb			sc->msr |= MSR_DDSR;
299221828Sgrehan		if ((msr & MSR_DCD) ^ (sc->msr & MSR_DCD))
300261088Sjhb			sc->msr |= MSR_DDCD;
301261088Sjhb		if ((sc->msr & MSR_RI) != 0 && (msr & MSR_RI) == 0)
302261088Sjhb			sc->msr |= MSR_TERI;
303261088Sjhb
304261088Sjhb		/*
305261088Sjhb		 * Update the value of MSR while retaining the delta
306261088Sjhb		 * bits.
307261088Sjhb		 */
308261088Sjhb		sc->msr &= MSR_DELTA_MASK;
309261088Sjhb		sc->msr |= msr;
310261088Sjhb		break;
311261088Sjhb	case REG_LSR:
312221828Sgrehan		/*
313221828Sgrehan		 * Line status register is not meant to be written to
314241041Sneel		 * during normal operation.
315221828Sgrehan		 */
316221828Sgrehan		break;
317221828Sgrehan	case REG_MSR:
318241178Sneel		/*
319221828Sgrehan		 * As far as I can tell MSR is a read-only register.
320221828Sgrehan		 */
321221828Sgrehan		break;
322221828Sgrehan	case REG_SCR:
323221828Sgrehan		sc->scr = value;
324221828Sgrehan		break;
325221828Sgrehan	default:
326221828Sgrehan		break;
327221828Sgrehan	}
328221828Sgrehan
329221828Sgrehandone:
330221828Sgrehan	uart_toggle_intr(sc);
331221828Sgrehan	uart_softc_unlock(sc->backend);
332221828Sgrehan}
333221828Sgrehan
334221828Sgrehanuint8_t
335221828Sgrehanuart_ns16550_read(struct uart_ns16550_softc *sc, int offset)
336221828Sgrehan{
337221828Sgrehan	uint8_t iir, intr_reason, reg;
338221828Sgrehan
339221828Sgrehan	uart_softc_lock(sc->backend);
340221828Sgrehan
341221828Sgrehan	/*
342221828Sgrehan	 * Take care of the special case DLAB accesses first
343221828Sgrehan	 */
344221828Sgrehan	if ((sc->lcr & LCR_DLAB) != 0) {
345221828Sgrehan		if (offset == REG_DLL) {
346221828Sgrehan			reg = sc->dll;
347221828Sgrehan			goto done;
348221828Sgrehan		}
349221828Sgrehan
350221828Sgrehan		if (offset == REG_DLH) {
351221828Sgrehan			reg = sc->dlh;
352221828Sgrehan			goto done;
353221828Sgrehan		}
354221828Sgrehan	}
355221828Sgrehan
356240922Sneel	switch (offset) {
357240922Sneel	case REG_DATA:
358240922Sneel		reg = uart_rxfifo_getchar(sc->backend);
359240922Sneel		break;
360240922Sneel	case REG_IER:
361240922Sneel		reg = sc->ier;
362240922Sneel		break;
363240922Sneel	case REG_IIR:
364240922Sneel		iir = (sc->fcr & FCR_ENABLE) ? IIR_FIFO_MASK : 0;
365240922Sneel
366256072Sneel		intr_reason = uart_intr_reason(sc);
367256072Sneel
368256072Sneel		/*
369256072Sneel		 * Deal with side effects of reading the IIR register
370256072Sneel		 */
371256072Sneel		if (intr_reason == IIR_TXRDY)
372261088Sjhb			sc->thre_int_pending = false;
373261088Sjhb
374261088Sjhb		iir |= intr_reason;
375221828Sgrehan
376221828Sgrehan		reg = iir;
377221828Sgrehan		break;
378221828Sgrehan	case REG_LCR:
379241489Sneel		reg = sc->lcr;
380241489Sneel		break;
381241489Sneel	case REG_MCR:
382241489Sneel		reg = sc->mcr;
383241489Sneel		break;
384241489Sneel	case REG_LSR:
385241489Sneel		/* Transmitter is always ready for more data */
386241489Sneel		sc->lsr |= LSR_TEMT | LSR_THRE;
387221828Sgrehan
388256072Sneel		/* Check for new receive data */
389256072Sneel		if (uart_rxfifo_numchars(sc->backend) > 0)
390221828Sgrehan			sc->lsr |= LSR_RXRDY;
391221828Sgrehan		else
392221828Sgrehan			sc->lsr &= ~LSR_RXRDY;
393221828Sgrehan
394256072Sneel		reg = sc->lsr;
395256072Sneel
396221828Sgrehan		/* The LSR_OE bit is cleared on LSR read */
397221828Sgrehan		sc->lsr &= ~LSR_OE;
398221828Sgrehan		break;
399221828Sgrehan	case REG_MSR:
400221828Sgrehan		/*
401256072Sneel		 * MSR delta bits are cleared on read
402256072Sneel		 */
403256072Sneel		reg = sc->msr;
404256072Sneel		sc->msr &= ~MSR_DELTA_MASK;
405221828Sgrehan		break;
406221828Sgrehan	case REG_SCR:
407221828Sgrehan		reg = sc->scr;
408221828Sgrehan		break;
409221828Sgrehan	default:
410256651Sneel		reg = 0xFF;
411221828Sgrehan		break;
412221828Sgrehan	}
413256651Sneel
414256651Sneeldone:
415256651Sneel	uart_toggle_intr(sc);
416241454Sneel	uart_softc_unlock(sc->backend);
417241454Sneel
418256651Sneel	return (reg);
419241454Sneel}
420241454Sneel
421256651Sneelint
422241454Sneeluart_legacy_alloc(int which, int *baseaddr, int *irq)
423241454Sneel{
424241454Sneel
425241454Sneel	if (which < 0 || which >= (int)UART_NLDEVS || uart_lres[which].inuse)
426241454Sneel		return (-1);
427221828Sgrehan
428221828Sgrehan	uart_lres[which].inuse = true;
429221828Sgrehan	*baseaddr = uart_lres[which].baseaddr;
430221828Sgrehan	*irq = uart_lres[which].irq;
431221828Sgrehan
432221828Sgrehan	return (0);
433221828Sgrehan}
434221828Sgrehan
435221828Sgrehanstruct uart_ns16550_softc *
436256651Sneeluart_ns16550_init(uart_intr_func_t intr_assert, uart_intr_func_t intr_deassert,
437221828Sgrehan    void *arg)
438221828Sgrehan{
439221828Sgrehan	struct uart_ns16550_softc *sc;
440221828Sgrehan
441221828Sgrehan	sc = calloc(1, sizeof(struct uart_ns16550_softc));
442221828Sgrehan
443221828Sgrehan	sc->arg = arg;
444221828Sgrehan	sc->intr_assert = intr_assert;
445256651Sneel	sc->intr_deassert = intr_deassert;
446221828Sgrehan	sc->backend = uart_init();
447221828Sgrehan
448221828Sgrehan	uart_reset(sc);
449241454Sneel
450256651Sneel	return (sc);
451256651Sneel}
452256651Sneel
453256651Sneelint
454256651Sneeluart_ns16550_tty_open(struct uart_ns16550_softc *sc, const char *device)
455256651Sneel{
456221828Sgrehan	return (uart_tty_open(sc->backend, device, uart_drain, sc));
457241454Sneel}
458256651Sneel
459256651Sneel#ifdef BHYVE_SNAPSHOT
460256651Sneelint
461256651Sneeluart_ns16550_snapshot(struct uart_ns16550_softc *sc,
462256651Sneel    struct vm_snapshot_meta *meta)
463256651Sneel{
464256651Sneel	int ret;
465256651Sneel
466256651Sneel	SNAPSHOT_VAR_OR_LEAVE(sc->data, meta, ret, done);
467256651Sneel	SNAPSHOT_VAR_OR_LEAVE(sc->ier, meta, ret, done);
468241454Sneel	SNAPSHOT_VAR_OR_LEAVE(sc->lcr, meta, ret, done);
469221828Sgrehan	SNAPSHOT_VAR_OR_LEAVE(sc->mcr, meta, ret, done);
470221828Sgrehan	SNAPSHOT_VAR_OR_LEAVE(sc->lsr, meta, ret, done);
471221828Sgrehan	SNAPSHOT_VAR_OR_LEAVE(sc->msr, meta, ret, done);
472221828Sgrehan	SNAPSHOT_VAR_OR_LEAVE(sc->fcr, meta, ret, done);
473221828Sgrehan	SNAPSHOT_VAR_OR_LEAVE(sc->scr, meta, ret, done);
474221828Sgrehan
475221828Sgrehan	SNAPSHOT_VAR_OR_LEAVE(sc->dll, meta, ret, done);
476221828Sgrehan	SNAPSHOT_VAR_OR_LEAVE(sc->dlh, meta, ret, done);
477221828Sgrehan
478256072Sneel	ret = uart_rxfifo_snapshot(sc->backend, meta);
479221828Sgrehan
480221828Sgrehan	sc->thre_int_pending = 1;
481221828Sgrehan
482221828Sgrehandone:
483221828Sgrehan	return (ret);
484221828Sgrehan}
485221828Sgrehan#endif
486221828Sgrehan