2 * Copyright 2013-2015 John Wehle <john@feith.com>
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
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.
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
28 * Amlogic aml8726 UART driver.
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.
34 * Also, it's assumed that the USE_XTAL_CLK feature (available on
35 * the aml8726-m6 and later) has not been activated.
38 #include <sys/cdefs.h>
39 __FBSDID("$FreeBSD$");
41 #include <sys/param.h>
42 #include <sys/systm.h>
45 #include <sys/kernel.h>
46 #include <sys/sysctl.h>
48 #include <machine/bus.h>
49 #include <machine/cpu.h>
51 #include <dev/ofw/ofw_bus.h>
52 #include <dev/ofw/ofw_bus_subr.h>
54 #include <dev/uart/uart.h>
55 #include <dev/uart/uart_cpu.h>
56 #include <dev/uart/uart_cpu_fdt.h>
57 #include <dev/uart/uart_bus.h>
59 #include <arm/amlogic/aml8726/aml8726_soc.h>
60 #include <arm/amlogic/aml8726/aml8726_uart.h>
67 #define uart_getreg(bas, reg) \
68 bus_space_read_4((bas)->bst, (bas)->bsh, reg)
69 #define uart_setreg(bas, reg, value) \
70 bus_space_write_4((bas)->bst, (bas)->bsh, reg, value)
72 #define SIGCHG(c, i, s, d) \
75 i |= (i & s) ? s : s | d; \
77 i = (i & s) ? (i & ~s) | d : i; \
82 aml8726_uart_divisor(int rclk, int baudrate)
84 int actual_baud, divisor;
90 /* integer version of (rclk / baudrate + .5) */
91 divisor = ((rclk << 1) + baudrate) / (baudrate << 1);
94 actual_baud = rclk / divisor;
96 /* 10 times error in percent: */
97 error = (((actual_baud - baudrate) * 2000) / baudrate + 1) >> 1;
99 /* 3.0% maximum error tolerance: */
100 if (error < -30 || error > 30)
107 aml8726_uart_param(struct uart_bas *bas, int baudrate, int databits, int stopbits,
115 cr = uart_getreg(bas, AML_UART_CONTROL_REG);
117 cr &= ~(AML_UART_CONTROL_DB_MASK | AML_UART_CONTROL_SB_MASK |
118 AML_UART_CONTROL_P_MASK);
121 case 5: cr |= AML_UART_CONTROL_5_DB; break;
122 case 6: cr |= AML_UART_CONTROL_6_DB; break;
123 case 7: cr |= AML_UART_CONTROL_7_DB; break;
124 case 8: cr |= AML_UART_CONTROL_8_DB; break;
125 default: return (EINVAL);
129 case 1: cr |= AML_UART_CONTROL_1_SB; break;
130 case 2: cr |= AML_UART_CONTROL_2_SB; break;
131 default: return (EINVAL);
135 case UART_PARITY_EVEN: cr |= AML_UART_CONTROL_P_EVEN;
136 cr |= AML_UART_CONTROL_P_EN;
139 case UART_PARITY_ODD: cr |= AML_UART_CONTROL_P_ODD;
140 cr |= AML_UART_CONTROL_P_EN;
143 case UART_PARITY_NONE: break;
145 default: return (EINVAL);
149 if (baudrate > 0 && bas->rclk != 0) {
150 divisor = aml8726_uart_divisor(bas->rclk / 4, baudrate) - 1;
152 switch (aml8726_soc_hw_rev) {
153 case AML_SOC_HW_REV_M6:
154 case AML_SOC_HW_REV_M8:
155 case AML_SOC_HW_REV_M8B:
156 if (divisor > (AML_UART_NEW_BAUD_RATE_MASK >>
157 AML_UART_NEW_BAUD_RATE_SHIFT))
160 nbr = uart_getreg(bas, AML_UART_NEW_BAUD_REG);
161 nbr &= ~(AML_UART_NEW_BAUD_USE_XTAL_CLK |
162 AML_UART_NEW_BAUD_RATE_MASK);
163 nbr |= AML_UART_NEW_BAUD_RATE_EN |
164 (divisor << AML_UART_NEW_BAUD_RATE_SHIFT);
165 uart_setreg(bas, AML_UART_NEW_BAUD_REG, nbr);
170 if (divisor > 0xffff)
175 cr &= ~AML_UART_CONTROL_BAUD_MASK;
176 cr |= (divisor & AML_UART_CONTROL_BAUD_MASK);
178 divisor >>= AML_UART_CONTROL_BAUD_WIDTH;
180 mr = uart_getreg(bas, AML_UART_MISC_REG);
181 mr &= ~(AML_UART_MISC_OLD_RX_BAUD |
182 AML_UART_MISC_BAUD_EXT_MASK);
183 mr |= ((divisor << AML_UART_MISC_BAUD_EXT_SHIFT) &
184 AML_UART_MISC_BAUD_EXT_MASK);
185 uart_setreg(bas, AML_UART_MISC_REG, mr);
188 uart_setreg(bas, AML_UART_CONTROL_REG, cr);
195 * Low-level UART interface.
199 aml8726_uart_probe(struct uart_bas *bas)
206 aml8726_uart_init(struct uart_bas *bas, int baudrate, int databits, int stopbits,
212 (void)aml8726_uart_param(bas, baudrate, databits, stopbits, parity);
214 cr = uart_getreg(bas, AML_UART_CONTROL_REG);
215 /* Disable all interrupt sources. */
216 cr &= ~(AML_UART_CONTROL_TX_INT_EN | AML_UART_CONTROL_RX_INT_EN);
217 /* Reset the transmitter and receiver. */
218 cr |= (AML_UART_CONTROL_TX_RST | AML_UART_CONTROL_RX_RST);
219 /* Enable the transmitter and receiver. */
220 cr |= (AML_UART_CONTROL_TX_EN | AML_UART_CONTROL_RX_EN);
221 uart_setreg(bas, AML_UART_CONTROL_REG, cr);
224 /* Clear RX FIFO level for generating interrupts. */
225 mr = uart_getreg(bas, AML_UART_MISC_REG);
226 mr &= ~AML_UART_MISC_RECV_IRQ_CNT_MASK;
227 uart_setreg(bas, AML_UART_MISC_REG, mr);
230 /* Ensure the reset bits are clear. */
231 cr &= ~(AML_UART_CONTROL_TX_RST | AML_UART_CONTROL_RX_RST);
232 uart_setreg(bas, AML_UART_CONTROL_REG, cr);
237 aml8726_uart_term(struct uart_bas *bas)
242 aml8726_uart_putc(struct uart_bas *bas, int c)
245 while ((uart_getreg(bas, AML_UART_STATUS_REG) &
246 AML_UART_STATUS_TX_FIFO_FULL) != 0)
249 uart_setreg(bas, AML_UART_WFIFO_REG, c);
254 aml8726_uart_rxready(struct uart_bas *bas)
257 return ((uart_getreg(bas, AML_UART_STATUS_REG) &
258 AML_UART_STATUS_RX_FIFO_EMPTY) == 0 ? 1 : 0);
262 aml8726_uart_getc(struct uart_bas *bas, struct mtx *hwmtx)
268 while ((uart_getreg(bas, AML_UART_STATUS_REG) &
269 AML_UART_STATUS_RX_FIFO_EMPTY) != 0) {
275 c = uart_getreg(bas, AML_UART_RFIFO_REG) & 0xff;
282 struct uart_ops aml8726_uart_ops = {
283 .probe = aml8726_uart_probe,
284 .init = aml8726_uart_init,
285 .term = aml8726_uart_term,
286 .putc = aml8726_uart_putc,
287 .rxready = aml8726_uart_rxready,
288 .getc = aml8726_uart_getc,
292 aml8726_uart_bus_clk(phandle_t node)
298 len = OF_getencprop(node, "clocks", &prop, sizeof(prop));
299 if ((len / sizeof(prop)) != 1 || prop == 0 ||
300 (clk_node = OF_node_from_xref(prop)) == 0)
303 len = OF_getencprop(clk_node, "clock-frequency", &prop, sizeof(prop));
304 if ((len / sizeof(prop)) != 1 || prop == 0)
307 return ((unsigned int)prop);
311 aml8726_uart_bus_probe(struct uart_softc *sc)
315 error = aml8726_uart_probe(&sc->sc_bas);
319 sc->sc_rxfifosz = 64;
320 sc->sc_txfifosz = 64;
324 device_set_desc(sc->sc_dev, "Amlogic aml8726 UART");
330 aml8726_uart_bus_getsig(struct uart_softc *sc)
332 uint32_t new, old, sig;
335 * Treat DSR, DCD, and CTS as always on.
341 SIGCHG(1, sig, SER_DSR, SER_DDSR);
342 SIGCHG(1, sig, SER_DCD, SER_DDCD);
343 SIGCHG(1, sig, SER_CTS, SER_DCTS);
344 new = sig & ~SER_MASK_DELTA;
345 } while (!atomic_cmpset_32(&sc->sc_hwsig, old, new));
351 aml8726_uart_bus_setsig(struct uart_softc *sc, int sig)
358 if (sig & SER_DDTR) {
359 SIGCHG(sig & SER_DTR, new, SER_DTR, SER_DDTR);
361 if (sig & SER_DRTS) {
362 SIGCHG(sig & SER_RTS, new, SER_RTS, SER_DRTS);
364 } while (!atomic_cmpset_32(&sc->sc_hwsig, old, new));
370 aml8726_uart_bus_attach(struct uart_softc *sc)
372 struct uart_bas *bas;
378 bas->rclk = aml8726_uart_bus_clk(ofw_bus_get_node(sc->sc_dev));
380 if (bas->rclk == 0) {
381 device_printf(sc->sc_dev, "missing clocks attribute in FDT\n");
385 cr = uart_getreg(bas, AML_UART_CONTROL_REG);
386 /* Disable all interrupt sources. */
387 cr &= ~(AML_UART_CONTROL_TX_INT_EN | AML_UART_CONTROL_RX_INT_EN);
388 /* Ensure the reset bits are clear. */
389 cr &= ~(AML_UART_CONTROL_TX_RST | AML_UART_CONTROL_RX_RST);
392 * Reset the transmitter and receiver only if not acting as a
393 * console, otherwise it means that:
395 * 1) aml8726_uart_init was already called which did the reset
397 * 2) there may be console bytes sitting in the transmit fifo
399 if (sc->sc_sysdev != NULL && sc->sc_sysdev->type == UART_DEV_CONSOLE)
402 cr |= (AML_UART_CONTROL_TX_RST | AML_UART_CONTROL_RX_RST);
404 /* Default to two wire mode. */
405 cr |= AML_UART_CONTROL_TWO_WIRE_EN;
406 /* Enable the transmitter and receiver. */
407 cr |= (AML_UART_CONTROL_TX_EN | AML_UART_CONTROL_RX_EN);
408 /* Reset error bits. */
409 cr |= AML_UART_CONTROL_CLR_ERR;
410 uart_setreg(bas, AML_UART_CONTROL_REG, cr);
413 /* Set FIFO levels for generating interrupts. */
414 mr = uart_getreg(bas, AML_UART_MISC_REG);
415 mr &= ~AML_UART_MISC_XMIT_IRQ_CNT_MASK;
416 mr |= (0 << AML_UART_MISC_XMIT_IRQ_CNT_SHIFT);
417 mr &= ~AML_UART_MISC_RECV_IRQ_CNT_MASK;
418 mr |= (1 << AML_UART_MISC_RECV_IRQ_CNT_SHIFT);
419 uart_setreg(bas, AML_UART_MISC_REG, mr);
422 aml8726_uart_bus_getsig(sc);
424 /* Ensure the reset bits are clear. */
425 cr &= ~(AML_UART_CONTROL_TX_RST | AML_UART_CONTROL_RX_RST);
426 cr &= ~AML_UART_CONTROL_CLR_ERR;
427 /* Enable the receive interrupt. */
428 cr |= AML_UART_CONTROL_RX_INT_EN;
429 uart_setreg(bas, AML_UART_CONTROL_REG, cr);
436 aml8726_uart_bus_detach(struct uart_softc *sc)
438 struct uart_bas *bas;
444 /* Disable all interrupt sources. */
445 cr = uart_getreg(bas, AML_UART_CONTROL_REG);
446 cr &= ~(AML_UART_CONTROL_TX_INT_EN | AML_UART_CONTROL_RX_INT_EN);
447 uart_setreg(bas, AML_UART_CONTROL_REG, cr);
450 /* Clear RX FIFO level for generating interrupts. */
451 mr = uart_getreg(bas, AML_UART_MISC_REG);
452 mr &= ~AML_UART_MISC_RECV_IRQ_CNT_MASK;
453 uart_setreg(bas, AML_UART_MISC_REG, mr);
460 aml8726_uart_bus_flush(struct uart_softc *sc, int what)
462 struct uart_bas *bas;
466 uart_lock(sc->sc_hwmtx);
468 cr = uart_getreg(bas, AML_UART_CONTROL_REG);
469 if (what & UART_FLUSH_TRANSMITTER)
470 cr |= AML_UART_CONTROL_TX_RST;
471 if (what & UART_FLUSH_RECEIVER)
472 cr |= AML_UART_CONTROL_RX_RST;
473 uart_setreg(bas, AML_UART_CONTROL_REG, cr);
476 /* Ensure the reset bits are clear. */
477 cr &= ~(AML_UART_CONTROL_TX_RST | AML_UART_CONTROL_RX_RST);
478 uart_setreg(bas, AML_UART_CONTROL_REG, cr);
481 uart_unlock(sc->sc_hwmtx);
487 aml8726_uart_bus_ioctl(struct uart_softc *sc, int request, intptr_t data)
489 struct uart_bas *bas;
490 int baudrate, divisor, error;
491 uint32_t cr, mr, nbr;
494 uart_lock(sc->sc_hwmtx);
498 case UART_IOCTL_BAUD:
499 cr = uart_getreg(bas, AML_UART_CONTROL_REG);
500 cr &= AML_UART_CONTROL_BAUD_MASK;
502 mr = uart_getreg(bas, AML_UART_MISC_REG);
503 mr &= AML_UART_MISC_BAUD_EXT_MASK;
505 divisor = ((mr >> AML_UART_MISC_BAUD_EXT_SHIFT) <<
506 AML_UART_CONTROL_BAUD_WIDTH) | cr;
508 switch (aml8726_soc_hw_rev) {
509 case AML_SOC_HW_REV_M6:
510 case AML_SOC_HW_REV_M8:
511 case AML_SOC_HW_REV_M8B:
512 nbr = uart_getreg(bas, AML_UART_NEW_BAUD_REG);
513 if ((nbr & AML_UART_NEW_BAUD_RATE_EN) != 0) {
514 divisor = (nbr & AML_UART_NEW_BAUD_RATE_MASK) >>
515 AML_UART_NEW_BAUD_RATE_SHIFT;
522 baudrate = bas->rclk / 4 / (divisor + 1);
524 *(int*)data = baudrate;
529 case UART_IOCTL_IFLOW:
530 case UART_IOCTL_OFLOW:
531 cr = uart_getreg(bas, AML_UART_CONTROL_REG);
533 cr &= ~AML_UART_CONTROL_TWO_WIRE_EN;
535 cr |= AML_UART_CONTROL_TWO_WIRE_EN;
536 uart_setreg(bas, AML_UART_CONTROL_REG, cr);
544 uart_unlock(sc->sc_hwmtx);
550 aml8726_uart_bus_ipend(struct uart_softc *sc)
552 struct uart_bas *bas;
558 uart_lock(sc->sc_hwmtx);
561 sr = uart_getreg(bas, AML_UART_STATUS_REG);
562 cr = uart_getreg(bas, AML_UART_CONTROL_REG);
564 if ((sr & AML_UART_STATUS_RX_FIFO_OVERFLOW) != 0)
565 ipend |= SER_INT_OVERRUN;
567 if ((sr & AML_UART_STATUS_TX_FIFO_EMPTY) != 0 &&
568 (cr & AML_UART_CONTROL_TX_INT_EN) != 0) {
569 ipend |= SER_INT_TXIDLE;
571 cr &= ~AML_UART_CONTROL_TX_INT_EN;
572 uart_setreg(bas, AML_UART_CONTROL_REG, cr);
576 if ((sr & AML_UART_STATUS_RX_FIFO_EMPTY) == 0)
577 ipend |= SER_INT_RXREADY;
579 uart_unlock(sc->sc_hwmtx);
585 aml8726_uart_bus_param(struct uart_softc *sc, int baudrate, int databits,
586 int stopbits, int parity)
588 struct uart_bas *bas;
592 uart_lock(sc->sc_hwmtx);
594 error = aml8726_uart_param(bas, baudrate, databits, stopbits, parity);
596 uart_unlock(sc->sc_hwmtx);
602 aml8726_uart_bus_receive(struct uart_softc *sc)
604 struct uart_bas *bas;
609 uart_lock(sc->sc_hwmtx);
611 sr = uart_getreg(bas, AML_UART_STATUS_REG);
612 while ((sr & AML_UART_STATUS_RX_FIFO_EMPTY) == 0) {
613 if (uart_rx_full(sc)) {
614 sc->sc_rxbuf[sc->sc_rxput] = UART_STAT_OVERRUN;
617 xc = uart_getreg(bas, AML_UART_RFIFO_REG) & 0xff;
618 if (sr & AML_UART_STATUS_FRAME_ERR)
619 xc |= UART_STAT_FRAMERR;
620 if (sr & AML_UART_STATUS_PARITY_ERR)
621 xc |= UART_STAT_PARERR;
623 sr = uart_getreg(bas, AML_UART_STATUS_REG);
625 /* Discard everything left in the RX FIFO. */
626 while ((sr & AML_UART_STATUS_RX_FIFO_EMPTY) == 0) {
627 (void)uart_getreg(bas, AML_UART_RFIFO_REG);
628 sr = uart_getreg(bas, AML_UART_STATUS_REG);
630 /* Reset error bits */
631 if ((sr & (AML_UART_STATUS_FRAME_ERR | AML_UART_STATUS_PARITY_ERR)) != 0) {
632 uart_setreg(bas, AML_UART_CONTROL_REG,
633 (uart_getreg(bas, AML_UART_CONTROL_REG) |
634 AML_UART_CONTROL_CLR_ERR));
636 uart_setreg(bas, AML_UART_CONTROL_REG,
637 (uart_getreg(bas, AML_UART_CONTROL_REG) &
638 ~AML_UART_CONTROL_CLR_ERR));
642 uart_unlock(sc->sc_hwmtx);
648 aml8726_uart_bus_transmit(struct uart_softc *sc)
650 struct uart_bas *bas;
655 uart_lock(sc->sc_hwmtx);
658 * Wait for sufficient space since aml8726_uart_putc
659 * may have been called after SER_INT_TXIDLE occurred.
661 while ((uart_getreg(bas, AML_UART_STATUS_REG) &
662 AML_UART_STATUS_TX_FIFO_EMPTY) == 0)
665 for (i = 0; i < sc->sc_txdatasz; i++) {
666 uart_setreg(bas, AML_UART_WFIFO_REG, sc->sc_txbuf[i]);
672 cr = uart_getreg(bas, AML_UART_CONTROL_REG);
673 cr |= AML_UART_CONTROL_TX_INT_EN;
674 uart_setreg(bas, AML_UART_CONTROL_REG, cr);
677 uart_unlock(sc->sc_hwmtx);
683 aml8726_uart_bus_grab(struct uart_softc *sc)
685 struct uart_bas *bas;
689 * Disable the receive interrupt to avoid a race between
690 * aml8726_uart_getc and aml8726_uart_bus_receive which
693 * panic: bad stray interrupt
695 * due to the RX FIFO receiving a character causing an
696 * interrupt which gets serviced after aml8726_uart_getc
697 * has been called (meaning the RX FIFO is now empty).
701 uart_lock(sc->sc_hwmtx);
703 cr = uart_getreg(bas, AML_UART_CONTROL_REG);
704 cr &= ~AML_UART_CONTROL_RX_INT_EN;
705 uart_setreg(bas, AML_UART_CONTROL_REG, cr);
708 uart_unlock(sc->sc_hwmtx);
712 aml8726_uart_bus_ungrab(struct uart_softc *sc)
714 struct uart_bas *bas;
719 * The RX FIFO level being set indicates that the device
720 * is currently attached meaning the receive interrupt
725 uart_lock(sc->sc_hwmtx);
727 mr = uart_getreg(bas, AML_UART_MISC_REG);
728 mr &= AML_UART_MISC_RECV_IRQ_CNT_MASK;
731 cr = uart_getreg(bas, AML_UART_CONTROL_REG);
732 cr |= AML_UART_CONTROL_RX_INT_EN;
733 uart_setreg(bas, AML_UART_CONTROL_REG, cr);
737 uart_unlock(sc->sc_hwmtx);
740 static kobj_method_t aml8726_uart_methods[] = {
741 KOBJMETHOD(uart_probe, aml8726_uart_bus_probe),
742 KOBJMETHOD(uart_attach, aml8726_uart_bus_attach),
743 KOBJMETHOD(uart_detach, aml8726_uart_bus_detach),
744 KOBJMETHOD(uart_flush, aml8726_uart_bus_flush),
745 KOBJMETHOD(uart_getsig, aml8726_uart_bus_getsig),
746 KOBJMETHOD(uart_setsig, aml8726_uart_bus_setsig),
747 KOBJMETHOD(uart_ioctl, aml8726_uart_bus_ioctl),
748 KOBJMETHOD(uart_ipend, aml8726_uart_bus_ipend),
749 KOBJMETHOD(uart_param, aml8726_uart_bus_param),
750 KOBJMETHOD(uart_receive, aml8726_uart_bus_receive),
751 KOBJMETHOD(uart_transmit, aml8726_uart_bus_transmit),
752 KOBJMETHOD(uart_grab, aml8726_uart_bus_grab),
753 KOBJMETHOD(uart_ungrab, aml8726_uart_bus_ungrab),
757 struct uart_class uart_aml8726_class = {
759 aml8726_uart_methods,
760 sizeof(struct uart_softc),
761 .uc_ops = &aml8726_uart_ops,
767 static struct ofw_compat_data compat_data[] = {
768 { "amlogic,meson-uart", (uintptr_t)&uart_aml8726_class },
769 { NULL, (uintptr_t)NULL }
771 UART_FDT_CLASS_AND_DEVICE(compat_data);