1/* $OpenBSD: mvuart.c,v 1.4 2021/10/24 17:52:26 mpi Exp $ */
2/*
3 * Copyright (c) 2005 Dale Rahn <drahn@motorola.com>
4 * Copyright (c) 2018 Patrick Wildt <patrick@blueri.se>
5 *
6 * Permission to use, copy, modify, and distribute this software for any
7 * purpose with or without fee is hereby granted, provided that the above
8 * copyright notice and this permission notice appear in all copies.
9 *
10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17 */
18
19#include <sys/param.h>
20#include <sys/ioctl.h>
21#include <sys/proc.h>
22#include <sys/tty.h>
23#include <sys/systm.h>
24#include <sys/device.h>
25#include <sys/conf.h>
26#include <sys/fcntl.h>
27
28#include <machine/bus.h>
29#include <machine/fdt.h>
30
31#include <dev/cons.h>
32
33#include <dev/ofw/openfirm.h>
34#include <dev/ofw/ofw_clock.h>
35#include <dev/ofw/ofw_pinctrl.h>
36#include <dev/ofw/fdt.h>
37
38#define MVUART_RBR			0x00
39#define MVUART_TSH			0x04
40#define MVUART_CTRL			0x08
41#define  MVUART_CTRL_RX_RDY_INT			(1 << 4)
42#define  MVUART_CTRL_TX_RDY_INT			(1 << 5)
43#define MVUART_STAT			0x0c
44#define  MVUART_STAT_STD_OVR_ERR		(1 << 0)
45#define  MVUART_STAT_STD_PAR_ERR		(1 << 1)
46#define  MVUART_STAT_STD_FRM_ERR		(1 << 2)
47#define  MVUART_STAT_STD_BRK_DET		(1 << 3)
48#define  MVUART_STAT_STD_ERROR_MASK		(0xf << 0)
49#define  MVUART_STAT_STD_RX_RDY			(1 << 4)
50#define  MVUART_STAT_STD_TX_RDY			(1 << 5)
51#define  MVUART_STAT_STD_TX_EMPTY		(1 << 6)
52#define  MVUART_STAT_STD_TX_FIFO_FULL		(1 << 11)
53#define  MVUART_STAT_STD_TX_FIFO_EMPTY		(1 << 13)
54#define MVUART_BAUD_RATE_DIV		0x10
55#define  MVUART_BAUD_RATE_DIV_MASK		0x3ff
56
57#define DEVUNIT(x)	(minor(x) & 0x7f)
58#define DEVCUA(x)	(minor(x) & 0x80)
59
60#define HREAD4(sc, reg)							\
61	(bus_space_read_4((sc)->sc_iot, (sc)->sc_ioh, (reg)))
62#define HWRITE4(sc, reg, val)						\
63	bus_space_write_4((sc)->sc_iot, (sc)->sc_ioh, (reg), (val))
64#define HSET4(sc, reg, bits)						\
65	HWRITE4((sc), (reg), HREAD4((sc), (reg)) | (bits))
66#define HCLR4(sc, reg, bits)						\
67	HWRITE4((sc), (reg), HREAD4((sc), (reg)) & ~(bits))
68
69struct mvuart_softc {
70	struct device		 sc_dev;
71	bus_space_tag_t		 sc_iot;
72	bus_space_handle_t	 sc_ioh;
73	int			 sc_node;
74	struct soft_intrhand	*sc_si;
75	void			*sc_ih;
76	struct tty		*sc_tty;
77	int			 sc_floods;
78	int			 sc_errors;
79	int			 sc_halt;
80	uint8_t			 sc_hwflags;
81#define COM_HW_NOIEN			0x01
82#define COM_HW_FIFO			0x02
83#define COM_HW_SIR			0x20
84#define COM_HW_CONSOLE			0x40
85	uint8_t			 sc_cua;
86	uint16_t 		*sc_ibuf, *sc_ibufp, *sc_ibufhigh, *sc_ibufend;
87#define MVUART_IBUFSIZE			 128
88#define MVUART_IHIGHWATER		 100
89	uint16_t		 sc_ibufs[2][MVUART_IBUFSIZE];
90};
91
92int	 mvuart_match(struct device *, void *, void *);
93void	 mvuart_attach(struct device *, struct device *, void *);
94
95void	 mvuartcnprobe(struct consdev *cp);
96void	 mvuartcninit(struct consdev *cp);
97int	 mvuartcnattach(bus_space_tag_t, bus_addr_t, int, tcflag_t);
98int	 mvuartcngetc(dev_t dev);
99void	 mvuartcnputc(dev_t dev, int c);
100void	 mvuartcnpollc(dev_t dev, int on);
101int	 mvuart_param(struct tty *, struct termios *);
102void	 mvuart_start(struct tty *);
103void	 mvuart_softint(void *arg);
104
105struct mvuart_softc *mvuart_sc(dev_t dev);
106
107int	 mvuart_intr(void *);
108int	 mvuart_intr_rx(struct mvuart_softc *);
109int	 mvuart_intr_tx(struct mvuart_softc *);
110
111/* XXX - we imitate 'com' serial ports and take over their entry points */
112/* XXX: These belong elsewhere */
113cdev_decl(com);
114cdev_decl(mvuart);
115
116struct cfdriver mvuart_cd = {
117	NULL, "mvuart", DV_TTY
118};
119
120const struct cfattach mvuart_ca = {
121	sizeof(struct mvuart_softc), mvuart_match, mvuart_attach
122};
123
124bus_space_tag_t		mvuartconsiot;
125bus_space_handle_t	mvuartconsioh;
126bus_addr_t		mvuartconsaddr;
127
128struct cdevsw mvuartdev =
129	cdev_tty_init(3/*XXX NMVUART */, mvuart);		/* 12: serial port */
130
131void
132mvuart_init_cons(void)
133{
134	struct fdt_reg reg;
135	void *node;
136
137	if ((node = fdt_find_cons("marvell,armada-3700-uart")) == NULL)
138		return;
139
140	if (fdt_get_reg(node, 0, &reg))
141		return;
142
143	mvuartcnattach(fdt_cons_bs_tag, reg.addr, B115200, TTYDEF_CFLAG);
144}
145
146int
147mvuart_match(struct device *parent, void *match, void *aux)
148{
149	struct fdt_attach_args *faa = aux;
150
151	return OF_is_compatible(faa->fa_node, "marvell,armada-3700-uart");
152}
153
154void
155mvuart_attach(struct device *parent, struct device *self, void *aux)
156{
157	struct mvuart_softc *sc = (struct mvuart_softc *)self;
158	struct fdt_attach_args *faa = aux;
159	int maj;
160
161	if (faa->fa_nreg < 1)
162		return;
163
164	pinctrl_byname(faa->fa_node, "default");
165
166	sc->sc_node = faa->fa_node;
167	sc->sc_iot = faa->fa_iot;
168	if (bus_space_map(sc->sc_iot, faa->fa_reg[0].addr,
169	    faa->fa_reg[0].size, 0, &sc->sc_ioh)) {
170		panic("%s: bus_space_map failed", sc->sc_dev.dv_xname);
171		return;
172	}
173
174	if (faa->fa_reg[0].addr == mvuartconsaddr) {
175		/* Locate the major number. */
176		for (maj = 0; maj < nchrdev; maj++)
177			if (cdevsw[maj].d_open == mvuartopen)
178				break;
179		cn_tab->cn_dev = makedev(maj, sc->sc_dev.dv_unit);
180
181		printf(": console");
182	}
183
184	printf("\n");
185
186	sc->sc_ih = fdt_intr_establish_idx(faa->fa_node, 0, IPL_TTY,
187	    mvuart_intr, sc, sc->sc_dev.dv_xname);
188	if (sc->sc_ih == NULL)
189		panic("%s: can't establish hard interrupt",
190		    sc->sc_dev.dv_xname);
191
192	sc->sc_si = softintr_establish(IPL_TTY, mvuart_softint, sc);
193	if (sc->sc_si == NULL)
194		panic("%s: can't establish soft interrupt",
195		    sc->sc_dev.dv_xname);
196}
197
198int
199mvuart_intr(void *arg)
200{
201	struct mvuart_softc *sc = arg;
202	uint32_t stat;
203	int ret = 0;
204
205	if (sc->sc_tty == NULL)
206		return 0;
207
208	stat = HREAD4(sc, MVUART_STAT);
209
210	if ((stat & MVUART_STAT_STD_RX_RDY) != 0)
211		ret |= mvuart_intr_rx(sc);
212
213	if ((stat & MVUART_STAT_STD_TX_RDY) != 0)
214		ret |= mvuart_intr_tx(sc);
215
216	return ret;
217}
218
219int
220mvuart_intr_rx(struct mvuart_softc *sc)
221{
222	uint32_t stat;
223	uint16_t *p, c;
224
225	p = sc->sc_ibufp;
226
227	stat = HREAD4(sc, MVUART_STAT);
228	while ((stat & MVUART_STAT_STD_RX_RDY) != 0) {
229		c = HREAD4(sc, MVUART_RBR);
230		c |= (stat & MVUART_STAT_STD_ERROR_MASK) << 8;
231		if (p >= sc->sc_ibufend) {
232			sc->sc_floods++;
233		} else {
234			*p++ = c;
235		}
236		stat = HREAD4(sc, MVUART_STAT);
237	}
238	sc->sc_ibufp = p;
239
240	softintr_schedule(sc->sc_si);
241	return 1;
242}
243
244int
245mvuart_intr_tx(struct mvuart_softc *sc)
246{
247	struct tty *tp = sc->sc_tty;
248
249	HCLR4(sc, MVUART_CTRL, MVUART_CTRL_TX_RDY_INT);
250	if (ISSET(tp->t_state, TS_BUSY)) {
251		CLR(tp->t_state, TS_BUSY | TS_FLUSH);
252		if (sc->sc_halt > 0)
253			wakeup(&tp->t_outq);
254		(*linesw[tp->t_line].l_start)(tp);
255	}
256
257	return 1;
258}
259
260int
261mvuart_param(struct tty *tp, struct termios *t)
262{
263	struct mvuart_softc *sc = mvuart_sc(tp->t_dev);
264	int error, ospeed = t->c_ospeed;
265	tcflag_t oldcflag;
266
267	if (t->c_ospeed < 0 || (t->c_ispeed && t->c_ispeed != t->c_ospeed))
268		return EINVAL;
269
270	switch (ISSET(t->c_cflag, CSIZE)) {
271	case CS5:
272	case CS6:
273	case CS7:
274		return EINVAL;
275	case CS8:
276		break;
277	}
278
279	if (ospeed != 0) {
280		while (ISSET(tp->t_state, TS_BUSY)) {
281			++sc->sc_halt;
282			error = ttysleep(tp, &tp->t_outq,
283			    TTOPRI | PCATCH, "mvuartprm");
284			--sc->sc_halt;
285			if (error) {
286				mvuart_start(tp);
287				return (error);
288			}
289		}
290	}
291
292	/* and copy to tty */
293	tp->t_ispeed = t->c_ispeed;
294	tp->t_ospeed = t->c_ospeed;
295	oldcflag = tp->t_cflag;
296	tp->t_cflag = t->c_cflag;
297
298	mvuart_start(tp);
299	return 0;
300}
301
302void
303mvuart_start(struct tty *tp)
304{
305	struct mvuart_softc *sc = mvuart_sc(tp->t_dev);
306	uint8_t buf;
307	int i, n, s;
308
309	s = spltty();
310	if (ISSET(tp->t_state, TS_BUSY)) {
311		splx(s);
312		return;
313	}
314	if (ISSET(tp->t_state, TS_TIMEOUT | TS_TTSTOP))
315		goto out;
316	if (tp->t_outq.c_cc <= tp->t_lowat) {
317		if (ISSET(tp->t_state, TS_ASLEEP)) {
318			CLR(tp->t_state, TS_ASLEEP);
319			wakeup(&tp->t_outq);
320		}
321		if (tp->t_outq.c_cc == 0)
322			goto out;
323		selwakeup(&tp->t_wsel);
324	}
325	SET(tp->t_state, TS_BUSY);
326
327	for (i = 0; i < 32; i++) {
328		n = q_to_b(&tp->t_outq, &buf, 1);
329		if (n < 1)
330			break;
331		HWRITE4(sc, MVUART_TSH, buf);
332		if (HREAD4(sc, MVUART_STAT) & MVUART_STAT_STD_TX_FIFO_FULL)
333			break;
334	}
335	HSET4(sc, MVUART_CTRL, MVUART_CTRL_TX_RDY_INT);
336
337out:
338	splx(s);
339}
340
341void
342mvuart_softint(void *arg)
343{
344	struct mvuart_softc *sc = arg;
345	struct tty *tp;
346	uint16_t *ibufp;
347	uint16_t *ibufend;
348	int c, err, s;
349
350	if (sc == NULL || sc->sc_ibufp == sc->sc_ibuf)
351		return;
352
353	tp = sc->sc_tty;
354	s = spltty();
355
356	ibufp = sc->sc_ibuf;
357	ibufend = sc->sc_ibufp;
358
359	if (ibufp == ibufend || tp == NULL || !ISSET(tp->t_state, TS_ISOPEN)) {
360		splx(s);
361		return;
362	}
363
364	sc->sc_ibufp = sc->sc_ibuf = (ibufp == sc->sc_ibufs[0]) ?
365	    sc->sc_ibufs[1] : sc->sc_ibufs[0];
366	sc->sc_ibufhigh = sc->sc_ibuf + MVUART_IHIGHWATER;
367	sc->sc_ibufend = sc->sc_ibuf + MVUART_IBUFSIZE;
368
369	splx(s);
370
371	while (ibufp < ibufend) {
372		err = 0;
373		c = *ibufp++;
374		if (ISSET(c, (MVUART_STAT_STD_PAR_ERR << 8)))
375			err |= TTY_PE;
376		if (ISSET(c, (MVUART_STAT_STD_FRM_ERR << 8)))
377			err |= TTY_FE;
378		c = (c & 0xff) | err;
379		(*linesw[tp->t_line].l_rint)(c, tp);
380	}
381}
382
383int
384mvuartopen(dev_t dev, int flag, int mode, struct proc *p)
385{
386	struct mvuart_softc *sc;
387	struct tty *tp;
388	int s, error = 0;
389
390	sc = mvuart_sc(dev);
391	if (sc == NULL)
392		return ENXIO;
393
394	s = spltty();
395	if (sc->sc_tty == NULL)
396		tp = sc->sc_tty = ttymalloc(0);
397	else
398		tp = sc->sc_tty;
399
400	splx(s);
401
402	tp->t_oproc = mvuart_start;
403	tp->t_param = mvuart_param;
404	tp->t_dev = dev;
405
406	if (!ISSET(tp->t_state, TS_ISOPEN)) {
407		SET(tp->t_state, TS_WOPEN);
408		ttychars(tp);
409		tp->t_iflag = TTYDEF_IFLAG;
410		tp->t_oflag = TTYDEF_OFLAG;
411
412		tp->t_cflag = TTYDEF_CFLAG;
413		tp->t_lflag = TTYDEF_LFLAG;
414		tp->t_ispeed = tp->t_ospeed = B115200;
415
416		s = spltty();
417
418		mvuart_param(tp, &tp->t_termios);
419		ttsetwater(tp);
420		sc->sc_ibufp = sc->sc_ibuf = sc->sc_ibufs[0];
421		sc->sc_ibufhigh = sc->sc_ibuf + MVUART_IHIGHWATER;
422		sc->sc_ibufend = sc->sc_ibuf + MVUART_IBUFSIZE;
423
424		/* Enable interrupts */
425		HSET4(sc, MVUART_CTRL, MVUART_CTRL_RX_RDY_INT);
426
427		SET(tp->t_state, TS_CARR_ON); /* XXX */
428	} else if (ISSET(tp->t_state, TS_XCLUDE) && suser(p) != 0)
429		return EBUSY;
430	else
431		s = spltty();
432
433	if (DEVCUA(dev)) {
434		if (ISSET(tp->t_state, TS_ISOPEN)) {
435			splx(s);
436			return EBUSY;
437		}
438		sc->sc_cua = 1;
439	} else {
440		/* tty (not cua) device; wait for carrier if necessary */
441		if (ISSET(flag, O_NONBLOCK)) {
442			if (sc->sc_cua) {
443				/* Opening TTY non-blocking... but the CUA is busy */
444				splx(s);
445				return EBUSY;
446			}
447		} else {
448			while (sc->sc_cua ||
449			    (!ISSET(tp->t_cflag, CLOCAL) &&
450				!ISSET(tp->t_state, TS_CARR_ON))) {
451				SET(tp->t_state, TS_WOPEN);
452				error = ttysleep(tp, &tp->t_rawq,
453				    TTIPRI | PCATCH, ttopen);
454				/*
455				 * If TS_WOPEN has been reset, that means the
456				 * cua device has been closed.  We don't want
457				 * to fail in that case,
458				 * so just go around again.
459				 */
460				if (error && ISSET(tp->t_state, TS_WOPEN)) {
461					CLR(tp->t_state, TS_WOPEN);
462					splx(s);
463					return error;
464				}
465			}
466		}
467	}
468	splx(s);
469	return (*linesw[tp->t_line].l_open)(dev,tp,p);
470}
471
472int
473mvuartclose(dev_t dev, int flag, int mode, struct proc *p)
474{
475	struct mvuart_softc *sc = mvuart_sc(dev);
476	struct tty *tp = sc->sc_tty;
477	int s;
478
479	if (!ISSET(tp->t_state, TS_ISOPEN))
480		return 0;
481
482	(*linesw[tp->t_line].l_close)(tp, flag, p);
483	s = spltty();
484	if (!ISSET(tp->t_state, TS_WOPEN)) {
485		/* Disable interrupts */
486		HCLR4(sc, MVUART_CTRL, MVUART_CTRL_RX_RDY_INT |
487		    MVUART_CTRL_TX_RDY_INT);
488	}
489	CLR(tp->t_state, TS_BUSY | TS_FLUSH);
490	sc->sc_cua = 0;
491	splx(s);
492	ttyclose(tp);
493
494	return 0;
495}
496
497int
498mvuartread(dev_t dev, struct uio *uio, int flag)
499{
500	struct tty *tty;
501
502	tty = mvuarttty(dev);
503	if (tty == NULL)
504		return ENODEV;
505
506	return((*linesw[tty->t_line].l_read)(tty, uio, flag));
507}
508
509int
510mvuartwrite(dev_t dev, struct uio *uio, int flag)
511{
512	struct tty *tty;
513
514	tty = mvuarttty(dev);
515	if (tty == NULL)
516		return ENODEV;
517
518	return((*linesw[tty->t_line].l_write)(tty, uio, flag));
519}
520
521int
522mvuartioctl(dev_t dev, u_long cmd, caddr_t data, int flag, struct proc *p)
523{
524	struct mvuart_softc *sc;
525	struct tty *tp;
526	int error;
527
528	sc = mvuart_sc(dev);
529	if (sc == NULL)
530		return (ENODEV);
531
532	tp = sc->sc_tty;
533	if (tp == NULL)
534		return (ENXIO);
535
536	error = (*linesw[tp->t_line].l_ioctl)(tp, cmd, data, flag, p);
537	if (error >= 0)
538		return (error);
539
540	error = ttioctl(tp, cmd, data, flag, p);
541	if (error >= 0)
542		return (error);
543
544	switch(cmd) {
545	case TIOCSBRK:
546	case TIOCCBRK:
547	case TIOCSDTR:
548	case TIOCCDTR:
549	case TIOCMSET:
550	case TIOCMBIS:
551	case TIOCMBIC:
552	case TIOCMGET:
553	case TIOCGFLAGS:
554		break;
555	case TIOCSFLAGS:
556		error = suser(p);
557		if (error != 0)
558			return(EPERM);
559		break;
560	default:
561		return (ENOTTY);
562	}
563
564	return 0;
565}
566
567int
568mvuartstop(struct tty *tp, int flag)
569{
570	return 0;
571}
572
573struct tty *
574mvuarttty(dev_t dev)
575{
576	struct mvuart_softc *sc;
577	sc = mvuart_sc(dev);
578	if (sc == NULL)
579		return NULL;
580	return sc->sc_tty;
581}
582
583struct mvuart_softc *
584mvuart_sc(dev_t dev)
585{
586	int unit;
587	unit = DEVUNIT(dev);
588	if (unit >= mvuart_cd.cd_ndevs)
589		return NULL;
590	return (struct mvuart_softc *)mvuart_cd.cd_devs[unit];
591}
592
593/* serial console */
594void
595mvuartcnprobe(struct consdev *cp)
596{
597}
598
599void
600mvuartcninit(struct consdev *cp)
601{
602}
603
604int
605mvuartcnattach(bus_space_tag_t iot, bus_addr_t iobase, int rate, tcflag_t cflag)
606{
607	static struct consdev mvuartcons = {
608		NULL, NULL, mvuartcngetc, mvuartcnputc, mvuartcnpollc, NULL,
609		NODEV, CN_MIDPRI
610	};
611	int maj;
612
613	if (bus_space_map(iot, iobase, 0x200, 0, &mvuartconsioh))
614		return ENOMEM;
615
616	/* Look for major of com(4) to replace. */
617	for (maj = 0; maj < nchrdev; maj++)
618		if (cdevsw[maj].d_open == comopen)
619			break;
620	if (maj == nchrdev)
621		return ENXIO;
622
623	cn_tab = &mvuartcons;
624	cn_tab->cn_dev = makedev(maj, 0);
625	cdevsw[maj] = mvuartdev; 	/* KLUDGE */
626
627	mvuartconsiot = iot;
628	mvuartconsaddr = iobase;
629
630	return 0;
631}
632
633int
634mvuartcngetc(dev_t dev)
635{
636	int c;
637	int s;
638	s = splhigh();
639	while ((bus_space_read_4(mvuartconsiot, mvuartconsioh, MVUART_STAT) &
640	    MVUART_STAT_STD_RX_RDY) == 0)
641		;
642	c = bus_space_read_4(mvuartconsiot, mvuartconsioh, MVUART_RBR);
643	splx(s);
644	return c;
645}
646
647void
648mvuartcnputc(dev_t dev, int c)
649{
650	int s;
651	s = splhigh();
652	while ((bus_space_read_4(mvuartconsiot, mvuartconsioh, MVUART_STAT) &
653	    MVUART_STAT_STD_TX_FIFO_FULL) != 0)
654		;
655	bus_space_write_4(mvuartconsiot, mvuartconsioh, MVUART_TSH, (uint8_t)c);
656	while ((bus_space_read_4(mvuartconsiot, mvuartconsioh, MVUART_STAT) &
657	    MVUART_STAT_STD_TX_FIFO_EMPTY) != 0)
658		;
659	splx(s);
660}
661
662void
663mvuartcnpollc(dev_t dev, int on)
664{
665}
666