1/*-
2 * Copyright 2013-2015 John Wehle <john@feith.com>
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 *    notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 *    notice, this list of conditions and the following disclaimer in the
12 *    documentation and/or other materials provided with the distribution.
13 *
14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24 * SUCH DAMAGE.
25 */
26
27/*
28 * Amlogic aml8726 UART driver.
29 *
30 * The current implementation only targets features common to all
31 * uarts.  For example ... though UART A as a 128 byte FIFO, the
32 * others only have a 64 byte FIFO.
33 *
34 * Also, it's assumed that the USE_XTAL_CLK feature (available on
35 * the aml8726-m6 and later) has not been activated.
36 */
37
38#include <sys/cdefs.h>
39__FBSDID("$FreeBSD$");
40
41#include <sys/param.h>
42#include <sys/systm.h>
43#include <sys/bus.h>
44#include <sys/conf.h>
45#include <sys/kernel.h>
46#include <sys/sysctl.h>
47
48#include <machine/bus.h>
49#include <machine/cpu.h>
50
51#include <dev/fdt/fdt_common.h>
52#include <dev/ofw/ofw_bus.h>
53#include <dev/ofw/ofw_bus_subr.h>
54
55#include <dev/uart/uart.h>
56#include <dev/uart/uart_cpu.h>
57#include <dev/uart/uart_cpu_fdt.h>
58#include <dev/uart/uart_bus.h>
59
60#include <arm/amlogic/aml8726/aml8726_soc.h>
61#include <arm/amlogic/aml8726/aml8726_uart.h>
62
63#include "uart_if.h"
64
65#undef	uart_getreg
66#undef	uart_setreg
67
68#define	uart_getreg(bas, reg)		\
69    bus_space_read_4((bas)->bst, (bas)->bsh, reg)
70#define	uart_setreg(bas, reg, value)	\
71    bus_space_write_4((bas)->bst, (bas)->bsh, reg, value)
72
73#define	SIGCHG(c, i, s, d)				\
74	do {						\
75		if (c) {				\
76			i |= (i & s) ? s : s | d;	\
77		} else {				\
78			i = (i & s) ? (i & ~s) | d : i;	\
79		}					\
80	} while (0)
81
82static int
83aml8726_uart_divisor(int rclk, int baudrate)
84{
85	int actual_baud, divisor;
86	int error;
87
88	if (baudrate == 0)
89		return (0);
90
91	/* integer version of (rclk / baudrate + .5) */
92	divisor = ((rclk << 1) + baudrate) / (baudrate << 1);
93	if (divisor == 0)
94		return (0);
95	actual_baud = rclk / divisor;
96
97	/* 10 times error in percent: */
98	error = (((actual_baud - baudrate) * 2000) / baudrate + 1) >> 1;
99
100	/* 3.0% maximum error tolerance: */
101	if (error < -30 || error > 30)
102		return (0);
103
104	return (divisor);
105}
106
107static int
108aml8726_uart_param(struct uart_bas *bas, int baudrate, int databits, int stopbits,
109    int parity)
110{
111	uint32_t cr;
112	uint32_t mr;
113	uint32_t nbr;
114	int divisor;
115
116	cr = uart_getreg(bas, AML_UART_CONTROL_REG);
117
118	cr &= ~(AML_UART_CONTROL_DB_MASK | AML_UART_CONTROL_SB_MASK |
119	    AML_UART_CONTROL_P_MASK);
120
121	switch (databits) {
122	case 5:		cr |= AML_UART_CONTROL_5_DB; break;
123	case 6:		cr |= AML_UART_CONTROL_6_DB; break;
124	case 7:		cr |= AML_UART_CONTROL_7_DB; break;
125	case 8:		cr |= AML_UART_CONTROL_8_DB; break;
126	default:	return (EINVAL);
127	}
128
129	switch (stopbits) {
130	case 1:		cr |= AML_UART_CONTROL_1_SB; break;
131	case 2:		cr |= AML_UART_CONTROL_2_SB; break;
132	default:	return (EINVAL);
133	}
134
135	switch (parity) {
136	case UART_PARITY_EVEN:	cr |= AML_UART_CONTROL_P_EVEN;
137				cr |= AML_UART_CONTROL_P_EN;
138				break;
139
140	case UART_PARITY_ODD:	cr |= AML_UART_CONTROL_P_ODD;
141				cr |= AML_UART_CONTROL_P_EN;
142				break;
143
144	case UART_PARITY_NONE:	break;
145
146	default:	return (EINVAL);
147	}
148
149	/* Set baudrate. */
150	if (baudrate > 0 && bas->rclk != 0) {
151		divisor = aml8726_uart_divisor(bas->rclk / 4, baudrate) - 1;
152
153		switch (aml8726_soc_hw_rev) {
154		case AML_SOC_HW_REV_M6:
155		case AML_SOC_HW_REV_M8:
156		case AML_SOC_HW_REV_M8B:
157			if (divisor > (AML_UART_NEW_BAUD_RATE_MASK >>
158			    AML_UART_NEW_BAUD_RATE_SHIFT))
159				return (EINVAL);
160
161			nbr = uart_getreg(bas, AML_UART_NEW_BAUD_REG);
162			nbr &= ~(AML_UART_NEW_BAUD_USE_XTAL_CLK |
163			    AML_UART_NEW_BAUD_RATE_MASK);
164			nbr |= AML_UART_NEW_BAUD_RATE_EN |
165			    (divisor << AML_UART_NEW_BAUD_RATE_SHIFT);
166			uart_setreg(bas, AML_UART_NEW_BAUD_REG, nbr);
167
168			divisor = 0;
169			break;
170		default:
171			if (divisor > 0xffff)
172				return (EINVAL);
173			break;
174		}
175
176		cr &= ~AML_UART_CONTROL_BAUD_MASK;
177		cr |= (divisor & AML_UART_CONTROL_BAUD_MASK);
178
179		divisor >>= AML_UART_CONTROL_BAUD_WIDTH;
180
181		mr = uart_getreg(bas, AML_UART_MISC_REG);
182		mr &= ~(AML_UART_MISC_OLD_RX_BAUD |
183		    AML_UART_MISC_BAUD_EXT_MASK);
184		mr |= ((divisor << AML_UART_MISC_BAUD_EXT_SHIFT) &
185		    AML_UART_MISC_BAUD_EXT_MASK);
186		uart_setreg(bas, AML_UART_MISC_REG, mr);
187	}
188
189	uart_setreg(bas, AML_UART_CONTROL_REG, cr);
190	uart_barrier(bas);
191
192	return (0);
193}
194
195/*
196 * Low-level UART interface.
197 */
198
199static int
200aml8726_uart_probe(struct uart_bas *bas)
201{
202
203	return (0);
204}
205
206static void
207aml8726_uart_init(struct uart_bas *bas, int baudrate, int databits, int stopbits,
208    int parity)
209{
210	uint32_t cr;
211	uint32_t mr;
212
213	(void)aml8726_uart_param(bas, baudrate, databits, stopbits, parity);
214
215	cr = uart_getreg(bas, AML_UART_CONTROL_REG);
216	/* Disable all interrupt sources. */
217	cr &= ~(AML_UART_CONTROL_TX_INT_EN | AML_UART_CONTROL_RX_INT_EN);
218	/* Reset the transmitter and receiver. */
219	cr |= (AML_UART_CONTROL_TX_RST | AML_UART_CONTROL_RX_RST);
220	/* Enable the transmitter and receiver. */
221	cr |= (AML_UART_CONTROL_TX_EN | AML_UART_CONTROL_RX_EN);
222	uart_setreg(bas, AML_UART_CONTROL_REG, cr);
223	uart_barrier(bas);
224
225	/* Clear RX FIFO level for generating interrupts. */
226	mr = uart_getreg(bas, AML_UART_MISC_REG);
227	mr &= ~AML_UART_MISC_RECV_IRQ_CNT_MASK;
228	uart_setreg(bas, AML_UART_MISC_REG, mr);
229	uart_barrier(bas);
230
231	/* Ensure the reset bits are clear. */
232	cr &= ~(AML_UART_CONTROL_TX_RST | AML_UART_CONTROL_RX_RST);
233	uart_setreg(bas, AML_UART_CONTROL_REG, cr);
234	uart_barrier(bas);
235}
236
237static void
238aml8726_uart_term(struct uart_bas *bas)
239{
240}
241
242static void
243aml8726_uart_putc(struct uart_bas *bas, int c)
244{
245
246	while ((uart_getreg(bas, AML_UART_STATUS_REG) &
247	    AML_UART_STATUS_TX_FIFO_FULL) != 0)
248		cpu_spinwait();
249
250	uart_setreg(bas, AML_UART_WFIFO_REG, c);
251	uart_barrier(bas);
252}
253
254static int
255aml8726_uart_rxready(struct uart_bas *bas)
256{
257
258	return ((uart_getreg(bas, AML_UART_STATUS_REG) &
259	    AML_UART_STATUS_RX_FIFO_EMPTY) == 0 ? 1 : 0);
260}
261
262static int
263aml8726_uart_getc(struct uart_bas *bas, struct mtx *hwmtx)
264{
265	int c;
266
267	uart_lock(hwmtx);
268
269	while ((uart_getreg(bas, AML_UART_STATUS_REG) &
270	    AML_UART_STATUS_RX_FIFO_EMPTY) != 0) {
271		uart_unlock(hwmtx);
272		DELAY(4);
273		uart_lock(hwmtx);
274	}
275
276	c = uart_getreg(bas, AML_UART_RFIFO_REG) & 0xff;
277
278	uart_unlock(hwmtx);
279
280	return (c);
281}
282
283struct uart_ops aml8726_uart_ops = {
284	.probe = aml8726_uart_probe,
285	.init = aml8726_uart_init,
286	.term = aml8726_uart_term,
287	.putc = aml8726_uart_putc,
288	.rxready = aml8726_uart_rxready,
289	.getc = aml8726_uart_getc,
290};
291
292static unsigned int
293aml8726_uart_bus_clk(phandle_t node)
294{
295	pcell_t prop;
296	ssize_t len;
297	phandle_t clk_node;
298
299	len = OF_getencprop(node, "clocks", &prop, sizeof(prop));
300	if ((len / sizeof(prop)) != 1 || prop == 0 ||
301	    (clk_node = OF_node_from_xref(prop)) == 0)
302		return (0);
303
304	len = OF_getencprop(clk_node, "clock-frequency", &prop, sizeof(prop));
305	if ((len / sizeof(prop)) != 1 || prop == 0)
306		return (0);
307
308	return ((unsigned int)prop);
309}
310
311static int
312aml8726_uart_bus_probe(struct uart_softc *sc)
313{
314	int error;
315
316	error = aml8726_uart_probe(&sc->sc_bas);
317	if (error)
318		return (error);
319
320	sc->sc_rxfifosz = 64;
321	sc->sc_txfifosz = 64;
322	sc->sc_hwiflow = 1;
323	sc->sc_hwoflow = 1;
324
325	device_set_desc(sc->sc_dev, "Amlogic aml8726 UART");
326
327	return (0);
328}
329
330static int
331aml8726_uart_bus_getsig(struct uart_softc *sc)
332{
333	uint32_t new, old, sig;
334
335	/*
336	 * Treat DSR, DCD, and CTS as always on.
337	 */
338
339	do {
340		old = sc->sc_hwsig;
341		sig = old;
342		SIGCHG(1, sig, SER_DSR, SER_DDSR);
343		SIGCHG(1, sig, SER_DCD, SER_DDCD);
344		SIGCHG(1, sig, SER_CTS, SER_DCTS);
345		new = sig & ~SER_MASK_DELTA;
346	} while (!atomic_cmpset_32(&sc->sc_hwsig, old, new));
347
348	return (sig);
349}
350
351static int
352aml8726_uart_bus_setsig(struct uart_softc *sc, int sig)
353{
354	uint32_t new, old;
355
356	do {
357		old = sc->sc_hwsig;
358		new = old;
359		if (sig & SER_DDTR) {
360			SIGCHG(sig & SER_DTR, new, SER_DTR, SER_DDTR);
361		}
362		if (sig & SER_DRTS) {
363			SIGCHG(sig & SER_RTS, new, SER_RTS, SER_DRTS);
364		}
365	 } while (!atomic_cmpset_32(&sc->sc_hwsig, old, new));
366
367	return (0);
368}
369
370static int
371aml8726_uart_bus_attach(struct uart_softc *sc)
372{
373	struct uart_bas *bas;
374	uint32_t cr;
375	uint32_t mr;
376
377	bas = &sc->sc_bas;
378
379	bas->rclk = aml8726_uart_bus_clk(ofw_bus_get_node(sc->sc_dev));
380
381	if (bas->rclk == 0) {
382		device_printf(sc->sc_dev, "missing clocks attribute in FDT\n");
383		return (ENXIO);
384	}
385
386	cr = uart_getreg(bas, AML_UART_CONTROL_REG);
387	/* Disable all interrupt sources. */
388	cr &= ~(AML_UART_CONTROL_TX_INT_EN | AML_UART_CONTROL_RX_INT_EN);
389	/* Ensure the reset bits are clear. */
390	cr &= ~(AML_UART_CONTROL_TX_RST | AML_UART_CONTROL_RX_RST);
391
392	/*
393	 * Reset the transmitter and receiver only if not acting as a
394	 * console, otherwise it means that:
395	 *
396	 * 1) aml8726_uart_init was already called which did the reset
397	 *
398	 * 2) there may be console bytes sitting in the transmit fifo
399	 */
400	if (sc->sc_sysdev != NULL && sc->sc_sysdev->type == UART_DEV_CONSOLE)
401		;
402	else
403		cr |= (AML_UART_CONTROL_TX_RST | AML_UART_CONTROL_RX_RST);
404
405	/* Default to two wire mode. */
406	cr |= AML_UART_CONTROL_TWO_WIRE_EN;
407	/* Enable the transmitter and receiver. */
408	cr |= (AML_UART_CONTROL_TX_EN | AML_UART_CONTROL_RX_EN);
409	/* Reset error bits. */
410	cr |= AML_UART_CONTROL_CLR_ERR;
411	uart_setreg(bas, AML_UART_CONTROL_REG, cr);
412	uart_barrier(bas);
413
414	/* Set FIFO levels for generating interrupts. */
415	mr = uart_getreg(bas, AML_UART_MISC_REG);
416	mr &= ~AML_UART_MISC_XMIT_IRQ_CNT_MASK;
417	mr |= (0 << AML_UART_MISC_XMIT_IRQ_CNT_SHIFT);
418	mr &= ~AML_UART_MISC_RECV_IRQ_CNT_MASK;
419	mr |= (1 << AML_UART_MISC_RECV_IRQ_CNT_SHIFT);
420	uart_setreg(bas, AML_UART_MISC_REG, mr);
421	uart_barrier(bas);
422
423	aml8726_uart_bus_getsig(sc);
424
425	/* Ensure the reset bits are clear. */
426	cr &= ~(AML_UART_CONTROL_TX_RST | AML_UART_CONTROL_RX_RST);
427	cr &= ~AML_UART_CONTROL_CLR_ERR;
428	/* Enable the receive interrupt. */
429	cr |= AML_UART_CONTROL_RX_INT_EN;
430	uart_setreg(bas, AML_UART_CONTROL_REG, cr);
431	uart_barrier(bas);
432
433	return (0);
434}
435
436static int
437aml8726_uart_bus_detach(struct uart_softc *sc)
438{
439	struct uart_bas *bas;
440	uint32_t cr;
441	uint32_t mr;
442
443	bas = &sc->sc_bas;
444
445	/* Disable all interrupt sources. */
446	cr = uart_getreg(bas, AML_UART_CONTROL_REG);
447	cr &= ~(AML_UART_CONTROL_TX_INT_EN | AML_UART_CONTROL_RX_INT_EN);
448	uart_setreg(bas, AML_UART_CONTROL_REG, cr);
449	uart_barrier(bas);
450
451	/* Clear RX FIFO level for generating interrupts. */
452	mr = uart_getreg(bas, AML_UART_MISC_REG);
453	mr &= ~AML_UART_MISC_RECV_IRQ_CNT_MASK;
454	uart_setreg(bas, AML_UART_MISC_REG, mr);
455	uart_barrier(bas);
456
457	return (0);
458}
459
460static int
461aml8726_uart_bus_flush(struct uart_softc *sc, int what)
462{
463	struct uart_bas *bas;
464	uint32_t cr;
465
466	bas = &sc->sc_bas;
467	uart_lock(sc->sc_hwmtx);
468
469	cr = uart_getreg(bas, AML_UART_CONTROL_REG);
470	if (what & UART_FLUSH_TRANSMITTER)
471		cr |= AML_UART_CONTROL_TX_RST;
472	if (what & UART_FLUSH_RECEIVER)
473		cr |= AML_UART_CONTROL_RX_RST;
474	uart_setreg(bas, AML_UART_CONTROL_REG, cr);
475	uart_barrier(bas);
476
477	/* Ensure the reset bits are clear. */
478	cr &= ~(AML_UART_CONTROL_TX_RST | AML_UART_CONTROL_RX_RST);
479	uart_setreg(bas, AML_UART_CONTROL_REG, cr);
480	uart_barrier(bas);
481
482	uart_unlock(sc->sc_hwmtx);
483
484	return (0);
485}
486
487static int
488aml8726_uart_bus_ioctl(struct uart_softc *sc, int request, intptr_t data)
489{
490	struct uart_bas *bas;
491	int baudrate, divisor, error;
492	uint32_t cr, mr, nbr;
493
494	bas = &sc->sc_bas;
495	uart_lock(sc->sc_hwmtx);
496
497	error = 0;
498	switch (request) {
499	case UART_IOCTL_BAUD:
500		cr = uart_getreg(bas, AML_UART_CONTROL_REG);
501		cr &= AML_UART_CONTROL_BAUD_MASK;
502
503		mr = uart_getreg(bas, AML_UART_MISC_REG);
504		mr &= AML_UART_MISC_BAUD_EXT_MASK;
505
506		divisor = ((mr >> AML_UART_MISC_BAUD_EXT_SHIFT) <<
507		    AML_UART_CONTROL_BAUD_WIDTH) | cr;
508
509		switch (aml8726_soc_hw_rev) {
510		case AML_SOC_HW_REV_M6:
511		case AML_SOC_HW_REV_M8:
512		case AML_SOC_HW_REV_M8B:
513			nbr = uart_getreg(bas, AML_UART_NEW_BAUD_REG);
514			if ((nbr & AML_UART_NEW_BAUD_RATE_EN) != 0) {
515				divisor = (nbr & AML_UART_NEW_BAUD_RATE_MASK) >>
516				    AML_UART_NEW_BAUD_RATE_SHIFT;
517			}
518			break;
519		default:
520			break;
521		}
522
523		baudrate = bas->rclk / 4 / (divisor + 1);
524		if (baudrate > 0)
525			*(int*)data = baudrate;
526		else
527			error = ENXIO;
528		break;
529
530	case UART_IOCTL_IFLOW:
531	case UART_IOCTL_OFLOW:
532		cr = uart_getreg(bas, AML_UART_CONTROL_REG);
533		if (data)
534			cr &= ~AML_UART_CONTROL_TWO_WIRE_EN;
535		else
536			cr |= AML_UART_CONTROL_TWO_WIRE_EN;
537		uart_setreg(bas, AML_UART_CONTROL_REG, cr);
538		break;
539
540	default:
541		error = EINVAL;
542		break;
543	}
544
545	uart_unlock(sc->sc_hwmtx);
546
547	return (error);
548}
549
550static int
551aml8726_uart_bus_ipend(struct uart_softc *sc)
552{
553	struct uart_bas *bas;
554	int ipend;
555	uint32_t sr;
556	uint32_t cr;
557
558	bas = &sc->sc_bas;
559	uart_lock(sc->sc_hwmtx);
560
561	ipend = 0;
562	sr = uart_getreg(bas, AML_UART_STATUS_REG);
563	cr = uart_getreg(bas, AML_UART_CONTROL_REG);
564
565	if ((sr & AML_UART_STATUS_RX_FIFO_OVERFLOW) != 0)
566		ipend |= SER_INT_OVERRUN;
567
568	if ((sr & AML_UART_STATUS_TX_FIFO_EMPTY) != 0 &&
569	    (cr & AML_UART_CONTROL_TX_INT_EN) != 0) {
570		ipend |= SER_INT_TXIDLE;
571
572		cr &= ~AML_UART_CONTROL_TX_INT_EN;
573		uart_setreg(bas, AML_UART_CONTROL_REG, cr);
574		uart_barrier(bas);
575	}
576
577	if ((sr & AML_UART_STATUS_RX_FIFO_EMPTY) == 0)
578		ipend |= SER_INT_RXREADY;
579
580	uart_unlock(sc->sc_hwmtx);
581
582	return (ipend);
583}
584
585static int
586aml8726_uart_bus_param(struct uart_softc *sc, int baudrate, int databits,
587    int stopbits, int parity)
588{
589	struct uart_bas *bas;
590	int error;
591
592	bas = &sc->sc_bas;
593	uart_lock(sc->sc_hwmtx);
594
595	error = aml8726_uart_param(bas, baudrate, databits, stopbits, parity);
596
597	uart_unlock(sc->sc_hwmtx);
598
599	return (error);
600}
601
602static int
603aml8726_uart_bus_receive(struct uart_softc *sc)
604{
605	struct uart_bas *bas;
606	int xc;
607	uint32_t sr;
608
609	bas = &sc->sc_bas;
610	uart_lock(sc->sc_hwmtx);
611
612	sr = uart_getreg(bas, AML_UART_STATUS_REG);
613	while ((sr & AML_UART_STATUS_RX_FIFO_EMPTY) == 0) {
614		if (uart_rx_full(sc)) {
615			sc->sc_rxbuf[sc->sc_rxput] = UART_STAT_OVERRUN;
616			break;
617		}
618		xc = uart_getreg(bas, AML_UART_RFIFO_REG) & 0xff;
619		if (sr & AML_UART_STATUS_FRAME_ERR)
620			xc |= UART_STAT_FRAMERR;
621		if (sr & AML_UART_STATUS_PARITY_ERR)
622			xc |= UART_STAT_PARERR;
623		uart_rx_put(sc, xc);
624		sr = uart_getreg(bas, AML_UART_STATUS_REG);
625	}
626	/* Discard everything left in the RX FIFO. */
627	while ((sr & AML_UART_STATUS_RX_FIFO_EMPTY) == 0) {
628		(void)uart_getreg(bas, AML_UART_RFIFO_REG);
629		sr = uart_getreg(bas, AML_UART_STATUS_REG);
630	}
631	/* Reset error bits */
632	if ((sr & (AML_UART_STATUS_FRAME_ERR | AML_UART_STATUS_PARITY_ERR)) != 0) {
633		uart_setreg(bas, AML_UART_CONTROL_REG,
634		    (uart_getreg(bas, AML_UART_CONTROL_REG) |
635		    AML_UART_CONTROL_CLR_ERR));
636		uart_barrier(bas);
637		uart_setreg(bas, AML_UART_CONTROL_REG,
638		    (uart_getreg(bas, AML_UART_CONTROL_REG) &
639		    ~AML_UART_CONTROL_CLR_ERR));
640		uart_barrier(bas);
641	}
642
643	uart_unlock(sc->sc_hwmtx);
644
645	return (0);
646}
647
648static int
649aml8726_uart_bus_transmit(struct uart_softc *sc)
650{
651	struct uart_bas *bas;
652	int i;
653	uint32_t cr;
654
655	bas = &sc->sc_bas;
656	uart_lock(sc->sc_hwmtx);
657
658	/*
659	 * Wait for sufficient space since aml8726_uart_putc
660	 * may have been called after SER_INT_TXIDLE occurred.
661	 */
662	while ((uart_getreg(bas, AML_UART_STATUS_REG) &
663	    AML_UART_STATUS_TX_FIFO_EMPTY) == 0)
664		cpu_spinwait();
665
666	for (i = 0; i < sc->sc_txdatasz; i++) {
667		uart_setreg(bas, AML_UART_WFIFO_REG, sc->sc_txbuf[i]);
668		uart_barrier(bas);
669	}
670
671	sc->sc_txbusy = 1;
672
673	cr = uart_getreg(bas, AML_UART_CONTROL_REG);
674	cr |= AML_UART_CONTROL_TX_INT_EN;
675	uart_setreg(bas, AML_UART_CONTROL_REG, cr);
676	uart_barrier(bas);
677
678	uart_unlock(sc->sc_hwmtx);
679
680	return (0);
681}
682
683static void
684aml8726_uart_bus_grab(struct uart_softc *sc)
685{
686	struct uart_bas *bas;
687	uint32_t cr;
688
689	/*
690	 * Disable the receive interrupt to avoid a race between
691	 * aml8726_uart_getc and aml8726_uart_bus_receive which
692	 * can trigger:
693	 *
694	 *   panic: bad stray interrupt
695	 *
696	 * due to the RX FIFO receiving a character causing an
697	 * interrupt which gets serviced after aml8726_uart_getc
698	 * has been called (meaning the RX FIFO is now empty).
699	 */
700
701	bas = &sc->sc_bas;
702	uart_lock(sc->sc_hwmtx);
703
704	cr = uart_getreg(bas, AML_UART_CONTROL_REG);
705	cr &= ~AML_UART_CONTROL_RX_INT_EN;
706	uart_setreg(bas, AML_UART_CONTROL_REG, cr);
707	uart_barrier(bas);
708
709	uart_unlock(sc->sc_hwmtx);
710}
711
712static void
713aml8726_uart_bus_ungrab(struct uart_softc *sc)
714{
715	struct uart_bas *bas;
716	uint32_t cr;
717	uint32_t mr;
718
719	/*
720	 * The RX FIFO level being set indicates that the device
721	 * is currently attached meaning the receive interrupt
722	 * should be enabled.
723	 */
724
725	bas = &sc->sc_bas;
726	uart_lock(sc->sc_hwmtx);
727
728	mr = uart_getreg(bas, AML_UART_MISC_REG);
729	mr &= AML_UART_MISC_RECV_IRQ_CNT_MASK;
730
731	if (mr != 0) {
732		cr = uart_getreg(bas, AML_UART_CONTROL_REG);
733		cr |= AML_UART_CONTROL_RX_INT_EN;
734		uart_setreg(bas, AML_UART_CONTROL_REG, cr);
735		uart_barrier(bas);
736	}
737
738	uart_unlock(sc->sc_hwmtx);
739}
740
741static kobj_method_t aml8726_uart_methods[] = {
742	KOBJMETHOD(uart_probe,		aml8726_uart_bus_probe),
743	KOBJMETHOD(uart_attach,		aml8726_uart_bus_attach),
744	KOBJMETHOD(uart_detach,		aml8726_uart_bus_detach),
745	KOBJMETHOD(uart_flush,		aml8726_uart_bus_flush),
746	KOBJMETHOD(uart_getsig,		aml8726_uart_bus_getsig),
747	KOBJMETHOD(uart_setsig,		aml8726_uart_bus_setsig),
748	KOBJMETHOD(uart_ioctl,		aml8726_uart_bus_ioctl),
749	KOBJMETHOD(uart_ipend,		aml8726_uart_bus_ipend),
750	KOBJMETHOD(uart_param,		aml8726_uart_bus_param),
751	KOBJMETHOD(uart_receive,	aml8726_uart_bus_receive),
752	KOBJMETHOD(uart_transmit,	aml8726_uart_bus_transmit),
753	KOBJMETHOD(uart_grab,		aml8726_uart_bus_grab),
754	KOBJMETHOD(uart_ungrab,		aml8726_uart_bus_ungrab),
755	{ 0, 0 }
756};
757
758struct uart_class uart_aml8726_class = {
759	"uart",
760	aml8726_uart_methods,
761	sizeof(struct uart_softc),
762	.uc_ops = &aml8726_uart_ops,
763	.uc_range = 24,
764	.uc_rclk = 0,
765	.uc_rshift = 0
766};
767
768static struct ofw_compat_data compat_data[] = {
769	{ "amlogic,meson-uart",		(uintptr_t)&uart_aml8726_class },
770	{ NULL,				(uintptr_t)NULL }
771};
772UART_FDT_CLASS_AND_DEVICE(compat_data);
773