1/*-
2 * SPDX-License-Identifier: BSD-2-Clause
3 *
4 * Copyright (c) 2020 Andrew Turner
5 *
6 * This work was supported by Innovate UK project 105694, "Digital Security
7 * by Design (DSbD) Technology Platform Prototype".
8 *
9 * Redistribution and use in source and binary forms, with or without
10 * modification, are permitted provided that the following conditions
11 * are met:
12 * 1. Redistributions of source code must retain the above copyright
13 *    notice, this list of conditions and the following disclaimer.
14 * 2. Redistributions in binary form must reproduce the above copyright
15 *    notice, this list of conditions and the following disclaimer in the
16 *    documentation and/or other materials provided with the distribution.
17 *
18 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
19 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
22 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
23 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
24 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
25 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
26 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
27 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
28 * SUCH DAMAGE.
29 */
30
31#include <sys/param.h>
32
33#include <assert.h>
34#include <stdbool.h>
35#include <stdio.h>
36#include <stdlib.h>
37
38#include "uart_backend.h"
39#include "uart_emul.h"
40
41#define	UART_FIFO_SIZE		16
42
43#define	UARTDR			0x00
44#define	UARTDR_RSR_SHIFT	8
45
46#define	UARTRSR			0x01
47#define	UARTRSR_OE		(1 << 3)
48
49#define	UARTFR			0x06
50#define	UARTFR_TXFE		(1 << 7)
51#define	UARTFR_RXFF		(1 << 6)
52#define	UARTFR_TXFF		(1 << 5)
53#define	UARTFR_RXFE		(1 << 4)
54
55#define	UARTRTINTR		(1 << 6)
56#define	UARTTXINTR		(1 << 5)
57#define	UARTRXINTR		(1 << 4)
58
59#define	UARTIBRD		0x09
60
61#define	UARTFBRD		0x0a
62#define	UARTFBRD_MASK		0x003f
63
64#define	UARTLCR_H		0x0b
65#define	UARTLCR_H_MASK		0x00ff
66#define	UARTLCR_H_FEN		(1 << 4)
67
68#define	UARTCR			0x0c
69/* TODO: Check the flags in the UARTCR register */
70#define	UARTCR_MASK		0xffc7
71#define	UARTCR_LBE		(1 << 7)
72
73#define	UARTIFLS		0x0d
74#define	UARTIFLS_MASK		0x003f
75#define	UARTIFLS_RXIFLSEL(x)	(((x) >> 3) & 0x7)
76#define	UARTIFLS_TXIFLSEL(x)	(((x) >> 0) & 0x7)
77
78#define	UARTIMSC		0x0e
79#define	UARTIMSC_MASK		0x07ff
80
81#define	UARTRIS			0x0f
82#define	UARTMIS			0x10
83
84#define	UARTICR			0x11
85
86#define	UARTPeriphID		0x00241011
87#define	UARTPeriphID0		0x3f8
88#define	UARTPeriphID0_VAL	(((UARTPeriphID) >>  0) & 0xff)
89#define	UARTPeriphID1		0x3f9
90#define	UARTPeriphID1_VAL	(((UARTPeriphID) >>  8) & 0xff)
91#define	UARTPeriphID2		0x3fa
92#define	UARTPeriphID2_VAL	(((UARTPeriphID) >> 16) & 0xff)
93#define	UARTPeriphID3		0x3fb
94#define	UARTPeriphID3_VAL	(((UARTPeriphID) >> 24) & 0xff)
95
96#define	UARTPCellID		0xb105f00d
97#define	UARTPCellID0		0x3fc
98#define	UARTPCellID0_VAL	(((UARTPCellID) >>  0) & 0xff)
99#define	UARTPCellID1		0x3fd
100#define	UARTPCellID1_VAL	(((UARTPCellID) >>  8) & 0xff)
101#define	UARTPCellID2		0x3fe
102#define	UARTPCellID2_VAL	(((UARTPCellID) >> 16) & 0xff)
103#define	UARTPCellID3		0x3ff
104#define	UARTPCellID3_VAL	(((UARTPCellID) >> 24) & 0xff)
105
106struct uart_pl011_softc {
107	struct uart_softc *backend;
108
109	uint16_t	irq_state;
110
111	uint16_t	rsr;
112
113	uint16_t	cr;
114	uint16_t	ifls;
115	uint16_t	imsc;
116	uint16_t	lcr_h;
117
118	uint16_t	ibrd;
119	uint16_t	fbrd;
120
121	void	*arg;
122	uart_intr_func_t intr_assert;
123	uart_intr_func_t intr_deassert;
124};
125
126static void
127uart_reset(struct uart_pl011_softc *sc)
128{
129	sc->ifls = 0x12;
130
131	/* no fifo until enabled by software */
132	uart_rxfifo_reset(sc->backend, 1);
133}
134
135static int
136uart_rx_trigger_level(struct uart_pl011_softc *sc)
137{
138	/* If the FIFO is disabled trigger when we have any data */
139	if ((sc->lcr_h & UARTLCR_H_FEN) != 0)
140		return (1);
141
142	/* Trigger base on how full the fifo is */
143	switch (UARTIFLS_RXIFLSEL(sc->ifls)) {
144	case 0:
145		return (UART_FIFO_SIZE / 8);
146	case 1:
147		return (UART_FIFO_SIZE / 4);
148	case 2:
149		return (UART_FIFO_SIZE / 2);
150	case 3:
151		return (UART_FIFO_SIZE * 3 / 4);
152	case 4:
153		return (UART_FIFO_SIZE * 7 / 8);
154	default:
155		/* TODO: Find out what happens in this case */
156		return (UART_FIFO_SIZE);
157	}
158}
159
160static void
161uart_toggle_intr(struct uart_pl011_softc *sc)
162{
163	if ((sc->irq_state & sc->imsc) == 0)
164		(*sc->intr_deassert)(sc->arg);
165	else
166		(*sc->intr_assert)(sc->arg);
167}
168
169static void
170uart_drain(int fd __unused, enum ev_type ev, void *arg)
171{
172	struct uart_pl011_softc *sc;
173	int old_size, trig_lvl;
174	bool loopback;
175
176	sc = arg;
177
178	assert(ev == EVF_READ);
179
180	/*
181	 * This routine is called in the context of the mevent thread
182	 * to take out the softc lock to protect against concurrent
183	 * access from a vCPU i/o exit
184	 */
185	uart_softc_lock(sc->backend);
186
187	old_size = uart_rxfifo_numchars(sc->backend);
188
189	loopback = (sc->cr & UARTCR_LBE) != 0;
190	uart_rxfifo_drain(sc->backend, loopback);
191
192	/* If we cross the trigger level raise UARTRXINTR */
193	trig_lvl = uart_rx_trigger_level(sc);
194	if (old_size < trig_lvl &&
195	    uart_rxfifo_numchars(sc->backend) >= trig_lvl)
196		sc->irq_state |= UARTRXINTR;
197
198	if (uart_rxfifo_numchars(sc->backend) > 0)
199		sc->irq_state |= UARTRTINTR;
200	if (!loopback)
201		uart_toggle_intr(sc);
202
203	uart_softc_unlock(sc->backend);
204}
205
206void
207uart_pl011_write(struct uart_pl011_softc *sc, int offset, uint32_t value)
208{
209	bool loopback;
210
211	uart_softc_lock(sc->backend);
212	switch (offset) {
213	case UARTDR:
214		loopback = (sc->cr & UARTCR_LBE) != 0;
215		if (uart_rxfifo_putchar(sc->backend, value & 0xff, loopback))
216			sc->rsr |= UARTRSR_OE;
217
218		/* We don't have a TX fifo, so trigger when we have data */
219		sc->irq_state |= UARTTXINTR;
220		break;
221	case UARTRSR:
222		/* Any write clears this register */
223		sc->rsr = 0;
224		break;
225	case UARTFR:
226		/* UARTFR is a read-only register */
227		break;
228	/* TODO: UARTILPR */
229	case UARTIBRD:
230		sc->ibrd = value;
231		break;
232	case UARTFBRD:
233		sc->fbrd = value & UARTFBRD_MASK;
234		break;
235	case UARTLCR_H:
236		/* Check if the FIFO enable bit changed */
237		if (((sc->lcr_h ^ value) & UARTLCR_H_FEN) != 0) {
238			if ((value & UARTLCR_H_FEN) != 0) {
239				uart_rxfifo_reset(sc->backend, UART_FIFO_SIZE);
240			} else {
241				uart_rxfifo_reset(sc->backend, 1);
242			}
243		}
244		sc->lcr_h = value & UARTLCR_H_MASK;
245		break;
246	case UARTCR:
247		sc->cr = value & UARTCR_MASK;
248		break;
249	case UARTIFLS:
250		sc->ifls = value & UARTCR_MASK;
251		break;
252	case UARTIMSC:
253		sc->imsc = value & UARTIMSC_MASK;
254		break;
255	case UARTRIS:
256	case UARTMIS:
257		/* UARTRIS and UARTMIS are read-only registers */
258		break;
259	case UARTICR:
260		sc->irq_state &= ~value;
261		break;
262	default:
263		/* Ignore writes to unassigned/ID registers */
264		break;
265	}
266	uart_toggle_intr(sc);
267	uart_softc_unlock(sc->backend);
268}
269
270uint32_t
271uart_pl011_read(struct uart_pl011_softc *sc, int offset)
272{
273	uint32_t reg;
274	int fifo_sz;
275
276	reg = 0;
277	uart_softc_lock(sc->backend);
278	switch (offset) {
279	case UARTDR:
280		reg = uart_rxfifo_getchar(sc->backend);
281		/* Deassert the irq if below the trigger level */
282		fifo_sz = uart_rxfifo_numchars(sc->backend);
283		if (fifo_sz < uart_rx_trigger_level(sc))
284			sc->irq_state &= ~UARTRXINTR;
285		if (fifo_sz == 0)
286			sc->irq_state &= ~UARTRTINTR;
287
288		reg |= sc->rsr << UARTDR_RSR_SHIFT;
289
290		/* After reading from the fifo there is now space in it */
291		sc->rsr &= UARTRSR_OE;
292		break;
293	case UARTRSR:
294		/* Any write clears this register */
295		reg = sc->rsr;
296		break;
297	case UARTFR:
298		/* Transmit is intstant, so the fifo is always empty */
299		reg = UARTFR_TXFE;
300
301		/* Set the receive fifo full/empty flags */
302		fifo_sz = uart_rxfifo_numchars(sc->backend);
303		if (fifo_sz == UART_FIFO_SIZE)
304			reg |= UARTFR_RXFF;
305		else if (fifo_sz == 0)
306			reg |= UARTFR_RXFE;
307		break;
308	/* TODO: UARTILPR */
309	case UARTIBRD:
310		reg = sc->ibrd;
311		break;
312	case UARTFBRD:
313		reg = sc->fbrd;
314		break;
315	case UARTLCR_H:
316		reg = sc->lcr_h;
317		break;
318	case UARTCR:
319		reg = sc->cr;
320		break;
321	case UARTIMSC:
322		reg = sc->imsc;
323		break;
324	case UARTRIS:
325		reg = sc->irq_state;
326		break;
327	case UARTMIS:
328		reg = sc->irq_state & sc->imsc;
329		break;
330	case UARTICR:
331		reg = 0;
332		break;
333	case UARTPeriphID0:
334		reg = UARTPeriphID0_VAL;
335		break;
336	case UARTPeriphID1:
337		reg =UARTPeriphID1_VAL;
338		break;
339	case UARTPeriphID2:
340		reg = UARTPeriphID2_VAL;
341		break;
342	case UARTPeriphID3:
343		reg = UARTPeriphID3_VAL;
344		break;
345	case UARTPCellID0:
346		reg = UARTPCellID0_VAL;
347		break;
348	case UARTPCellID1:
349		reg = UARTPCellID1_VAL;
350		break;
351	case UARTPCellID2:
352		reg = UARTPCellID2_VAL;
353		break;
354	case UARTPCellID3:
355		reg = UARTPCellID3_VAL;
356		break;
357	default:
358		/* Return 0 in reads from unasigned registers */
359		reg = 0;
360		break;
361	}
362	uart_toggle_intr(sc);
363	uart_softc_unlock(sc->backend);
364
365	return (reg);
366}
367
368struct uart_pl011_softc *
369uart_pl011_init(uart_intr_func_t intr_assert, uart_intr_func_t intr_deassert,
370    void *arg)
371{
372	struct uart_pl011_softc *sc;
373
374	sc = calloc(1, sizeof(struct uart_pl011_softc));
375
376	sc->arg = arg;
377	sc->intr_assert = intr_assert;
378	sc->intr_deassert = intr_deassert;
379	sc->backend = uart_init();
380
381	uart_reset(sc);
382
383	return (sc);
384}
385
386int
387uart_pl011_tty_open(struct uart_pl011_softc *sc, const char *device)
388{
389	return (uart_tty_open(sc->backend, device, uart_drain, sc));
390}
391