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