xref: /openbsd-src/sys/dev/fdt/exuart.c (revision 4e1ee0786f11cc571bd0be17d38e46f635c719fc)
1 /* $OpenBSD: exuart.c,v 1.9 2021/09/01 09:29:31 jan Exp $ */
2 /*
3  * Copyright (c) 2005 Dale Rahn <drahn@motorola.com>
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/ioctl.h>
20 #include <sys/proc.h>
21 #include <sys/tty.h>
22 #include <sys/uio.h>
23 #include <sys/systm.h>
24 #include <sys/time.h>
25 #include <sys/device.h>
26 #include <sys/syslog.h>
27 #include <sys/conf.h>
28 #include <sys/fcntl.h>
29 #include <sys/select.h>
30 #include <sys/kernel.h>
31 
32 #include <machine/bus.h>
33 #include <machine/fdt.h>
34 
35 #include <dev/cons.h>
36 
37 #ifdef DDB
38 #include <ddb/db_var.h>
39 #endif
40 
41 #include <dev/fdt/exuartreg.h>
42 
43 #include <dev/ofw/openfirm.h>
44 #include <dev/ofw/fdt.h>
45 
46 #define DEVUNIT(x)      (minor(x) & 0x7f)
47 #define DEVCUA(x)       (minor(x) & 0x80)
48 
49 struct exuart_softc {
50 	struct device	sc_dev;
51 	bus_space_tag_t sc_iot;
52 	bus_space_handle_t sc_ioh;
53 	struct soft_intrhand *sc_si;
54 	void *sc_irq;
55 	struct tty	*sc_tty;
56 	struct timeout	sc_diag_tmo;
57 	struct timeout	sc_dtr_tmo;
58 
59 	uint32_t	sc_rx_fifo_cnt_mask;
60 	uint32_t	sc_rx_fifo_full;
61 	uint32_t	sc_tx_fifo_full;
62 	int		sc_type;
63 #define EXUART_TYPE_EXYNOS	0
64 #define EXUART_TYPE_S5L		1
65 
66 	int		sc_fifo;
67 	int		sc_overflows;
68 	int		sc_floods;
69 	int		sc_errors;
70 	int		sc_halt;
71 	u_int32_t	sc_ulcon;
72 	u_int32_t	sc_ucon;
73 	u_int32_t	sc_ufcon;
74 	u_int32_t	sc_umcon;
75 	u_int32_t	sc_uintm;
76 	u_int8_t	sc_hwflags;
77 #define COM_HW_NOIEN    0x01
78 #define COM_HW_FIFO     0x02
79 #define COM_HW_SIR      0x20
80 #define COM_HW_CONSOLE  0x40
81 	u_int8_t	sc_swflags;
82 #define COM_SW_SOFTCAR  0x01
83 #define COM_SW_CLOCAL   0x02
84 #define COM_SW_CRTSCTS  0x04
85 #define COM_SW_MDMBUF   0x08
86 #define COM_SW_PPS      0x10
87 
88 	u_int8_t	sc_initialize;
89 	u_int8_t	sc_cua;
90 	u_int16_t 	*sc_ibuf, *sc_ibufp, *sc_ibufhigh, *sc_ibufend;
91 #define EXUART_IBUFSIZE 128
92 #define EXUART_IHIGHWATER 100
93 	u_int16_t		sc_ibufs[2][EXUART_IBUFSIZE];
94 };
95 
96 
97 int	 exuart_match(struct device *, void *, void *);
98 void	 exuart_attach(struct device *, struct device *, void *);
99 
100 void exuartcnprobe(struct consdev *cp);
101 void exuartcninit(struct consdev *cp);
102 int exuartcnattach(bus_space_tag_t iot, bus_addr_t iobase, int rate,
103     tcflag_t cflag);
104 int exuartcngetc(dev_t dev);
105 void exuartcnputc(dev_t dev, int c);
106 void exuartcnpollc(dev_t dev, int on);
107 int  exuart_param(struct tty *tp, struct termios *t);
108 void exuart_start(struct tty *);
109 void exuart_diag(void *arg);
110 void exuart_raisedtr(void *arg);
111 void exuart_softint(void *arg);
112 struct exuart_softc *exuart_sc(dev_t dev);
113 
114 int exuart_intr(void *);
115 int exuart_s5l_intr(void *);
116 
117 /* XXX - we imitate 'com' serial ports and take over their entry points */
118 /* XXX: These belong elsewhere */
119 cdev_decl(com);
120 cdev_decl(exuart);
121 
122 struct cfdriver exuart_cd = {
123 	NULL, "exuart", DV_TTY
124 };
125 
126 struct cfattach exuart_ca = {
127 	sizeof(struct exuart_softc), exuart_match, exuart_attach
128 };
129 
130 bus_space_tag_t	exuartconsiot;
131 bus_space_handle_t exuartconsioh;
132 bus_addr_t	exuartconsaddr;
133 tcflag_t	exuartconscflag = TTYDEF_CFLAG;
134 int		exuartdefaultrate = B115200;
135 
136 uint32_t	exuart_rx_fifo_cnt_mask;
137 uint32_t	exuart_rx_fifo_full;
138 uint32_t	exuart_tx_fifo_full;
139 
140 struct cdevsw exuartdev =
141 	cdev_tty_init(3/*XXX NEXUART */ ,exuart);		/* 12: serial port */
142 
143 void
144 exuart_init_cons(void)
145 {
146 	struct fdt_reg reg;
147 	void *node, *root;
148 
149 	if ((node = fdt_find_cons("apple,s5l-uart")) == NULL &&
150 	    (node = fdt_find_cons("samsung,exynos4210-uart")) == NULL)
151 		return;
152 
153 	/* dtb uses serial2, qemu uses serial0 */
154 	root = fdt_find_node("/");
155 	if (root == NULL)
156 		panic("%s: could not get fdt root node", __func__);
157 	if (fdt_is_compatible(root, "samsung,universal_c210")) {
158 		if ((node = fdt_find_node("/serial@13800000")) == NULL) {
159 			return;
160 		}
161 		stdout_node = OF_finddevice("/serial@13800000");
162 	}
163 
164 	if (fdt_get_reg(node, 0, &reg))
165 		return;
166 
167 	if (fdt_is_compatible(node, "apple,s5l-uart")) {
168 		exuart_rx_fifo_cnt_mask = EXUART_S5L_UFSTAT_RX_FIFO_CNT_MASK;
169 		exuart_rx_fifo_full = EXUART_S5L_UFSTAT_RX_FIFO_FULL;
170 		exuart_tx_fifo_full = EXUART_S5L_UFSTAT_TX_FIFO_FULL;
171 	} else {
172 		exuart_rx_fifo_cnt_mask = EXUART_UFSTAT_RX_FIFO_CNT_MASK;
173 		exuart_rx_fifo_full = EXUART_UFSTAT_RX_FIFO_FULL;
174 		exuart_tx_fifo_full = EXUART_UFSTAT_TX_FIFO_FULL;
175 	}
176 
177 	exuartcnattach(fdt_cons_bs_tag, reg.addr, B115200, TTYDEF_CFLAG);
178 }
179 
180 int
181 exuart_match(struct device *parent, void *self, void *aux)
182 {
183 	struct fdt_attach_args *faa = aux;
184 
185 	return (OF_is_compatible(faa->fa_node, "apple,s5l-uart") ||
186 	    OF_is_compatible(faa->fa_node, "samsung,exynos4210-uart"));
187 }
188 
189 void
190 exuart_attach(struct device *parent, struct device *self, void *aux)
191 {
192 	struct exuart_softc *sc = (struct exuart_softc *) self;
193 	struct fdt_attach_args *faa = aux;
194 	int maj;
195 
196 	if (faa->fa_nreg < 1)
197 		return;
198 
199 	sc->sc_iot = faa->fa_iot;
200 	if (bus_space_map(sc->sc_iot, faa->fa_reg[0].addr, faa->fa_reg[0].size,
201 	    0, &sc->sc_ioh))
202 		panic("%s: bus_space_map failed!", __func__);
203 
204 	if (stdout_node == faa->fa_node) {
205 		/* Locate the major number. */
206 		for (maj = 0; maj < nchrdev; maj++)
207 			if (cdevsw[maj].d_open == exuartopen)
208 				break;
209 		cn_tab->cn_dev = makedev(maj, sc->sc_dev.dv_unit);
210 
211 		printf(": console");
212 	}
213 
214 	if (OF_is_compatible(faa->fa_node, "apple,s5l-uart")) {
215 		sc->sc_type = EXUART_TYPE_S5L;
216 		sc->sc_rx_fifo_cnt_mask = EXUART_S5L_UFSTAT_RX_FIFO_CNT_MASK;
217 		sc->sc_rx_fifo_full = EXUART_S5L_UFSTAT_RX_FIFO_FULL;
218 		sc->sc_tx_fifo_full = EXUART_S5L_UFSTAT_TX_FIFO_FULL;
219 
220 		/* Mask and clear interrupts. */
221 		sc->sc_ucon = bus_space_read_4(sc->sc_iot, sc->sc_ioh,
222 		    EXUART_UCON);
223 		CLR(sc->sc_ucon, EXUART_S5L_UCON_RX_TIMEOUT);
224 		CLR(sc->sc_ucon, EXUART_S5L_UCON_RXTHRESH);
225 		CLR(sc->sc_ucon, EXUART_S5L_UCON_TXTHRESH);
226 		bus_space_write_4(sc->sc_iot, sc->sc_ioh, EXUART_UCON,
227 		    sc->sc_ucon);
228 		bus_space_write_4(sc->sc_iot, sc->sc_ioh, EXUART_UTRSTAT,
229 		    EXUART_S5L_UTRSTAT_RX_TIMEOUT |
230 		    EXUART_S5L_UTRSTAT_RXTHRESH |
231 		    EXUART_S5L_UTRSTAT_TXTHRESH);
232 
233 		sc->sc_ucon = bus_space_read_4(sc->sc_iot, sc->sc_ioh,
234 		    EXUART_UCON);
235 		SET(sc->sc_ucon, EXUART_UCON_RX_TIMEOUT);
236 		bus_space_write_4(sc->sc_iot, sc->sc_ioh, EXUART_UCON,
237 		    sc->sc_ucon);
238 
239 		sc->sc_irq = fdt_intr_establish(faa->fa_node, IPL_TTY,
240 		    exuart_s5l_intr, sc, sc->sc_dev.dv_xname);
241 	} else {
242 		sc->sc_type = EXUART_TYPE_EXYNOS;
243 		sc->sc_rx_fifo_cnt_mask = EXUART_UFSTAT_RX_FIFO_CNT_MASK;
244 		sc->sc_rx_fifo_full = EXUART_UFSTAT_RX_FIFO_FULL;
245 		sc->sc_tx_fifo_full = EXUART_UFSTAT_TX_FIFO_FULL;
246 
247 		/* Mask and clear interrupts. */
248 		bus_space_write_4(sc->sc_iot, sc->sc_ioh, EXUART_UINTM,
249 		    EXUART_UINTM_RXD | EXUART_UINTM_ERROR |
250 		    EXUART_UINTM_TXD | EXUART_UINTM_MODEM);
251 		bus_space_write_4(sc->sc_iot, sc->sc_ioh, EXUART_UINTP,
252 		    EXUART_UINTP_RXD | EXUART_UINTP_ERROR |
253 		    EXUART_UINTP_TXD | EXUART_UINTP_MODEM);
254 
255 		sc->sc_ucon = bus_space_read_4(sc->sc_iot, sc->sc_ioh,
256 		    EXUART_UCON);
257 		CLR(sc->sc_ucon, EXUART_UCON_RX_TIMEOUT_EMPTY_FIFO);
258 		SET(sc->sc_ucon, EXUART_UCON_RX_INT_TYPE_LEVEL);
259 		SET(sc->sc_ucon, EXUART_UCON_RX_TIMEOUT);
260 		bus_space_write_4(sc->sc_iot, sc->sc_ioh, EXUART_UCON,
261 		    sc->sc_ucon);
262 
263 		sc->sc_irq = fdt_intr_establish(faa->fa_node, IPL_TTY,
264 		    exuart_intr, sc, sc->sc_dev.dv_xname);
265 	}
266 
267 	timeout_set(&sc->sc_diag_tmo, exuart_diag, sc);
268 	timeout_set(&sc->sc_dtr_tmo, exuart_raisedtr, sc);
269 	sc->sc_si = softintr_establish(IPL_TTY, exuart_softint, sc);
270 
271 	if(sc->sc_si == NULL)
272 		panic("%s: can't establish soft interrupt.",
273 		    sc->sc_dev.dv_xname);
274 
275 	printf("\n");
276 }
277 
278 void
279 exuart_rx_intr(struct exuart_softc *sc)
280 {
281 	bus_space_tag_t iot = sc->sc_iot;
282 	bus_space_handle_t ioh = sc->sc_ioh;
283 	u_int16_t *p;
284 	u_int16_t c;
285 
286 	p = sc->sc_ibufp;
287 
288 	while (bus_space_read_4(iot, ioh, EXUART_UFSTAT) &
289 	    (sc->sc_rx_fifo_cnt_mask | sc->sc_rx_fifo_full)) {
290 		c = bus_space_read_4(iot, ioh, EXUART_URXH);
291 		if (p >= sc->sc_ibufend) {
292 			sc->sc_floods++;
293 			if (sc->sc_errors++ == 0)
294 				timeout_add_sec(&sc->sc_diag_tmo, 60);
295 		} else {
296 			*p++ = c;
297 #if 0
298 			if (p == sc->sc_ibufhigh &&
299 			    ISSET(tp->t_cflag, CRTSCTS)) {
300 				/* XXX */
301 			}
302 #endif
303 		}
304 	}
305 
306 	sc->sc_ibufp = p;
307 
308 	softintr_schedule(sc->sc_si);
309 }
310 
311 void
312 exuart_tx_intr(struct exuart_softc *sc)
313 {
314 	struct tty *tp = sc->sc_tty;
315 
316 	if (ISSET(tp->t_state, TS_BUSY)) {
317 		CLR(tp->t_state, TS_BUSY | TS_FLUSH);
318 		if (sc->sc_halt > 0)
319 			wakeup(&tp->t_outq);
320 		(*linesw[tp->t_line].l_start)(tp);
321 	}
322 }
323 
324 int
325 exuart_intr(void *arg)
326 {
327 	struct exuart_softc *sc = arg;
328 	bus_space_tag_t iot = sc->sc_iot;
329 	bus_space_handle_t ioh = sc->sc_ioh;
330 	u_int32_t uintp;
331 
332 	uintp = bus_space_read_4(iot, ioh, EXUART_UINTP);
333 	if (uintp == 0)
334 		return (0);
335 
336 	if (sc->sc_tty == NULL)
337 		return (0);
338 
339 	if (ISSET(uintp, EXUART_UINTP_RXD)) {
340 		exuart_rx_intr(sc);
341 		bus_space_write_4(iot, ioh, EXUART_UINTP, EXUART_UINTP_RXD);
342 	}
343 
344 	if (ISSET(uintp, EXUART_UINTP_TXD)) {
345 		exuart_tx_intr(sc);
346 		bus_space_write_4(iot, ioh, EXUART_UINTP, EXUART_UINTP_TXD);
347 	}
348 
349 #if 0
350 	if(!ISSET(bus_space_read_2(iot, ioh, EXUART_USR2), EXUART_SR2_RDR))
351 		return 0;
352 
353 	p = sc->sc_ibufp;
354 
355 	while(ISSET(bus_space_read_2(iot, ioh, EXUART_USR2), EXUART_SR2_RDR)) {
356 		c = bus_space_read_4(iot, ioh, EXUART_URXH);
357 		if (p >= sc->sc_ibufend) {
358 			sc->sc_floods++;
359 			if (sc->sc_errors++ == 0)
360 				timeout_add_sec(&sc->sc_diag_tmo, 60);
361 		} else {
362 			*p++ = c;
363 			if (p == sc->sc_ibufhigh && ISSET(tp->t_cflag, CRTSCTS))
364 				/* XXX */
365 				//CLR(sc->sc_ucr3, EXUART_CR3_DSR);
366 				//bus_space_write_2(iot, ioh, EXUART_UCR3,
367 				//    sc->sc_ucr3);
368 
369 		}
370 		/* XXX - msr stuff ? */
371 	}
372 	sc->sc_ibufp = p;
373 
374 	softintr_schedule(sc->sc_si);
375 #endif
376 
377 	return 1;
378 }
379 
380 int
381 exuart_s5l_intr(void *arg)
382 {
383 	struct exuart_softc *sc = arg;
384 	bus_space_tag_t iot = sc->sc_iot;
385 	bus_space_handle_t ioh = sc->sc_ioh;
386 	u_int32_t utrstat;
387 
388 	utrstat = bus_space_read_4(iot, ioh, EXUART_UTRSTAT);
389 
390 	if (sc->sc_tty == NULL)
391 		return (0);
392 
393 	if (utrstat & (EXUART_S5L_UTRSTAT_RXTHRESH |
394 	    EXUART_S5L_UTRSTAT_RX_TIMEOUT))
395 		exuart_rx_intr(sc);
396 
397 	if (utrstat & EXUART_S5L_UTRSTAT_TXTHRESH)
398 		exuart_tx_intr(sc);
399 
400 	bus_space_write_4(iot, ioh, EXUART_UTRSTAT, utrstat);
401 
402 	return 1;
403 }
404 
405 int
406 exuart_param(struct tty *tp, struct termios *t)
407 {
408 	struct exuart_softc *sc = exuart_cd.cd_devs[DEVUNIT(tp->t_dev)];
409 	bus_space_tag_t iot = sc->sc_iot;
410 	bus_space_handle_t ioh = sc->sc_ioh;
411 	int ospeed = t->c_ospeed;
412 	int error;
413 	tcflag_t oldcflag;
414 
415 
416 	if (t->c_ospeed < 0 || (t->c_ispeed && t->c_ispeed != t->c_ospeed))
417 		return EINVAL;
418 
419 	switch (ISSET(t->c_cflag, CSIZE)) {
420 	case CS5:
421 		CLR(sc->sc_ulcon, EXUART_ULCON_WORD_MASK);
422 		SET(sc->sc_ulcon, EXUART_ULCON_WORD_FIVE);
423 		break;
424 	case CS6:
425 		CLR(sc->sc_ulcon, EXUART_ULCON_WORD_MASK);
426 		SET(sc->sc_ulcon, EXUART_ULCON_WORD_SIX);
427 		break;
428 	case CS7:
429 		CLR(sc->sc_ulcon, EXUART_ULCON_WORD_MASK);
430 		SET(sc->sc_ulcon, EXUART_ULCON_WORD_SEVEN);
431 		break;
432 	case CS8:
433 		CLR(sc->sc_ulcon, EXUART_ULCON_WORD_MASK);
434 		SET(sc->sc_ulcon, EXUART_ULCON_WORD_EIGHT);
435 		break;
436 	}
437 
438 	CLR(sc->sc_ulcon, EXUART_ULCON_PARITY_MASK);
439 	if (ISSET(t->c_cflag, PARENB)) {
440 		if (ISSET(t->c_cflag, PARODD))
441 			SET(sc->sc_ulcon, EXUART_ULCON_PARITY_ODD);
442 		else
443 			SET(sc->sc_ulcon, EXUART_ULCON_PARITY_EVEN);
444 	}
445 
446 	if (ISSET(t->c_cflag, CSTOPB))
447 		SET(sc->sc_ulcon, EXUART_ULCON_STOP_TWO);
448 	else
449 		CLR(sc->sc_ulcon, EXUART_ULCON_STOP_ONE);
450 
451 	bus_space_write_4(iot, ioh, EXUART_ULCON, sc->sc_ulcon);
452 
453 	if (ospeed == 0) {
454 		/* lower dtr */
455 	}
456 
457 	if (ospeed != 0) {
458 		while (ISSET(tp->t_state, TS_BUSY)) {
459 			++sc->sc_halt;
460 			error = ttysleep(tp, &tp->t_outq,
461 			    TTOPRI | PCATCH, "exuartprm");
462 			--sc->sc_halt;
463 			if (error) {
464 				exuart_start(tp);
465 				return (error);
466 			}
467 		}
468 		/* set speed */
469 	}
470 
471 	/* setup fifo */
472 
473 	/* When not using CRTSCTS, RTS follows DTR. */
474 	/* sc->sc_dtr = MCR_DTR; */
475 
476 
477 	/* and copy to tty */
478 	tp->t_ispeed = t->c_ispeed;
479 	tp->t_ospeed = t->c_ospeed;
480 	oldcflag = tp->t_cflag;
481 	tp->t_cflag = t->c_cflag;
482 
483         /*
484 	 * If DCD is off and MDMBUF is changed, ask the tty layer if we should
485 	 * stop the device.
486 	 */
487 	 /* XXX */
488 
489 	exuart_start(tp);
490 
491 	return 0;
492 }
493 
494 void
495 exuart_start(struct tty *tp)
496 {
497         struct exuart_softc *sc = exuart_cd.cd_devs[DEVUNIT(tp->t_dev)];
498 	bus_space_tag_t iot = sc->sc_iot;
499 	bus_space_handle_t ioh = sc->sc_ioh;
500 	int s;
501 
502 	s = spltty();
503 	if (ISSET(tp->t_state, TS_BUSY))
504 		goto out;
505 	if (ISSET(tp->t_state, TS_TIMEOUT | TS_TTSTOP) || sc->sc_halt > 0)
506 		goto stopped;
507 #ifdef DAMNFUCKSHIT
508 	/* clear to send (IE the RTS pin on this shit) is not directly \
509 	 * readable - skip check for now
510 	 */
511 	if (ISSET(tp->t_cflag, CRTSCTS) && !ISSET(sc->sc_msr, EXUART_CTS))
512 		goto stopped;
513 #endif
514 	ttwakeupwr(tp);
515 	if (tp->t_outq.c_cc == 0)
516 		goto stopped;
517 	SET(tp->t_state, TS_BUSY);
518 
519 	{
520 		u_char buffer[16];
521 		int i, n;
522 
523 		n = q_to_b(&tp->t_outq, buffer, sizeof buffer);
524 		for (i = 0; i < n; i++)
525 			bus_space_write_4(iot, ioh, EXUART_UTXH, buffer[i]);
526 		bzero(buffer, n);
527 	}
528 
529 	if (sc->sc_type == EXUART_TYPE_S5L) {
530 		if (!ISSET(sc->sc_ucon, EXUART_S5L_UCON_TXTHRESH)) {
531 			SET(sc->sc_ucon, EXUART_S5L_UCON_TXTHRESH);
532 			bus_space_write_4(iot, ioh, EXUART_UCON, sc->sc_ucon);
533 		}
534 	} else {
535 		if (ISSET(sc->sc_uintm, EXUART_UINTM_TXD)) {
536 			CLR(sc->sc_uintm, EXUART_UINTM_TXD);
537 			bus_space_write_4(iot, ioh, EXUART_UINTM, sc->sc_uintm);
538 		}
539 	}
540 
541 out:
542 	splx(s);
543 	return;
544 stopped:
545 	if (sc->sc_type == EXUART_TYPE_S5L) {
546 		if (ISSET(sc->sc_ucon, EXUART_S5L_UCON_TXTHRESH)) {
547 			CLR(sc->sc_ucon, EXUART_S5L_UCON_TXTHRESH);
548 			bus_space_write_4(iot, ioh, EXUART_UCON, sc->sc_ucon);
549 		}
550 	} else {
551 		if (!ISSET(sc->sc_uintm, EXUART_UINTM_TXD)) {
552 			SET(sc->sc_uintm, EXUART_UINTM_TXD);
553 			bus_space_write_4(iot, ioh, EXUART_UINTM, sc->sc_uintm);
554 		}
555 	}
556 	splx(s);
557 }
558 
559 void
560 exuart_diag(void *arg)
561 {
562 	struct exuart_softc *sc = arg;
563 	int overflows, floods;
564 	int s;
565 
566 	s = spltty();
567 	sc->sc_errors = 0;
568 	overflows = sc->sc_overflows;
569 	sc->sc_overflows = 0;
570 	floods = sc->sc_floods;
571 	sc->sc_floods = 0;
572 	splx(s);
573 	log(LOG_WARNING, "%s: %d silo overflow%s, %d ibuf overflow%s\n",
574 	    sc->sc_dev.dv_xname,
575 	    overflows, overflows == 1 ? "" : "s",
576 	    floods, floods == 1 ? "" : "s");
577 }
578 
579 void
580 exuart_raisedtr(void *arg)
581 {
582 	//struct exuart_softc *sc = arg;
583 
584 	//SET(sc->sc_ucr3, EXUART_CR3_DSR); /* XXX */
585 	//bus_space_write_2(sc->sc_iot, sc->sc_ioh, EXUART_UCR3, sc->sc_ucr3);
586 }
587 
588 void
589 exuart_softint(void *arg)
590 {
591 	struct exuart_softc *sc = arg;
592 	struct tty *tp;
593 	u_int16_t *ibufp;
594 	u_int16_t *ibufend;
595 	int c;
596 	int err;
597 	int s;
598 
599 	if (sc == NULL || sc->sc_ibufp == sc->sc_ibuf)
600 		return;
601 
602 	tp = sc->sc_tty;
603 
604 	s = spltty();
605 
606 	ibufp = sc->sc_ibuf;
607 	ibufend = sc->sc_ibufp;
608 
609 	if (ibufp == ibufend) {
610 		splx(s);
611 		return;
612 	}
613 
614 	sc->sc_ibufp = sc->sc_ibuf = (ibufp == sc->sc_ibufs[0]) ?
615 	    sc->sc_ibufs[1] : sc->sc_ibufs[0];
616 	sc->sc_ibufhigh = sc->sc_ibuf + EXUART_IHIGHWATER;
617 	sc->sc_ibufend = sc->sc_ibuf + EXUART_IBUFSIZE;
618 
619 	if (tp == NULL || !ISSET(tp->t_state, TS_ISOPEN)) {
620 		splx(s);
621 		return;
622 	}
623 
624 #if 0
625 	if (ISSET(tp->t_cflag, CRTSCTS) &&
626 	    !ISSET(sc->sc_ucr3, EXUART_CR3_DSR)) {
627 		/* XXX */
628 		SET(sc->sc_ucr3, EXUART_CR3_DSR);
629 		bus_space_write_2(sc->sc_iot, sc->sc_ioh, EXUART_UCR3,
630 		    sc->sc_ucr3);
631 	}
632 #endif
633 
634 	splx(s);
635 
636 	while (ibufp < ibufend) {
637 		c = *ibufp++;
638 #if 0
639 		if (ISSET(c, EXUART_UERSTAT_OVERRUN)) {
640 			sc->sc_overflows++;
641 			if (sc->sc_errors++ == 0)
642 				timeout_add_sec(&sc->sc_diag_tmo, 60);
643 		}
644 #endif
645 		err = 0;
646 #if 0
647 		if (ISSET(c, EXUART_UERSTAT_PARITY))
648 			err |= TTY_PE;
649 		if (ISSET(c, EXUART_UERSTAT_FRAME))
650 			err |= TTY_FE;
651 #endif
652 		c = (c & 0xff) | err;
653 		(*linesw[tp->t_line].l_rint)(c, tp);
654 	}
655 }
656 
657 int
658 exuartopen(dev_t dev, int flag, int mode, struct proc *p)
659 {
660 	int unit = DEVUNIT(dev);
661 	struct exuart_softc *sc;
662 	bus_space_tag_t iot;
663 	bus_space_handle_t ioh;
664 	struct tty *tp;
665 	int s;
666 	int error = 0;
667 
668 	if (unit >= exuart_cd.cd_ndevs)
669 		return ENXIO;
670 	sc = exuart_cd.cd_devs[unit];
671 	if (sc == NULL)
672 		return ENXIO;
673 
674 	s = spltty();
675 	if (sc->sc_tty == NULL)
676 		tp = sc->sc_tty = ttymalloc(0);
677 	else
678 		tp = sc->sc_tty;
679 	splx(s);
680 
681 	tp->t_oproc = exuart_start;
682 	tp->t_param = exuart_param;
683 	tp->t_dev = dev;
684 	if (!ISSET(tp->t_state, TS_ISOPEN)) {
685 		SET(tp->t_state, TS_WOPEN);
686 		ttychars(tp);
687 		tp->t_iflag = TTYDEF_IFLAG;
688 		tp->t_oflag = TTYDEF_OFLAG;
689 
690 		if (ISSET(sc->sc_hwflags, COM_HW_CONSOLE))
691 			tp->t_cflag = exuartconscflag;
692 		else
693 			tp->t_cflag = TTYDEF_CFLAG;
694 		if (ISSET(sc->sc_swflags, COM_SW_CLOCAL))
695 			SET(tp->t_cflag, CLOCAL);
696 		if (ISSET(sc->sc_swflags, COM_SW_CRTSCTS))
697 			SET(tp->t_cflag, CRTSCTS);
698 		if (ISSET(sc->sc_swflags, COM_SW_MDMBUF))
699 			SET(tp->t_cflag, MDMBUF);
700 		tp->t_lflag = TTYDEF_LFLAG;
701 		tp->t_ispeed = tp->t_ospeed = exuartdefaultrate;
702 
703 		s = spltty();
704 
705 		sc->sc_initialize = 1;
706 		exuart_param(tp, &tp->t_termios);
707 		ttsetwater(tp);
708 
709 		sc->sc_ibufp = sc->sc_ibuf = sc->sc_ibufs[0];
710 		sc->sc_ibufhigh = sc->sc_ibuf + EXUART_IHIGHWATER;
711 		sc->sc_ibufend = sc->sc_ibuf + EXUART_IBUFSIZE;
712 
713 		iot = sc->sc_iot;
714 		ioh = sc->sc_ioh;
715 
716 		sc->sc_ulcon = bus_space_read_4(iot, ioh, EXUART_ULCON);
717 		sc->sc_ucon = bus_space_read_4(iot, ioh, EXUART_UCON);
718 		sc->sc_ufcon = bus_space_read_4(iot, ioh, EXUART_UFCON);
719 		sc->sc_umcon = bus_space_read_4(iot, ioh, EXUART_UMCON);
720 
721 		if (sc->sc_type == EXUART_TYPE_S5L) {
722 			SET(sc->sc_ucon, EXUART_UCON_RX_TIMEOUT);
723 			SET(sc->sc_ucon, EXUART_S5L_UCON_RXTHRESH);
724 			SET(sc->sc_ucon, EXUART_S5L_UCON_RX_TIMEOUT);
725 			bus_space_write_4(iot, ioh, EXUART_UCON, sc->sc_ucon);
726 		} else {
727 			sc->sc_uintm = bus_space_read_4(iot, ioh, EXUART_UINTM);
728 			CLR(sc->sc_uintm, EXUART_UINTM_RXD);
729 			bus_space_write_4(iot, ioh, EXUART_UINTM, sc->sc_uintm);
730 		}
731 
732 #if 0
733 		/* interrupt after one char on tx/rx */
734 		/* reference frequency divider: 1 */
735 		bus_space_write_2(iot, ioh, EXUART_UFCR,
736 		    1 << EXUART_FCR_TXTL_SH |
737 		    5 << EXUART_FCR_RFDIV_SH |
738 		    1 << EXUART_FCR_RXTL_SH);
739 
740 		bus_space_write_2(iot, ioh, EXUART_UBIR,
741 		    (exuartdefaultrate / 100) - 1);
742 
743 		/* formula: clk / (rfdiv * 1600) */
744 		bus_space_write_2(iot, ioh, EXUART_UBMR,
745 		    (exccm_get_uartclk() * 1000) / 1600);
746 
747 		SET(sc->sc_ucr1, EXUART_CR1_EN|EXUART_CR1_RRDYEN);
748 		SET(sc->sc_ucr2, EXUART_CR2_TXEN|EXUART_CR2_RXEN);
749 		bus_space_write_2(iot, ioh, EXUART_UCR1, sc->sc_ucr1);
750 		bus_space_write_2(iot, ioh, EXUART_UCR2, sc->sc_ucr2);
751 
752 		/* sc->sc_mcr = MCR_DTR | MCR_RTS;  XXX */
753 		SET(sc->sc_ucr3, EXUART_CR3_DSR); /* XXX */
754 		bus_space_write_2(iot, ioh, EXUART_UCR3, sc->sc_ucr3);
755 #endif
756 
757 		SET(tp->t_state, TS_CARR_ON); /* XXX */
758 
759 
760 	} else if (ISSET(tp->t_state, TS_XCLUDE) && suser(p) != 0)
761 		return EBUSY;
762 	else
763 		s = spltty();
764 
765 	if (DEVCUA(dev)) {
766 		if (ISSET(tp->t_state, TS_ISOPEN)) {
767 			splx(s);
768 			return EBUSY;
769 		}
770 		sc->sc_cua = 1;
771 	} else {
772 		/* tty (not cua) device; wait for carrier if necessary */
773 		if (ISSET(flag, O_NONBLOCK)) {
774 			if (sc->sc_cua) {
775 				/* Opening TTY non-blocking... but the CUA is busy */
776 				splx(s);
777 				return EBUSY;
778 			}
779 		} else {
780 			while (sc->sc_cua ||
781 			    (!ISSET(tp->t_cflag, CLOCAL) &&
782 				!ISSET(tp->t_state, TS_CARR_ON))) {
783 				SET(tp->t_state, TS_WOPEN);
784 				error = ttysleep(tp, &tp->t_rawq,
785 				    TTIPRI | PCATCH, ttopen);
786 				/*
787 				 * If TS_WOPEN has been reset, that means the
788 				 * cua device has been closed.  We don't want
789 				 * to fail in that case,
790 				 * so just go around again.
791 				 */
792 				if (error && ISSET(tp->t_state, TS_WOPEN)) {
793 					CLR(tp->t_state, TS_WOPEN);
794 					splx(s);
795 					return error;
796 				}
797 			}
798 		}
799 	}
800 	splx(s);
801 
802 	return (*linesw[tp->t_line].l_open)(dev,tp,p);
803 }
804 
805 int
806 exuartclose(dev_t dev, int flag, int mode, struct proc *p)
807 {
808 	int unit = DEVUNIT(dev);
809 	struct exuart_softc *sc = exuart_cd.cd_devs[unit];
810 	//bus_space_tag_t iot = sc->sc_iot;
811 	//bus_space_handle_t ioh = sc->sc_ioh;
812 	struct tty *tp = sc->sc_tty;
813 	int s;
814 
815 	/* XXX This is for cons.c. */
816 	if (!ISSET(tp->t_state, TS_ISOPEN))
817 		return 0;
818 
819 	(*linesw[tp->t_line].l_close)(tp, flag, p);
820 	s = spltty();
821 	if (ISSET(tp->t_state, TS_WOPEN)) {
822 		/* tty device is waiting for carrier; drop dtr then re-raise */
823 		//CLR(sc->sc_ucr3, EXUART_CR3_DSR);
824 		//bus_space_write_2(iot, ioh, EXUART_UCR3, sc->sc_ucr3);
825 		timeout_add_sec(&sc->sc_dtr_tmo, 2);
826 	}
827 	CLR(tp->t_state, TS_BUSY | TS_FLUSH);
828 	sc->sc_cua = 0;
829 	splx(s);
830 	ttyclose(tp);
831 
832 	return 0;
833 }
834 
835 int
836 exuartread(dev_t dev, struct uio *uio, int flag)
837 {
838 	struct tty *tty;
839 
840 	tty = exuarttty(dev);
841 	if (tty == NULL)
842 		return ENODEV;
843 
844 	return((*linesw[tty->t_line].l_read)(tty, uio, flag));
845 }
846 
847 int
848 exuartwrite(dev_t dev, struct uio *uio, int flag)
849 {
850 	struct tty *tty;
851 
852 	tty = exuarttty(dev);
853 	if (tty == NULL)
854 		return ENODEV;
855 
856 	return((*linesw[tty->t_line].l_write)(tty, uio, flag));
857 }
858 
859 int
860 exuartioctl( dev_t dev, u_long cmd, caddr_t data, int flag, struct proc *p)
861 {
862 	struct exuart_softc *sc;
863 	struct tty *tp;
864 	int error;
865 
866 	sc = exuart_sc(dev);
867 	if (sc == NULL)
868 		return (ENODEV);
869 
870 	tp = sc->sc_tty;
871 	if (tp == NULL)
872 		return (ENXIO);
873 
874 	error = (*linesw[tp->t_line].l_ioctl)(tp, cmd, data, flag, p);
875 	if (error >= 0)
876 		return (error);
877 
878 	error = ttioctl(tp, cmd, data, flag, p);
879 	if (error >= 0)
880 		return (error);
881 
882 	switch(cmd) {
883 	case TIOCSBRK:
884 		/* */
885 		break;
886 
887 	case TIOCCBRK:
888 		/* */
889 		break;
890 
891 	case TIOCSDTR:
892 #if 0
893 		(void) clmctl(dev, TIOCM_DTR | TIOCM_RTS, DMBIS);
894 #endif
895 		break;
896 
897 	case TIOCCDTR:
898 #if 0
899 		(void) clmctl(dev, TIOCM_DTR | TIOCM_RTS, DMBIC);
900 #endif
901 		break;
902 
903 	case TIOCMSET:
904 #if 0
905 		(void) clmctl(dev, *(int *) data, DMSET);
906 #endif
907 		break;
908 
909 	case TIOCMBIS:
910 #if 0
911 		(void) clmctl(dev, *(int *) data, DMBIS);
912 #endif
913 		break;
914 
915 	case TIOCMBIC:
916 #if 0
917 		(void) clmctl(dev, *(int *) data, DMBIC);
918 #endif
919 		break;
920 
921         case TIOCMGET:
922 #if 0
923 		*(int *)data = clmctl(dev, 0, DMGET);
924 #endif
925 		break;
926 
927 	case TIOCGFLAGS:
928 #if 0
929 		*(int *)data = cl->cl_swflags;
930 #endif
931 		break;
932 
933 	case TIOCSFLAGS:
934 		error = suser(p);
935 		if (error != 0)
936 			return(EPERM);
937 
938 #if 0
939 		cl->cl_swflags = *(int *)data;
940 		cl->cl_swflags &= /* only allow valid flags */
941 		    (TIOCFLAG_SOFTCAR | TIOCFLAG_CLOCAL | TIOCFLAG_CRTSCTS);
942 #endif
943 		break;
944 	default:
945 		return (ENOTTY);
946 	}
947 
948 	return 0;
949 }
950 
951 int
952 exuartstop(struct tty *tp, int flag)
953 {
954 	return 0;
955 }
956 
957 struct tty *
958 exuarttty(dev_t dev)
959 {
960 	int unit;
961 	struct exuart_softc *sc;
962 	unit = DEVUNIT(dev);
963 	if (unit >= exuart_cd.cd_ndevs)
964 		return NULL;
965 	sc = (struct exuart_softc *)exuart_cd.cd_devs[unit];
966 	if (sc == NULL)
967 		return NULL;
968 	return sc->sc_tty;
969 }
970 
971 struct exuart_softc *
972 exuart_sc(dev_t dev)
973 {
974 	int unit;
975 	struct exuart_softc *sc;
976 	unit = DEVUNIT(dev);
977 	if (unit >= exuart_cd.cd_ndevs)
978 		return NULL;
979 	sc = (struct exuart_softc *)exuart_cd.cd_devs[unit];
980 	return sc;
981 }
982 
983 
984 /* serial console */
985 void
986 exuartcnprobe(struct consdev *cp)
987 {
988 }
989 
990 void
991 exuartcninit(struct consdev *cp)
992 {
993 }
994 
995 int
996 exuartcnattach(bus_space_tag_t iot, bus_addr_t iobase, int rate, tcflag_t cflag)
997 {
998 	static struct consdev exuartcons = {
999 		NULL, NULL, exuartcngetc, exuartcnputc, exuartcnpollc, NULL,
1000 		NODEV, CN_MIDPRI
1001 	};
1002 	int maj;
1003 
1004 	if (bus_space_map(iot, iobase, 0x100, 0, &exuartconsioh))
1005 		return ENOMEM;
1006 
1007 	/* Look for major of com(4) to replace. */
1008 	for (maj = 0; maj < nchrdev; maj++)
1009 		if (cdevsw[maj].d_open == comopen)
1010 			break;
1011 	if (maj == nchrdev)
1012 		return ENXIO;
1013 
1014 	cn_tab = &exuartcons;
1015 	cn_tab->cn_dev = makedev(maj, 0);
1016 	cdevsw[maj] = exuartdev; 	/* KLUDGE */
1017 
1018 	exuartconsiot = iot;
1019 	exuartconsaddr = iobase;
1020 	exuartconscflag = cflag;
1021 
1022 	return 0;
1023 }
1024 
1025 int
1026 exuartcngetc(dev_t dev)
1027 {
1028 	int c;
1029 	int s;
1030 	s = splhigh();
1031 	while((bus_space_read_4(exuartconsiot, exuartconsioh, EXUART_UTRSTAT) &
1032 	    EXUART_UTRSTAT_RXBREADY) == 0 &&
1033 	      (bus_space_read_4(exuartconsiot, exuartconsioh, EXUART_UFSTAT) &
1034 	    (exuart_rx_fifo_cnt_mask | exuart_rx_fifo_full)) == 0)
1035 		;
1036 	c = bus_space_read_4(exuartconsiot, exuartconsioh, EXUART_URXH);
1037 	splx(s);
1038 	return c;
1039 }
1040 
1041 void
1042 exuartcnputc(dev_t dev, int c)
1043 {
1044 	int s;
1045 	s = splhigh();
1046 	while (bus_space_read_4(exuartconsiot, exuartconsioh, EXUART_UFSTAT) &
1047 	   exuart_tx_fifo_full)
1048 		;
1049 	bus_space_write_4(exuartconsiot, exuartconsioh, EXUART_UTXH, c);
1050 	splx(s);
1051 }
1052 
1053 void
1054 exuartcnpollc(dev_t dev, int on)
1055 {
1056 }
1057