xref: /freebsd/sys/riscv/sifive/sifive_uart.c (revision cc68614da8232d8baaca0ae0d0dd8f890f06623e)
1 /*-
2  * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
3  *
4  * Copyright (c) 2019 Axiado Corporation
5  * All rights reserved.
6  *
7  * This software was developed in part by Kristof Provost under contract for
8  * Axiado Corporation.
9  *
10  * Redistribution and use in source and binary forms, with or without
11  * modification, are permitted provided that the following conditions
12  * are met:
13  * 1. Redistributions of source code must retain the above copyright
14  *    notice, this list of conditions and the following disclaimer.
15  * 2. Redistributions in binary form must reproduce the above copyright
16  *    notice, this list of conditions and the following disclaimer in the
17  *    documentation and/or other materials provided with the distribution.
18  *
19  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
20  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
23  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
25  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
26  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
27  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
28  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
29  * SUCH DAMAGE.
30  */
31 
32 #include <sys/cdefs.h>
33 __FBSDID("$FreeBSD$");
34 
35 #include <sys/param.h>
36 #include <sys/systm.h>
37 #include <sys/bus.h>
38 #include <sys/kernel.h>
39 #include <sys/lock.h>
40 #include <sys/module.h>
41 #include <sys/mutex.h>
42 #include <sys/rman.h>
43 
44 #include <machine/bus.h>
45 #include <machine/cpu.h>
46 
47 #include <dev/extres/clk/clk.h>
48 
49 #include <dev/ofw/ofw_bus.h>
50 #include <dev/ofw/ofw_bus_subr.h>
51 #include <dev/ofw/openfirm.h>
52 
53 #include <dev/uart/uart.h>
54 #include <dev/uart/uart_bus.h>
55 #include <dev/uart/uart_cpu.h>
56 #include <dev/uart/uart_cpu_fdt.h>
57 
58 #include "uart_if.h"
59 
60 #define	SFUART_TXDATA			0x00
61 #define		SFUART_TXDATA_FULL	(1 << 31)
62 #define	SFUART_RXDATA			0x04
63 #define		SFUART_RXDATA_EMPTY	(1 << 31)
64 #define	SFUART_TXCTRL			0x08
65 #define		SFUART_TXCTRL_ENABLE	0x01
66 #define		SFUART_TXCTRL_NSTOP	0x02
67 #define		SFUART_TXCTRL_TXCNT	0x70000
68 #define		SFUART_TXCTRL_TXCNT_SHIFT	16
69 #define	SFUART_RXCTRL			0x0c
70 #define		SFUART_RXCTRL_ENABLE	0x01
71 #define		SFUART_RXCTRL_RXCNT	0x70000
72 #define		SFUART_RXCTRL_RXCNT_SHIFT	16
73 #define	SFUART_IRQ_ENABLE		0x10
74 #define		SFUART_IRQ_ENABLE_TXWM	0x01
75 #define		SFUART_IRQ_ENABLE_RXWM	0x02
76 #define	SFUART_IRQ_PENDING		0x14
77 #define		SFUART_IRQ_PENDING_TXWM	0x01
78 #define		SFUART_IRQ_PENDING_RXQM	0x02
79 #define	SFUART_DIV			0x18
80 #define	SFUART_REGS_SIZE		0x1c
81 
82 #define	SFUART_RX_FIFO_DEPTH		8
83 #define	SFUART_TX_FIFO_DEPTH		8
84 
85 struct sfuart_softc {
86 	struct uart_softc	uart_softc;
87 	clk_t			clk;
88 };
89 
90 static int
91 sfuart_probe(struct uart_bas *bas)
92 {
93 
94 	bas->regiowidth = 4;
95 
96 	return (0);
97 }
98 
99 static void
100 sfuart_init(struct uart_bas *bas, int baudrate, int databits, int stopbits,
101     int parity)
102 {
103 	uint32_t reg;
104 
105 	uart_setreg(bas, SFUART_IRQ_ENABLE, 0);
106 
107 	/* Enable RX and configure the watermark so that we get an interrupt
108 	 * when a single character arrives (if interrupts are enabled). */
109 	reg = SFUART_RXCTRL_ENABLE;
110 	reg |= (0 << SFUART_RXCTRL_RXCNT_SHIFT);
111 	uart_setreg(bas, SFUART_RXCTRL, reg);
112 
113 	/* Enable TX and configure the watermark so that we get an interrupt
114 	 * when there's room for one more character in the TX fifo (if
115 	 * interrupts are enabled). */
116 	reg = SFUART_TXCTRL_ENABLE;
117 	reg |= (1 << SFUART_TXCTRL_TXCNT_SHIFT);
118 	if (stopbits == 2)
119 		reg |= SFUART_TXCTRL_NSTOP;
120 	uart_setreg(bas, SFUART_TXCTRL, reg);
121 
122 	/* Don't touch DIV. Assume that's set correctly until we can
123 	 * reconfigure. */
124 }
125 
126 static void
127 sfuart_putc(struct uart_bas *bas, int c)
128 {
129 
130 	while ((uart_getreg(bas, SFUART_TXDATA) & SFUART_TXDATA_FULL)
131 	    != 0)
132 		cpu_spinwait();
133 
134 	uart_setreg(bas, SFUART_TXDATA, c);
135 }
136 
137 static int
138 sfuart_rxready(struct uart_bas *bas)
139 {
140 	/*
141 	 * Unfortunately the FIFO empty flag is in the FIFO data register so
142 	 * reading it would dequeue the character. Instead, rely on the fact
143 	 * we've configured the watermark to be 0 and that interrupts are off
144 	 * when using the low-level console function, and read the interrupt
145 	 * pending state instead.
146 	 */
147 	return ((uart_getreg(bas, SFUART_IRQ_PENDING) &
148 	    SFUART_IRQ_PENDING_RXQM) != 0);
149 }
150 
151 static int
152 sfuart_getc(struct uart_bas *bas, struct mtx *hwmtx)
153 {
154 	int c;
155 
156 	uart_lock(hwmtx);
157 
158 	while (((c = uart_getreg(bas, SFUART_RXDATA)) &
159 	    SFUART_RXDATA_EMPTY) != 0) {
160 		uart_unlock(hwmtx);
161 		DELAY(4);
162 		uart_lock(hwmtx);
163 	}
164 
165 	uart_unlock(hwmtx);
166 
167 	return (c & 0xff);
168 }
169 
170 static int
171 sfuart_bus_probe(struct uart_softc *sc)
172 {
173 	int error;
174 
175 	error = sfuart_probe(&sc->sc_bas);
176 	if (error)
177 		return (error);
178 
179 	sc->sc_rxfifosz = SFUART_RX_FIFO_DEPTH;
180 	sc->sc_txfifosz = SFUART_TX_FIFO_DEPTH;
181 	sc->sc_hwiflow = 0;
182 	sc->sc_hwoflow = 0;
183 
184 	device_set_desc(sc->sc_dev, "SiFive UART");
185 
186 	return (0);
187 }
188 
189 static int
190 sfuart_bus_attach(struct uart_softc *sc)
191 {
192 	struct uart_bas *bas;
193 	struct sfuart_softc *sfsc;
194 	uint64_t freq;
195 	uint32_t reg;
196 	int error;
197 
198 	sfsc = (struct sfuart_softc *)sc;
199 	bas = &sc->sc_bas;
200 
201 	error = clk_get_by_ofw_index(sc->sc_dev, 0, 0, &sfsc->clk);
202 	if (error) {
203 		device_printf(sc->sc_dev, "couldn't allocate clock\n");
204 		return (ENXIO);
205 	}
206 
207 	error = clk_enable(sfsc->clk);
208 	if (error) {
209 		device_printf(sc->sc_dev, "couldn't enable clock\n");
210 		return (ENXIO);
211 	}
212 
213 	error = clk_get_freq(sfsc->clk, &freq);
214 	if (error || freq == 0) {
215 		clk_disable(sfsc->clk);
216 		device_printf(sc->sc_dev, "couldn't get clock frequency\n");
217 		return (ENXIO);
218 	}
219 
220 	bas->rclk = freq;
221 
222 	/* Enable RX/RX */
223 	reg = SFUART_RXCTRL_ENABLE;
224 	reg |= (0 << SFUART_RXCTRL_RXCNT_SHIFT);
225 	uart_setreg(bas, SFUART_RXCTRL, reg);
226 
227 	reg = SFUART_TXCTRL_ENABLE;
228 	reg |= (1 << SFUART_TXCTRL_TXCNT_SHIFT);
229 	uart_setreg(bas, SFUART_TXCTRL, reg);
230 
231 	/* Enable RX interrupt */
232 	uart_setreg(bas, SFUART_IRQ_ENABLE, SFUART_IRQ_ENABLE_RXWM);
233 
234 	return (0);
235 }
236 
237 static int
238 sfuart_bus_detach(struct uart_softc *sc)
239 {
240 	struct sfuart_softc *sfsc;
241 	struct uart_bas *bas;
242 
243 	sfsc = (struct sfuart_softc *)sc;
244 	bas = &sc->sc_bas;
245 
246 	/* Disable RX/TX */
247 	uart_setreg(bas, SFUART_RXCTRL, 0);
248 	uart_setreg(bas, SFUART_TXCTRL, 0);
249 
250 	/* Disable interrupts */
251 	uart_setreg(bas, SFUART_IRQ_ENABLE, 0);
252 
253 	clk_disable(sfsc->clk);
254 
255 	return (0);
256 }
257 
258 static int
259 sfuart_bus_flush(struct uart_softc *sc, int what)
260 {
261 	struct uart_bas *bas;
262 	uint32_t reg;
263 
264 	bas = &sc->sc_bas;
265 	uart_lock(sc->sc_hwmtx);
266 
267 	if (what & UART_FLUSH_TRANSMITTER) {
268 		do {
269 			reg = uart_getreg(bas, SFUART_TXDATA);
270 		} while ((reg & SFUART_TXDATA_FULL) != 0);
271 	}
272 
273 	if (what & UART_FLUSH_RECEIVER) {
274 		do {
275 			reg = uart_getreg(bas, SFUART_RXDATA);
276 		} while ((reg & SFUART_RXDATA_EMPTY) == 0);
277 	}
278 	uart_unlock(sc->sc_hwmtx);
279 
280 	return (0);
281 }
282 
283 #define	SIGCHG(c, i, s, d)						\
284 	do {								\
285 		if (c)							\
286 			i |= ((i) & (s)) ? (s) : (s) | (d);		\
287 		else		 					\
288 			i = ((i) & (s)) ? ((i) & ~(s)) | (d) : (i);	\
289 	} while (0)
290 
291 static int
292 sfuart_bus_getsig(struct uart_softc *sc)
293 {
294 	uint32_t new, old, sig;
295 
296 	do {
297 		old = sc->sc_hwsig;
298 		sig = old;
299 		SIGCHG(1, sig, SER_DSR, SER_DDSR);
300 		SIGCHG(1, sig, SER_DCD, SER_DDCD);
301 		SIGCHG(1, sig, SER_CTS, SER_DCTS);
302 		new = sig & ~SER_MASK_DELTA;
303 	} while (!atomic_cmpset_32(&sc->sc_hwsig, old, new));
304 
305 	return (sig);
306 }
307 
308 static int
309 sfuart_bus_setsig(struct uart_softc *sc, int sig)
310 {
311 	uint32_t new, old;
312 
313 	do {
314 		old = sc->sc_hwsig;
315 		new = old;
316 		if (sig & SER_DDTR) {
317 			SIGCHG(sig & SER_DTR, new, SER_DTR, SER_DDTR);
318 		}
319 		if (sig & SER_DRTS) {
320 			SIGCHG(sig & SER_RTS, new, SER_RTS, SER_DRTS);
321 		}
322 	 } while (!atomic_cmpset_32(&sc->sc_hwsig, old, new));
323 
324 	return (0);
325 }
326 
327 static int
328 sfuart_bus_ioctl(struct uart_softc *sc, int request, intptr_t data)
329 {
330 	struct uart_bas *bas;
331 	uint32_t reg;
332 	int error;
333 
334 	bas = &sc->sc_bas;
335 
336 	uart_lock(sc->sc_hwmtx);
337 
338 	switch (request) {
339 	case UART_IOCTL_BAUD:
340 		reg = uart_getreg(bas, SFUART_DIV);
341 		if (reg == 0) {
342 			/* Possible if the divisor hasn't been set up yet. */
343 			error = ENXIO;
344 			break;
345 		}
346 		*(int*)data = bas->rclk / (reg + 1);
347 		error = 0;
348 		break;
349 	default:
350 		error = EINVAL;
351 		break;
352 	}
353 
354 	uart_unlock(sc->sc_hwmtx);
355 
356 	return (error);
357 }
358 
359 static int
360 sfuart_bus_ipend(struct uart_softc *sc)
361 {
362 	struct uart_bas *bas;
363 	int ipend;
364 	uint32_t reg, ie;
365 
366 	bas = &sc->sc_bas;
367 	uart_lock(sc->sc_hwmtx);
368 
369 	ipend = 0;
370 	reg = uart_getreg(bas, SFUART_IRQ_PENDING);
371 	ie = uart_getreg(bas, SFUART_IRQ_ENABLE);
372 
373 	if ((reg & SFUART_IRQ_PENDING_TXWM) != 0 &&
374 	    (ie & SFUART_IRQ_ENABLE_TXWM) != 0) {
375 		ipend |= SER_INT_TXIDLE;
376 
377 		/* Disable TX interrupt */
378 		ie &= ~(SFUART_IRQ_ENABLE_TXWM);
379 		uart_setreg(bas, SFUART_IRQ_ENABLE, ie);
380 	}
381 
382 	if ((reg & SFUART_IRQ_PENDING_RXQM) != 0)
383 		ipend |= SER_INT_RXREADY;
384 
385 	uart_unlock(sc->sc_hwmtx);
386 
387 	return (ipend);
388 }
389 
390 static int
391 sfuart_bus_param(struct uart_softc *sc, int baudrate, int databits,
392     int stopbits, int parity)
393 {
394 	struct uart_bas *bas;
395 	uint32_t reg;
396 
397 	bas = &sc->sc_bas;
398 
399 	if (databits != 8)
400 		return (EINVAL);
401 
402 	if (parity != UART_PARITY_NONE)
403 		return (EINVAL);
404 
405 	uart_lock(sc->sc_hwmtx);
406 
407 	reg = uart_getreg(bas, SFUART_TXCTRL);
408 	if (stopbits == 2) {
409 		reg |= SFUART_TXCTRL_NSTOP;
410 	} else if (stopbits == 1) {
411 		reg &= ~SFUART_TXCTRL_NSTOP;
412 	} else {
413 		uart_unlock(sc->sc_hwmtx);
414 		return (EINVAL);
415 	}
416 
417 	if (baudrate > 0 && bas->rclk != 0) {
418 		reg = (bas->rclk / baudrate) - 1;
419 		uart_setreg(bas, SFUART_DIV, reg);
420 	}
421 
422 	uart_unlock(sc->sc_hwmtx);
423 	return (0);
424 }
425 
426 static int
427 sfuart_bus_receive(struct uart_softc *sc)
428 {
429 	struct uart_bas *bas;
430 	uint32_t reg;
431 
432 	bas = &sc->sc_bas;
433 	uart_lock(sc->sc_hwmtx);
434 
435 	reg = uart_getreg(bas, SFUART_RXDATA);
436 	while ((reg & SFUART_RXDATA_EMPTY) == 0) {
437 		if (uart_rx_full(sc)) {
438 			sc->sc_rxbuf[sc->sc_rxput] = UART_STAT_OVERRUN;
439 			break;
440 		}
441 
442 		uart_rx_put(sc, reg & 0xff);
443 
444 		reg = uart_getreg(bas, SFUART_RXDATA);
445 	}
446 
447 	uart_unlock(sc->sc_hwmtx);
448 
449 	return (0);
450 }
451 
452 static int
453 sfuart_bus_transmit(struct uart_softc *sc)
454 {
455 	struct uart_bas *bas;
456 	int i;
457 	uint32_t reg;
458 
459 	bas = &sc->sc_bas;
460 	uart_lock(sc->sc_hwmtx);
461 
462 	reg = uart_getreg(bas, SFUART_IRQ_ENABLE);
463 	reg |= SFUART_IRQ_ENABLE_TXWM;
464 	uart_setreg(bas, SFUART_IRQ_ENABLE, reg);
465 
466 	for (i = 0; i < sc->sc_txdatasz; i++)
467 		sfuart_putc(bas, sc->sc_txbuf[i]);
468 
469 	sc->sc_txbusy = 1;
470 
471 	uart_unlock(sc->sc_hwmtx);
472 
473 	return (0);
474 }
475 
476 static void
477 sfuart_bus_grab(struct uart_softc *sc)
478 {
479 	struct uart_bas *bas;
480 	uint32_t reg;
481 
482 	bas = &sc->sc_bas;
483 	uart_lock(sc->sc_hwmtx);
484 
485 	reg = uart_getreg(bas, SFUART_IRQ_ENABLE);
486 	reg &= ~(SFUART_IRQ_ENABLE_TXWM | SFUART_IRQ_PENDING_RXQM);
487 	uart_setreg(bas, SFUART_IRQ_ENABLE, reg);
488 
489 	uart_unlock(sc->sc_hwmtx);
490 }
491 
492 static void
493 sfuart_bus_ungrab(struct uart_softc *sc)
494 {
495 	struct uart_bas *bas;
496 	uint32_t reg;
497 
498 	bas = &sc->sc_bas;
499 	uart_lock(sc->sc_hwmtx);
500 
501 	reg = uart_getreg(bas, SFUART_IRQ_ENABLE);
502 	reg |= SFUART_IRQ_ENABLE_TXWM | SFUART_IRQ_PENDING_RXQM;
503 	uart_setreg(bas, SFUART_IRQ_ENABLE, reg);
504 
505 	uart_unlock(sc->sc_hwmtx);
506 }
507 
508 static kobj_method_t sfuart_methods[] = {
509 	KOBJMETHOD(uart_probe,		sfuart_bus_probe),
510 	KOBJMETHOD(uart_attach,		sfuart_bus_attach),
511 	KOBJMETHOD(uart_detach,		sfuart_bus_detach),
512 	KOBJMETHOD(uart_flush,		sfuart_bus_flush),
513 	KOBJMETHOD(uart_getsig,		sfuart_bus_getsig),
514 	KOBJMETHOD(uart_setsig,		sfuart_bus_setsig),
515 	KOBJMETHOD(uart_ioctl,		sfuart_bus_ioctl),
516 	KOBJMETHOD(uart_ipend,		sfuart_bus_ipend),
517 	KOBJMETHOD(uart_param,		sfuart_bus_param),
518 	KOBJMETHOD(uart_receive,	sfuart_bus_receive),
519 	KOBJMETHOD(uart_transmit,	sfuart_bus_transmit),
520 	KOBJMETHOD(uart_grab,		sfuart_bus_grab),
521 	KOBJMETHOD(uart_ungrab,		sfuart_bus_ungrab),
522 	KOBJMETHOD_END
523 };
524 
525 static struct uart_ops sfuart_ops = {
526 	.probe = sfuart_probe,
527 	.init = sfuart_init,
528 	.term = NULL,
529 	.putc = sfuart_putc,
530 	.rxready = sfuart_rxready,
531 	.getc = sfuart_getc,
532 };
533 
534 struct uart_class sfuart_class = {
535 	"sifiveuart",
536 	sfuart_methods,
537 	sizeof(struct sfuart_softc),
538 	.uc_ops = &sfuart_ops,
539 	.uc_range = SFUART_REGS_SIZE,
540 	.uc_rclk = 0,
541 	.uc_rshift = 0
542 };
543 
544 static struct ofw_compat_data compat_data[] = {
545 	{ "sifive,uart0",	(uintptr_t)&sfuart_class },
546 	{ NULL,			(uintptr_t)NULL }
547 };
548 
549 UART_FDT_CLASS_AND_DEVICE(compat_data);
550