1/*	$NetBSD: txcom.c,v 1.52 2021/08/07 16:18:54 thorpej Exp $ */
2
3/*-
4 * Copyright (c) 1999, 2000, 2004 The NetBSD Foundation, Inc.
5 * All rights reserved.
6 *
7 * This code is derived from software contributed to The NetBSD Foundation
8 * by UCHIYAMA Yasushi.
9 *
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
12 * are met:
13 * 1. Redistributions of source code must retain the above copyright
14 *    notice, this list of conditions and the following disclaimer.
15 * 2. Redistributions in binary form must reproduce the above copyright
16 *    notice, this list of conditions and the following disclaimer in the
17 *    documentation and/or other materials provided with the distribution.
18 *
19 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
20 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
21 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
22 * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
23 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29 * POSSIBILITY OF SUCH DAMAGE.
30 */
31
32#include <sys/cdefs.h>
33__KERNEL_RCSID(0, "$NetBSD: txcom.c,v 1.52 2021/08/07 16:18:54 thorpej Exp $");
34
35#include "opt_tx39uart_debug.h"
36
37#include <sys/param.h>
38#include <sys/systm.h>
39#include <sys/kernel.h>
40#include <sys/device.h>
41#include <sys/kmem.h>
42#include <sys/kauth.h>
43
44#include <sys/proc.h> /* tsleep/wakeup */
45
46#include <sys/ioctl.h>
47#include <sys/select.h>
48#include <sys/file.h>
49
50#include <sys/tty.h>
51#include <sys/conf.h>
52#include <dev/cons.h> /* consdev */
53
54#include <machine/bus.h>
55#include <machine/config_hook.h>
56
57#include <hpcmips/tx/tx39var.h>
58#include <hpcmips/tx/tx39icureg.h>
59#include <hpcmips/tx/tx39uartvar.h>
60#include <hpcmips/tx/tx39uartreg.h>
61
62#include <hpcmips/tx/tx39irvar.h>
63
64#include <hpcmips/tx/tx39clockreg.h> /* XXX */
65
66/*
67 * UARTA channel has DTR, DSR, RTS, CTS lines. and they  wired to MFIO/IO port.
68 */
69#define IS_COM0(s)	((s) == 0)
70#define IS_COM1(s)	((s) == 1)
71#define ON		((void *)1)
72#define OFF		((void *)0)
73
74#ifdef	TX39UART_DEBUG
75#define DPRINTF_ENABLE
76#define DPRINTF_DEBUG	tx39uart_debug
77#endif
78#include <machine/debug.h>
79
80#define TXCOM_HW_CONSOLE	0x40
81#define	TXCOM_RING_SIZE		256 /* must be a power of two! */
82#define TXCOM_RING_MASK		(TXCOM_RING_SIZE - 1)
83
84struct txcom_chip {
85	tx_chipset_tag_t sc_tc;
86	int sc_slot;	/* UARTA or UARTB */
87	int sc_cflag;
88	int sc_speed;
89	int sc_swflags;
90	int sc_hwflags;
91
92	int sc_dcd;
93	int sc_msr_cts;
94	int sc_tx_stopped;
95};
96
97struct txcom_softc {
98	struct tty		*sc_tty;
99	struct txcom_chip	*sc_chip;
100
101	void		*sc_txsoft_cookie;
102	void		*sc_rxsoft_cookie;
103
104 	u_int8_t	*sc_tba;	/* transmit buffer address */
105 	int		sc_tbc;		/* transmit byte count */
106	int		sc_heldtbc;
107	u_int8_t	*sc_rbuf;	/* receive buffer address */
108	int		sc_rbput;	/* receive byte count */
109	int		sc_rbget;
110};
111
112extern struct cfdriver txcom_cd;
113
114int	txcom_match(device_t, cfdata_t, void *);
115void	txcom_attach(device_t, device_t, void *);
116int	txcom_print(void *, const char *);
117
118int	txcom_txintr(void *);
119int	txcom_rxintr(void *);
120int	txcom_frameerr_intr(void *);
121int	txcom_parityerr_intr(void *);
122int	txcom_break_intr(void *);
123
124void	txcom_rxsoft(void *);
125void	txcom_txsoft(void *);
126
127int	txcom_stsoft(void *);
128int	txcom_stsoft2(void *);
129int	txcom_stsoft3(void *);
130int	txcom_stsoft4(void *);
131
132
133void	txcom_shutdown(struct txcom_softc *);
134void	txcom_break(struct txcom_softc *, int);
135void	txcom_modem(struct txcom_softc *, int);
136void	txcomstart(struct tty *);
137int	txcomparam(struct tty *, struct termios *);
138
139void	txcom_reset	(struct txcom_chip *);
140int	txcom_enable	(struct txcom_chip *, bool);
141void	txcom_disable	(struct txcom_chip *);
142void	txcom_setmode	(struct txcom_chip *);
143void	txcom_setbaudrate(struct txcom_chip *);
144int	txcom_cngetc	(dev_t);
145void	txcom_cnputc	(dev_t, int);
146void	txcom_cnpollc	(dev_t, int);
147
148int	txcom_dcd_hook(void *, int, long, void *);
149int	txcom_cts_hook(void *, int, long, void *);
150
151
152inline int	__txcom_txbufready(struct txcom_chip *, int);
153const char *__txcom_slotname(int);
154
155#ifdef TX39UARTDEBUG
156void	txcom_dump(struct txcom_chip *);
157#endif
158
159struct consdev txcomcons = {
160	NULL, NULL, txcom_cngetc, txcom_cnputc, txcom_cnpollc, NULL, NULL,
161	NULL, NODEV, CN_NORMAL
162};
163
164/* Serial console */
165struct txcom_chip txcom_chip;
166
167CFATTACH_DECL_NEW(txcom, sizeof(struct txcom_softc),
168    txcom_match, txcom_attach, NULL, NULL);
169
170dev_type_open(txcomopen);
171dev_type_close(txcomclose);
172dev_type_read(txcomread);
173dev_type_write(txcomwrite);
174dev_type_ioctl(txcomioctl);
175dev_type_stop(txcomstop);
176dev_type_tty(txcomtty);
177dev_type_poll(txcompoll);
178
179const struct cdevsw txcom_cdevsw = {
180	.d_open = txcomopen,
181	.d_close = txcomclose,
182	.d_read = txcomread,
183	.d_write = txcomwrite,
184	.d_ioctl = txcomioctl,
185	.d_stop = txcomstop,
186	.d_tty = txcomtty,
187	.d_poll = txcompoll,
188	.d_mmap = nommap,
189	.d_kqfilter = ttykqfilter,
190	.d_discard = nodiscard,
191	.d_flag = D_TTY
192};
193
194int
195txcom_match(device_t parent, cfdata_t cf, void *aux)
196{
197	/* if the autoconfiguration got this far, there's a slot here */
198	return 1;
199}
200
201void
202txcom_attach(device_t parent, device_t self, void *aux)
203{
204	struct tx39uart_attach_args *ua = aux;
205	struct txcom_softc *sc = device_private(self);
206	tx_chipset_tag_t tc;
207	struct tty *tp;
208	struct txcom_chip *chip;
209	int slot, console;
210
211	/* Check this slot used as serial console */
212	console = (ua->ua_slot == txcom_chip.sc_slot) &&
213	    (txcom_chip.sc_hwflags & TXCOM_HW_CONSOLE);
214
215	if (console) {
216		sc->sc_chip = &txcom_chip;
217	} else {
218		sc->sc_chip = kmem_zalloc(sizeof(struct txcom_chip),
219		    KM_SLEEP);
220	}
221
222	chip = sc->sc_chip;
223	tc = chip->sc_tc = ua->ua_tc;
224	slot = chip->sc_slot = ua->ua_slot;
225
226#ifdef TX39UARTDEBUG
227	txcom_dump(chip);
228#endif
229	if (!console)
230		txcom_reset(chip);
231
232	sc->sc_rbuf = kmem_zalloc(TXCOM_RING_SIZE, KM_SLEEP);
233
234	tp = tty_alloc();
235	tp->t_oproc = txcomstart;
236	tp->t_param = txcomparam;
237	tp->t_hwiflow = NULL;
238	sc->sc_tty = tp;
239	tty_attach(tp);
240
241	if (ISSET(chip->sc_hwflags, TXCOM_HW_CONSOLE)) {
242		int maj;
243		/* locate the major number */
244		maj = cdevsw_lookup_major(&txcom_cdevsw);
245
246		cn_tab->cn_dev = makedev(maj, device_unit(self));
247
248		printf(": console");
249	}
250
251	printf("\n");
252
253	/*
254	 * Enable interrupt
255	 */
256#define TXCOMINTR(i, s) MAKEINTR(2, TX39_INTRSTATUS2_UART##i##INT(s))
257
258	tx_intr_establish(tc, TXCOMINTR(RX, slot), IST_EDGE, IPL_TTY,
259	    txcom_rxintr, sc);
260	tx_intr_establish(tc, TXCOMINTR(TX, slot), IST_EDGE, IPL_TTY,
261	    txcom_txintr, sc);
262	tx_intr_establish(tc, TXCOMINTR(RXOVERRUN, slot), IST_EDGE, IPL_TTY,
263	    txcom_rxintr, sc);
264	tx_intr_establish(tc, TXCOMINTR(TXOVERRUN, slot), IST_EDGE, IPL_TTY,
265	    txcom_txintr, sc);
266	tx_intr_establish(tc, TXCOMINTR(FRAMEERR, slot), IST_EDGE, IPL_TTY,
267	    txcom_frameerr_intr, sc);
268	tx_intr_establish(tc, TXCOMINTR(PARITYERR, slot), IST_EDGE, IPL_TTY,
269	    txcom_parityerr_intr, sc);
270	tx_intr_establish(tc, TXCOMINTR(BREAK, slot), IST_EDGE, IPL_TTY,
271	    txcom_break_intr, sc);
272
273	sc->sc_txsoft_cookie =
274	    softint_establish(SOFTINT_SERIAL, txcom_txsoft, sc);
275	sc->sc_rxsoft_cookie =
276	    softint_establish(SOFTINT_SERIAL, txcom_rxsoft, sc);
277
278	/*
279	 * UARTA has external signal line. (its wiring is platform dependent)
280	 */
281	if (IS_COM0(slot)) {
282		/* install DCD, CTS hooks. */
283		config_hook(CONFIG_HOOK_EVENT, CONFIG_HOOK_COM0_DCD,
284		    CONFIG_HOOK_EXCLUSIVE, txcom_dcd_hook, sc);
285		config_hook(CONFIG_HOOK_EVENT, CONFIG_HOOK_COM0_CTS,
286		    CONFIG_HOOK_EXCLUSIVE, txcom_cts_hook, sc);
287	}
288
289	/*
290	 * UARTB can connect IR module
291	 */
292	if (IS_COM1(slot)) {
293		struct txcom_attach_args tca;
294		tca.tca_tc = tc;
295		tca.tca_parent = self;
296		config_found(self, &tca, txcom_print, CFARGS_NONE);
297	}
298}
299
300int
301txcom_print(void *aux, const char *pnp)
302{
303	return pnp ? QUIET : UNCONF;
304}
305
306void
307txcom_reset(struct txcom_chip *chip)
308{
309	tx_chipset_tag_t tc;
310	int slot, ofs;
311	txreg_t reg;
312
313	tc = chip->sc_tc;
314	slot = chip->sc_slot;
315	ofs = TX39_UARTCTRL1_REG(slot);
316
317	/* Supply clock */
318	reg = tx_conf_read(tc, TX39_CLOCKCTRL_REG);
319	reg |= (slot ? TX39_CLOCK_ENUARTBCLK : TX39_CLOCK_ENUARTACLK);
320	tx_conf_write(tc, TX39_CLOCKCTRL_REG, reg);
321
322	/* reset UART module */
323	tx_conf_write(tc, ofs, 0);
324}
325
326int
327txcom_enable(struct txcom_chip *chip, bool console)
328{
329	tx_chipset_tag_t tc;
330	txreg_t reg;
331	int slot, ofs, timeout;
332
333	tc = chip->sc_tc;
334	slot = chip->sc_slot;
335	ofs = TX39_UARTCTRL1_REG(slot);
336
337	/*
338	 * External power supply (if any)
339	 * When serial console, Windows CE already powered on it.
340	 */
341	if (!console) {
342		config_hook_call(CONFIG_HOOK_POWERCONTROL,
343		    CONFIG_HOOK_POWERCONTROL_COM0, PWCTL_ON);
344		delay(3);
345	}
346
347	/* Supply clock */
348	reg = tx_conf_read(tc, TX39_CLOCKCTRL_REG);
349	reg |= (slot ? TX39_CLOCK_ENUARTBCLK : TX39_CLOCK_ENUARTACLK);
350	tx_conf_write(tc, TX39_CLOCKCTRL_REG, reg);
351
352	/*
353	 * XXX Disable DMA (DMA not coded yet)
354	 */
355	reg = tx_conf_read(tc, ofs);
356	reg &= ~(TX39_UARTCTRL1_ENDMARX | TX39_UARTCTRL1_ENDMATX);
357	tx_conf_write(tc, ofs, reg);
358
359	/* enable */
360	reg = tx_conf_read(tc, ofs);
361	reg |= TX39_UARTCTRL1_ENUART;
362	reg &= ~TX39_UARTCTRL1_ENBREAHALT;
363	tx_conf_write(tc, ofs, reg);
364
365	timeout = 100000;
366
367	while(!(tx_conf_read(tc, ofs) & TX39_UARTCTRL1_UARTON) &&
368	    --timeout > 0)
369		;
370
371	if (timeout == 0 && !cold) {
372		printf("%s never power up\n", __txcom_slotname(slot));
373		return 1;
374	}
375
376	return 0;
377}
378
379void
380txcom_disable(struct txcom_chip *chip)
381{
382	tx_chipset_tag_t tc;
383	txreg_t reg;
384	int slot;
385
386	tc = chip->sc_tc;
387	slot = chip->sc_slot;
388
389	reg = tx_conf_read(tc, TX39_UARTCTRL1_REG(slot));
390	/* DMA */
391	reg &= ~(TX39_UARTCTRL1_ENDMARX | TX39_UARTCTRL1_ENDMATX);
392
393	/* disable module */
394	reg &= ~TX39_UARTCTRL1_ENUART;
395	tx_conf_write(tc, TX39_UARTCTRL1_REG(slot), reg);
396
397	/* Clock */
398	reg = tx_conf_read(tc, TX39_CLOCKCTRL_REG);
399	reg &= ~(slot ? TX39_CLOCK_ENUARTBCLK : TX39_CLOCK_ENUARTACLK);
400	tx_conf_write(tc, TX39_CLOCKCTRL_REG, reg);
401
402}
403
404inline int
405__txcom_txbufready(struct txcom_chip *chip, int retry)
406{
407	tx_chipset_tag_t tc = chip->sc_tc;
408	int ofs = TX39_UARTCTRL1_REG(chip->sc_slot);
409
410	do {
411		if (tx_conf_read(tc, ofs) & TX39_UARTCTRL1_EMPTY)
412			return 1;
413	} while(--retry != 0);
414
415	return 0;
416}
417
418void
419txcom_pulse_mode(device_t dev)
420{
421	struct txcom_softc *sc = device_private(dev);
422	struct txcom_chip *chip = sc->sc_chip;
423	tx_chipset_tag_t tc = chip->sc_tc;
424	int ofs;
425	txreg_t reg;
426
427	ofs = TX39_UARTCTRL1_REG(chip->sc_slot);
428
429	reg = tx_conf_read(tc, ofs);
430	/* WindowsCE use this setting */
431	reg |= TX39_UARTCTRL1_PULSEOPT1;
432	reg &= ~TX39_UARTCTRL1_PULSEOPT2;
433	reg |= TX39_UARTCTRL1_DTINVERT;
434
435	tx_conf_write(tc, ofs, reg);
436}
437
438/*
439 * console
440 */
441int
442txcom_cngetc(dev_t dev)
443{
444	tx_chipset_tag_t tc;
445	int ofs, c, s;
446
447	s = spltty();
448
449	tc = txcom_chip.sc_tc;
450	ofs = TX39_UARTCTRL1_REG(txcom_chip.sc_slot);
451
452	while(!(TX39_UARTCTRL1_RXHOLDFULL & tx_conf_read(tc, ofs)))
453		;
454
455	c = TX39_UARTRXHOLD_RXDATA(
456		tx_conf_read(tc, TX39_UARTRXHOLD_REG(txcom_chip.sc_slot)));
457
458	if (c == '\r')
459		c = '\n';
460
461	splx(s);
462
463	return c;
464}
465
466void
467txcom_cnputc(dev_t dev, int c)
468{
469	struct txcom_chip *chip = &txcom_chip;
470	tx_chipset_tag_t tc = chip->sc_tc;
471	int s;
472
473	s = spltty();
474
475	/* Wait for transmitter to empty */
476	__txcom_txbufready(chip, -1);
477
478	tx_conf_write(tc, TX39_UARTTXHOLD_REG(chip->sc_slot),
479	    (c & TX39_UARTTXHOLD_TXDATA_MASK));
480
481	__txcom_txbufready(chip, -1);
482
483	splx(s);
484}
485
486void
487txcom_cnpollc(dev_t dev, int on)
488{
489}
490
491void
492txcom_setmode(struct txcom_chip *chip)
493{
494	tcflag_t cflag = chip->sc_cflag;
495	int ofs = TX39_UARTCTRL1_REG(chip->sc_slot);
496	txreg_t reg;
497
498	reg = tx_conf_read(chip->sc_tc, ofs);
499	reg &= ~TX39_UARTCTRL1_ENUART;
500	tx_conf_write(chip->sc_tc, ofs, reg);
501
502	switch (ISSET(cflag, CSIZE)) {
503	default:
504		printf("txcom_setmode: CS7, CS8 only. use CS7");
505		/* FALL THROUGH */
506	case CS7:
507		reg |= TX39_UARTCTRL1_BIT7;
508		break;
509	case CS8:
510		reg &= ~TX39_UARTCTRL1_BIT7;
511		break;
512	}
513
514	if (ISSET(cflag, PARENB)) {
515		reg |= TX39_UARTCTRL1_ENPARITY;
516		if (ISSET(cflag, PARODD)) {
517			reg &= ~TX39_UARTCTRL1_EVENPARITY;
518		} else {
519			reg |= TX39_UARTCTRL1_EVENPARITY;
520		}
521	} else {
522		reg &= ~TX39_UARTCTRL1_ENPARITY;
523	}
524
525	if (ISSET(cflag, CSTOPB))
526		reg |= TX39_UARTCTRL1_TWOSTOP;
527	else
528		reg &= ~TX39_UARTCTRL1_TWOSTOP;
529
530	reg |= TX39_UARTCTRL1_ENUART;
531	tx_conf_write(chip->sc_tc, ofs, reg);
532}
533
534void
535txcom_setbaudrate(struct txcom_chip *chip)
536{
537	int baudrate;
538	int ofs = TX39_UARTCTRL1_REG(chip->sc_slot);
539	txreg_t reg, reg1;
540
541	if (chip->sc_speed == 0)
542		return;
543
544	if (!cold)
545		DPRINTF("%d\n", chip->sc_speed);
546
547	reg1 = tx_conf_read(chip->sc_tc, ofs);
548	reg1 &= ~TX39_UARTCTRL1_ENUART;
549	tx_conf_write(chip->sc_tc, ofs, reg1);
550
551	baudrate = TX39_UARTCLOCKHZ / (chip->sc_speed * 16) - 1;
552	reg = TX39_UARTCTRL2_BAUDRATE_SET(0, baudrate);
553
554	tx_conf_write(chip->sc_tc, TX39_UARTCTRL2_REG(chip->sc_slot), reg);
555
556	reg1 |= TX39_UARTCTRL1_ENUART;
557	tx_conf_write(chip->sc_tc, ofs, reg1);
558}
559
560int
561txcom_cnattach(int slot, int speed, int cflag)
562{
563	cn_tab = &txcomcons;
564
565	txcom_chip.sc_tc	= tx_conf_get_tag();
566	txcom_chip.sc_slot	= slot;
567	txcom_chip.sc_cflag	= cflag;
568	txcom_chip.sc_speed	= speed;
569	txcom_chip.sc_hwflags |= TXCOM_HW_CONSOLE;
570#if notyet
571	txcom_reset(&txcom_chip);
572#endif
573	txcom_setmode(&txcom_chip);
574	txcom_setbaudrate(&txcom_chip);
575
576	if (txcom_enable(&txcom_chip, true) != 0)
577		return 1;
578
579	return 0;
580}
581
582/*
583 * tty
584 */
585void
586txcom_break(struct txcom_softc *sc, int on)
587{
588	struct txcom_chip *chip = sc->sc_chip;
589
590	tx_conf_write(chip->sc_tc, TX39_UARTTXHOLD_REG(chip->sc_slot),
591	    on ? TX39_UARTTXHOLD_BREAK : 0);
592}
593
594void
595txcom_modem(struct txcom_softc *sc, int on)
596{
597	struct txcom_chip *chip = sc->sc_chip;
598	tx_chipset_tag_t tc = chip->sc_tc;
599	int slot = chip->sc_slot;
600	txreg_t reg;
601
602	/* assert DTR */
603	if (IS_COM0(slot)) {
604		config_hook_call(CONFIG_HOOK_SET,
605		    CONFIG_HOOK_COM0_DTR,
606		    (void *)on);
607	}
608
609	reg = tx_conf_read(tc, TX39_UARTCTRL1_REG(slot));
610	reg &= ~TX39_UARTCTRL1_ENUART;
611	tx_conf_write(tc, TX39_UARTCTRL1_REG(slot), reg);
612
613	if (on) {
614		reg &= ~TX39_UARTCTRL1_DISTXD;
615	} else {
616		reg |= TX39_UARTCTRL1_DISTXD; /* low UARTTXD */
617	}
618
619	reg |= TX39_UARTCTRL1_ENUART;
620	tx_conf_write(tc, TX39_UARTCTRL1_REG(slot), reg);
621}
622
623void
624txcom_shutdown(struct txcom_softc *sc)
625{
626	struct tty *tp = sc->sc_tty;
627	int s = spltty();
628
629	/* Clear any break condition set with TIOCSBRK. */
630	txcom_break(sc, 0);
631
632	/*
633	 * Hang up if necessary.  Wait a bit, so the other side has time to
634	 * notice even if we immediately open the port again.
635	 */
636	if (ISSET(tp->t_cflag, HUPCL)) {
637		txcom_modem(sc, 0);
638		(void) tsleep(sc, TTIPRI, ttclos, hz);
639	}
640
641
642	/* Turn off interrupts if not the console. */
643	if (!ISSET(sc->sc_chip->sc_hwflags, TXCOM_HW_CONSOLE)) {
644		txcom_disable(sc->sc_chip);
645	}
646
647	splx(s);
648}
649
650const char *
651__txcom_slotname(int slot)
652{
653	static const char *slotname[] = {"UARTA", "UARTB", "unknown"};
654
655	if (slot != 0 && slot != 1)
656		return slotname[2];
657
658	return slotname[slot];
659}
660
661int
662txcom_frameerr_intr(void *arg)
663{
664	struct txcom_softc *sc = arg;
665
666	printf("%s frame error\n", __txcom_slotname(sc->sc_chip->sc_slot));
667
668	return 0;
669}
670
671int
672txcom_parityerr_intr(void *arg)
673{
674	struct txcom_softc *sc = arg;
675
676	printf("%s parity error\n", __txcom_slotname(sc->sc_chip->sc_slot));
677
678	return 0;
679}
680
681int
682txcom_break_intr(void *arg)
683{
684	struct txcom_softc *sc = arg;
685
686	printf("%s break\n", __txcom_slotname(sc->sc_chip->sc_slot));
687
688	return 0;
689}
690
691int
692txcom_rxintr(void *arg)
693{
694	struct txcom_softc *sc = arg;
695	struct txcom_chip *chip = sc->sc_chip;
696	u_int8_t c;
697
698	c = TX39_UARTRXHOLD_RXDATA(
699		tx_conf_read(chip->sc_tc,
700		    TX39_UARTRXHOLD_REG(chip->sc_slot)));
701
702	sc->sc_rbuf[sc->sc_rbput] = c;
703	sc->sc_rbput = (sc->sc_rbput + 1) % TXCOM_RING_MASK;
704
705	softint_schedule(sc->sc_rxsoft_cookie);
706
707	return 0;
708}
709
710void
711txcom_rxsoft(void *arg)
712{
713	struct txcom_softc *sc = arg;
714	struct tty *tp = sc->sc_tty;
715	int (*rint)(int, struct tty *);
716	int code;
717	int s, end, get;
718
719	rint = tp->t_linesw->l_rint;
720
721	s = spltty();
722	end = sc->sc_rbput;
723	get = sc->sc_rbget;
724
725	while (get != end) {
726		code = sc->sc_rbuf[get];
727
728		if ((*rint)(code, tp) == -1) {
729			/*
730			 * The line discipline's buffer is out of space.
731			 */
732		}
733		get = (get + 1) % TXCOM_RING_MASK;
734	}
735	sc->sc_rbget = get;
736
737	splx(s);
738}
739
740int
741txcom_txintr(void *arg)
742{
743	struct txcom_softc *sc = arg;
744	struct txcom_chip *chip = sc->sc_chip;
745	tx_chipset_tag_t tc = chip->sc_tc;
746
747	if (sc->sc_tbc > 0) {
748		tx_conf_write(tc, TX39_UARTTXHOLD_REG(chip->sc_slot),
749		    (*sc->sc_tba &
750			TX39_UARTTXHOLD_TXDATA_MASK));
751		sc->sc_tbc--;
752		sc->sc_tba++;
753	} else {
754		softint_schedule(sc->sc_txsoft_cookie);
755	}
756
757	return 0;
758}
759
760void
761txcom_txsoft(void *arg)
762{
763	struct txcom_softc *sc = arg;
764	struct tty *tp = sc->sc_tty;
765	int s = spltty();
766
767	CLR(tp->t_state, TS_BUSY);
768	if (ISSET(tp->t_state, TS_FLUSH)) {
769		CLR(tp->t_state, TS_FLUSH);
770	} else {
771		ndflush(&tp->t_outq, (int)(sc->sc_tba - tp->t_outq.c_cf));
772	}
773
774	(*tp->t_linesw->l_start)(tp);
775
776	splx(s);
777}
778
779int
780txcomopen(dev_t dev, int flag, int mode, struct lwp *l)
781{
782	struct txcom_softc *sc;
783	struct txcom_chip *chip;
784	struct tty *tp;
785	int s, err = ENXIO;
786
787	sc = device_lookup_private(&txcom_cd, minor(dev));
788	if (sc == NULL)
789		return err;
790
791	chip = sc->sc_chip;
792	tp = sc->sc_tty;
793
794	if (kauth_authorize_device_tty(l->l_cred, KAUTH_DEVICE_TTY_OPEN, tp))
795		return (EBUSY);
796
797	s = spltty();
798
799	if (txcom_enable(sc->sc_chip, false)) {
800		splx(s);
801		goto out;
802	}
803	/*
804	 * Do the following iff this is a first open.
805	 */
806	if (!ISSET(tp->t_state, TS_ISOPEN) && tp->t_wopen == 0) {
807		struct termios t;
808
809		tp->t_dev = dev;
810
811		t.c_ispeed = 0;
812		if (ISSET(chip->sc_hwflags, TXCOM_HW_CONSOLE)) {
813			t.c_ospeed = chip->sc_speed;
814			t.c_cflag = chip->sc_cflag;
815		} else {
816			t.c_ospeed = TTYDEF_SPEED;
817			t.c_cflag = TTYDEF_CFLAG;
818		}
819
820		if (ISSET(chip->sc_swflags, TIOCFLAG_CLOCAL))
821			SET(t.c_cflag, CLOCAL);
822		if (ISSET(chip->sc_swflags, TIOCFLAG_CRTSCTS))
823			SET(t.c_cflag, CRTSCTS);
824		if (ISSET(chip->sc_swflags, TIOCFLAG_MDMBUF))
825			SET(t.c_cflag, MDMBUF);
826
827		/* Make sure txcomparam() will do something. */
828		tp->t_ospeed = 0;
829		txcomparam(tp, &t);
830
831		tp->t_iflag = TTYDEF_IFLAG;
832		tp->t_oflag = TTYDEF_OFLAG;
833		tp->t_lflag = TTYDEF_LFLAG;
834
835		ttychars(tp);
836		ttsetwater(tp);
837
838		/*
839		 * Turn on DTR.  We must always do this, even if carrier is not
840		 * present, because otherwise we'd have to use TIOCSDTR
841		 * immediately after setting CLOCAL, which applications do not
842		 * expect.  We always assert DTR while the device is open
843		 * unless explicitly requested to deassert it.
844		 */
845		txcom_modem(sc, 1);
846
847		/* Clear the input ring, and unblock. */
848		sc->sc_rbget = sc->sc_rbput = 0;
849	}
850
851	splx(s);
852#define	TXCOMDIALOUT(x)	TTDIALOUT(x)
853	if ((err = ttyopen(tp, TXCOMDIALOUT(dev), ISSET(flag, O_NONBLOCK)))) {
854		DPRINTF("ttyopen failed\n");
855		goto out;
856	}
857	if ((err = (*tp->t_linesw->l_open)(dev, tp))) {
858		DPRINTF("line dicipline open failed\n");
859		goto out;
860	}
861
862	return err;
863
864 out:
865	if (!ISSET(tp->t_state, TS_ISOPEN) && tp->t_wopen == 0) {
866		/*
867		 * We failed to open the device, and nobody else had it opened.
868		 * Clean up the state as appropriate.
869		 */
870		txcom_shutdown(sc);
871	}
872
873	return err;
874
875}
876
877int
878txcomclose(dev_t dev, int flag, int mode, struct lwp *l)
879{
880	struct txcom_softc *sc = device_lookup_private(&txcom_cd, minor(dev));
881	struct tty *tp = sc->sc_tty;
882
883	/* XXX This is for cons.c. */
884	if (!ISSET(tp->t_state, TS_ISOPEN))
885		return 0;
886
887	(*tp->t_linesw->l_close)(tp, flag);
888	ttyclose(tp);
889
890	if (!ISSET(tp->t_state, TS_ISOPEN) && tp->t_wopen == 0) {
891		/*
892		 * Although we got a last close, the device may still be in
893		 * use; e.g. if this was the dialout node, and there are still
894		 * processes waiting for carrier on the non-dialout node.
895		 */
896		txcom_shutdown(sc);
897	}
898
899	return 0;
900}
901
902int
903txcomread(dev_t dev, struct uio *uio, int flag)
904{
905	struct txcom_softc *sc = device_lookup_private(&txcom_cd, minor(dev));
906	struct tty *tp = sc->sc_tty;
907
908	return ((*tp->t_linesw->l_read)(tp, uio, flag));
909}
910
911int
912txcomwrite(dev_t dev, struct uio *uio, int flag)
913{
914	struct txcom_softc *sc = device_lookup_private(&txcom_cd, minor(dev));
915	struct tty *tp = sc->sc_tty;
916
917	return ((*tp->t_linesw->l_write)(tp, uio, flag));
918}
919
920int
921txcompoll(dev_t dev, int events, struct lwp *l)
922{
923	struct txcom_softc *sc = device_lookup_private(&txcom_cd, minor(dev));
924	struct tty *tp = sc->sc_tty;
925
926	return ((*tp->t_linesw->l_poll)(tp, events, l));
927}
928
929struct tty *
930txcomtty(dev_t dev)
931{
932	struct txcom_softc *sc = device_lookup_private(&txcom_cd, minor(dev));
933
934	return sc->sc_tty;
935}
936
937int
938txcomioctl(dev_t dev, u_long cmd, void *data, int flag, struct lwp *l)
939{
940	struct txcom_softc *sc = device_lookup_private(&txcom_cd, minor(dev));
941	struct tty *tp = sc->sc_tty;
942	int s, err;
943
944	err = (*tp->t_linesw->l_ioctl)(tp, cmd, data, flag, l);
945	if (err != EPASSTHROUGH) {
946		return err;
947	}
948
949	err = ttioctl(tp, cmd, data, flag, l);
950	if (err != EPASSTHROUGH) {
951		return err;
952	}
953
954	err = 0;
955
956	s = spltty();
957
958	switch (cmd) {
959	default:
960		err = EPASSTHROUGH;
961		break;
962
963	case TIOCSBRK:
964		txcom_break(sc, 1);
965		break;
966
967	case TIOCCBRK:
968		txcom_break(sc, 0);
969		break;
970
971	case TIOCSDTR:
972		txcom_modem(sc, 1);
973		break;
974
975	case TIOCCDTR:
976		txcom_modem(sc, 0);
977		break;
978
979	case TIOCGFLAGS:
980		*(int *)data = sc->sc_chip->sc_swflags;
981		break;
982
983	case TIOCSFLAGS:
984		err = kauth_authorize_device_tty(l->l_cred,
985		    KAUTH_DEVICE_TTY_PRIVSET, tp);
986		if (err) {
987			break;
988		}
989		sc->sc_chip->sc_swflags = *(int *)data;
990		break;
991
992	}
993
994	splx(s);
995
996	return err;
997}
998
999void
1000txcomstop(struct tty *tp, int flag)
1001{
1002	struct txcom_softc *sc;
1003	int s;
1004
1005	sc = device_lookup_private(&txcom_cd, minor(tp->t_dev));
1006
1007	s = spltty();
1008
1009	if (ISSET(tp->t_state, TS_BUSY)) {
1010		/* Stop transmitting at the next chunk. */
1011		sc->sc_tbc = 0;
1012		sc->sc_heldtbc = 0;
1013		if (!ISSET(tp->t_state, TS_TTSTOP))
1014			SET(tp->t_state, TS_FLUSH);
1015	}
1016
1017	splx(s);
1018}
1019
1020void
1021txcomstart(struct tty *tp)
1022{
1023	struct txcom_softc *sc;
1024	struct txcom_chip *chip;
1025	tx_chipset_tag_t tc;
1026	int slot;
1027	int s;
1028
1029	sc = device_lookup_private(&txcom_cd, minor(tp->t_dev));
1030	chip = sc->sc_chip;
1031	tc = chip->sc_tc;
1032	slot = chip->sc_slot;
1033
1034	s = spltty();
1035
1036	if (!__txcom_txbufready(chip, 0) ||
1037	    ISSET(tp->t_state, TS_BUSY | TS_TIMEOUT | TS_TTSTOP))
1038		goto out;
1039
1040	if (!ttypull(tp))
1041		goto out;
1042
1043	sc->sc_tba = tp->t_outq.c_cf;
1044	sc->sc_tbc = ndqb(&tp->t_outq, 0);
1045	SET(tp->t_state, TS_BUSY);
1046
1047	/* Output the first character of the contiguous buffer. */
1048	tx_conf_write(tc, TX39_UARTTXHOLD_REG(slot),
1049	    (*sc->sc_tba & TX39_UARTTXHOLD_TXDATA_MASK));
1050
1051	sc->sc_tbc--;
1052	sc->sc_tba++;
1053
1054 out:
1055	splx(s);
1056}
1057
1058/*
1059 * Set TXcom tty parameters from termios.
1060 */
1061int
1062txcomparam(struct tty *tp, struct termios *t)
1063{
1064	struct txcom_softc *sc;
1065	struct txcom_chip *chip;
1066	int ospeed;
1067	int s;
1068
1069	sc = device_lookup_private(&txcom_cd, minor(tp->t_dev));
1070	if (sc == NULL)
1071		return ENXIO;
1072
1073	ospeed = t->c_ospeed;
1074
1075	/* Check requested parameters. */
1076	if (ospeed < 0) {
1077		return EINVAL;
1078	}
1079	if (t->c_ispeed && t->c_ispeed != ospeed) {
1080		return EINVAL;
1081	}
1082
1083	s = spltty();
1084	chip = sc->sc_chip;
1085	/*
1086	 * For the console, always force CLOCAL and !HUPCL, so that the port
1087	 * is always active.
1088	 */
1089	if (ISSET(chip->sc_swflags, TIOCFLAG_SOFTCAR) ||
1090	    ISSET(chip->sc_hwflags, TXCOM_HW_CONSOLE)) {
1091		SET(t->c_cflag, CLOCAL);
1092		CLR(t->c_cflag, HUPCL);
1093	}
1094	splx(s);
1095
1096	/*
1097	 * If we're not in a mode that assumes a connection is present, then
1098	 * ignore carrier changes.
1099	 */
1100	if (ISSET(t->c_cflag, CLOCAL | MDMBUF))
1101		chip->sc_dcd = 0;
1102	else
1103		chip->sc_dcd = 1;
1104
1105	/*
1106	 * Only whack the UART when params change.
1107	 * Some callers need to clear tp->t_ospeed
1108	 * to make sure initialization gets done.
1109	 */
1110	if (tp->t_ospeed == ospeed && tp->t_cflag == t->c_cflag) {
1111		return 0;
1112	}
1113
1114	s = spltty();
1115	chip = sc->sc_chip;
1116	chip->sc_speed = ospeed;
1117	chip->sc_cflag = t->c_cflag;
1118
1119	txcom_setmode(chip);
1120	txcom_setbaudrate(chip);
1121
1122	/* And copy to tty. */
1123	tp->t_ispeed = 0;
1124	tp->t_ospeed = chip->sc_speed;
1125	tp->t_cflag = chip->sc_cflag;
1126
1127	/*
1128	 * Update the tty layer's idea of the carrier bit, in case we changed
1129	 * CLOCAL or MDMBUF.  We don't hang up here; we only do that by
1130	 * explicit request.
1131	 */
1132	(void) (*tp->t_linesw->l_modem)(tp, chip->sc_dcd);
1133
1134	/*
1135	 * If hardware flow control is disabled, unblock any hard flow
1136	 * control state.
1137	 */
1138	if (!ISSET(chip->sc_cflag, CHWFLOW)) {
1139		txcomstart(tp);
1140	}
1141
1142	splx(s);
1143
1144	return 0;
1145}
1146
1147int
1148txcom_dcd_hook(void *arg, int type, long id, void *msg)
1149{
1150	struct txcom_softc *sc = arg;
1151	struct tty *tp = sc->sc_tty;
1152	struct txcom_chip *chip = sc->sc_chip;
1153	int modem = !(int)msg; /* p-edge 1, n-edge 0 */
1154
1155	DPRINTF("DCD %s\n", modem ? "ON" : "OFF");
1156
1157	if (modem && chip->sc_dcd)
1158		(void) (*tp->t_linesw->l_modem)(tp, chip->sc_dcd);
1159
1160	return 0;
1161}
1162
1163int
1164txcom_cts_hook(void *arg, int type, long id, void *msg)
1165{
1166	struct txcom_softc *sc = arg;
1167	struct tty *tp = sc->sc_tty;
1168	struct txcom_chip *chip = sc->sc_chip;
1169	int clear = !(int)msg; /* p-edge 1, n-edge 0 */
1170
1171	DPRINTF("CTS %s\n", clear ? "ON"  : "OFF");
1172
1173	if (chip->sc_msr_cts) {
1174		if (!clear) {
1175			chip->sc_tx_stopped = 1;
1176		} else {
1177			chip->sc_tx_stopped = 0;
1178			(*tp->t_linesw->l_start)(tp);
1179		}
1180	}
1181
1182	return 0;
1183}
1184
1185#ifdef TX39UARTDEBUG
1186void
1187txcom_dump(struct txcom_chip *chip)
1188{
1189	tx_chipset_tag_t tc = chip->sc_tc;
1190	int slot = chip->sc_slot;
1191	txreg_t reg;
1192
1193	reg = tx_conf_read(tc, TX39_UARTCTRL1_REG(slot));
1194#define ISSETPRINT(r, m) \
1195	dbg_bitmask_print(r, TX39_UARTCTRL1_##m, #m)
1196	ISSETPRINT(reg, UARTON);
1197	ISSETPRINT(reg, EMPTY);
1198	ISSETPRINT(reg, PRXHOLDFULL);
1199	ISSETPRINT(reg, RXHOLDFULL);
1200	ISSETPRINT(reg, ENDMARX);
1201	ISSETPRINT(reg, ENDMATX);
1202	ISSETPRINT(reg, TESTMODE);
1203	ISSETPRINT(reg, ENBREAHALT);
1204	ISSETPRINT(reg, ENDMATEST);
1205	ISSETPRINT(reg, ENDMALOOP);
1206	ISSETPRINT(reg, PULSEOPT2);
1207	ISSETPRINT(reg, PULSEOPT1);
1208	ISSETPRINT(reg, DTINVERT);
1209	ISSETPRINT(reg, DISTXD);
1210	ISSETPRINT(reg, TWOSTOP);
1211	ISSETPRINT(reg, LOOPBACK);
1212	ISSETPRINT(reg, BIT7);
1213	ISSETPRINT(reg, EVENPARITY);
1214	ISSETPRINT(reg, ENPARITY);
1215	ISSETPRINT(reg, ENUART);
1216}
1217#endif /* TX39UARTDEBUG */
1218