xref: /freebsd/sys/riscv/sifive/sifive_uart.c (revision be82b3a0bf72ed3b5f01ac9fcd8dcd3802e3c742)
101774d0dSKristof Provost /*-
24d846d26SWarner Losh  * SPDX-License-Identifier: BSD-2-Clause
301774d0dSKristof Provost  *
401774d0dSKristof Provost  * Copyright (c) 2019 Axiado Corporation
501774d0dSKristof Provost  * All rights reserved.
601774d0dSKristof Provost  *
701774d0dSKristof Provost  * This software was developed in part by Kristof Provost under contract for
801774d0dSKristof Provost  * Axiado Corporation.
901774d0dSKristof Provost  *
1001774d0dSKristof Provost  * Redistribution and use in source and binary forms, with or without
1101774d0dSKristof Provost  * modification, are permitted provided that the following conditions
1201774d0dSKristof Provost  * are met:
1301774d0dSKristof Provost  * 1. Redistributions of source code must retain the above copyright
1401774d0dSKristof Provost  *    notice, this list of conditions and the following disclaimer.
1501774d0dSKristof Provost  * 2. Redistributions in binary form must reproduce the above copyright
1601774d0dSKristof Provost  *    notice, this list of conditions and the following disclaimer in the
1701774d0dSKristof Provost  *    documentation and/or other materials provided with the distribution.
1801774d0dSKristof Provost  *
1901774d0dSKristof Provost  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
2001774d0dSKristof Provost  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
2101774d0dSKristof Provost  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
2201774d0dSKristof Provost  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
2301774d0dSKristof Provost  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2401774d0dSKristof Provost  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2501774d0dSKristof Provost  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2601774d0dSKristof Provost  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2701774d0dSKristof Provost  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2801774d0dSKristof Provost  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
2901774d0dSKristof Provost  * SUCH DAMAGE.
3001774d0dSKristof Provost  */
3101774d0dSKristof Provost 
3201774d0dSKristof Provost #include <sys/param.h>
3301774d0dSKristof Provost #include <sys/systm.h>
3401774d0dSKristof Provost #include <sys/bus.h>
3501774d0dSKristof Provost #include <sys/kernel.h>
3601774d0dSKristof Provost #include <sys/lock.h>
3701774d0dSKristof Provost #include <sys/module.h>
3801774d0dSKristof Provost #include <sys/mutex.h>
3901774d0dSKristof Provost #include <sys/rman.h>
4001774d0dSKristof Provost 
4101774d0dSKristof Provost #include <machine/bus.h>
4201774d0dSKristof Provost #include <machine/cpu.h>
4301774d0dSKristof Provost 
44*be82b3a0SEmmanuel Vadot #include <dev/clk/clk.h>
4501774d0dSKristof Provost 
4601774d0dSKristof Provost #include <dev/ofw/ofw_bus.h>
4701774d0dSKristof Provost #include <dev/ofw/ofw_bus_subr.h>
4801774d0dSKristof Provost #include <dev/ofw/openfirm.h>
4901774d0dSKristof Provost 
5001774d0dSKristof Provost #include <dev/uart/uart.h>
5101774d0dSKristof Provost #include <dev/uart/uart_bus.h>
5201774d0dSKristof Provost #include <dev/uart/uart_cpu.h>
5301774d0dSKristof Provost #include <dev/uart/uart_cpu_fdt.h>
5401774d0dSKristof Provost 
5501774d0dSKristof Provost #include "uart_if.h"
5601774d0dSKristof Provost 
5701774d0dSKristof Provost #define	SFUART_TXDATA			0x00
5801774d0dSKristof Provost #define		SFUART_TXDATA_FULL	(1 << 31)
5901774d0dSKristof Provost #define	SFUART_RXDATA			0x04
6001774d0dSKristof Provost #define		SFUART_RXDATA_EMPTY	(1 << 31)
6101774d0dSKristof Provost #define	SFUART_TXCTRL			0x08
6201774d0dSKristof Provost #define		SFUART_TXCTRL_ENABLE	0x01
6301774d0dSKristof Provost #define		SFUART_TXCTRL_NSTOP	0x02
6476c6e771SKristof Provost #define		SFUART_TXCTRL_TXCNT	0x70000
6501774d0dSKristof Provost #define		SFUART_TXCTRL_TXCNT_SHIFT	16
6601774d0dSKristof Provost #define	SFUART_RXCTRL			0x0c
6701774d0dSKristof Provost #define		SFUART_RXCTRL_ENABLE	0x01
6876c6e771SKristof Provost #define		SFUART_RXCTRL_RXCNT	0x70000
6901774d0dSKristof Provost #define		SFUART_RXCTRL_RXCNT_SHIFT	16
7001774d0dSKristof Provost #define	SFUART_IRQ_ENABLE		0x10
7101774d0dSKristof Provost #define		SFUART_IRQ_ENABLE_TXWM	0x01
7201774d0dSKristof Provost #define		SFUART_IRQ_ENABLE_RXWM	0x02
7301774d0dSKristof Provost #define	SFUART_IRQ_PENDING		0x14
7401774d0dSKristof Provost #define		SFUART_IRQ_PENDING_TXWM	0x01
7501774d0dSKristof Provost #define		SFUART_IRQ_PENDING_RXQM	0x02
7601774d0dSKristof Provost #define	SFUART_DIV			0x18
7701774d0dSKristof Provost #define	SFUART_REGS_SIZE		0x1c
7801774d0dSKristof Provost 
7901774d0dSKristof Provost #define	SFUART_RX_FIFO_DEPTH		8
8001774d0dSKristof Provost #define	SFUART_TX_FIFO_DEPTH		8
8101774d0dSKristof Provost 
8201774d0dSKristof Provost struct sfuart_softc {
8301774d0dSKristof Provost 	struct uart_softc	uart_softc;
8401774d0dSKristof Provost 	clk_t			clk;
8501774d0dSKristof Provost };
8601774d0dSKristof Provost 
8701774d0dSKristof Provost static int
sfuart_probe(struct uart_bas * bas)8801774d0dSKristof Provost sfuart_probe(struct uart_bas *bas)
8901774d0dSKristof Provost {
9001774d0dSKristof Provost 
9101774d0dSKristof Provost 	bas->regiowidth = 4;
9201774d0dSKristof Provost 
9301774d0dSKristof Provost 	return (0);
9401774d0dSKristof Provost }
9501774d0dSKristof Provost 
9601774d0dSKristof Provost static void
sfuart_init(struct uart_bas * bas,int baudrate,int databits,int stopbits,int parity)9701774d0dSKristof Provost sfuart_init(struct uart_bas *bas, int baudrate, int databits, int stopbits,
9801774d0dSKristof Provost     int parity)
9901774d0dSKristof Provost {
10001774d0dSKristof Provost 	uint32_t reg;
10101774d0dSKristof Provost 
10201774d0dSKristof Provost 	uart_setreg(bas, SFUART_IRQ_ENABLE, 0);
10301774d0dSKristof Provost 
10401774d0dSKristof Provost 	/* Enable RX and configure the watermark so that we get an interrupt
10501774d0dSKristof Provost 	 * when a single character arrives (if interrupts are enabled). */
10601774d0dSKristof Provost 	reg = SFUART_RXCTRL_ENABLE;
10701774d0dSKristof Provost 	reg |= (0 << SFUART_RXCTRL_RXCNT_SHIFT);
10801774d0dSKristof Provost 	uart_setreg(bas, SFUART_RXCTRL, reg);
10901774d0dSKristof Provost 
11001774d0dSKristof Provost 	/* Enable TX and configure the watermark so that we get an interrupt
11101774d0dSKristof Provost 	 * when there's room for one more character in the TX fifo (if
11201774d0dSKristof Provost 	 * interrupts are enabled). */
11301774d0dSKristof Provost 	reg = SFUART_TXCTRL_ENABLE;
11401774d0dSKristof Provost 	reg |= (1 << SFUART_TXCTRL_TXCNT_SHIFT);
11501774d0dSKristof Provost 	if (stopbits == 2)
11601774d0dSKristof Provost 		reg |= SFUART_TXCTRL_NSTOP;
11701774d0dSKristof Provost 	uart_setreg(bas, SFUART_TXCTRL, reg);
11801774d0dSKristof Provost 
11901774d0dSKristof Provost 	/* Don't touch DIV. Assume that's set correctly until we can
12001774d0dSKristof Provost 	 * reconfigure. */
12101774d0dSKristof Provost }
12201774d0dSKristof Provost 
12301774d0dSKristof Provost static void
sfuart_putc(struct uart_bas * bas,int c)12401774d0dSKristof Provost sfuart_putc(struct uart_bas *bas, int c)
12501774d0dSKristof Provost {
12601774d0dSKristof Provost 
12701774d0dSKristof Provost 	while ((uart_getreg(bas, SFUART_TXDATA) & SFUART_TXDATA_FULL)
12801774d0dSKristof Provost 	    != 0)
12901774d0dSKristof Provost 		cpu_spinwait();
13001774d0dSKristof Provost 
13101774d0dSKristof Provost 	uart_setreg(bas, SFUART_TXDATA, c);
13201774d0dSKristof Provost }
13301774d0dSKristof Provost 
13401774d0dSKristof Provost static int
sfuart_rxready(struct uart_bas * bas)13501774d0dSKristof Provost sfuart_rxready(struct uart_bas *bas)
13601774d0dSKristof Provost {
137a1f9cdb1SJessica Clarke 	/*
138a1f9cdb1SJessica Clarke 	 * Unfortunately the FIFO empty flag is in the FIFO data register so
139a1f9cdb1SJessica Clarke 	 * reading it would dequeue the character. Instead, rely on the fact
140a1f9cdb1SJessica Clarke 	 * we've configured the watermark to be 0 and that interrupts are off
141a1f9cdb1SJessica Clarke 	 * when using the low-level console function, and read the interrupt
142a1f9cdb1SJessica Clarke 	 * pending state instead.
143a1f9cdb1SJessica Clarke 	 */
144a1f9cdb1SJessica Clarke 	return ((uart_getreg(bas, SFUART_IRQ_PENDING) &
145a1f9cdb1SJessica Clarke 	    SFUART_IRQ_PENDING_RXQM) != 0);
14601774d0dSKristof Provost }
14701774d0dSKristof Provost 
14801774d0dSKristof Provost static int
sfuart_getc(struct uart_bas * bas,struct mtx * hwmtx)14901774d0dSKristof Provost sfuart_getc(struct uart_bas *bas, struct mtx *hwmtx)
15001774d0dSKristof Provost {
15101774d0dSKristof Provost 	int c;
15201774d0dSKristof Provost 
15301774d0dSKristof Provost 	uart_lock(hwmtx);
15401774d0dSKristof Provost 
15501774d0dSKristof Provost 	while (((c = uart_getreg(bas, SFUART_RXDATA)) &
15601774d0dSKristof Provost 	    SFUART_RXDATA_EMPTY) != 0) {
15701774d0dSKristof Provost 		uart_unlock(hwmtx);
15801774d0dSKristof Provost 		DELAY(4);
15901774d0dSKristof Provost 		uart_lock(hwmtx);
16001774d0dSKristof Provost 	}
16101774d0dSKristof Provost 
16201774d0dSKristof Provost 	uart_unlock(hwmtx);
16301774d0dSKristof Provost 
16401774d0dSKristof Provost 	return (c & 0xff);
16501774d0dSKristof Provost }
16601774d0dSKristof Provost 
16701774d0dSKristof Provost static int
sfuart_bus_probe(struct uart_softc * sc)16801774d0dSKristof Provost sfuart_bus_probe(struct uart_softc *sc)
16901774d0dSKristof Provost {
17001774d0dSKristof Provost 	int error;
17101774d0dSKristof Provost 
17201774d0dSKristof Provost 	error = sfuart_probe(&sc->sc_bas);
17301774d0dSKristof Provost 	if (error)
17401774d0dSKristof Provost 		return (error);
17501774d0dSKristof Provost 
17601774d0dSKristof Provost 	sc->sc_rxfifosz = SFUART_RX_FIFO_DEPTH;
17701774d0dSKristof Provost 	sc->sc_txfifosz = SFUART_TX_FIFO_DEPTH;
17801774d0dSKristof Provost 	sc->sc_hwiflow = 0;
17901774d0dSKristof Provost 	sc->sc_hwoflow = 0;
18001774d0dSKristof Provost 
18101774d0dSKristof Provost 	device_set_desc(sc->sc_dev, "SiFive UART");
18201774d0dSKristof Provost 
18301774d0dSKristof Provost 	return (0);
18401774d0dSKristof Provost }
18501774d0dSKristof Provost 
18601774d0dSKristof Provost static int
sfuart_bus_attach(struct uart_softc * sc)18701774d0dSKristof Provost sfuart_bus_attach(struct uart_softc *sc)
18801774d0dSKristof Provost {
18901774d0dSKristof Provost 	struct uart_bas *bas;
19001774d0dSKristof Provost 	struct sfuart_softc *sfsc;
19101774d0dSKristof Provost 	uint64_t freq;
19201774d0dSKristof Provost 	uint32_t reg;
19301774d0dSKristof Provost 	int error;
19401774d0dSKristof Provost 
19501774d0dSKristof Provost 	sfsc = (struct sfuart_softc *)sc;
19601774d0dSKristof Provost 	bas = &sc->sc_bas;
19701774d0dSKristof Provost 
19801774d0dSKristof Provost 	error = clk_get_by_ofw_index(sc->sc_dev, 0, 0, &sfsc->clk);
19901774d0dSKristof Provost 	if (error) {
20001774d0dSKristof Provost 		device_printf(sc->sc_dev, "couldn't allocate clock\n");
20101774d0dSKristof Provost 		return (ENXIO);
20201774d0dSKristof Provost 	}
20301774d0dSKristof Provost 
20401774d0dSKristof Provost 	error = clk_enable(sfsc->clk);
20501774d0dSKristof Provost 	if (error) {
20601774d0dSKristof Provost 		device_printf(sc->sc_dev, "couldn't enable clock\n");
20701774d0dSKristof Provost 		return (ENXIO);
20801774d0dSKristof Provost 	}
20901774d0dSKristof Provost 
21001774d0dSKristof Provost 	error = clk_get_freq(sfsc->clk, &freq);
21101774d0dSKristof Provost 	if (error || freq == 0) {
21201774d0dSKristof Provost 		clk_disable(sfsc->clk);
21301774d0dSKristof Provost 		device_printf(sc->sc_dev, "couldn't get clock frequency\n");
21401774d0dSKristof Provost 		return (ENXIO);
21501774d0dSKristof Provost 	}
21601774d0dSKristof Provost 
21701774d0dSKristof Provost 	bas->rclk = freq;
21801774d0dSKristof Provost 
21901774d0dSKristof Provost 	/* Enable RX/RX */
22001774d0dSKristof Provost 	reg = SFUART_RXCTRL_ENABLE;
22101774d0dSKristof Provost 	reg |= (0 << SFUART_RXCTRL_RXCNT_SHIFT);
22201774d0dSKristof Provost 	uart_setreg(bas, SFUART_RXCTRL, reg);
22301774d0dSKristof Provost 
22401774d0dSKristof Provost 	reg = SFUART_TXCTRL_ENABLE;
22501774d0dSKristof Provost 	reg |= (1 << SFUART_TXCTRL_TXCNT_SHIFT);
22601774d0dSKristof Provost 	uart_setreg(bas, SFUART_TXCTRL, reg);
22701774d0dSKristof Provost 
22801774d0dSKristof Provost 	/* Enable RX interrupt */
22901774d0dSKristof Provost 	uart_setreg(bas, SFUART_IRQ_ENABLE, SFUART_IRQ_ENABLE_RXWM);
23001774d0dSKristof Provost 
23101774d0dSKristof Provost 	return (0);
23201774d0dSKristof Provost }
23301774d0dSKristof Provost 
23401774d0dSKristof Provost static int
sfuart_bus_detach(struct uart_softc * sc)23501774d0dSKristof Provost sfuart_bus_detach(struct uart_softc *sc)
23601774d0dSKristof Provost {
23701774d0dSKristof Provost 	struct sfuart_softc *sfsc;
23801774d0dSKristof Provost 	struct uart_bas *bas;
23901774d0dSKristof Provost 
24001774d0dSKristof Provost 	sfsc = (struct sfuart_softc *)sc;
24101774d0dSKristof Provost 	bas = &sc->sc_bas;
24201774d0dSKristof Provost 
24301774d0dSKristof Provost 	/* Disable RX/TX */
24401774d0dSKristof Provost 	uart_setreg(bas, SFUART_RXCTRL, 0);
24501774d0dSKristof Provost 	uart_setreg(bas, SFUART_TXCTRL, 0);
24601774d0dSKristof Provost 
24701774d0dSKristof Provost 	/* Disable interrupts */
24801774d0dSKristof Provost 	uart_setreg(bas, SFUART_IRQ_ENABLE, 0);
24901774d0dSKristof Provost 
25001774d0dSKristof Provost 	clk_disable(sfsc->clk);
25101774d0dSKristof Provost 
25201774d0dSKristof Provost 	return (0);
25301774d0dSKristof Provost }
25401774d0dSKristof Provost 
25501774d0dSKristof Provost static int
sfuart_bus_flush(struct uart_softc * sc,int what)25601774d0dSKristof Provost sfuart_bus_flush(struct uart_softc *sc, int what)
25701774d0dSKristof Provost {
25801774d0dSKristof Provost 	struct uart_bas *bas;
25901774d0dSKristof Provost 	uint32_t reg;
26001774d0dSKristof Provost 
26101774d0dSKristof Provost 	bas = &sc->sc_bas;
26201774d0dSKristof Provost 	uart_lock(sc->sc_hwmtx);
26301774d0dSKristof Provost 
26401774d0dSKristof Provost 	if (what & UART_FLUSH_TRANSMITTER) {
26501774d0dSKristof Provost 		do {
26601774d0dSKristof Provost 			reg = uart_getreg(bas, SFUART_TXDATA);
26701774d0dSKristof Provost 		} while ((reg & SFUART_TXDATA_FULL) != 0);
26801774d0dSKristof Provost 	}
26901774d0dSKristof Provost 
27001774d0dSKristof Provost 	if (what & UART_FLUSH_RECEIVER) {
27101774d0dSKristof Provost 		do {
27201774d0dSKristof Provost 			reg = uart_getreg(bas, SFUART_RXDATA);
27301774d0dSKristof Provost 		} while ((reg & SFUART_RXDATA_EMPTY) == 0);
27401774d0dSKristof Provost 	}
27501774d0dSKristof Provost 	uart_unlock(sc->sc_hwmtx);
27601774d0dSKristof Provost 
27701774d0dSKristof Provost 	return (0);
27801774d0dSKristof Provost }
27901774d0dSKristof Provost 
28001774d0dSKristof Provost #define	SIGCHG(c, i, s, d)						\
28101774d0dSKristof Provost 	do {								\
28201774d0dSKristof Provost 		if (c)							\
28301774d0dSKristof Provost 			i |= ((i) & (s)) ? (s) : (s) | (d);		\
28401774d0dSKristof Provost 		else		 					\
285cbc9be94SMitchell Horne 			i = ((i) & (s)) ? ((i) & ~(s)) | (d) : (i);	\
28601774d0dSKristof Provost 	} while (0)
28701774d0dSKristof Provost 
28801774d0dSKristof Provost static int
sfuart_bus_getsig(struct uart_softc * sc)28901774d0dSKristof Provost sfuart_bus_getsig(struct uart_softc *sc)
29001774d0dSKristof Provost {
29101774d0dSKristof Provost 	uint32_t new, old, sig;
29201774d0dSKristof Provost 
29301774d0dSKristof Provost 	do {
29401774d0dSKristof Provost 		old = sc->sc_hwsig;
29501774d0dSKristof Provost 		sig = old;
29601774d0dSKristof Provost 		SIGCHG(1, sig, SER_DSR, SER_DDSR);
29701774d0dSKristof Provost 		SIGCHG(1, sig, SER_DCD, SER_DDCD);
29801774d0dSKristof Provost 		SIGCHG(1, sig, SER_CTS, SER_DCTS);
29901774d0dSKristof Provost 		new = sig & ~SER_MASK_DELTA;
30001774d0dSKristof Provost 	} while (!atomic_cmpset_32(&sc->sc_hwsig, old, new));
30101774d0dSKristof Provost 
30201774d0dSKristof Provost 	return (sig);
30301774d0dSKristof Provost }
30401774d0dSKristof Provost 
30501774d0dSKristof Provost static int
sfuart_bus_setsig(struct uart_softc * sc,int sig)30601774d0dSKristof Provost sfuart_bus_setsig(struct uart_softc *sc, int sig)
30701774d0dSKristof Provost {
30801774d0dSKristof Provost 	uint32_t new, old;
30901774d0dSKristof Provost 
31001774d0dSKristof Provost 	do {
31101774d0dSKristof Provost 		old = sc->sc_hwsig;
31201774d0dSKristof Provost 		new = old;
31301774d0dSKristof Provost 		if (sig & SER_DDTR) {
31401774d0dSKristof Provost 			SIGCHG(sig & SER_DTR, new, SER_DTR, SER_DDTR);
31501774d0dSKristof Provost 		}
31601774d0dSKristof Provost 		if (sig & SER_DRTS) {
31701774d0dSKristof Provost 			SIGCHG(sig & SER_RTS, new, SER_RTS, SER_DRTS);
31801774d0dSKristof Provost 		}
31901774d0dSKristof Provost 	 } while (!atomic_cmpset_32(&sc->sc_hwsig, old, new));
32001774d0dSKristof Provost 
32101774d0dSKristof Provost 	return (0);
32201774d0dSKristof Provost }
32301774d0dSKristof Provost 
32401774d0dSKristof Provost static int
sfuart_bus_ioctl(struct uart_softc * sc,int request,intptr_t data)32501774d0dSKristof Provost sfuart_bus_ioctl(struct uart_softc *sc, int request, intptr_t data)
32601774d0dSKristof Provost {
32701774d0dSKristof Provost 	struct uart_bas *bas;
32801774d0dSKristof Provost 	uint32_t reg;
32901774d0dSKristof Provost 	int error;
33001774d0dSKristof Provost 
33101774d0dSKristof Provost 	bas = &sc->sc_bas;
33201774d0dSKristof Provost 
33301774d0dSKristof Provost 	uart_lock(sc->sc_hwmtx);
33401774d0dSKristof Provost 
33501774d0dSKristof Provost 	switch (request) {
33601774d0dSKristof Provost 	case UART_IOCTL_BAUD:
33701774d0dSKristof Provost 		reg = uart_getreg(bas, SFUART_DIV);
33801774d0dSKristof Provost 		if (reg == 0) {
33901774d0dSKristof Provost 			/* Possible if the divisor hasn't been set up yet. */
34001774d0dSKristof Provost 			error = ENXIO;
34101774d0dSKristof Provost 			break;
34201774d0dSKristof Provost 		}
34301774d0dSKristof Provost 		*(int*)data = bas->rclk / (reg + 1);
34401774d0dSKristof Provost 		error = 0;
34501774d0dSKristof Provost 		break;
34601774d0dSKristof Provost 	default:
34701774d0dSKristof Provost 		error = EINVAL;
34801774d0dSKristof Provost 		break;
34901774d0dSKristof Provost 	}
35001774d0dSKristof Provost 
35101774d0dSKristof Provost 	uart_unlock(sc->sc_hwmtx);
35201774d0dSKristof Provost 
35301774d0dSKristof Provost 	return (error);
35401774d0dSKristof Provost }
35501774d0dSKristof Provost 
35601774d0dSKristof Provost static int
sfuart_bus_ipend(struct uart_softc * sc)35701774d0dSKristof Provost sfuart_bus_ipend(struct uart_softc *sc)
35801774d0dSKristof Provost {
35901774d0dSKristof Provost 	struct uart_bas *bas;
36001774d0dSKristof Provost 	int ipend;
36101774d0dSKristof Provost 	uint32_t reg, ie;
36201774d0dSKristof Provost 
36301774d0dSKristof Provost 	bas = &sc->sc_bas;
36401774d0dSKristof Provost 	uart_lock(sc->sc_hwmtx);
36501774d0dSKristof Provost 
36601774d0dSKristof Provost 	ipend = 0;
36701774d0dSKristof Provost 	reg = uart_getreg(bas, SFUART_IRQ_PENDING);
36801774d0dSKristof Provost 	ie = uart_getreg(bas, SFUART_IRQ_ENABLE);
36901774d0dSKristof Provost 
37001774d0dSKristof Provost 	if ((reg & SFUART_IRQ_PENDING_TXWM) != 0 &&
37101774d0dSKristof Provost 	    (ie & SFUART_IRQ_ENABLE_TXWM) != 0) {
37201774d0dSKristof Provost 		ipend |= SER_INT_TXIDLE;
37301774d0dSKristof Provost 
37401774d0dSKristof Provost 		/* Disable TX interrupt */
37501774d0dSKristof Provost 		ie &= ~(SFUART_IRQ_ENABLE_TXWM);
37601774d0dSKristof Provost 		uart_setreg(bas, SFUART_IRQ_ENABLE, ie);
37701774d0dSKristof Provost 	}
37801774d0dSKristof Provost 
37901774d0dSKristof Provost 	if ((reg & SFUART_IRQ_PENDING_RXQM) != 0)
38001774d0dSKristof Provost 		ipend |= SER_INT_RXREADY;
38101774d0dSKristof Provost 
38201774d0dSKristof Provost 	uart_unlock(sc->sc_hwmtx);
38301774d0dSKristof Provost 
38401774d0dSKristof Provost 	return (ipend);
38501774d0dSKristof Provost }
38601774d0dSKristof Provost 
38701774d0dSKristof Provost static int
sfuart_bus_param(struct uart_softc * sc,int baudrate,int databits,int stopbits,int parity)38801774d0dSKristof Provost sfuart_bus_param(struct uart_softc *sc, int baudrate, int databits,
38901774d0dSKristof Provost     int stopbits, int parity)
39001774d0dSKristof Provost {
39101774d0dSKristof Provost 	struct uart_bas *bas;
39201774d0dSKristof Provost 	uint32_t reg;
39301774d0dSKristof Provost 
39401774d0dSKristof Provost 	bas = &sc->sc_bas;
39501774d0dSKristof Provost 
39601774d0dSKristof Provost 	if (databits != 8)
39701774d0dSKristof Provost 		return (EINVAL);
39801774d0dSKristof Provost 
39901774d0dSKristof Provost 	if (parity != UART_PARITY_NONE)
40001774d0dSKristof Provost 		return (EINVAL);
40101774d0dSKristof Provost 
40201774d0dSKristof Provost 	uart_lock(sc->sc_hwmtx);
40301774d0dSKristof Provost 
40401774d0dSKristof Provost 	reg = uart_getreg(bas, SFUART_TXCTRL);
40501774d0dSKristof Provost 	if (stopbits == 2) {
40601774d0dSKristof Provost 		reg |= SFUART_TXCTRL_NSTOP;
40701774d0dSKristof Provost 	} else if (stopbits == 1) {
40801774d0dSKristof Provost 		reg &= ~SFUART_TXCTRL_NSTOP;
40901774d0dSKristof Provost 	} else {
41001774d0dSKristof Provost 		uart_unlock(sc->sc_hwmtx);
41101774d0dSKristof Provost 		return (EINVAL);
41201774d0dSKristof Provost 	}
41301774d0dSKristof Provost 
41401774d0dSKristof Provost 	if (baudrate > 0 && bas->rclk != 0) {
41501774d0dSKristof Provost 		reg = (bas->rclk / baudrate) - 1;
41601774d0dSKristof Provost 		uart_setreg(bas, SFUART_DIV, reg);
41701774d0dSKristof Provost 	}
41801774d0dSKristof Provost 
41901774d0dSKristof Provost 	uart_unlock(sc->sc_hwmtx);
42001774d0dSKristof Provost 	return (0);
42101774d0dSKristof Provost }
42201774d0dSKristof Provost 
42301774d0dSKristof Provost static int
sfuart_bus_receive(struct uart_softc * sc)42401774d0dSKristof Provost sfuart_bus_receive(struct uart_softc *sc)
42501774d0dSKristof Provost {
42601774d0dSKristof Provost 	struct uart_bas *bas;
42701774d0dSKristof Provost 	uint32_t reg;
42801774d0dSKristof Provost 
42901774d0dSKristof Provost 	bas = &sc->sc_bas;
43001774d0dSKristof Provost 	uart_lock(sc->sc_hwmtx);
43101774d0dSKristof Provost 
43201774d0dSKristof Provost 	reg = uart_getreg(bas, SFUART_RXDATA);
43301774d0dSKristof Provost 	while ((reg & SFUART_RXDATA_EMPTY) == 0) {
43401774d0dSKristof Provost 		if (uart_rx_full(sc)) {
43501774d0dSKristof Provost 			sc->sc_rxbuf[sc->sc_rxput] = UART_STAT_OVERRUN;
43601774d0dSKristof Provost 			break;
43701774d0dSKristof Provost 		}
43801774d0dSKristof Provost 
43901774d0dSKristof Provost 		uart_rx_put(sc, reg & 0xff);
44001774d0dSKristof Provost 
44101774d0dSKristof Provost 		reg = uart_getreg(bas, SFUART_RXDATA);
44201774d0dSKristof Provost 	}
44301774d0dSKristof Provost 
44401774d0dSKristof Provost 	uart_unlock(sc->sc_hwmtx);
44501774d0dSKristof Provost 
44601774d0dSKristof Provost 	return (0);
44701774d0dSKristof Provost }
44801774d0dSKristof Provost 
44901774d0dSKristof Provost static int
sfuart_bus_transmit(struct uart_softc * sc)45001774d0dSKristof Provost sfuart_bus_transmit(struct uart_softc *sc)
45101774d0dSKristof Provost {
45201774d0dSKristof Provost 	struct uart_bas *bas;
45301774d0dSKristof Provost 	int i;
45401774d0dSKristof Provost 	uint32_t reg;
45501774d0dSKristof Provost 
45601774d0dSKristof Provost 	bas = &sc->sc_bas;
45701774d0dSKristof Provost 	uart_lock(sc->sc_hwmtx);
45801774d0dSKristof Provost 
45901774d0dSKristof Provost 	reg = uart_getreg(bas, SFUART_IRQ_ENABLE);
46001774d0dSKristof Provost 	reg |= SFUART_IRQ_ENABLE_TXWM;
46101774d0dSKristof Provost 	uart_setreg(bas, SFUART_IRQ_ENABLE, reg);
46201774d0dSKristof Provost 
46301774d0dSKristof Provost 	for (i = 0; i < sc->sc_txdatasz; i++)
46401774d0dSKristof Provost 		sfuart_putc(bas, sc->sc_txbuf[i]);
46501774d0dSKristof Provost 
46601774d0dSKristof Provost 	sc->sc_txbusy = 1;
46701774d0dSKristof Provost 
46801774d0dSKristof Provost 	uart_unlock(sc->sc_hwmtx);
46901774d0dSKristof Provost 
47001774d0dSKristof Provost 	return (0);
47101774d0dSKristof Provost }
47201774d0dSKristof Provost 
47301774d0dSKristof Provost static void
sfuart_bus_grab(struct uart_softc * sc)47401774d0dSKristof Provost sfuart_bus_grab(struct uart_softc *sc)
47501774d0dSKristof Provost {
47601774d0dSKristof Provost 	struct uart_bas *bas;
47701774d0dSKristof Provost 	uint32_t reg;
47801774d0dSKristof Provost 
47901774d0dSKristof Provost 	bas = &sc->sc_bas;
48001774d0dSKristof Provost 	uart_lock(sc->sc_hwmtx);
48101774d0dSKristof Provost 
48201774d0dSKristof Provost 	reg = uart_getreg(bas, SFUART_IRQ_ENABLE);
48301774d0dSKristof Provost 	reg &= ~(SFUART_IRQ_ENABLE_TXWM | SFUART_IRQ_PENDING_RXQM);
48401774d0dSKristof Provost 	uart_setreg(bas, SFUART_IRQ_ENABLE, reg);
48501774d0dSKristof Provost 
48601774d0dSKristof Provost 	uart_unlock(sc->sc_hwmtx);
48701774d0dSKristof Provost }
48801774d0dSKristof Provost 
48901774d0dSKristof Provost static void
sfuart_bus_ungrab(struct uart_softc * sc)49001774d0dSKristof Provost sfuart_bus_ungrab(struct uart_softc *sc)
49101774d0dSKristof Provost {
49201774d0dSKristof Provost 	struct uart_bas *bas;
49301774d0dSKristof Provost 	uint32_t reg;
49401774d0dSKristof Provost 
49501774d0dSKristof Provost 	bas = &sc->sc_bas;
49601774d0dSKristof Provost 	uart_lock(sc->sc_hwmtx);
49701774d0dSKristof Provost 
49801774d0dSKristof Provost 	reg = uart_getreg(bas, SFUART_IRQ_ENABLE);
49901774d0dSKristof Provost 	reg |= SFUART_IRQ_ENABLE_TXWM | SFUART_IRQ_PENDING_RXQM;
50001774d0dSKristof Provost 	uart_setreg(bas, SFUART_IRQ_ENABLE, reg);
50101774d0dSKristof Provost 
50201774d0dSKristof Provost 	uart_unlock(sc->sc_hwmtx);
50301774d0dSKristof Provost }
50401774d0dSKristof Provost 
50501774d0dSKristof Provost static kobj_method_t sfuart_methods[] = {
50601774d0dSKristof Provost 	KOBJMETHOD(uart_probe,		sfuart_bus_probe),
50701774d0dSKristof Provost 	KOBJMETHOD(uart_attach,		sfuart_bus_attach),
50801774d0dSKristof Provost 	KOBJMETHOD(uart_detach,		sfuart_bus_detach),
50901774d0dSKristof Provost 	KOBJMETHOD(uart_flush,		sfuart_bus_flush),
51001774d0dSKristof Provost 	KOBJMETHOD(uart_getsig,		sfuart_bus_getsig),
51101774d0dSKristof Provost 	KOBJMETHOD(uart_setsig,		sfuart_bus_setsig),
51201774d0dSKristof Provost 	KOBJMETHOD(uart_ioctl,		sfuart_bus_ioctl),
51301774d0dSKristof Provost 	KOBJMETHOD(uart_ipend,		sfuart_bus_ipend),
51401774d0dSKristof Provost 	KOBJMETHOD(uart_param,		sfuart_bus_param),
51501774d0dSKristof Provost 	KOBJMETHOD(uart_receive,	sfuart_bus_receive),
51601774d0dSKristof Provost 	KOBJMETHOD(uart_transmit,	sfuart_bus_transmit),
51701774d0dSKristof Provost 	KOBJMETHOD(uart_grab,		sfuart_bus_grab),
51801774d0dSKristof Provost 	KOBJMETHOD(uart_ungrab,		sfuart_bus_ungrab),
51901774d0dSKristof Provost 	KOBJMETHOD_END
52001774d0dSKristof Provost };
52101774d0dSKristof Provost 
52201774d0dSKristof Provost static struct uart_ops sfuart_ops = {
52301774d0dSKristof Provost 	.probe = sfuart_probe,
52401774d0dSKristof Provost 	.init = sfuart_init,
52501774d0dSKristof Provost 	.term = NULL,
52601774d0dSKristof Provost 	.putc = sfuart_putc,
52701774d0dSKristof Provost 	.rxready = sfuart_rxready,
52801774d0dSKristof Provost 	.getc = sfuart_getc,
52901774d0dSKristof Provost };
53001774d0dSKristof Provost 
53101774d0dSKristof Provost struct uart_class sfuart_class = {
53201774d0dSKristof Provost 	"sifiveuart",
53301774d0dSKristof Provost 	sfuart_methods,
53401774d0dSKristof Provost 	sizeof(struct sfuart_softc),
53501774d0dSKristof Provost 	.uc_ops = &sfuart_ops,
53601774d0dSKristof Provost 	.uc_range = SFUART_REGS_SIZE,
53701774d0dSKristof Provost 	.uc_rclk = 0,
53801774d0dSKristof Provost 	.uc_rshift = 0
53901774d0dSKristof Provost };
54001774d0dSKristof Provost 
54101774d0dSKristof Provost static struct ofw_compat_data compat_data[] = {
54201774d0dSKristof Provost 	{ "sifive,uart0",	(uintptr_t)&sfuart_class },
54301774d0dSKristof Provost 	{ NULL,			(uintptr_t)NULL }
54401774d0dSKristof Provost };
54501774d0dSKristof Provost 
54601774d0dSKristof Provost UART_FDT_CLASS_AND_DEVICE(compat_data);
547