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