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