xref: /netbsd-src/sys/arch/arm/clps711x/clpscom.c (revision 5f873d3f828b894c32b261c168a15e549b070e4c)
1*5f873d3fSandvar /*      $NetBSD: clpscom.c,v 1.11 2023/09/01 08:53:52 andvar Exp $      */
20514024aSkiyohara /*
30514024aSkiyohara  * Copyright (c) 2013 KIYOHARA Takashi
40514024aSkiyohara  * All rights reserved.
50514024aSkiyohara  *
60514024aSkiyohara  * Redistribution and use in source and binary forms, with or without
70514024aSkiyohara  * modification, are permitted provided that the following conditions
80514024aSkiyohara  * are met:
90514024aSkiyohara  * 1. Redistributions of source code must retain the above copyright
100514024aSkiyohara  *    notice, this list of conditions and the following disclaimer.
110514024aSkiyohara  * 2. Redistributions in binary form must reproduce the above copyright
120514024aSkiyohara  *    notice, this list of conditions and the following disclaimer in the
130514024aSkiyohara  *    documentation and/or other materials provided with the distribution.
140514024aSkiyohara  *
150514024aSkiyohara  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
160514024aSkiyohara  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
170514024aSkiyohara  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
180514024aSkiyohara  * DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
190514024aSkiyohara  * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
200514024aSkiyohara  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
210514024aSkiyohara  * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
220514024aSkiyohara  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
230514024aSkiyohara  * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
240514024aSkiyohara  * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
250514024aSkiyohara  * POSSIBILITY OF SUCH DAMAGE.
260514024aSkiyohara  */
270514024aSkiyohara #include <sys/cdefs.h>
28*5f873d3fSandvar __KERNEL_RCSID(0, "$NetBSD: clpscom.c,v 1.11 2023/09/01 08:53:52 andvar Exp $");
290514024aSkiyohara 
300514024aSkiyohara #include "rnd.h"
310514024aSkiyohara 
320514024aSkiyohara #include <sys/param.h>
330514024aSkiyohara #include <sys/bus.h>
340514024aSkiyohara #include <sys/conf.h>
350514024aSkiyohara #include <sys/device.h>
360514024aSkiyohara #include <sys/errno.h>
370514024aSkiyohara #include <sys/fcntl.h>
380514024aSkiyohara #include <sys/intr.h>
390514024aSkiyohara #include <sys/kauth.h>
400514024aSkiyohara #include <sys/lwp.h>
4138fdb085Sthorpej #include <sys/kmem.h>
420514024aSkiyohara #include <sys/systm.h>
430514024aSkiyohara #include <sys/termios.h>
440514024aSkiyohara #include <sys/tty.h>
450514024aSkiyohara #include <sys/types.h>
460514024aSkiyohara 
47dbfa10e5Sriastradh #include <ddb/db_active.h>
48dbfa10e5Sriastradh 
490514024aSkiyohara #include <arm/clps711x/clps711xreg.h>
500514024aSkiyohara #include <arm/clps711x/clpssocvar.h>
510514024aSkiyohara 
520514024aSkiyohara #include <dev/cons.h>
530514024aSkiyohara 
540514024aSkiyohara #ifdef RND_COM
55445478ceSriastradh #include <sys/rndsource.h>
560514024aSkiyohara #endif
570514024aSkiyohara 
580514024aSkiyohara #include "ioconf.h"
590514024aSkiyohara #include "locators.h"
600514024aSkiyohara 
61a0a6c85fSchristos #define COMUNIT(x)	TTUNIT(x)
62a0a6c85fSchristos #define COMDIALOUT(x)	TTDIALOUT(x)
630514024aSkiyohara 
640514024aSkiyohara #define CLPSCOM_RING_SIZE	2048
650514024aSkiyohara #define UART_FIFO_SIZE		16
660514024aSkiyohara 
670514024aSkiyohara #define CLPSCOM_READ_CON(sc) \
680514024aSkiyohara 	bus_space_read_4((sc)->sc_iot, (sc)->sc_ioh, PS711X_SYSCON)
690514024aSkiyohara #define CLPSCOM_WRITE_CON(sc, val) \
700514024aSkiyohara 	bus_space_write_4((sc)->sc_iot, (sc)->sc_ioh, PS711X_SYSCON, (val))
710514024aSkiyohara #define CLPSCOM_READ_FLG(sc) \
720514024aSkiyohara 	bus_space_read_4((sc)->sc_iot, (sc)->sc_ioh, PS711X_SYSFLG)
730514024aSkiyohara #define CLPSCOM_WRITE_FLG(sc, val) \
740514024aSkiyohara 	bus_space_write_4((sc)->sc_iot, (sc)->sc_ioh, PS711X_SYSFLG, (val))
750514024aSkiyohara #define CLPSCOM_READ(sc) \
760514024aSkiyohara 	bus_space_read_4((sc)->sc_iot, (sc)->sc_ioh, PS711X_UARTDR)
770514024aSkiyohara #define CLPSCOM_WRITE(sc, val) \
780514024aSkiyohara 	bus_space_write_1((sc)->sc_iot, (sc)->sc_ioh, PS711X_UARTDR, (val))
790514024aSkiyohara #define CLPSCOM_WRITE_MULTI(sc, val, n) \
800514024aSkiyohara   bus_space_write_multi_1((sc)->sc_iot, (sc)->sc_ioh, PS711X_UARTDR, (val), (n))
810514024aSkiyohara #define CLPSCOM_READ_UBRLCR(sc) \
820514024aSkiyohara 	bus_space_read_4((sc)->sc_iot, (sc)->sc_ioh, PS711X_UBRLCR)
830514024aSkiyohara #define CLPSCOM_WRITE_UBRLCR(sc, val) \
840514024aSkiyohara 	bus_space_write_4((sc)->sc_iot, (sc)->sc_ioh, PS711X_UBRLCR, (val))
850514024aSkiyohara 
860514024aSkiyohara struct clpscom_softc {
870514024aSkiyohara 	device_t sc_dev;
880514024aSkiyohara 	bus_space_tag_t sc_iot;
890514024aSkiyohara 	bus_space_handle_t sc_ioh;
900514024aSkiyohara 	int sc_irq[3];
910514024aSkiyohara 	void *sc_ih[3];
920514024aSkiyohara #define CLPSCOM_TXINT	0
930514024aSkiyohara #define CLPSCOM_RXINT	1
940514024aSkiyohara #define CLPSCOM_MSINT	2
950514024aSkiyohara 
960514024aSkiyohara 	void *sc_si;
970514024aSkiyohara 
980514024aSkiyohara 	struct tty *sc_tty;
990514024aSkiyohara 
1000514024aSkiyohara 	u_char *sc_tba;
1010514024aSkiyohara 	u_int sc_tbc;
1020514024aSkiyohara 	u_char *sc_rbuf;
1030514024aSkiyohara 	char *volatile sc_rbget;
1040514024aSkiyohara 	char *volatile sc_rbput;
1050514024aSkiyohara 	volatile int sc_rbavail;
1060514024aSkiyohara 
1070514024aSkiyohara #define CLPSCOM_MODEM_STATUS_MASK (SYSFLG_DCD | SYSFLG_DSR | SYSFLG_CTS)
1080514024aSkiyohara 	uint32_t sc_ms;
1090514024aSkiyohara 	uint32_t sc_ms_dcd;
1100514024aSkiyohara 	uint32_t sc_ms_cts;
1110514024aSkiyohara 	uint32_t sc_ms_mask;
1120514024aSkiyohara 	uint32_t sc_ms_delta;
1130514024aSkiyohara 
1140514024aSkiyohara 	int sc_tx_stopped;
1150514024aSkiyohara 
1160514024aSkiyohara 	int sc_tx_done;
1170514024aSkiyohara 	int sc_rx_ready;
1180514024aSkiyohara 	int sc_ms_changed;
1190514024aSkiyohara 
1200514024aSkiyohara 	int sc_hwflags;
1210514024aSkiyohara #define COM_HW_CONSOLE	(1 << 0)
1220514024aSkiyohara #define COM_HW_DEV_OK	(1 << 1)
1230514024aSkiyohara #define COM_HW_KGDB	(1 << 2)
1240514024aSkiyohara 	int sc_swflags;
1250514024aSkiyohara 
1260514024aSkiyohara #ifdef RND_COM
127*5f873d3fSandvar 	krndsource_t rnd_source;
1280514024aSkiyohara #endif
1290514024aSkiyohara };
1300514024aSkiyohara 
1310514024aSkiyohara static int clpscom_match(device_t, cfdata_t, void *);
1320514024aSkiyohara static void clpscom_attach(device_t, device_t, void *);
1330514024aSkiyohara 
1340514024aSkiyohara static int clpscom_txintr(void *);
1350514024aSkiyohara static int clpscom_rxintr(void *);
1360514024aSkiyohara static int clpscom_msintr(void *);
1370514024aSkiyohara static void clpscom_soft(void *);
1380514024aSkiyohara 
1390514024aSkiyohara static void clpscom_start(struct tty *);
1400514024aSkiyohara static int clpscom_param(struct tty *, struct termios *);
1410514024aSkiyohara static int clpscom_hwiflow(struct tty *, int);
1420514024aSkiyohara 
1430514024aSkiyohara dev_type_open(clpscomopen);
1440514024aSkiyohara dev_type_close(clpscomclose);
1450514024aSkiyohara dev_type_read(clpscomread);
1460514024aSkiyohara dev_type_write(clpscomwrite);
1470514024aSkiyohara dev_type_ioctl(clpscomioctl);
1480514024aSkiyohara dev_type_stop(clpscomstop);
1490514024aSkiyohara dev_type_tty(clpscomtty);
1500514024aSkiyohara dev_type_poll(clpscompoll);
1510514024aSkiyohara 
1520514024aSkiyohara static void clpscom_iflush(struct clpscom_softc *);
1530514024aSkiyohara static void clpscom_shutdown(struct clpscom_softc *);
1540514024aSkiyohara static void clpscom_break(struct clpscom_softc *, int);
1550514024aSkiyohara static int clpscom_to_tiocm(struct clpscom_softc *);
1560514024aSkiyohara 
1570514024aSkiyohara static void clpscom_rxsoft(struct clpscom_softc *, struct tty *);
1580514024aSkiyohara static void clpscom_mssoft(struct clpscom_softc *, struct tty *);
1590514024aSkiyohara 
1600514024aSkiyohara static inline uint32_t clpscom_rate2ubrlcr(int);
1610514024aSkiyohara static uint32_t clpscom_cflag2ubrlcr(tcflag_t);
1620514024aSkiyohara 
1630514024aSkiyohara static int clpscom_cngetc(dev_t);
1640514024aSkiyohara static void clpscom_cnputc(dev_t, int);
1650514024aSkiyohara static void clpscom_cnpollc(dev_t, int);
1660514024aSkiyohara 
1670514024aSkiyohara CFATTACH_DECL_NEW(clpscom, sizeof(struct clpscom_softc),
1680514024aSkiyohara     clpscom_match, clpscom_attach, NULL, NULL);
1690514024aSkiyohara 
1700514024aSkiyohara const struct cdevsw clpscom_cdevsw = {
171a68f9396Sdholland 	.d_open = clpscomopen,
172a68f9396Sdholland 	.d_close = clpscomclose,
173a68f9396Sdholland 	.d_read = clpscomread,
174a68f9396Sdholland 	.d_write = clpscomwrite,
175a68f9396Sdholland 	.d_ioctl = clpscomioctl,
176a68f9396Sdholland 	.d_stop = clpscomstop,
177a68f9396Sdholland 	.d_tty = clpscomtty,
178a68f9396Sdholland 	.d_poll = clpscompoll,
179a68f9396Sdholland 	.d_mmap = nommap,
180a68f9396Sdholland 	.d_kqfilter = ttykqfilter,
181f9228f42Sdholland 	.d_discard = nodiscard,
182a68f9396Sdholland 	.d_flag = D_TTY
1830514024aSkiyohara };
1840514024aSkiyohara 
1850514024aSkiyohara static struct cnm_state clpscom_cnm_state;
1860514024aSkiyohara static vaddr_t clpscom_cnaddr = 0;
1870514024aSkiyohara static int clpscom_cnrate;
1880514024aSkiyohara static tcflag_t clpscom_cncflag;
1890514024aSkiyohara 
1900514024aSkiyohara 
1910514024aSkiyohara /* ARGSUSED */
1920514024aSkiyohara static int
clpscom_match(device_t parent,cfdata_t match,void * aux)1930514024aSkiyohara clpscom_match(device_t parent, cfdata_t match, void *aux)
1940514024aSkiyohara {
1950514024aSkiyohara 
1960514024aSkiyohara 	return 1;
1970514024aSkiyohara }
1980514024aSkiyohara 
1990514024aSkiyohara /* ARGSUSED */
2000514024aSkiyohara static void
clpscom_attach(device_t parent,device_t self,void * aux)2010514024aSkiyohara clpscom_attach(device_t parent, device_t self, void *aux)
2020514024aSkiyohara {
2030514024aSkiyohara 	struct clpscom_softc *sc = device_private(self);
2040514024aSkiyohara 	struct clpssoc_attach_args *aa = aux;
2050514024aSkiyohara 	int i;
2060514024aSkiyohara 
2070514024aSkiyohara 	aprint_naive("\n");
2080514024aSkiyohara 	aprint_normal("\n");
2090514024aSkiyohara 
2100514024aSkiyohara 	sc->sc_dev = self;
2110514024aSkiyohara 	sc->sc_iot = aa->aa_iot;
2120514024aSkiyohara 	sc->sc_ioh = *aa->aa_ioh;
2130514024aSkiyohara 	for (i = 0; i < __arraycount(aa->aa_irq); i++) {
2140514024aSkiyohara 		sc->sc_irq[i] = aa->aa_irq[i];
2150514024aSkiyohara 		sc->sc_ih[i] = NULL;
2160514024aSkiyohara 	}
2170514024aSkiyohara 
2180514024aSkiyohara 	if (clpscom_cnaddr != 0)
2190514024aSkiyohara 		SET(sc->sc_hwflags, COM_HW_CONSOLE);
2200514024aSkiyohara 
2210514024aSkiyohara 	sc->sc_tty = tty_alloc();
2220514024aSkiyohara 	sc->sc_tty->t_oproc = clpscom_start;
2230514024aSkiyohara 	sc->sc_tty->t_param = clpscom_param;
2240514024aSkiyohara 	sc->sc_tty->t_hwiflow = clpscom_hwiflow;
2250514024aSkiyohara 
2260514024aSkiyohara 	sc->sc_tbc = 0;
22738fdb085Sthorpej 	sc->sc_rbuf = kmem_alloc(CLPSCOM_RING_SIZE << 1, KM_SLEEP);
2280514024aSkiyohara 	sc->sc_rbput = sc->sc_rbget = sc->sc_rbuf;
2290514024aSkiyohara 	sc->sc_rbavail = CLPSCOM_RING_SIZE;
2300514024aSkiyohara 
2310514024aSkiyohara 	tty_attach(sc->sc_tty);
2320514024aSkiyohara 
2330514024aSkiyohara 	if (ISSET(sc->sc_hwflags, COM_HW_CONSOLE)) {
2340514024aSkiyohara 		int maj = cdevsw_lookup_major(&clpscom_cdevsw);
2350514024aSkiyohara 
2360514024aSkiyohara 		sc->sc_tty->t_dev = makedev(maj, device_unit(sc->sc_dev));
2370514024aSkiyohara 		cn_tab->cn_dev = sc->sc_tty->t_dev;
2380514024aSkiyohara 
2390514024aSkiyohara 		aprint_normal_dev(self, "console\n");
2400514024aSkiyohara 	}
2410514024aSkiyohara 
2420514024aSkiyohara 	sc->sc_si = softint_establish(SOFTINT_SERIAL, clpscom_soft, sc);
2430514024aSkiyohara 
2440514024aSkiyohara #ifdef RND_COM
2450514024aSkiyohara 	rnd_attach_source(&sc->rnd_source, device_xname(sc->sc_dev),
246ea6af427Stls 	    RND_TYPE_TTY, RND_FLAG_DEFAULT);
2470514024aSkiyohara #endif
2480514024aSkiyohara 
2490514024aSkiyohara 	SET(sc->sc_hwflags, COM_HW_DEV_OK);
2500514024aSkiyohara }
2510514024aSkiyohara 
2520514024aSkiyohara static int
clpscom_txintr(void * arg)2530514024aSkiyohara clpscom_txintr(void *arg)
2540514024aSkiyohara {
2550514024aSkiyohara 	struct clpscom_softc *sc = arg;
2560514024aSkiyohara 	uint32_t sysflg;
2570514024aSkiyohara 
2580514024aSkiyohara 	if (!device_is_active(sc->sc_dev))
2590514024aSkiyohara 		return 0;
2600514024aSkiyohara 
2610514024aSkiyohara 	sysflg = CLPSCOM_READ_FLG(sc);
2620514024aSkiyohara 
2630514024aSkiyohara 	/*
2640514024aSkiyohara 	 * Done handling any receive interrupts. See if data can be
2650514024aSkiyohara 	 * transmitted as well. Schedule tx done event if no data left
2660514024aSkiyohara 	 * and tty was marked busy.
2670514024aSkiyohara 	 */
2680514024aSkiyohara 
2690514024aSkiyohara 	if (!ISSET(sysflg, SYSFLG_UTXFF)) {
2700514024aSkiyohara 		/* Output the next chunk of the contiguous buffer, if any. */
2710514024aSkiyohara 		if (sc->sc_tbc > 0) {
2720514024aSkiyohara 			while (sc->sc_tbc > 0 && !ISSET(sysflg, SYSFLG_UTXFF)) {
2730514024aSkiyohara 				CLPSCOM_WRITE(sc, *sc->sc_tba);
2740514024aSkiyohara 				sc->sc_tba++;
2750514024aSkiyohara 				sc->sc_tbc--;
2760514024aSkiyohara 				sysflg = CLPSCOM_READ_FLG(sc);
2770514024aSkiyohara 			}
2780514024aSkiyohara 		} else if (!ISSET(sysflg, SYSFLG_UBUSY) &&
2790514024aSkiyohara 					sc->sc_ih[CLPSCOM_TXINT] != NULL) {
2800514024aSkiyohara 			intr_disestablish(sc->sc_ih[CLPSCOM_TXINT]);
2810514024aSkiyohara 			sc->sc_ih[CLPSCOM_TXINT] = NULL;
2820514024aSkiyohara 			sc->sc_tx_done = 1;
2830514024aSkiyohara 		}
2840514024aSkiyohara 	}
2850514024aSkiyohara 
2860514024aSkiyohara 	/* Wake up the poller. */
2870514024aSkiyohara 	softint_schedule(sc->sc_si);
2880514024aSkiyohara 
2890514024aSkiyohara 	return 1;
2900514024aSkiyohara }
2910514024aSkiyohara 
2920514024aSkiyohara static int
clpscom_rxintr(void * arg)2930514024aSkiyohara clpscom_rxintr(void *arg)
2940514024aSkiyohara {
2950514024aSkiyohara 	struct clpscom_softc *sc = arg;
2960514024aSkiyohara 	int cc;
2970514024aSkiyohara 	uint32_t sysflg;
2980514024aSkiyohara 	uint16_t data;
2990514024aSkiyohara 	u_char *put;
3000514024aSkiyohara 
3010514024aSkiyohara 	if (!device_is_active(sc->sc_dev))
3020514024aSkiyohara 		return 0;
3030514024aSkiyohara 
3040514024aSkiyohara 	if (sc->sc_ih[CLPSCOM_RXINT] != NULL) {
3050514024aSkiyohara 		put = sc->sc_rbput;
3060514024aSkiyohara 		cc = sc->sc_rbavail;
3070514024aSkiyohara 		while (cc > 0) {
3080514024aSkiyohara 			sysflg = CLPSCOM_READ_FLG(sc);
3090514024aSkiyohara 			if (ISSET(sysflg, SYSFLG_URXFE))
3100514024aSkiyohara 				break;
3110514024aSkiyohara 			data = CLPSCOM_READ(sc);
3120514024aSkiyohara 			cn_check_magic(sc->sc_tty->t_dev, data & 0xff,
3130514024aSkiyohara 			    clpscom_cnm_state);
3140514024aSkiyohara 
3150514024aSkiyohara 			put[0] = data & 0xff;
3160514024aSkiyohara 			put[1] = (data >> 8) & 0xff;
3170514024aSkiyohara 			put += 2;
3180514024aSkiyohara 			if (put >= sc->sc_rbuf + (CLPSCOM_RING_SIZE << 1))
3190514024aSkiyohara 				put = sc->sc_rbuf;
3200514024aSkiyohara 			cc--;
3210514024aSkiyohara 			sc->sc_rx_ready = 1;
3220514024aSkiyohara 		}
3230514024aSkiyohara 
3240514024aSkiyohara 		/*
3250514024aSkiyohara 		 * Current string of incoming characters ended because
3260514024aSkiyohara 		 * no more data was available or we ran out of space.
3270514024aSkiyohara 		 * Schedule a receive event if any data was received.
3280514024aSkiyohara 		 * If we're out of space, turn off receive interrupts.
3290514024aSkiyohara 		 */
3300514024aSkiyohara 		sc->sc_rbput = put;
3310514024aSkiyohara 		sc->sc_rbavail = cc;
3320514024aSkiyohara 
3330514024aSkiyohara 		/*
3340514024aSkiyohara 		 * See if we are in danger of overflowing a buffer. If
3350514024aSkiyohara 		 * so, use hardware flow control to ease the pressure.
3360514024aSkiyohara 		 */
3370514024aSkiyohara 
3380514024aSkiyohara 		/* but clpscom cannot. X-( */
3390514024aSkiyohara 
3400514024aSkiyohara 		/*
3410514024aSkiyohara 		 * If we're out of space, disable receive interrupts
3420514024aSkiyohara 		 * until the queue has drained a bit.
3430514024aSkiyohara 		 */
3440514024aSkiyohara 		if (cc <= 0) {
3450514024aSkiyohara 			intr_disestablish(sc->sc_ih[CLPSCOM_RXINT]);
3460514024aSkiyohara 			sc->sc_ih[CLPSCOM_RXINT] = NULL;
3470514024aSkiyohara 		}
3480514024aSkiyohara 	}
3490514024aSkiyohara 
3500514024aSkiyohara 	/* Wake up the poller. */
3510514024aSkiyohara 	softint_schedule(sc->sc_si);
3520514024aSkiyohara 
3530514024aSkiyohara 	return 1;
3540514024aSkiyohara }
3550514024aSkiyohara 
3560514024aSkiyohara static int
clpscom_msintr(void * arg)3570514024aSkiyohara clpscom_msintr(void *arg)
3580514024aSkiyohara {
3590514024aSkiyohara 	struct clpscom_softc *sc = arg;
3600514024aSkiyohara 	uint32_t ms, delta;
3610514024aSkiyohara 
3620514024aSkiyohara 	if (!device_is_active(sc->sc_dev))
3630514024aSkiyohara 		return 0;
3640514024aSkiyohara 
3650514024aSkiyohara 	if (sc->sc_ih[CLPSCOM_MSINT] != NULL) {
3660514024aSkiyohara 		ms = CLPSCOM_READ_FLG(sc) & CLPSCOM_MODEM_STATUS_MASK;
3670514024aSkiyohara 		delta = ms ^ sc->sc_ms;
3680514024aSkiyohara 		sc->sc_ms = ms;
3690514024aSkiyohara 
3700514024aSkiyohara 		if (ISSET(delta, sc->sc_ms_mask)) {
3710514024aSkiyohara 			SET(sc->sc_ms_delta, delta);
3720514024aSkiyohara 
3730514024aSkiyohara 			/*
3740514024aSkiyohara 			 * Stop output immediately if we lose the output
3750514024aSkiyohara 			 * flow control signal or carrier detect.
3760514024aSkiyohara 			 */
3770514024aSkiyohara 			if (ISSET(~ms, sc->sc_ms_mask))
3780514024aSkiyohara 				sc->sc_tbc = 0;
3790514024aSkiyohara 			sc->sc_ms_changed = 1;
3800514024aSkiyohara 		}
3810514024aSkiyohara 	}
3820514024aSkiyohara 	bus_space_write_4(sc->sc_iot, sc->sc_ioh, PS711X_UMSEOI, 1);
3830514024aSkiyohara 
3840514024aSkiyohara 	/* Wake up the poller. */
3850514024aSkiyohara 	softint_schedule(sc->sc_si);
3860514024aSkiyohara 
3870514024aSkiyohara 	return 1;
3880514024aSkiyohara }
3890514024aSkiyohara 
3900514024aSkiyohara static void
clpscom_soft(void * arg)3910514024aSkiyohara clpscom_soft(void *arg)
3920514024aSkiyohara {
3930514024aSkiyohara 	struct clpscom_softc *sc = arg;
3940514024aSkiyohara 	struct tty *tp = sc->sc_tty;
3950514024aSkiyohara 
3960514024aSkiyohara 	if (!device_is_active(sc->sc_dev))
3970514024aSkiyohara 		return;
3980514024aSkiyohara 
3990514024aSkiyohara 	if (sc->sc_rx_ready) {
4000514024aSkiyohara 		sc->sc_rx_ready = 0;
4010514024aSkiyohara 		clpscom_rxsoft(sc, tp);
4020514024aSkiyohara 	}
4030514024aSkiyohara 	if (sc->sc_tx_done) {
4040514024aSkiyohara 		sc->sc_tx_done = 0;
4050514024aSkiyohara 		CLR(tp->t_state, TS_BUSY);
4060514024aSkiyohara 		if (ISSET(tp->t_state, TS_FLUSH))
4070514024aSkiyohara 			CLR(tp->t_state, TS_FLUSH);
4080514024aSkiyohara 		else
4090514024aSkiyohara 			ndflush(&tp->t_outq,
4100514024aSkiyohara 			    (int)(sc->sc_tba - tp->t_outq.c_cf));
4110514024aSkiyohara 		(*tp->t_linesw->l_start)(tp);
4120514024aSkiyohara 	}
4130514024aSkiyohara 	if (sc->sc_ms_changed == 1) {
4140514024aSkiyohara 		sc->sc_ms_changed = 0;
4150514024aSkiyohara 		clpscom_mssoft(sc, tp);
4160514024aSkiyohara 	}
4170514024aSkiyohara }
4180514024aSkiyohara 
4190514024aSkiyohara static void
clpscom_start(struct tty * tp)4200514024aSkiyohara clpscom_start(struct tty *tp)
4210514024aSkiyohara {
4220514024aSkiyohara 	struct clpscom_softc *sc
4230514024aSkiyohara 		= device_lookup_private(&clpscom_cd, COMUNIT(tp->t_dev));
4240514024aSkiyohara 	int s, n;
4250514024aSkiyohara 
4260514024aSkiyohara 	if (!device_is_active(sc->sc_dev))
4270514024aSkiyohara 		return;
4280514024aSkiyohara 
4290514024aSkiyohara 	s = spltty();
4300514024aSkiyohara 	if (ISSET(tp->t_state, TS_BUSY | TS_TIMEOUT | TS_TTSTOP))
4310514024aSkiyohara 		goto out;
4320514024aSkiyohara 	if (sc->sc_tx_stopped)
4330514024aSkiyohara 		goto out;
4340514024aSkiyohara 	if (!ttypull(tp))
4350514024aSkiyohara 		goto out;
4360514024aSkiyohara 
4370514024aSkiyohara 	/* Grab the first contiguous region of buffer space. */
4380514024aSkiyohara 	{
4390514024aSkiyohara 		u_char *tba;
4400514024aSkiyohara 		int tbc;
4410514024aSkiyohara 
4420514024aSkiyohara 		tba = tp->t_outq.c_cf;
4430514024aSkiyohara 		tbc = ndqb(&tp->t_outq, 0);
4440514024aSkiyohara 
4450514024aSkiyohara 		(void)splserial();
4460514024aSkiyohara 
4470514024aSkiyohara 		sc->sc_tba = tba;
4480514024aSkiyohara 		sc->sc_tbc = tbc;
4490514024aSkiyohara 	}
4500514024aSkiyohara 
4510514024aSkiyohara 	SET(tp->t_state, TS_BUSY);
4520514024aSkiyohara 
4530514024aSkiyohara 	if (sc->sc_ih[CLPSCOM_TXINT] == NULL) {
4540514024aSkiyohara 		sc->sc_ih[CLPSCOM_TXINT] =
4550514024aSkiyohara 		    intr_establish(sc->sc_irq[CLPSCOM_TXINT], IPL_SERIAL, 0,
4560514024aSkiyohara 		    clpscom_txintr, sc);
4570514024aSkiyohara 		if (sc->sc_ih[CLPSCOM_TXINT] == NULL)
4580514024aSkiyohara 			printf("%s: can't establish tx interrupt\n",
4590514024aSkiyohara 			    device_xname(sc->sc_dev));
4600514024aSkiyohara 
4610514024aSkiyohara 		/* Output the first chunk of the contiguous buffer. */
462d1579b2dSriastradh 		n = uimin(sc->sc_tbc, UART_FIFO_SIZE);
4630514024aSkiyohara 		CLPSCOM_WRITE_MULTI(sc, sc->sc_tba, n);
4640514024aSkiyohara 		sc->sc_tba += n;
4650514024aSkiyohara 		sc->sc_tbc -= n;
4660514024aSkiyohara 	}
4670514024aSkiyohara out:
4680514024aSkiyohara 	splx(s);
4690514024aSkiyohara 	return;
4700514024aSkiyohara }
4710514024aSkiyohara 
4720514024aSkiyohara static int
clpscom_param(struct tty * tp,struct termios * t)4730514024aSkiyohara clpscom_param(struct tty *tp, struct termios *t)
4740514024aSkiyohara {
4750514024aSkiyohara 	struct clpscom_softc *sc =
4760514024aSkiyohara 	    device_lookup_private(&clpscom_cd, COMUNIT(tp->t_dev));
4770514024aSkiyohara 	int s;
4780514024aSkiyohara 
4790514024aSkiyohara 	if (!device_is_active(sc->sc_dev))
4800514024aSkiyohara 		return ENXIO;
4810514024aSkiyohara 	if (t->c_ispeed && t->c_ispeed != t->c_ospeed)
4820514024aSkiyohara 		return EINVAL;
4830514024aSkiyohara 
4840514024aSkiyohara 	/*
4850514024aSkiyohara 	 * For the console, always force CLOCAL and !HUPCL, so that the port
4860514024aSkiyohara 	 * is always active.
4870514024aSkiyohara 	 */
4880514024aSkiyohara 	if (ISSET(sc->sc_swflags, TIOCFLAG_SOFTCAR) ||
4890514024aSkiyohara 	    ISSET(sc->sc_hwflags, COM_HW_CONSOLE)) {
4900514024aSkiyohara 		SET(t->c_cflag, CLOCAL);
4910514024aSkiyohara 		CLR(t->c_cflag, HUPCL);
4920514024aSkiyohara 	}
4930514024aSkiyohara 
4940514024aSkiyohara 	/*
4950514024aSkiyohara 	 * If there were no changes, don't do anything.  This avoids dropping
4960514024aSkiyohara 	 * input and improves performance when all we did was frob things like
4970514024aSkiyohara 	 * VMIN and VTIME.
4980514024aSkiyohara 	 */
4990514024aSkiyohara 	if (tp->t_ospeed == t->c_ospeed &&
5000514024aSkiyohara 	    tp->t_cflag == t->c_cflag)
5010514024aSkiyohara 		return 0;
5020514024aSkiyohara 
5030514024aSkiyohara 	/*
5040514024aSkiyohara 	 * If we're not in a mode that assumes a connection is present, then
5050514024aSkiyohara 	 * ignore carrier changes.
5060514024aSkiyohara 	 */
5070514024aSkiyohara 	if (ISSET(t->c_cflag, CLOCAL | MDMBUF))
5080514024aSkiyohara 		sc->sc_ms_dcd = 0;
5090514024aSkiyohara 	else
5100514024aSkiyohara 		sc->sc_ms_dcd = SYSFLG_DCD;
5110514024aSkiyohara 	/*
5120514024aSkiyohara 	 * Set the flow control pins depending on the current flow control
5130514024aSkiyohara 	 * mode.
5140514024aSkiyohara 	 */
5150514024aSkiyohara 	if (ISSET(t->c_cflag, CRTSCTS)) {
5160514024aSkiyohara 		sc->sc_ms_cts = SYSFLG_CTS;
5170514024aSkiyohara 	} else if (ISSET(t->c_cflag, MDMBUF)) {
5180514024aSkiyohara 		/*
5190514024aSkiyohara 		 * For DTR/DCD flow control, make sure we don't toggle DTR for
5200514024aSkiyohara 		 * carrier detection.
5210514024aSkiyohara 		 */
5220514024aSkiyohara 		sc->sc_ms_cts = SYSFLG_DCD;
5230514024aSkiyohara 	} else {
5240514024aSkiyohara 		/*
5250514024aSkiyohara 		 * If no flow control, then always set RTS.  This will make
5260514024aSkiyohara 		 * the other side happy if it mistakenly thinks we're doing
5270514024aSkiyohara 		 * RTS/CTS flow control.
5280514024aSkiyohara 		 */
5290514024aSkiyohara 		sc->sc_ms_cts = 0;
5300514024aSkiyohara 	}
5310514024aSkiyohara 	sc->sc_ms_mask = sc->sc_ms_cts | sc->sc_ms_dcd;
5320514024aSkiyohara 
5330514024aSkiyohara 	s = splserial();
5340514024aSkiyohara 	CLPSCOM_WRITE_UBRLCR(sc,
5350514024aSkiyohara 	    UBRLCR_FIFOEN |
5360514024aSkiyohara 	    clpscom_rate2ubrlcr(t->c_ospeed) |
5370514024aSkiyohara 	    clpscom_cflag2ubrlcr(t->c_cflag));
5380514024aSkiyohara 
5390514024aSkiyohara 	/* And copy to tty. */
5400514024aSkiyohara 	tp->t_ispeed = 0;
5410514024aSkiyohara 	tp->t_ospeed = t->c_ospeed;
5420514024aSkiyohara 	tp->t_cflag = t->c_cflag;
5430514024aSkiyohara 	splx(s);
5440514024aSkiyohara 
5450514024aSkiyohara 	/*
5460514024aSkiyohara 	 * Update the tty layer's idea of the carrier bit, in case we changed
5470514024aSkiyohara 	 * CLOCAL or MDMBUF.  We don't hang up here; we only do that by
5480514024aSkiyohara 	 * explicit request.
5490514024aSkiyohara 	 */
5500514024aSkiyohara 	(*tp->t_linesw->l_modem)(tp, ISSET(sc->sc_ms, SYSFLG_DCD));
5510514024aSkiyohara 
5520514024aSkiyohara 	if (!ISSET(t->c_cflag, CHWFLOW))
5530514024aSkiyohara 		if (sc->sc_tx_stopped) {
5540514024aSkiyohara 			sc->sc_tx_stopped = 0;
5550514024aSkiyohara 			clpscom_start(tp);
5560514024aSkiyohara 		}
5570514024aSkiyohara 
5580514024aSkiyohara 	return 0;
5590514024aSkiyohara }
5600514024aSkiyohara 
5610514024aSkiyohara static int
clpscom_hwiflow(struct tty * tp,int block)5620514024aSkiyohara clpscom_hwiflow(struct tty *tp, int block)
5630514024aSkiyohara {
5640514024aSkiyohara 	/* Nothing */
5650514024aSkiyohara 	return 0;
5660514024aSkiyohara }
5670514024aSkiyohara 
5680514024aSkiyohara /* ARGSUSED */
5690514024aSkiyohara int
clpscomopen(dev_t dev,int flag,int mode,struct lwp * l)5700514024aSkiyohara clpscomopen(dev_t dev, int flag, int mode, struct lwp *l)
5710514024aSkiyohara {
5720514024aSkiyohara 	struct clpscom_softc *sc;
5730514024aSkiyohara 	struct tty *tp;
5740514024aSkiyohara 	int error, s, s2;
5750514024aSkiyohara 
5760514024aSkiyohara 	sc = device_lookup_private(&clpscom_cd, COMUNIT(dev));
5770514024aSkiyohara 	if (sc == NULL || !ISSET(sc->sc_hwflags, COM_HW_DEV_OK))
5780514024aSkiyohara 		return ENXIO;
5790514024aSkiyohara 	if (!device_is_active(sc->sc_dev))
5800514024aSkiyohara 		return ENXIO;
5810514024aSkiyohara 
5820514024aSkiyohara #ifdef KGDB
5830514024aSkiyohara 	/*
5840514024aSkiyohara 	 * If this is the kgdb port, no other use is permitted.
5850514024aSkiyohara 	 */
5860514024aSkiyohara 	if (ISSET(sc->sc_hwflags, COM_HW_KGDB))
5870514024aSkiyohara 		return EBUSY;
5880514024aSkiyohara #endif
5890514024aSkiyohara 
5900514024aSkiyohara 	tp = sc->sc_tty;
5910514024aSkiyohara 
5920514024aSkiyohara 	if (kauth_authorize_device_tty(l->l_cred, KAUTH_DEVICE_TTY_OPEN, tp))
5930514024aSkiyohara 		return EBUSY;
5940514024aSkiyohara 
5950514024aSkiyohara 	s = spltty();
5960514024aSkiyohara 
5970514024aSkiyohara 	/*
5980514024aSkiyohara 	 * Do the following iff this is a first open.
5990514024aSkiyohara 	 */
6000514024aSkiyohara 	if (!ISSET(tp->t_state, TS_ISOPEN) && tp->t_wopen == 0) {
6010514024aSkiyohara 		struct termios t;
6020514024aSkiyohara 
6030514024aSkiyohara 		tp->t_dev = dev;
6040514024aSkiyohara 
6050514024aSkiyohara 		/* Enable and turn on interrupt */
6060514024aSkiyohara 		CLPSCOM_WRITE_CON(sc, CLPSCOM_READ_CON(sc) | SYSCON_UARTEN);
6070514024aSkiyohara 
6080514024aSkiyohara 		/* Fetch the current modem control status, needed later. */
6090514024aSkiyohara 		sc->sc_ms = CLPSCOM_READ_FLG(sc) & CLPSCOM_MODEM_STATUS_MASK;
6100514024aSkiyohara 
6110514024aSkiyohara 		/*
6120514024aSkiyohara 		 * Initialize the termios status to the defaults.  Add in the
6130514024aSkiyohara 		 * sticky bits from TIOCSFLAGS.
6140514024aSkiyohara 		 */
6150514024aSkiyohara 		t.c_ispeed = 0;
6160514024aSkiyohara 		if (ISSET(sc->sc_hwflags, COM_HW_CONSOLE)) {
6170514024aSkiyohara 			t.c_ospeed = clpscom_cnrate;
6180514024aSkiyohara 			t.c_cflag = clpscom_cncflag;
6190514024aSkiyohara 		} else {
6200514024aSkiyohara 			t.c_ospeed = TTYDEF_SPEED;
6210514024aSkiyohara 			t.c_cflag = TTYDEF_CFLAG;
6220514024aSkiyohara 		}
6230514024aSkiyohara 		if (ISSET(sc->sc_swflags, TIOCFLAG_CLOCAL))
6240514024aSkiyohara 			SET(t.c_cflag, CLOCAL);
6250514024aSkiyohara 		if (ISSET(sc->sc_swflags, TIOCFLAG_CRTSCTS))
6260514024aSkiyohara 			SET(t.c_cflag, CRTSCTS);
6270514024aSkiyohara 		if (ISSET(sc->sc_swflags, TIOCFLAG_MDMBUF))
6280514024aSkiyohara 			SET(t.c_cflag, MDMBUF);
6290514024aSkiyohara 		/* Make sure pscom_param() we do something */
6300514024aSkiyohara 		tp->t_ospeed = 0;
6310514024aSkiyohara 		clpscom_param(tp, &t);
6320514024aSkiyohara 		tp->t_iflag = TTYDEF_IFLAG;
6330514024aSkiyohara 		tp->t_oflag = TTYDEF_OFLAG;
6340514024aSkiyohara 		tp->t_lflag = TTYDEF_LFLAG;
6350514024aSkiyohara 		ttychars(tp);
6360514024aSkiyohara 		ttsetwater(tp);
6370514024aSkiyohara 
6380514024aSkiyohara 		s2 = splserial();
6390514024aSkiyohara 
6400514024aSkiyohara 		/* Clear the input ring. */
6410514024aSkiyohara 		sc->sc_rbput = sc->sc_rbget = sc->sc_rbuf;
6420514024aSkiyohara 		sc->sc_rbavail = CLPSCOM_RING_SIZE;
6430514024aSkiyohara 		clpscom_iflush(sc);
6440514024aSkiyohara 
6450514024aSkiyohara 		splx(s2);
6460514024aSkiyohara 	}
6470514024aSkiyohara 
6480514024aSkiyohara 	splx(s);
6490514024aSkiyohara 
6500514024aSkiyohara 	error = ttyopen(tp, COMDIALOUT(dev), ISSET(flag, O_NONBLOCK));
6510514024aSkiyohara 	if (error)
6520514024aSkiyohara 		goto bad;
6530514024aSkiyohara 
6540514024aSkiyohara 	error = (*tp->t_linesw->l_open)(dev, tp);
6550514024aSkiyohara 	if (error)
6560514024aSkiyohara 		goto bad;
6570514024aSkiyohara 	return 0;
6580514024aSkiyohara 
6590514024aSkiyohara bad:
6600514024aSkiyohara 	if (!ISSET(tp->t_state, TS_ISOPEN) && tp->t_wopen == 0) {
6610514024aSkiyohara 		/*
6620514024aSkiyohara 		 * We failed to open the device, and nobody else had it opened.
6630514024aSkiyohara 		 * Clean up the state as appropriate.
6640514024aSkiyohara 		 */
6650514024aSkiyohara 		clpscom_shutdown(sc);
6660514024aSkiyohara 
6670514024aSkiyohara 		/* Disable UART */
6680514024aSkiyohara 		if (!ISSET(sc->sc_hwflags, COM_HW_CONSOLE))
6690514024aSkiyohara 			CLPSCOM_WRITE_CON(sc,
6700514024aSkiyohara 			    CLPSCOM_READ_CON(sc) & ~SYSCON_UARTEN);
6710514024aSkiyohara 	}
6720514024aSkiyohara 
6730514024aSkiyohara 	return error;
6740514024aSkiyohara }
6750514024aSkiyohara 
6760514024aSkiyohara /* ARGSUSED */
6770514024aSkiyohara int
clpscomclose(dev_t dev,int flag,int mode,struct lwp * l)6780514024aSkiyohara clpscomclose(dev_t dev, int flag, int mode, struct lwp *l)
6790514024aSkiyohara {
6800514024aSkiyohara 	struct clpscom_softc *sc =
6810514024aSkiyohara 	    device_lookup_private(&clpscom_cd, COMUNIT(dev));
6820514024aSkiyohara 	struct tty *tp = sc->sc_tty;
6830514024aSkiyohara 
6840514024aSkiyohara 	/* XXXX This is for cons.c. */
6850514024aSkiyohara 	if (!ISSET(tp->t_state, TS_ISOPEN))
6860514024aSkiyohara 		return 0;
6870514024aSkiyohara 
6880514024aSkiyohara 	(*tp->t_linesw->l_close)(tp, flag);
6890514024aSkiyohara 	ttyclose(tp);
6900514024aSkiyohara 
6910514024aSkiyohara 	if (!device_is_active(sc->sc_dev))
6920514024aSkiyohara 		return 0;
6930514024aSkiyohara 
6940514024aSkiyohara 	if (!ISSET(tp->t_state, TS_ISOPEN) && tp->t_wopen == 0) {
6950514024aSkiyohara 		/*
6960514024aSkiyohara 		 * Although we got a last close, the device may still be in
6970514024aSkiyohara 		 * use; e.g. if this was the dialout node, and there are still
6980514024aSkiyohara 		 * processes waiting for carrier on the non-dialout node.
6990514024aSkiyohara 		 */
7000514024aSkiyohara 		clpscom_shutdown(sc);
7010514024aSkiyohara 
7020514024aSkiyohara 		/* Disable UART */
7030514024aSkiyohara 		if (!ISSET(sc->sc_hwflags, COM_HW_CONSOLE))
7040514024aSkiyohara 			CLPSCOM_WRITE_CON(sc,
7050514024aSkiyohara 			    CLPSCOM_READ_CON(sc) & ~SYSCON_UARTEN);
7060514024aSkiyohara 	}
7070514024aSkiyohara 
7080514024aSkiyohara 	return 0;
7090514024aSkiyohara }
7100514024aSkiyohara 
7110514024aSkiyohara int
clpscomread(dev_t dev,struct uio * uio,int flag)7120514024aSkiyohara clpscomread(dev_t dev, struct uio *uio, int flag)
7130514024aSkiyohara {
7140514024aSkiyohara 	struct clpscom_softc *sc =
7150514024aSkiyohara 	    device_lookup_private(&clpscom_cd, COMUNIT(dev));
7160514024aSkiyohara 	struct tty *tp = sc->sc_tty;
7170514024aSkiyohara 
7180514024aSkiyohara 	if (!device_is_active(sc->sc_dev))
7190514024aSkiyohara 		return EIO;
7200514024aSkiyohara 
7210514024aSkiyohara 	return (*tp->t_linesw->l_read)(tp, uio, flag);
7220514024aSkiyohara }
7230514024aSkiyohara 
7240514024aSkiyohara int
clpscomwrite(dev_t dev,struct uio * uio,int flag)7250514024aSkiyohara clpscomwrite(dev_t dev, struct uio *uio, int flag)
7260514024aSkiyohara {
7270514024aSkiyohara 	struct clpscom_softc *sc =
7280514024aSkiyohara 	    device_lookup_private(&clpscom_cd, COMUNIT(dev));
7290514024aSkiyohara 	struct tty *tp = sc->sc_tty;
7300514024aSkiyohara 
7310514024aSkiyohara 	if (!device_is_active(sc->sc_dev))
7320514024aSkiyohara 		return EIO;
7330514024aSkiyohara 
7340514024aSkiyohara 	return (*tp->t_linesw->l_write)(tp, uio, flag);
7350514024aSkiyohara }
7360514024aSkiyohara 
7370514024aSkiyohara int
clpscomioctl(dev_t dev,u_long cmd,void * data,int flag,struct lwp * l)7380514024aSkiyohara clpscomioctl(dev_t dev, u_long cmd, void *data, int flag, struct lwp *l)
7390514024aSkiyohara {
7400514024aSkiyohara 	struct clpscom_softc *sc =
7410514024aSkiyohara 	    device_lookup_private(&clpscom_cd, COMUNIT(dev));
7420514024aSkiyohara 	struct tty *tp = sc->sc_tty;
7430514024aSkiyohara 	int error, s;
7440514024aSkiyohara 
7450514024aSkiyohara 	if (!device_is_active(sc->sc_dev))
7460514024aSkiyohara 		return EIO;
7470514024aSkiyohara 
7480514024aSkiyohara 	error = (*tp->t_linesw->l_ioctl)(tp, cmd, data, flag, l);
7490514024aSkiyohara 	if (error != EPASSTHROUGH)
7500514024aSkiyohara 		return error;
7510514024aSkiyohara 
7520514024aSkiyohara 	error = ttioctl(tp, cmd, data, flag, l);
7530514024aSkiyohara 	if (error != EPASSTHROUGH)
7540514024aSkiyohara 		return error;
7550514024aSkiyohara 
7560514024aSkiyohara 	switch (cmd) {
7570514024aSkiyohara 	case TIOCSFLAGS:
7580514024aSkiyohara 		error = kauth_authorize_device_tty(l->l_cred,
7590514024aSkiyohara 		    KAUTH_DEVICE_TTY_PRIVSET, tp);
7600514024aSkiyohara 		break;
7610514024aSkiyohara 	default:
7620514024aSkiyohara 		break;
7630514024aSkiyohara 	}
7640514024aSkiyohara 	if (error)
7650514024aSkiyohara 		return error;
7660514024aSkiyohara 
7670514024aSkiyohara 	s = splserial();
7680514024aSkiyohara 	error = 0;
7690514024aSkiyohara 	switch (cmd) {
7700514024aSkiyohara 	case TIOCSBRK:
7710514024aSkiyohara 		clpscom_break(sc, 1);
7720514024aSkiyohara 		break;
7730514024aSkiyohara 
7740514024aSkiyohara 	case TIOCCBRK:
7750514024aSkiyohara 		clpscom_break(sc, 0);
7760514024aSkiyohara 		break;
7770514024aSkiyohara 
7780514024aSkiyohara 	case TIOCGFLAGS:
7790514024aSkiyohara 		*(int *)data = sc->sc_swflags;
7800514024aSkiyohara 		break;
7810514024aSkiyohara 
7820514024aSkiyohara 	case TIOCSFLAGS:
7830514024aSkiyohara 		sc->sc_swflags = *(int *)data;
7840514024aSkiyohara 		break;
7850514024aSkiyohara 
7860514024aSkiyohara 	case TIOCMGET:
7870514024aSkiyohara 		*(int *)data = clpscom_to_tiocm(sc);
7880514024aSkiyohara 		break;
7890514024aSkiyohara 
7900514024aSkiyohara 	default:
7910514024aSkiyohara 		error = EPASSTHROUGH;
7920514024aSkiyohara 		break;
7930514024aSkiyohara 	}
7940514024aSkiyohara 	splx(s);
7950514024aSkiyohara 	return error;
7960514024aSkiyohara }
7970514024aSkiyohara 
7980514024aSkiyohara int
clpscompoll(dev_t dev,int events,struct lwp * l)7990514024aSkiyohara clpscompoll(dev_t dev, int events, struct lwp *l)
8000514024aSkiyohara {
8010514024aSkiyohara 	struct clpscom_softc *sc =
8020514024aSkiyohara 	    device_lookup_private(&clpscom_cd, COMUNIT(dev));
8030514024aSkiyohara 	struct tty *tp = sc->sc_tty;
8040514024aSkiyohara 
8050514024aSkiyohara 	if (!device_is_active(sc->sc_dev))
8060514024aSkiyohara 		return EIO;
8070514024aSkiyohara 
8080514024aSkiyohara 	return (*tp->t_linesw->l_poll)(tp, events, l);
8090514024aSkiyohara }
8100514024aSkiyohara 
8110514024aSkiyohara struct tty *
clpscomtty(dev_t dev)8120514024aSkiyohara clpscomtty(dev_t dev)
8130514024aSkiyohara {
8140514024aSkiyohara 	struct clpscom_softc *sc =
8150514024aSkiyohara 	    device_lookup_private(&clpscom_cd, COMUNIT(dev));
8160514024aSkiyohara 
8170514024aSkiyohara 	return sc->sc_tty;
8180514024aSkiyohara }
8190514024aSkiyohara 
8200514024aSkiyohara void
clpscomstop(struct tty * tp,int flag)8210514024aSkiyohara clpscomstop(struct tty *tp, int flag)
8220514024aSkiyohara {
8230514024aSkiyohara 	int s;
8240514024aSkiyohara 
8250514024aSkiyohara 	s = splserial();
8260514024aSkiyohara 	if (ISSET(tp->t_state, TS_BUSY)) {
8270514024aSkiyohara 		/* Stop transmitting at the next chunk. */
8280514024aSkiyohara 		if (!ISSET(tp->t_state, TS_TTSTOP))
8290514024aSkiyohara 			SET(tp->t_state, TS_FLUSH);
8300514024aSkiyohara 	}
8310514024aSkiyohara 	splx(s);
8320514024aSkiyohara }
8330514024aSkiyohara 
8340514024aSkiyohara 
8350514024aSkiyohara static void
clpscom_iflush(struct clpscom_softc * sc)8360514024aSkiyohara clpscom_iflush(struct clpscom_softc *sc)
8370514024aSkiyohara {
8380514024aSkiyohara 	int timo;
8390514024aSkiyohara 
8400514024aSkiyohara 	timo = 50000;
8410514024aSkiyohara 	while ((CLPSCOM_READ_FLG(sc) & SYSFLG_URXFE) == 0
8420514024aSkiyohara 	    && timo--)
8430514024aSkiyohara 		CLPSCOM_READ(sc);
8440514024aSkiyohara 	if (timo == 0)
8450514024aSkiyohara 		printf("%s: iflush timeout\n", device_xname(sc->sc_dev));
8460514024aSkiyohara }
8470514024aSkiyohara 
8480514024aSkiyohara static void
clpscom_shutdown(struct clpscom_softc * sc)8490514024aSkiyohara clpscom_shutdown(struct clpscom_softc *sc)
8500514024aSkiyohara {
8510514024aSkiyohara 	int s;
8520514024aSkiyohara 
8530514024aSkiyohara 	s = splserial();
8540514024aSkiyohara 
8550514024aSkiyohara 	/* Turn off all interrupts */
8560514024aSkiyohara 	if (sc->sc_ih[CLPSCOM_TXINT] != NULL)
8570514024aSkiyohara 		intr_disestablish(sc->sc_ih[CLPSCOM_TXINT]);
8580514024aSkiyohara 	sc->sc_ih[CLPSCOM_TXINT] = NULL;
8590514024aSkiyohara 	if (sc->sc_ih[CLPSCOM_RXINT] != NULL)
8600514024aSkiyohara 		intr_disestablish(sc->sc_ih[CLPSCOM_RXINT]);
8610514024aSkiyohara 	sc->sc_ih[CLPSCOM_RXINT] = NULL;
8620514024aSkiyohara 	if (sc->sc_ih[CLPSCOM_MSINT] != NULL)
8630514024aSkiyohara 		intr_disestablish(sc->sc_ih[CLPSCOM_MSINT]);
8640514024aSkiyohara 	sc->sc_ih[CLPSCOM_MSINT] = NULL;
8650514024aSkiyohara 
8660514024aSkiyohara 	/* Clear any break condition set with TIOCSBRK. */
8670514024aSkiyohara 	clpscom_break(sc, 0);
8680514024aSkiyohara 
8690514024aSkiyohara 	splx(s);
8700514024aSkiyohara }
8710514024aSkiyohara 
8720514024aSkiyohara static void
clpscom_break(struct clpscom_softc * sc,int onoff)8730514024aSkiyohara clpscom_break(struct clpscom_softc *sc, int onoff)
8740514024aSkiyohara {
8750514024aSkiyohara 	int s;
8760514024aSkiyohara 	uint8_t ubrlcr;
8770514024aSkiyohara 
8780514024aSkiyohara 	s = splserial();
8790514024aSkiyohara 	ubrlcr = CLPSCOM_READ_UBRLCR(sc);
8800514024aSkiyohara 	if (onoff)
8810514024aSkiyohara 		SET(ubrlcr, UBRLCR_BREAK);
8820514024aSkiyohara 	else
8830514024aSkiyohara 		CLR(ubrlcr, UBRLCR_BREAK);
8840514024aSkiyohara 	CLPSCOM_WRITE_UBRLCR(sc, ubrlcr);
8850514024aSkiyohara 	splx(s);
8860514024aSkiyohara }
8870514024aSkiyohara 
8880514024aSkiyohara static int
clpscom_to_tiocm(struct clpscom_softc * sc)8890514024aSkiyohara clpscom_to_tiocm(struct clpscom_softc *sc)
8900514024aSkiyohara {
8910514024aSkiyohara 	uint32_t combits;
8920514024aSkiyohara 	int ttybits = 0;
8930514024aSkiyohara 
8940514024aSkiyohara 	combits = sc->sc_ms;
8950514024aSkiyohara 	if (ISSET(combits, SYSFLG_DCD))
8960514024aSkiyohara 		SET(ttybits, TIOCM_CD);
8970514024aSkiyohara 	if (ISSET(combits, SYSFLG_CTS))
8980514024aSkiyohara 		SET(ttybits, TIOCM_CTS);
8990514024aSkiyohara 	if (ISSET(combits, SYSFLG_DSR))
9000514024aSkiyohara 		SET(ttybits, TIOCM_DSR);
9010514024aSkiyohara 
9020514024aSkiyohara 	return ttybits;
9030514024aSkiyohara }
9040514024aSkiyohara 
9050514024aSkiyohara static void
clpscom_rxsoft(struct clpscom_softc * sc,struct tty * tp)9060514024aSkiyohara clpscom_rxsoft(struct clpscom_softc *sc, struct tty *tp)
9070514024aSkiyohara {
9080514024aSkiyohara 	int code, s;
9090514024aSkiyohara 	u_int cc, scc;
9100514024aSkiyohara 	u_char sts, *get;
9110514024aSkiyohara 
9120514024aSkiyohara 	get = sc->sc_rbget;
9130514024aSkiyohara 	scc = cc = CLPSCOM_RING_SIZE - sc->sc_rbavail;
9140514024aSkiyohara 	while (cc) {
9150514024aSkiyohara 		code = get[0];
9160514024aSkiyohara 		sts = get[1];
9170514024aSkiyohara 		if (ISSET(sts, UARTDR_FRMERR | UARTDR_PARERR | UARTDR_OVERR)) {
9180514024aSkiyohara 			if (ISSET(sts, (UARTDR_FRMERR)))
9190514024aSkiyohara 				SET(code, TTY_FE);
9200514024aSkiyohara 			if (ISSET(sts, UARTDR_PARERR))
9210514024aSkiyohara 				SET(code, TTY_PE);
9220514024aSkiyohara 			if (ISSET(sts, UARTDR_OVERR))
9230514024aSkiyohara 				;		/* XXXXX: Overrun */
9240514024aSkiyohara 		}
9250514024aSkiyohara 		if ((*tp->t_linesw->l_rint)(code, tp) == -1) {
9260514024aSkiyohara 			/*
9270514024aSkiyohara 			 * The line discipline's buffer is out of space.
9280514024aSkiyohara 			 */
9290514024aSkiyohara 			/*
9300514024aSkiyohara 			 * We're either not using flow control, or the
9310514024aSkiyohara 			 * line discipline didn't tell us to block for
9320514024aSkiyohara 			 * some reason.  Either way, we have no way to
9330514024aSkiyohara 			 * know when there's more space available, so
9340514024aSkiyohara 			 * just drop the rest of the data.
9350514024aSkiyohara 			 */
9360514024aSkiyohara 			get += cc << 1;
9370514024aSkiyohara 			if (get >= sc->sc_rbuf + (CLPSCOM_RING_SIZE << 1))
9380514024aSkiyohara 				get -= (CLPSCOM_RING_SIZE << 1);
9390514024aSkiyohara 			cc = 0;
9400514024aSkiyohara 			break;
9410514024aSkiyohara 		}
9420514024aSkiyohara 		get += 2;
9430514024aSkiyohara 		if (get >= sc->sc_rbuf + (CLPSCOM_RING_SIZE << 1))
9440514024aSkiyohara 			get = sc->sc_rbuf;
9450514024aSkiyohara 		cc--;
9460514024aSkiyohara 	}
9470514024aSkiyohara 
9480514024aSkiyohara 	if (cc != scc) {
9490514024aSkiyohara 		sc->sc_rbget = get;
9500514024aSkiyohara 		s = splserial();
9510514024aSkiyohara 
9520514024aSkiyohara 		cc = sc->sc_rbavail += scc - cc;
9530514024aSkiyohara 		/* Buffers should be ok again, release possible block. */
9540514024aSkiyohara 		if (cc >= 1) {
9550514024aSkiyohara 			if (sc->sc_ih[CLPSCOM_RXINT] == NULL) {
9560514024aSkiyohara 				sc->sc_ih[CLPSCOM_RXINT] =
9570514024aSkiyohara 				    intr_establish(sc->sc_irq[CLPSCOM_RXINT],
9580514024aSkiyohara 				    IPL_SERIAL, 0, clpscom_rxintr, sc);
9590514024aSkiyohara 				if (sc->sc_ih[CLPSCOM_RXINT] == NULL)
9600514024aSkiyohara 					printf("%s: can't establish"
9610514024aSkiyohara 					    " rx interrupt\n",
9620514024aSkiyohara 					    device_xname(sc->sc_dev));
9630514024aSkiyohara 			}
9640514024aSkiyohara 			if (sc->sc_ih[CLPSCOM_MSINT] == NULL) {
9650514024aSkiyohara 				sc->sc_ih[CLPSCOM_MSINT] =
9660514024aSkiyohara 				    intr_establish(sc->sc_irq[CLPSCOM_MSINT],
9670514024aSkiyohara 				    IPL_SERIAL, 0, clpscom_msintr, sc);
9680514024aSkiyohara 				if (sc->sc_ih[CLPSCOM_MSINT] == NULL)
9690514024aSkiyohara 					printf("%s: can't establish"
9700514024aSkiyohara 					    " ms interrupt\n",
9710514024aSkiyohara 					    device_xname(sc->sc_dev));
9720514024aSkiyohara 			}
9730514024aSkiyohara 		}
9740514024aSkiyohara 		splx(s);
9750514024aSkiyohara 	}
9760514024aSkiyohara }
9770514024aSkiyohara 
9780514024aSkiyohara static void
clpscom_mssoft(struct clpscom_softc * sc,struct tty * tp)9790514024aSkiyohara clpscom_mssoft(struct clpscom_softc *sc, struct tty *tp)
9800514024aSkiyohara {
9810514024aSkiyohara 	uint32_t ms, delta;
9820514024aSkiyohara 
9830514024aSkiyohara 	ms = sc->sc_ms;
9840514024aSkiyohara 	delta = sc->sc_ms_delta;
9850514024aSkiyohara 	sc->sc_ms_delta = 0;
9860514024aSkiyohara 
9870514024aSkiyohara 	if (ISSET(delta, sc->sc_ms_dcd))
9880514024aSkiyohara 		/*
9890514024aSkiyohara 		 * Inform the tty layer that carrier detect changed.
9900514024aSkiyohara 		 */
9910514024aSkiyohara 		(void) (*tp->t_linesw->l_modem)(tp, ISSET(ms, SYSFLG_DCD));
9920514024aSkiyohara 
9930514024aSkiyohara 	if (ISSET(delta, sc->sc_ms_cts)) {
9940514024aSkiyohara 		/* Block or unblock output according to flow control. */
9950514024aSkiyohara 		if (ISSET(ms, sc->sc_ms_cts)) {
9960514024aSkiyohara 			sc->sc_tx_stopped = 0;
9970514024aSkiyohara 			(*tp->t_linesw->l_start)(tp);
9980514024aSkiyohara 		} else
9990514024aSkiyohara 			sc->sc_tx_stopped = 1;
10000514024aSkiyohara 	}
10010514024aSkiyohara }
10020514024aSkiyohara 
10030514024aSkiyohara static inline uint32_t
clpscom_rate2ubrlcr(int rate)10040514024aSkiyohara clpscom_rate2ubrlcr(int rate)
10050514024aSkiyohara {
10060514024aSkiyohara 
10070514024aSkiyohara 	return 230400 / rate - 1;
10080514024aSkiyohara }
10090514024aSkiyohara 
10100514024aSkiyohara static uint32_t
clpscom_cflag2ubrlcr(tcflag_t cflag)10110514024aSkiyohara clpscom_cflag2ubrlcr(tcflag_t cflag)
10120514024aSkiyohara {
10130514024aSkiyohara 	int32_t ubrlcr = 0;
10140514024aSkiyohara 
10150514024aSkiyohara 	switch (cflag & CSIZE) {
10160514024aSkiyohara 	case CS5: SET(ubrlcr, UBRLCR_WRDLEN_5B); break;
10170514024aSkiyohara 	case CS6: SET(ubrlcr, UBRLCR_WRDLEN_6B); break;
10180514024aSkiyohara 	case CS7: SET(ubrlcr, UBRLCR_WRDLEN_7B); break;
10190514024aSkiyohara 	case CS8: SET(ubrlcr, UBRLCR_WRDLEN_8B); break;
10200514024aSkiyohara 	default:  SET(ubrlcr, UBRLCR_WRDLEN_8B); break;
10210514024aSkiyohara 	}
10220514024aSkiyohara 	if (cflag & CSTOPB)
10230514024aSkiyohara 		SET(ubrlcr, UBRLCR_XSTOP);
10240514024aSkiyohara 	if (cflag & PARENB) {
10250514024aSkiyohara 		SET(ubrlcr, (UBRLCR_PRTEN | UBRLCR_EVENPRT));
10260514024aSkiyohara 		if (cflag & PARODD)
10270514024aSkiyohara 			CLR(ubrlcr, UBRLCR_EVENPRT);
10280514024aSkiyohara 	}
10290514024aSkiyohara 	return ubrlcr;
10300514024aSkiyohara }
10310514024aSkiyohara 
10320514024aSkiyohara #define CLPSCOM_CNREAD() \
10330514024aSkiyohara 	(*(volatile uint32_t *)(clpscom_cnaddr + PS711X_UARTDR))
10340514024aSkiyohara #define CLPSCOM_CNWRITE(val) \
10350514024aSkiyohara 	(*(volatile uint8_t *)(clpscom_cnaddr + PS711X_UARTDR) = val)
10360514024aSkiyohara #define CLPSCOM_CNSTATUS() \
10370514024aSkiyohara 	(*(volatile uint32_t *)(clpscom_cnaddr + PS711X_SYSFLG))
10380514024aSkiyohara 
10390514024aSkiyohara static struct consdev clpscomcons = {
10400514024aSkiyohara 	NULL, NULL, clpscom_cngetc, clpscom_cnputc, clpscom_cnpollc,
10410514024aSkiyohara 	NULL, NULL, NULL, NODEV, CN_NORMAL
10420514024aSkiyohara };
10430514024aSkiyohara 
10440514024aSkiyohara int
clpscom_cnattach(vaddr_t addr,int rate,tcflag_t cflag)10450514024aSkiyohara clpscom_cnattach(vaddr_t addr, int rate, tcflag_t cflag)
10460514024aSkiyohara {
10470514024aSkiyohara 
10480514024aSkiyohara 	clpscom_cnaddr = addr;
10490514024aSkiyohara 	clpscom_cnrate = rate;
10500514024aSkiyohara 	clpscom_cncflag = cflag;
10510514024aSkiyohara 	*(volatile uint32_t *)(clpscom_cnaddr + PS711X_SYSFLG) |= SYSCON_UARTEN;
10520514024aSkiyohara 	*(volatile uint32_t *)(clpscom_cnaddr + PS711X_UBRLCR) =
10530514024aSkiyohara 	    UBRLCR_FIFOEN |
10540514024aSkiyohara 	    clpscom_cflag2ubrlcr(cflag) |
10550514024aSkiyohara 	    clpscom_rate2ubrlcr(rate);
10560514024aSkiyohara 
10570514024aSkiyohara 	cn_tab = &clpscomcons;
10580514024aSkiyohara 	cn_init_magic(&clpscom_cnm_state);
10590514024aSkiyohara 	cn_set_magic("\047\001");	/* default magic is BREAK */
10600514024aSkiyohara 
10610514024aSkiyohara 	return 0;
10620514024aSkiyohara }
10630514024aSkiyohara 
10640514024aSkiyohara /* ARGSUSED */
10650514024aSkiyohara static int
clpscom_cngetc(dev_t dev)10660514024aSkiyohara clpscom_cngetc(dev_t dev)
10670514024aSkiyohara {
10680514024aSkiyohara 	int s = splserial();
10690514024aSkiyohara 	char ch;
10700514024aSkiyohara 
10710514024aSkiyohara 	while (CLPSCOM_CNSTATUS() & SYSFLG_URXFE);
10720514024aSkiyohara 
10730514024aSkiyohara 	ch = CLPSCOM_CNREAD();
10740514024aSkiyohara 
10750514024aSkiyohara 	if (!db_active)
10760514024aSkiyohara 		cn_check_magic(dev, ch, clpscom_cnm_state);
10770514024aSkiyohara 
10780514024aSkiyohara 	splx(s);
10790514024aSkiyohara 	return ch;
10800514024aSkiyohara }
10810514024aSkiyohara 
10820514024aSkiyohara /* ARGSUSED */
10830514024aSkiyohara static void
clpscom_cnputc(dev_t dev,int c)10840514024aSkiyohara clpscom_cnputc(dev_t dev, int c)
10850514024aSkiyohara {
10860514024aSkiyohara 	int s = splserial();
10870514024aSkiyohara 
10880514024aSkiyohara 	while (CLPSCOM_CNSTATUS() & SYSFLG_UTXFF);
10890514024aSkiyohara 
10900514024aSkiyohara 	CLPSCOM_CNWRITE(c);
10910514024aSkiyohara 
10920514024aSkiyohara 	/* Make sure output. */
10930514024aSkiyohara 	while (CLPSCOM_CNSTATUS() & SYSFLG_UBUSY);
10940514024aSkiyohara 
10950514024aSkiyohara 	splx(s);
10960514024aSkiyohara }
10970514024aSkiyohara 
10980514024aSkiyohara /* ARGSUSED */
10990514024aSkiyohara static void
clpscom_cnpollc(dev_t dev,int on)11000514024aSkiyohara clpscom_cnpollc(dev_t dev, int on)
11010514024aSkiyohara {
11020514024aSkiyohara 	/* Nothing */
11030514024aSkiyohara }
1104