xref: /openbsd-src/sys/arch/riscv64/dev/sfuart.c (revision 5b3eaf5874413698328fdf993b90658aef5f6bb3)
1 /*	$OpenBSD: sfuart.c,v 1.6 2022/07/12 17:14:12 jca Exp $	*/
2 /*
3  * Copyright (c) 2019 Mark Kettenis <kettenis@openbsd.org>
4  *
5  * Permission to use, copy, modify, and distribute this software for any
6  * purpose with or without fee is hereby granted, provided that the above
7  * copyright notice and this permission notice appear in all copies.
8  *
9  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
16  */
17 
18 #include <sys/param.h>
19 #include <sys/conf.h>
20 #include <sys/fcntl.h>
21 #include <sys/proc.h>
22 #include <sys/systm.h>
23 #include <sys/tty.h>
24 
25 #include <machine/bus.h>
26 #include <machine/fdt.h>
27 
28 #include <dev/cons.h>
29 
30 #include <dev/ofw/fdt.h>
31 #include <dev/ofw/openfirm.h>
32 #include <dev/ofw/ofw_clock.h>
33 
34 #define UART_TXDATA			0x0000
35 #define  UART_TXDATA_FULL		(1U << 31)
36 #define UART_RXDATA			0x0004
37 #define  UART_RXDATA_EMPTY		(1U << 31)
38 #define UART_TXCTRL			0x0008
39 #define  UART_TXCTRL_TXEN		(1 << 0)
40 #define  UART_TXCTRL_NSTOP		(1 << 1)
41 #define  UART_TXCTRL_TXCNT_SHIFT	16
42 #define  UART_TXCTRL_TXCNT_MASK		(7 << 16)
43 #define UART_RXCTRL			0x000c
44 #define  UART_RXCTRL_RXEN		(1 << 0)
45 #define  UART_RXCTRL_RXCNT_SHIFT	16
46 #define  UART_RXCTRL_RXCNT_MASK		(7 << 16)
47 #define UART_IE				0x0010
48 #define  UART_IE_TXWM			(1 << 0)
49 #define  UART_IE_RXWM			(1 << 1)
50 #define UART_IP				0x0014
51 #define  UART_IP_TXWM			(1 << 0)
52 #define  UART_IP_RXWM			(1 << 1)
53 #define UART_DIV			0x0018
54 
55 #define UART_SPACE			28
56 #define UART_FIFO_SIZE			8
57 
58 #define HREAD4(sc, reg)							\
59 	(bus_space_read_4((sc)->sc_iot, (sc)->sc_ioh, (reg)))
60 #define HWRITE4(sc, reg, val)						\
61 	bus_space_write_4((sc)->sc_iot, (sc)->sc_ioh, (reg), (val))
62 #define HSET4(sc, reg, bits)						\
63 	HWRITE4((sc), (reg), HREAD4((sc), (reg)) | (bits))
64 #define HCLR4(sc, reg, bits)						\
65 	HWRITE4((sc), (reg), HREAD4((sc), (reg)) & ~(bits))
66 
67 cdev_decl(com);
68 cdev_decl(sfuart);
69 
70 #define DEVUNIT(x)	(minor(x) & 0x7f)
71 #define DEVCUA(x)	(minor(x) & 0x80)
72 
73 struct cdevsw sfuartdev = cdev_tty_init(2, sfuart);
74 
75 struct sfuart_softc {
76 	struct device		sc_dev;
77 	bus_space_tag_t		sc_iot;
78 	bus_space_handle_t	sc_ioh;
79 
80 	uint32_t		sc_frequency;
81 
82 	struct soft_intrhand	*sc_si;
83 	void			*sc_ih;
84 
85 	struct tty		*sc_tty;
86 	int			sc_conspeed;
87 	int			sc_floods;
88 	int			sc_overflows;
89 	int			sc_halt;
90 	int			sc_cua;
91 	int	 		*sc_ibuf, *sc_ibufp, *sc_ibufhigh, *sc_ibufend;
92 #define SFUART_IBUFSIZE		128
93 #define SFUART_IHIGHWATER	100
94 	int			sc_ibufs[2][SFUART_IBUFSIZE];
95 };
96 
97 int	sfuart_match(struct device *, void *, void *);
98 void	sfuart_attach(struct device *, struct device *, void *);
99 
100 struct cfdriver sfuart_cd = {
101 	NULL, "sfuart", DV_TTY
102 };
103 
104 const struct cfattach sfuart_ca = {
105 	sizeof(struct sfuart_softc), sfuart_match, sfuart_attach
106 };
107 
108 bus_space_tag_t	sfuartconsiot;
109 bus_space_handle_t sfuartconsioh;
110 
111 struct sfuart_softc *sfuart_sc(dev_t);
112 
113 int	sfuart_intr(void *);
114 void	sfuart_softintr(void *);
115 void	sfuart_start(struct tty *);
116 
117 int	sfuartcnattach(bus_space_tag_t, bus_addr_t);
118 int	sfuartcngetc(dev_t);
119 void	sfuartcnputc(dev_t, int);
120 void	sfuartcnpollc(dev_t, int);
121 
122 void
sfuart_init_cons(void)123 sfuart_init_cons(void)
124 {
125 	struct fdt_reg reg;
126 	void *node;
127 
128 	if ((node = fdt_find_cons("sifive,uart0")) == NULL)
129 		return;
130 	if (fdt_get_reg(node, 0, &reg))
131 		return;
132 
133 	sfuartcnattach(fdt_cons_bs_tag, reg.addr);
134 }
135 
136 int
sfuart_match(struct device * parent,void * match,void * aux)137 sfuart_match(struct device *parent, void *match, void *aux)
138 {
139 	struct fdt_attach_args *faa = aux;
140 
141 	return OF_is_compatible(faa->fa_node, "sifive,uart0");
142 }
143 
144 void
sfuart_attach(struct device * parent,struct device * self,void * aux)145 sfuart_attach(struct device *parent, struct device *self, void *aux)
146 {
147 	struct sfuart_softc *sc = (struct sfuart_softc *)self;
148 	struct fdt_attach_args *faa = aux;
149 	int maj;
150 
151 	if (faa->fa_nreg < 1) {
152 		printf(": no registers\n");
153 		return;
154 	}
155 
156 	sc->sc_iot = faa->fa_iot;
157 	if (bus_space_map(sc->sc_iot, faa->fa_reg[0].addr,
158 	    faa->fa_reg[0].size, 0, &sc->sc_ioh)) {
159 		printf(": can't map registers\n");
160 		return;
161 	}
162 
163 	sc->sc_frequency = clock_get_frequency(faa->fa_node, NULL);
164 	if (faa->fa_node == stdout_node) {
165 		/* Locate the major number. */
166 		for (maj = 0; maj < nchrdev; maj++)
167 			if (cdevsw[maj].d_open == sfuartopen)
168 				break;
169 		cn_tab->cn_dev = makedev(maj, sc->sc_dev.dv_unit);
170 		sc->sc_conspeed = stdout_speed;
171 		printf(": console");
172 	}
173 
174 	sc->sc_si = softintr_establish(IPL_TTY, sfuart_softintr, sc);
175 	if (sc->sc_si == NULL) {
176 		printf(": can't establish soft interrupt\n");
177 		return;
178 	}
179 
180 	sc->sc_ih = fdt_intr_establish_idx(faa->fa_node, 0, IPL_TTY,
181 	    sfuart_intr, sc, sc->sc_dev.dv_xname);
182 	if (sc->sc_ih == NULL) {
183 		printf(": can't establish hard interrupt\n");
184 		return;
185 	}
186 
187 	printf("\n");
188 }
189 
190 int
sfuart_intr(void * arg)191 sfuart_intr(void *arg)
192 {
193 	struct sfuart_softc *sc = arg;
194 	struct tty *tp = sc->sc_tty;
195 	int *p;
196 	uint32_t val;
197 	int c, handled = 0;
198 
199 	if (tp == NULL)
200 		return 0;
201 
202 	if (!ISSET(HREAD4(sc, UART_TXDATA), UART_TXDATA_FULL) &&
203 	    ISSET(tp->t_state, TS_BUSY)) {
204 		CLR(tp->t_state, TS_BUSY | TS_FLUSH);
205 		if (sc->sc_halt > 0)
206 			wakeup(&tp->t_outq);
207 		(*linesw[tp->t_line].l_start)(tp);
208 		handled = 1;
209 	}
210 
211 	p = sc->sc_ibufp;
212 	val = HREAD4(sc, UART_RXDATA);
213 	while (!ISSET(val, UART_RXDATA_EMPTY)) {
214 		c = val & 0xff;
215 
216 		if (p >= sc->sc_ibufend)
217 			sc->sc_floods++;
218 		else
219 			*p++ = c;
220 
221 		val = HREAD4(sc, UART_RXDATA);
222 		handled = 1;
223 	}
224 	if (sc->sc_ibufp != p) {
225 		sc->sc_ibufp = p;
226 		softintr_schedule(sc->sc_si);
227 	}
228 
229 	return handled;
230 }
231 
232 void
sfuart_softintr(void * arg)233 sfuart_softintr(void *arg)
234 {
235 	struct sfuart_softc *sc = arg;
236 	struct tty *tp = sc->sc_tty;
237 	int *ibufp, *ibufend;
238 	int s;
239 
240 	if (sc->sc_ibufp == sc->sc_ibuf)
241 		return;
242 
243 	s = spltty();
244 
245 	ibufp = sc->sc_ibuf;
246 	ibufend = sc->sc_ibufp;
247 
248 	if (ibufp == ibufend) {
249 		splx(s);
250 		return;
251 	}
252 
253 	sc->sc_ibufp = sc->sc_ibuf = (ibufp == sc->sc_ibufs[0]) ?
254 	    sc->sc_ibufs[1] : sc->sc_ibufs[0];
255 	sc->sc_ibufhigh = sc->sc_ibuf + SFUART_IHIGHWATER;
256 	sc->sc_ibufend = sc->sc_ibuf + SFUART_IBUFSIZE;
257 
258 	if (tp == NULL || !ISSET(tp->t_state, TS_ISOPEN)) {
259 		splx(s);
260 		return;
261 	}
262 
263 	splx(s);
264 
265 	while (ibufp < ibufend) {
266 		int i = *ibufp++;
267 #ifdef DDB
268 		if (tp->t_dev == cn_tab->cn_dev) {
269 			int j = db_rint(i);
270 
271 			if (j == 1)	/* Escape received, skip */
272 				continue;
273 			if (j == 2)	/* Second char wasn't 'D' */
274 				(*linesw[tp->t_line].l_rint)(27, tp);
275 		}
276 #endif
277 		(*linesw[tp->t_line].l_rint)(i, tp);
278 	}
279 }
280 
281 int
sfuart_param(struct tty * tp,struct termios * t)282 sfuart_param(struct tty *tp, struct termios *t)
283 {
284 	struct sfuart_softc *sc = sfuart_sc(tp->t_dev);
285 	int ospeed = t->c_ospeed;
286 	uint32_t div;
287 
288 	/* Check requested parameters. */
289 	if (ospeed < 0 || (t->c_ispeed && t->c_ispeed != t->c_ospeed))
290 		return EINVAL;
291 
292 	switch (ISSET(t->c_cflag, CSIZE)) {
293 	case CS5:
294 	case CS6:
295 	case CS7:
296 		return EINVAL;
297 	case CS8:
298 		break;
299 	}
300 
301 	if (ospeed != 0) {
302 		while (ISSET(tp->t_state, TS_BUSY)) {
303 			int error;
304 
305 			sc->sc_halt++;
306 			error = ttysleep(tp, &tp->t_outq,
307 			    TTOPRI | PCATCH, "sfuprm");
308 			sc->sc_halt--;
309 			if (error) {
310 				sfuart_start(tp);
311 				return error;
312 			}
313 		}
314 
315 		div = (sc->sc_frequency + ospeed / 2) / ospeed;
316 		if (div < 16 || div > 65536)
317 			return EINVAL;
318 		HWRITE4(sc, UART_DIV, div - 1);
319 	}
320 
321 	tp->t_ispeed = t->c_ispeed;
322 	tp->t_ospeed = t->c_ospeed;
323 	tp->t_cflag = t->c_cflag;
324 
325 	/* Just to be sure... */
326 	sfuart_start(tp);
327 	return 0;
328 }
329 
330 void
sfuart_start(struct tty * tp)331 sfuart_start(struct tty *tp)
332 {
333 	struct sfuart_softc *sc = sfuart_sc(tp->t_dev);
334 	int stat;
335 	int s;
336 
337 	s = spltty();
338 	if (ISSET(tp->t_state, TS_BUSY))
339 		goto out;
340 	if (ISSET(tp->t_state, TS_TIMEOUT | TS_TTSTOP) || sc->sc_halt > 0)
341 		goto out;
342 	ttwakeupwr(tp);
343 	if (tp->t_outq.c_cc == 0) {
344 		HCLR4(sc, UART_IE, UART_IE_TXWM);
345 		goto out;
346 	}
347 	SET(tp->t_state, TS_BUSY);
348 
349 	stat = HREAD4(sc, UART_TXDATA);
350 	while ((stat & UART_TXDATA_FULL) == 0 && tp->t_outq.c_cc != 0) {
351 		HWRITE4(sc, UART_TXDATA, getc(&tp->t_outq));
352 		stat = HREAD4(sc, UART_TXDATA);
353 	}
354 	HSET4(sc, UART_IE, UART_IE_TXWM);
355 out:
356 	splx(s);
357 }
358 
359 int
sfuartopen(dev_t dev,int flag,int mode,struct proc * p)360 sfuartopen(dev_t dev, int flag, int mode, struct proc *p)
361 {
362 	struct sfuart_softc *sc = sfuart_sc(dev);
363 	struct tty *tp;
364 	int error;
365 	int s;
366 
367 	if (sc == NULL)
368 		return ENXIO;
369 
370 	s = spltty();
371 	if (sc->sc_tty == NULL)
372 		tp = sc->sc_tty = ttymalloc(0);
373 	else
374 		tp = sc->sc_tty;
375 	splx(s);
376 
377 	tp->t_oproc = sfuart_start;
378 	tp->t_param = sfuart_param;
379 	tp->t_dev = dev;
380 
381 	if (!ISSET(tp->t_state, TS_ISOPEN)) {
382 		SET(tp->t_state, TS_WOPEN);
383 		ttychars(tp);
384 		tp->t_iflag = TTYDEF_IFLAG;
385 		tp->t_oflag = TTYDEF_OFLAG;
386 		tp->t_cflag = TTYDEF_CFLAG;
387 		tp->t_lflag = TTYDEF_LFLAG;
388 		tp->t_ispeed = tp->t_ospeed =
389 		    sc->sc_conspeed ? sc->sc_conspeed : B115200;
390 
391 		s = spltty();
392 
393 		sfuart_param(tp, &tp->t_termios);
394 		ttsetwater(tp);
395 
396 		sc->sc_ibufp = sc->sc_ibuf = sc->sc_ibufs[0];
397 		sc->sc_ibufhigh = sc->sc_ibuf + SFUART_IHIGHWATER;
398 		sc->sc_ibufend = sc->sc_ibuf + SFUART_IBUFSIZE;
399 
400 		/* Enable transmit. */
401 		HWRITE4(sc, UART_TXCTRL, UART_TXCTRL_TXEN |
402 		    ((UART_FIFO_SIZE / 2) << UART_TXCTRL_TXCNT_SHIFT));
403 
404 		/* Enable receive. */
405 		HWRITE4(sc, UART_RXCTRL, UART_RXCTRL_RXEN |
406 		    (0 << UART_RXCTRL_RXCNT_SHIFT));
407 
408 		/* Enable interrupts. */
409 		HSET4(sc, UART_IE, UART_IE_RXWM);
410 
411 		/* No carrier detect support. */
412 		SET(tp->t_state, TS_CARR_ON);
413 	} else if (ISSET(tp->t_state, TS_XCLUDE) && suser(p) != 0)
414 		return EBUSY;
415 	else
416 		s = spltty();
417 
418 	if (DEVCUA(dev)) {
419 		if (ISSET(tp->t_state, TS_ISOPEN)) {
420 			/* Ah, but someone already is dialed in... */
421 			splx(s);
422 			return EBUSY;
423 		}
424 		sc->sc_cua = 1;		/* We go into CUA mode. */
425 	} else {
426 		if (ISSET(flag, O_NONBLOCK) && sc->sc_cua) {
427 			/* Opening TTY non-blocking... but the CUA is busy. */
428 			splx(s);
429 			return EBUSY;
430 		} else {
431 			while (sc->sc_cua) {
432 				SET(tp->t_state, TS_WOPEN);
433 				error = ttysleep(tp, &tp->t_rawq,
434 				    TTIPRI | PCATCH, ttopen);
435 				/*
436 				 * If TS_WOPEN has been reset, that means the
437 				 * cua device has been closed.
438 				 * We don't want to fail in that case,
439 				 * so just go around again.
440 				 */
441 				if (error && ISSET(tp->t_state, TS_WOPEN)) {
442 					CLR(tp->t_state, TS_WOPEN);
443 					splx(s);
444 					return error;
445 				}
446 			}
447 		}
448 	}
449 	splx(s);
450 
451 	return (*linesw[tp->t_line].l_open)(dev, tp, p);
452 }
453 
454 int
sfuartclose(dev_t dev,int flag,int mode,struct proc * p)455 sfuartclose(dev_t dev, int flag, int mode, struct proc *p)
456 {
457 	struct sfuart_softc *sc = sfuart_sc(dev);
458 	struct tty *tp = sc->sc_tty;
459 	int s;
460 
461 	if (!ISSET(tp->t_state, TS_ISOPEN))
462 		return 0;
463 
464 	(*linesw[tp->t_line].l_close)(tp, flag, p);
465 	s = spltty();
466 	if (!ISSET(tp->t_state, TS_WOPEN)) {
467 		/* Disable interrupts */
468 		HCLR4(sc, UART_IE, UART_IE_TXWM | UART_IE_RXWM);
469 	}
470 	CLR(tp->t_state, TS_BUSY | TS_FLUSH);
471 	sc->sc_cua = 0;
472 	splx(s);
473 	ttyclose(tp);
474 
475 	return 0;
476 }
477 
478 int
sfuartread(dev_t dev,struct uio * uio,int flag)479 sfuartread(dev_t dev, struct uio *uio, int flag)
480 {
481 	struct tty *tp = sfuarttty(dev);
482 
483 	if (tp == NULL)
484 		return ENODEV;
485 
486 	return (*linesw[tp->t_line].l_read)(tp, uio, flag);
487 }
488 
489 int
sfuartwrite(dev_t dev,struct uio * uio,int flag)490 sfuartwrite(dev_t dev, struct uio *uio, int flag)
491 {
492 	struct tty *tp = sfuarttty(dev);
493 
494 	if (tp == NULL)
495 		return ENODEV;
496 
497 	return (*linesw[tp->t_line].l_write)(tp, uio, flag);
498 }
499 
500 int
sfuartioctl(dev_t dev,u_long cmd,caddr_t data,int flag,struct proc * p)501 sfuartioctl(dev_t dev, u_long cmd, caddr_t data, int flag, struct proc *p)
502 {
503 	struct sfuart_softc *sc = sfuart_sc(dev);
504 	struct tty *tp;
505 	int error;
506 
507 	if (sc == NULL)
508 		return ENODEV;
509 
510 	tp = sc->sc_tty;
511 	if (tp == NULL)
512 		return ENXIO;
513 
514 	error = (*linesw[tp->t_line].l_ioctl)(tp, cmd, data, flag, p);
515 	if (error >= 0)
516 		return error;
517 
518 	error = ttioctl(tp, cmd, data, flag, p);
519 	if (error >= 0)
520 		return error;
521 
522 	switch(cmd) {
523 	case TIOCSBRK:
524 	case TIOCCBRK:
525 	case TIOCSDTR:
526 	case TIOCCDTR:
527 	case TIOCMSET:
528 	case TIOCMBIS:
529 	case TIOCMBIC:
530 	case TIOCMGET:
531 	case TIOCGFLAGS:
532 		break;
533 	case TIOCSFLAGS:
534 		error = suser(p);
535 		if (error != 0)
536 			return EPERM;
537 		break;
538 	default:
539 		return ENOTTY;
540 	}
541 
542 	return 0;
543 }
544 
545 int
sfuartstop(struct tty * tp,int flag)546 sfuartstop(struct tty *tp, int flag)
547 {
548 	return 0;
549 }
550 
551 struct tty *
sfuarttty(dev_t dev)552 sfuarttty(dev_t dev)
553 {
554 	struct sfuart_softc *sc = sfuart_sc(dev);
555 
556 	if (sc == NULL)
557 		return NULL;
558 	return sc->sc_tty;
559 }
560 
561 struct sfuart_softc *
sfuart_sc(dev_t dev)562 sfuart_sc(dev_t dev)
563 {
564 	int unit = DEVUNIT(dev);
565 
566 	if (unit >= sfuart_cd.cd_ndevs)
567 		return NULL;
568 	return (struct sfuart_softc *)sfuart_cd.cd_devs[unit];
569 }
570 
571 int
sfuartcnattach(bus_space_tag_t iot,bus_addr_t iobase)572 sfuartcnattach(bus_space_tag_t iot, bus_addr_t iobase)
573 {
574 	static struct consdev sfuartcons = {
575 		NULL, NULL, sfuartcngetc, sfuartcnputc, sfuartcnpollc, NULL,
576 		NODEV, CN_MIDPRI
577 	};
578 	int maj;
579 
580 	sfuartconsiot = iot;
581 	if (bus_space_map(iot, iobase, UART_SPACE, 0, &sfuartconsioh))
582 		return ENOMEM;
583 
584 	/* Look for major of com(4) to replace. */
585 	for (maj = 0; maj < nchrdev; maj++)
586 		if (cdevsw[maj].d_open == comopen)
587 			break;
588 	if (maj == nchrdev)
589 		return ENXIO;
590 
591 	cn_tab = &sfuartcons;
592 	cn_tab->cn_dev = makedev(maj, 0);
593 	cdevsw[maj] = sfuartdev; 	/* KLUDGE */
594 
595 	return 0;
596 }
597 
598 int
sfuartcngetc(dev_t dev)599 sfuartcngetc(dev_t dev)
600 {
601 	uint32_t val;
602 
603 	do {
604 		val = bus_space_read_4(sfuartconsiot, sfuartconsioh, UART_RXDATA);
605 		if (val & UART_RXDATA_EMPTY)
606 			CPU_BUSY_CYCLE();
607 	} while ((val & UART_RXDATA_EMPTY));
608 
609 	return (val & 0xff);
610 }
611 
612 void
sfuartcnputc(dev_t dev,int c)613 sfuartcnputc(dev_t dev, int c)
614 {
615 	while (bus_space_read_4(sfuartconsiot, sfuartconsioh, UART_TXDATA) &
616 	    UART_TXDATA_FULL)
617 		CPU_BUSY_CYCLE();
618 	bus_space_write_4(sfuartconsiot, sfuartconsioh, UART_TXDATA, c);
619 }
620 
621 void
sfuartcnpollc(dev_t dev,int on)622 sfuartcnpollc(dev_t dev, int on)
623 {
624 }
625