xref: /openbsd/sys/dev/fdt/amluart.c (revision 771fbea0)
1 /*	$OpenBSD: amluart.c,v 1.1 2019/08/26 09:10:22 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 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
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
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
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
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
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 		(*linesw[tp->t_line].l_rint)(*ibufp++, tp);
281 }
282 
283 int
284 amluart_param(struct tty *tp, struct termios *t)
285 {
286 	struct amluart_softc *sc = amluart_sc(tp->t_dev);
287 	int ospeed = t->c_ospeed;
288 
289 	/* Check requested parameters. */
290 	if (ospeed < 0 || (t->c_ispeed && t->c_ispeed != t->c_ospeed))
291 		return EINVAL;
292 
293 	switch (ISSET(t->c_cflag, CSIZE)) {
294 	case CS5:
295 	case CS6:
296 	case CS7:
297 		return EINVAL;
298 	case CS8:
299 		break;
300 	}
301 
302 	if (ospeed != 0) {
303 		while (ISSET(tp->t_state, TS_BUSY)) {
304 			int error;
305 
306 			sc->sc_halt++;
307 			error = ttysleep(tp, &tp->t_outq,
308 			    TTOPRI | PCATCH, "amluprm");
309 			sc->sc_halt--;
310 			if (error) {
311 				amluart_start(tp);
312 				return error;
313 			}
314 		}
315 	}
316 
317 	tp->t_ispeed = t->c_ispeed;
318 	tp->t_ospeed = t->c_ospeed;
319 	tp->t_cflag = t->c_cflag;
320 
321 	/* Just to be sure... */
322 	amluart_start(tp);
323 	return 0;
324 }
325 
326 void
327 amluart_start(struct tty *tp)
328 {
329 	struct amluart_softc *sc = amluart_sc(tp->t_dev);
330 	int stat;
331 	int s;
332 
333 	s = spltty();
334 	if (ISSET(tp->t_state, TS_BUSY))
335 		goto out;
336 	if (ISSET(tp->t_state, TS_TIMEOUT | TS_TTSTOP) || sc->sc_halt > 0)
337 		goto out;
338 	ttwakeupwr(tp);
339 	if (tp->t_outq.c_cc == 0)
340 		goto out;
341 	SET(tp->t_state, TS_BUSY);
342 
343 	stat = HREAD4(sc, UART_STATUS);
344 	while ((stat & UART_STATUS_TX_FIFO_FULL) == 0) {
345 		HWRITE4(sc, UART_WFIFO, getc(&tp->t_outq));
346 		stat = HREAD4(sc, UART_STATUS);
347 	}
348 out:
349 	splx(s);
350 }
351 
352 int
353 amluartopen(dev_t dev, int flag, int mode, struct proc *p)
354 {
355 	struct amluart_softc *sc = amluart_sc(dev);
356 	struct tty *tp;
357 	int error;
358 	int s;
359 
360 	if (sc == NULL)
361 		return ENXIO;
362 
363 	s = spltty();
364 	if (sc->sc_tty == NULL)
365 		tp = sc->sc_tty = ttymalloc(0);
366 	else
367 		tp = sc->sc_tty;
368 	splx(s);
369 
370 	tp->t_oproc = amluart_start;
371 	tp->t_param = amluart_param;
372 	tp->t_dev = dev;
373 
374 	if (!ISSET(tp->t_state, TS_ISOPEN)) {
375 		SET(tp->t_state, TS_WOPEN);
376 		ttychars(tp);
377 		tp->t_iflag = TTYDEF_IFLAG;
378 		tp->t_oflag = TTYDEF_OFLAG;
379 		tp->t_cflag = TTYDEF_CFLAG;
380 		tp->t_lflag = TTYDEF_LFLAG;
381 		tp->t_ispeed = tp->t_ospeed =
382 		    sc->sc_conspeed ? sc->sc_conspeed : B115200;
383 
384 		s = spltty();
385 
386 		amluart_param(tp, &tp->t_termios);
387 		ttsetwater(tp);
388 
389 		sc->sc_ibufp = sc->sc_ibuf = sc->sc_ibufs[0];
390 		sc->sc_ibufhigh = sc->sc_ibuf + AMLUART_IHIGHWATER;
391 		sc->sc_ibufend = sc->sc_ibuf + AMLUART_IBUFSIZE;
392 
393 		/* Enable interrupts */
394 		HSET4(sc, UART_CONTROL,
395 		    UART_CONTROL_TX_INT | UART_CONTROL_RX_INT);
396 
397 		/* No carrier detect support. */
398 		SET(tp->t_state, TS_CARR_ON);
399 	} else if (ISSET(tp->t_state, TS_XCLUDE) && p->p_ucred->cr_uid != 0)
400 		return EBUSY;
401 	else
402 		s = spltty();
403 
404 	if (DEVCUA(dev)) {
405 		if (ISSET(tp->t_state, TS_ISOPEN)) {
406 			/* Ah, but someone already is dialed in... */
407 			splx(s);
408 			return EBUSY;
409 		}
410 		sc->sc_cua = 1;		/* We go into CUA mode. */
411 	} else {
412 		if (ISSET(flag, O_NONBLOCK) && sc->sc_cua) {
413 			/* Opening TTY non-blocking... but the CUA is busy. */
414 			splx(s);
415 			return EBUSY;
416 		} else {
417 			while (sc->sc_cua) {
418 				SET(tp->t_state, TS_WOPEN);
419 				error = ttysleep(tp, &tp->t_rawq,
420 				    TTIPRI | PCATCH, ttopen);
421 				/*
422 				 * If TS_WOPEN has been reset, that means the
423 				 * cua device has been closed.
424 				 * We don't want to fail in that case,
425 				 * so just go around again.
426 				 */
427 				if (error && ISSET(tp->t_state, TS_WOPEN)) {
428 					CLR(tp->t_state, TS_WOPEN);
429 					splx(s);
430 					return error;
431 				}
432 			}
433 		}
434 	}
435 	splx(s);
436 
437 	return (*linesw[tp->t_line].l_open)(dev, tp, p);
438 }
439 
440 int
441 amluartclose(dev_t dev, int flag, int mode, struct proc *p)
442 {
443 	struct amluart_softc *sc = amluart_sc(dev);
444 	struct tty *tp = sc->sc_tty;
445 	int s;
446 
447 	if (!ISSET(tp->t_state, TS_ISOPEN))
448 		return 0;
449 
450 	(*linesw[tp->t_line].l_close)(tp, flag, p);
451 	s = spltty();
452 	if (!ISSET(tp->t_state, TS_WOPEN)) {
453 		/* Disable interrupts */
454 		HCLR4(sc, UART_CONTROL,
455 		    UART_CONTROL_TX_INT | UART_CONTROL_RX_INT);
456 	}
457 	CLR(tp->t_state, TS_BUSY | TS_FLUSH);
458 	sc->sc_cua = 0;
459 	splx(s);
460 	ttyclose(tp);
461 
462 	return 0;
463 }
464 
465 int
466 amluartread(dev_t dev, struct uio *uio, int flag)
467 {
468 	struct tty *tp = amluarttty(dev);
469 
470 	if (tp == NULL)
471 		return ENODEV;
472 
473 	return (*linesw[tp->t_line].l_read)(tp, uio, flag);
474 }
475 
476 int
477 amluartwrite(dev_t dev, struct uio *uio, int flag)
478 {
479 	struct tty *tp = amluarttty(dev);
480 
481 	if (tp == NULL)
482 		return ENODEV;
483 
484 	return (*linesw[tp->t_line].l_write)(tp, uio, flag);
485 }
486 
487 int
488 amluartioctl(dev_t dev, u_long cmd, caddr_t data, int flag, struct proc *p)
489 {
490 	struct amluart_softc *sc = amluart_sc(dev);
491 	struct tty *tp;
492 	int error;
493 
494 	if (sc == NULL)
495 		return ENODEV;
496 
497 	tp = sc->sc_tty;
498 	if (tp == NULL)
499 		return ENXIO;
500 
501 	error = (*linesw[tp->t_line].l_ioctl)(tp, cmd, data, flag, p);
502 	if (error >= 0)
503 		return error;
504 
505 	error = ttioctl(tp, cmd, data, flag, p);
506 	if (error >= 0)
507 		return error;
508 
509 	switch(cmd) {
510 	case TIOCSBRK:
511 	case TIOCCBRK:
512 	case TIOCSDTR:
513 	case TIOCCDTR:
514 	case TIOCMSET:
515 	case TIOCMBIS:
516 	case TIOCMBIC:
517 	case TIOCMGET:
518 	case TIOCGFLAGS:
519 		break;
520 	case TIOCSFLAGS:
521 		error = suser(p);
522 		if (error != 0)
523 			return EPERM;
524 		break;
525 	default:
526 		return ENOTTY;
527 	}
528 
529 	return 0;
530 }
531 
532 int
533 amluartstop(struct tty *tp, int flag)
534 {
535 	return 0;
536 }
537 
538 struct tty *
539 amluarttty(dev_t dev)
540 {
541 	struct amluart_softc *sc = amluart_sc(dev);
542 
543 	if (sc == NULL)
544 		return NULL;
545 	return sc->sc_tty;
546 }
547 
548 struct amluart_softc *
549 amluart_sc(dev_t dev)
550 {
551 	int unit = DEVUNIT(dev);
552 
553 	if (unit >= amluart_cd.cd_ndevs)
554 		return NULL;
555 	return (struct amluart_softc *)amluart_cd.cd_devs[unit];
556 }
557 
558 int
559 amluartcnattach(bus_space_tag_t iot, bus_addr_t iobase)
560 {
561 	static struct consdev amluartcons = {
562 		NULL, NULL, amluartcngetc, amluartcnputc, amluartcnpollc, NULL,
563 		NODEV, CN_MIDPRI
564 	};
565 	int maj;
566 
567 	amluartconsiot = iot;
568 	if (bus_space_map(iot, iobase, UART_SPACE, 0, &amluartconsioh))
569 		return ENOMEM;
570 
571 	/* Look for major of com(4) to replace. */
572 	for (maj = 0; maj < nchrdev; maj++)
573 		if (cdevsw[maj].d_open == comopen)
574 			break;
575 	if (maj == nchrdev)
576 		return ENXIO;
577 
578 	cn_tab = &amluartcons;
579 	cn_tab->cn_dev = makedev(maj, 0);
580 	cdevsw[maj] = amluartdev; 	/* KLUDGE */
581 
582 	return 0;
583 }
584 
585 int
586 amluartcngetc(dev_t dev)
587 {
588 	uint8_t c;
589 
590 	while (bus_space_read_4(amluartconsiot, amluartconsioh, UART_STATUS) &
591 	    UART_STATUS_RX_FIFO_EMPTY)
592 		CPU_BUSY_CYCLE();
593 	c = bus_space_read_4(amluartconsiot, amluartconsioh, UART_RFIFO);
594 	return c;
595 }
596 
597 void
598 amluartcnputc(dev_t dev, int c)
599 {
600 	while (bus_space_read_4(amluartconsiot, amluartconsioh, UART_STATUS) &
601 	    UART_STATUS_TX_FIFO_FULL)
602 		CPU_BUSY_CYCLE();
603 	bus_space_write_4(amluartconsiot, amluartconsioh, UART_WFIFO, c);
604 }
605 
606 void
607 amluartcnpollc(dev_t dev, int on)
608 {
609 }
610