xref: /openbsd/sys/dev/fdt/amluart.c (revision 467651fa)
1 /*	$OpenBSD: amluart.c,v 1.4 2022/07/15 17:14:49 kettenis 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 
33 #define UART_WFIFO			0x0000
34 #define UART_RFIFO			0x0004
35 #define UART_CONTROL			0x0008
36 #define  UART_CONTROL_TX_INT		(1 << 28)
37 #define  UART_CONTROL_RX_INT		(1 << 27)
38 #define  UART_CONTROL_CLEAR_ERROR	(1 << 24)
39 #define UART_STATUS			0x000c
40 #define  UART_STATUS_RX_FIFO_OVERFLOW	(1 << 24)
41 #define  UART_STATUS_TX_FIFO_FULL	(1 << 21)
42 #define  UART_STATUS_RX_FIFO_EMPTY	(1 << 20)
43 #define  UART_STATUS_FRAME_ERROR	(1 << 17)
44 #define  UART_STATUS_PARITY_ERROR	(1 << 16)
45 #define  UART_STATUS_ERROR 		(1 << 24 | 0x7 << 16)
46 #define UART_MISC			0x0010
47 #define  UART_MISC_TX_INT_CNT_MASK	(0xff << 16)
48 #define  UART_MISC_TX_INT_CNT_SHIFT	16
49 #define  UART_MISC_RX_INT_CNT_MASK	(0xff << 0)
50 #define  UART_MISC_RX_INT_CNT_SHIFT	0
51 
52 #define UART_SPACE			24
53 
54 #define HREAD4(sc, reg)							\
55 	(bus_space_read_4((sc)->sc_iot, (sc)->sc_ioh, (reg)))
56 #define HWRITE4(sc, reg, val)						\
57 	bus_space_write_4((sc)->sc_iot, (sc)->sc_ioh, (reg), (val))
58 #define HSET4(sc, reg, bits)						\
59 	HWRITE4((sc), (reg), HREAD4((sc), (reg)) | (bits))
60 #define HCLR4(sc, reg, bits)						\
61 	HWRITE4((sc), (reg), HREAD4((sc), (reg)) & ~(bits))
62 
63 cdev_decl(com);
64 cdev_decl(amluart);
65 
66 #define DEVUNIT(x)	(minor(x) & 0x7f)
67 #define DEVCUA(x)	(minor(x) & 0x80)
68 
69 struct cdevsw amluartdev = cdev_tty_init(3, amluart);
70 
71 struct amluart_softc {
72 	struct device		sc_dev;
73 	bus_space_tag_t		sc_iot;
74 	bus_space_handle_t	sc_ioh;
75 
76 	struct soft_intrhand	*sc_si;
77 	void			*sc_ih;
78 
79 	struct tty		*sc_tty;
80 	int			sc_conspeed;
81 	int			sc_floods;
82 	int			sc_overflows;
83 	int			sc_halt;
84 	int			sc_cua;
85 	int	 		*sc_ibuf, *sc_ibufp, *sc_ibufhigh, *sc_ibufend;
86 #define AMLUART_IBUFSIZE	128
87 #define AMLUART_IHIGHWATER	100
88 	int			sc_ibufs[2][AMLUART_IBUFSIZE];
89 };
90 
91 int	amluart_match(struct device *, void *, void *);
92 void	amluart_attach(struct device *, struct device *, void *);
93 
94 struct cfdriver amluart_cd = {
95 	NULL, "amluart", DV_TTY
96 };
97 
98 const struct cfattach amluart_ca = {
99 	sizeof(struct amluart_softc), amluart_match, amluart_attach
100 };
101 
102 bus_space_tag_t	amluartconsiot;
103 bus_space_handle_t amluartconsioh;
104 
105 struct amluart_softc *amluart_sc(dev_t);
106 
107 int	amluart_intr(void *);
108 void	amluart_softintr(void *);
109 void	amluart_start(struct tty *);
110 
111 int	amluartcnattach(bus_space_tag_t, bus_addr_t);
112 int	amluartcngetc(dev_t);
113 void	amluartcnputc(dev_t, int);
114 void	amluartcnpollc(dev_t, int);
115 
116 void
amluart_init_cons(void)117 amluart_init_cons(void)
118 {
119 	struct fdt_reg reg;
120 	void *node;
121 
122 	if ((node = fdt_find_cons("amlogic,meson-gx-uart")) == NULL)
123 		return;
124 	if (fdt_get_reg(node, 0, &reg))
125 		return;
126 
127 	amluartcnattach(fdt_cons_bs_tag, reg.addr);
128 }
129 
130 int
amluart_match(struct device * parent,void * match,void * aux)131 amluart_match(struct device *parent, void *match, void *aux)
132 {
133 	struct fdt_attach_args *faa = aux;
134 
135 	return OF_is_compatible(faa->fa_node, "amlogic,meson-gx-uart");
136 }
137 
138 void
amluart_attach(struct device * parent,struct device * self,void * aux)139 amluart_attach(struct device *parent, struct device *self, void *aux)
140 {
141 	struct amluart_softc *sc = (struct amluart_softc *)self;
142 	struct fdt_attach_args *faa = aux;
143 	uint32_t reg;
144 	int maj;
145 
146 	if (faa->fa_nreg < 1) {
147 		printf(": no registers\n");
148 		return;
149 	}
150 
151 	sc->sc_iot = faa->fa_iot;
152 	if (bus_space_map(sc->sc_iot, faa->fa_reg[0].addr,
153 	    faa->fa_reg[0].size, 0, &sc->sc_ioh)) {
154 		printf(": can't map registers\n");
155 		return;
156 	}
157 
158 	if (faa->fa_node == stdout_node) {
159 		/* Locate the major number. */
160 		for (maj = 0; maj < nchrdev; maj++)
161 			if (cdevsw[maj].d_open == amluartopen)
162 				break;
163 		cn_tab->cn_dev = makedev(maj, sc->sc_dev.dv_unit);
164 		sc->sc_conspeed = stdout_speed;
165 		printf(": console");
166 	}
167 
168 	sc->sc_si = softintr_establish(IPL_TTY, amluart_softintr, sc);
169 	if (sc->sc_si == NULL) {
170 		printf(": can't establish soft interrupt\n");
171 		return;
172 	}
173 
174 	sc->sc_ih = fdt_intr_establish_idx(faa->fa_node, 0, IPL_TTY,
175 	    amluart_intr, sc, sc->sc_dev.dv_xname);
176 	if (sc->sc_ih == NULL) {
177 		printf(": can't establish hard interrupt\n");
178 		return;
179 	}
180 
181 	printf("\n");
182 
183 	/*
184 	 * Generate interrupts if the Tx FIFO is half-empty or if
185 	 * there is anything in the Rx FIFO.
186 	 */
187 	reg = HREAD4(sc, UART_MISC);
188 	reg &= ~UART_MISC_TX_INT_CNT_MASK;
189 	reg |= (32 << UART_MISC_TX_INT_CNT_SHIFT);
190 	reg &= ~UART_MISC_RX_INT_CNT_MASK;
191 	reg |= (1 << UART_MISC_RX_INT_CNT_SHIFT);
192 	HWRITE4(sc, UART_MISC, reg);
193 }
194 
195 int
amluart_intr(void * arg)196 amluart_intr(void *arg)
197 {
198 	struct amluart_softc *sc = arg;
199 	struct tty *tp = sc->sc_tty;
200 	int *p;
201 	u_int32_t stat;
202 	u_char c;
203 	int handled = 0;
204 
205 	if (tp == NULL)
206 		return 0;
207 
208 	stat = HREAD4(sc, UART_STATUS);
209 	if (!ISSET(stat, UART_STATUS_TX_FIFO_FULL) &&
210 	    ISSET(tp->t_state, TS_BUSY)) {
211 		CLR(tp->t_state, TS_BUSY | TS_FLUSH);
212 		if (sc->sc_halt > 0)
213 			wakeup(&tp->t_outq);
214 		(*linesw[tp->t_line].l_start)(tp);
215 		handled = 1;
216 	}
217 
218 	p = sc->sc_ibufp;
219 	while (!ISSET(stat, UART_STATUS_RX_FIFO_EMPTY)) {
220 		c = HREAD4(sc, UART_RFIFO);
221 		if (ISSET(stat, UART_STATUS_FRAME_ERROR))
222 			c |= TTY_FE;
223 		if (ISSET(stat, UART_STATUS_PARITY_ERROR))
224 			c |= TTY_PE;
225 		if (ISSET(stat, UART_STATUS_RX_FIFO_OVERFLOW))
226 			sc->sc_overflows++;
227 
228 		if (p >= sc->sc_ibufend)
229 			sc->sc_floods++;
230 		else
231 			*p++ = c;
232 
233 		if (stat & UART_STATUS_ERROR)
234 			HSET4(sc, UART_CONTROL, UART_CONTROL_CLEAR_ERROR);
235 		stat = HREAD4(sc, UART_STATUS);
236 		handled = 1;
237 	}
238 	if (sc->sc_ibufp != p) {
239 		sc->sc_ibufp = p;
240 		softintr_schedule(sc->sc_si);
241 	}
242 
243 	return handled;
244 }
245 
246 void
amluart_softintr(void * arg)247 amluart_softintr(void *arg)
248 {
249 	struct amluart_softc *sc = arg;
250 	struct tty *tp = sc->sc_tty;
251 	int *ibufp, *ibufend;
252 	int s;
253 
254 	if (sc->sc_ibufp == sc->sc_ibuf)
255 		return;
256 
257 	s = spltty();
258 
259 	ibufp = sc->sc_ibuf;
260 	ibufend = sc->sc_ibufp;
261 
262 	if (ibufp == ibufend) {
263 		splx(s);
264 		return;
265 	}
266 
267 	sc->sc_ibufp = sc->sc_ibuf = (ibufp == sc->sc_ibufs[0]) ?
268 	    sc->sc_ibufs[1] : sc->sc_ibufs[0];
269 	sc->sc_ibufhigh = sc->sc_ibuf + AMLUART_IHIGHWATER;
270 	sc->sc_ibufend = sc->sc_ibuf + AMLUART_IBUFSIZE;
271 
272 	if (tp == NULL || !ISSET(tp->t_state, TS_ISOPEN)) {
273 		splx(s);
274 		return;
275 	}
276 
277 	splx(s);
278 
279 	while (ibufp < ibufend) {
280 		int i = *ibufp++;
281 #ifdef DDB
282 		if (tp->t_dev == cn_tab->cn_dev) {
283 			int j = db_rint(i);
284 
285 			if (j == 1)	/* Escape received, skip */
286 				continue;
287 			if (j == 2)	/* Second char wasn't 'D' */
288 				(*linesw[tp->t_line].l_rint)(27, tp);
289 		}
290 #endif
291 		(*linesw[tp->t_line].l_rint)(i, tp);
292 	}
293 }
294 
295 int
amluart_param(struct tty * tp,struct termios * t)296 amluart_param(struct tty *tp, struct termios *t)
297 {
298 	struct amluart_softc *sc = amluart_sc(tp->t_dev);
299 	int ospeed = t->c_ospeed;
300 
301 	/* Check requested parameters. */
302 	if (ospeed < 0 || (t->c_ispeed && t->c_ispeed != t->c_ospeed))
303 		return EINVAL;
304 
305 	switch (ISSET(t->c_cflag, CSIZE)) {
306 	case CS5:
307 	case CS6:
308 	case CS7:
309 		return EINVAL;
310 	case CS8:
311 		break;
312 	}
313 
314 	if (ospeed != 0) {
315 		while (ISSET(tp->t_state, TS_BUSY)) {
316 			int error;
317 
318 			sc->sc_halt++;
319 			error = ttysleep(tp, &tp->t_outq,
320 			    TTOPRI | PCATCH, "amluprm");
321 			sc->sc_halt--;
322 			if (error) {
323 				amluart_start(tp);
324 				return error;
325 			}
326 		}
327 	}
328 
329 	tp->t_ispeed = t->c_ispeed;
330 	tp->t_ospeed = t->c_ospeed;
331 	tp->t_cflag = t->c_cflag;
332 
333 	/* Just to be sure... */
334 	amluart_start(tp);
335 	return 0;
336 }
337 
338 void
amluart_start(struct tty * tp)339 amluart_start(struct tty *tp)
340 {
341 	struct amluart_softc *sc = amluart_sc(tp->t_dev);
342 	int stat;
343 	int s;
344 
345 	s = spltty();
346 	if (ISSET(tp->t_state, TS_BUSY))
347 		goto out;
348 	if (ISSET(tp->t_state, TS_TIMEOUT | TS_TTSTOP) || sc->sc_halt > 0)
349 		goto out;
350 	ttwakeupwr(tp);
351 	if (tp->t_outq.c_cc == 0)
352 		goto out;
353 	SET(tp->t_state, TS_BUSY);
354 
355 	stat = HREAD4(sc, UART_STATUS);
356 	while ((stat & UART_STATUS_TX_FIFO_FULL) == 0) {
357 		HWRITE4(sc, UART_WFIFO, getc(&tp->t_outq));
358 		stat = HREAD4(sc, UART_STATUS);
359 	}
360 out:
361 	splx(s);
362 }
363 
364 int
amluartopen(dev_t dev,int flag,int mode,struct proc * p)365 amluartopen(dev_t dev, int flag, int mode, struct proc *p)
366 {
367 	struct amluart_softc *sc = amluart_sc(dev);
368 	struct tty *tp;
369 	int error;
370 	int s;
371 
372 	if (sc == NULL)
373 		return ENXIO;
374 
375 	s = spltty();
376 	if (sc->sc_tty == NULL)
377 		tp = sc->sc_tty = ttymalloc(0);
378 	else
379 		tp = sc->sc_tty;
380 	splx(s);
381 
382 	tp->t_oproc = amluart_start;
383 	tp->t_param = amluart_param;
384 	tp->t_dev = dev;
385 
386 	if (!ISSET(tp->t_state, TS_ISOPEN)) {
387 		SET(tp->t_state, TS_WOPEN);
388 		ttychars(tp);
389 		tp->t_iflag = TTYDEF_IFLAG;
390 		tp->t_oflag = TTYDEF_OFLAG;
391 		tp->t_cflag = TTYDEF_CFLAG;
392 		tp->t_lflag = TTYDEF_LFLAG;
393 		tp->t_ispeed = tp->t_ospeed =
394 		    sc->sc_conspeed ? sc->sc_conspeed : B115200;
395 
396 		s = spltty();
397 
398 		amluart_param(tp, &tp->t_termios);
399 		ttsetwater(tp);
400 
401 		sc->sc_ibufp = sc->sc_ibuf = sc->sc_ibufs[0];
402 		sc->sc_ibufhigh = sc->sc_ibuf + AMLUART_IHIGHWATER;
403 		sc->sc_ibufend = sc->sc_ibuf + AMLUART_IBUFSIZE;
404 
405 		/* Enable interrupts */
406 		HSET4(sc, UART_CONTROL,
407 		    UART_CONTROL_TX_INT | UART_CONTROL_RX_INT);
408 
409 		/* No carrier detect support. */
410 		SET(tp->t_state, TS_CARR_ON);
411 	} else if (ISSET(tp->t_state, TS_XCLUDE) && suser(p) != 0)
412 		return EBUSY;
413 	else
414 		s = spltty();
415 
416 	if (DEVCUA(dev)) {
417 		if (ISSET(tp->t_state, TS_ISOPEN)) {
418 			/* Ah, but someone already is dialed in... */
419 			splx(s);
420 			return EBUSY;
421 		}
422 		sc->sc_cua = 1;		/* We go into CUA mode. */
423 	} else {
424 		if (ISSET(flag, O_NONBLOCK) && sc->sc_cua) {
425 			/* Opening TTY non-blocking... but the CUA is busy. */
426 			splx(s);
427 			return EBUSY;
428 		} else {
429 			while (sc->sc_cua) {
430 				SET(tp->t_state, TS_WOPEN);
431 				error = ttysleep(tp, &tp->t_rawq,
432 				    TTIPRI | PCATCH, ttopen);
433 				/*
434 				 * If TS_WOPEN has been reset, that means the
435 				 * cua device has been closed.
436 				 * We don't want to fail in that case,
437 				 * so just go around again.
438 				 */
439 				if (error && ISSET(tp->t_state, TS_WOPEN)) {
440 					CLR(tp->t_state, TS_WOPEN);
441 					splx(s);
442 					return error;
443 				}
444 			}
445 		}
446 	}
447 	splx(s);
448 
449 	return (*linesw[tp->t_line].l_open)(dev, tp, p);
450 }
451 
452 int
amluartclose(dev_t dev,int flag,int mode,struct proc * p)453 amluartclose(dev_t dev, int flag, int mode, struct proc *p)
454 {
455 	struct amluart_softc *sc = amluart_sc(dev);
456 	struct tty *tp = sc->sc_tty;
457 	int s;
458 
459 	if (!ISSET(tp->t_state, TS_ISOPEN))
460 		return 0;
461 
462 	(*linesw[tp->t_line].l_close)(tp, flag, p);
463 	s = spltty();
464 	if (!ISSET(tp->t_state, TS_WOPEN)) {
465 		/* Disable interrupts */
466 		HCLR4(sc, UART_CONTROL,
467 		    UART_CONTROL_TX_INT | UART_CONTROL_RX_INT);
468 	}
469 	CLR(tp->t_state, TS_BUSY | TS_FLUSH);
470 	sc->sc_cua = 0;
471 	splx(s);
472 	ttyclose(tp);
473 
474 	return 0;
475 }
476 
477 int
amluartread(dev_t dev,struct uio * uio,int flag)478 amluartread(dev_t dev, struct uio *uio, int flag)
479 {
480 	struct tty *tp = amluarttty(dev);
481 
482 	if (tp == NULL)
483 		return ENODEV;
484 
485 	return (*linesw[tp->t_line].l_read)(tp, uio, flag);
486 }
487 
488 int
amluartwrite(dev_t dev,struct uio * uio,int flag)489 amluartwrite(dev_t dev, struct uio *uio, int flag)
490 {
491 	struct tty *tp = amluarttty(dev);
492 
493 	if (tp == NULL)
494 		return ENODEV;
495 
496 	return (*linesw[tp->t_line].l_write)(tp, uio, flag);
497 }
498 
499 int
amluartioctl(dev_t dev,u_long cmd,caddr_t data,int flag,struct proc * p)500 amluartioctl(dev_t dev, u_long cmd, caddr_t data, int flag, struct proc *p)
501 {
502 	struct amluart_softc *sc = amluart_sc(dev);
503 	struct tty *tp;
504 	int error;
505 
506 	if (sc == NULL)
507 		return ENODEV;
508 
509 	tp = sc->sc_tty;
510 	if (tp == NULL)
511 		return ENXIO;
512 
513 	error = (*linesw[tp->t_line].l_ioctl)(tp, cmd, data, flag, p);
514 	if (error >= 0)
515 		return error;
516 
517 	error = ttioctl(tp, cmd, data, flag, p);
518 	if (error >= 0)
519 		return error;
520 
521 	switch(cmd) {
522 	case TIOCSBRK:
523 	case TIOCCBRK:
524 	case TIOCSDTR:
525 	case TIOCCDTR:
526 	case TIOCMSET:
527 	case TIOCMBIS:
528 	case TIOCMBIC:
529 	case TIOCMGET:
530 	case TIOCGFLAGS:
531 		break;
532 	case TIOCSFLAGS:
533 		error = suser(p);
534 		if (error != 0)
535 			return EPERM;
536 		break;
537 	default:
538 		return ENOTTY;
539 	}
540 
541 	return 0;
542 }
543 
544 int
amluartstop(struct tty * tp,int flag)545 amluartstop(struct tty *tp, int flag)
546 {
547 	return 0;
548 }
549 
550 struct tty *
amluarttty(dev_t dev)551 amluarttty(dev_t dev)
552 {
553 	struct amluart_softc *sc = amluart_sc(dev);
554 
555 	if (sc == NULL)
556 		return NULL;
557 	return sc->sc_tty;
558 }
559 
560 struct amluart_softc *
amluart_sc(dev_t dev)561 amluart_sc(dev_t dev)
562 {
563 	int unit = DEVUNIT(dev);
564 
565 	if (unit >= amluart_cd.cd_ndevs)
566 		return NULL;
567 	return (struct amluart_softc *)amluart_cd.cd_devs[unit];
568 }
569 
570 int
amluartcnattach(bus_space_tag_t iot,bus_addr_t iobase)571 amluartcnattach(bus_space_tag_t iot, bus_addr_t iobase)
572 {
573 	static struct consdev amluartcons = {
574 		NULL, NULL, amluartcngetc, amluartcnputc, amluartcnpollc, NULL,
575 		NODEV, CN_MIDPRI
576 	};
577 	int maj;
578 
579 	amluartconsiot = iot;
580 	if (bus_space_map(iot, iobase, UART_SPACE, 0, &amluartconsioh))
581 		return ENOMEM;
582 
583 	/* Look for major of com(4) to replace. */
584 	for (maj = 0; maj < nchrdev; maj++)
585 		if (cdevsw[maj].d_open == comopen)
586 			break;
587 	if (maj == nchrdev)
588 		return ENXIO;
589 
590 	cn_tab = &amluartcons;
591 	cn_tab->cn_dev = makedev(maj, 0);
592 	cdevsw[maj] = amluartdev; 	/* KLUDGE */
593 
594 	return 0;
595 }
596 
597 int
amluartcngetc(dev_t dev)598 amluartcngetc(dev_t dev)
599 {
600 	uint8_t c;
601 
602 	while (bus_space_read_4(amluartconsiot, amluartconsioh, UART_STATUS) &
603 	    UART_STATUS_RX_FIFO_EMPTY)
604 		CPU_BUSY_CYCLE();
605 	c = bus_space_read_4(amluartconsiot, amluartconsioh, UART_RFIFO);
606 	return c;
607 }
608 
609 void
amluartcnputc(dev_t dev,int c)610 amluartcnputc(dev_t dev, int c)
611 {
612 	while (bus_space_read_4(amluartconsiot, amluartconsioh, UART_STATUS) &
613 	    UART_STATUS_TX_FIFO_FULL)
614 		CPU_BUSY_CYCLE();
615 	bus_space_write_4(amluartconsiot, amluartconsioh, UART_WFIFO, c);
616 }
617 
618 void
amluartcnpollc(dev_t dev,int on)619 amluartcnpollc(dev_t dev, int on)
620 {
621 }
622