xref: /netbsd-src/sys/arch/evbarm/dev/plcom.c (revision 5e632a5947e6e4089ed2ee52b049afb550de2e30)
1*5e632a59Sriastradh /*	$NetBSD: plcom.c,v 1.69 2023/04/11 12:58:03 riastradh Exp $	*/
219cf921bSrearnsha 
319cf921bSrearnsha /*-
419cf921bSrearnsha  * Copyright (c) 2001 ARM Ltd
519cf921bSrearnsha  * All rights reserved.
619cf921bSrearnsha  *
719cf921bSrearnsha  * Redistribution and use in source and binary forms, with or without
819cf921bSrearnsha  * modification, are permitted provided that the following conditions
919cf921bSrearnsha  * are met:
1019cf921bSrearnsha  * 1. Redistributions of source code must retain the above copyright
1119cf921bSrearnsha  *    notice, this list of conditions and the following disclaimer.
1219cf921bSrearnsha  * 2. Redistributions in binary form must reproduce the above copyright
1319cf921bSrearnsha  *    notice, this list of conditions and the following disclaimer in the
1419cf921bSrearnsha  *    documentation and/or other materials provided with the distribution.
1519cf921bSrearnsha  * 3. The name of the company may not be used to endorse or promote
1619cf921bSrearnsha  *    products derived from this software without specific prior written
1719cf921bSrearnsha  *    permission.
1819cf921bSrearnsha  *
1919cf921bSrearnsha  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
2019cf921bSrearnsha  * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
2119cf921bSrearnsha  * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
2219cf921bSrearnsha  * IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
2319cf921bSrearnsha  * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
2419cf921bSrearnsha  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
2519cf921bSrearnsha  * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2619cf921bSrearnsha  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2719cf921bSrearnsha  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2819cf921bSrearnsha  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
2919cf921bSrearnsha  * SUCH DAMAGE.
3019cf921bSrearnsha  *
314c5a9380Sskrll  * Copyright (c) 1998, 1999, 2012 The NetBSD Foundation, Inc.
3219cf921bSrearnsha  * All rights reserved.
3319cf921bSrearnsha  *
3419cf921bSrearnsha  * This code is derived from software contributed to The NetBSD Foundation
354c5a9380Sskrll  * by Charles M. Hannum and Nick Hudson.
3619cf921bSrearnsha  *
3719cf921bSrearnsha  * Redistribution and use in source and binary forms, with or without
3819cf921bSrearnsha  * modification, are permitted provided that the following conditions
3919cf921bSrearnsha  * are met:
4019cf921bSrearnsha  * 1. Redistributions of source code must retain the above copyright
4119cf921bSrearnsha  *    notice, this list of conditions and the following disclaimer.
4219cf921bSrearnsha  * 2. Redistributions in binary form must reproduce the above copyright
4319cf921bSrearnsha  *    notice, this list of conditions and the following disclaimer in the
4419cf921bSrearnsha  *    documentation and/or other materials provided with the distribution.
4519cf921bSrearnsha  *
4619cf921bSrearnsha  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
4719cf921bSrearnsha  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
4819cf921bSrearnsha  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
4919cf921bSrearnsha  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
5019cf921bSrearnsha  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
5119cf921bSrearnsha  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
5219cf921bSrearnsha  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
5319cf921bSrearnsha  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
5419cf921bSrearnsha  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
5519cf921bSrearnsha  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
5619cf921bSrearnsha  * POSSIBILITY OF SUCH DAMAGE.
5719cf921bSrearnsha  */
5819cf921bSrearnsha 
5919cf921bSrearnsha /*
6019cf921bSrearnsha  * Copyright (c) 1991 The Regents of the University of California.
6119cf921bSrearnsha  * All rights reserved.
6219cf921bSrearnsha  *
6319cf921bSrearnsha  * Redistribution and use in source and binary forms, with or without
6419cf921bSrearnsha  * modification, are permitted provided that the following conditions
6519cf921bSrearnsha  * are met:
6619cf921bSrearnsha  * 1. Redistributions of source code must retain the above copyright
6719cf921bSrearnsha  *    notice, this list of conditions and the following disclaimer.
6819cf921bSrearnsha  * 2. Redistributions in binary form must reproduce the above copyright
6919cf921bSrearnsha  *    notice, this list of conditions and the following disclaimer in the
7019cf921bSrearnsha  *    documentation and/or other materials provided with the distribution.
71aad01611Sagc  * 3. Neither the name of the University nor the names of its contributors
7219cf921bSrearnsha  *    may be used to endorse or promote products derived from this software
7319cf921bSrearnsha  *    without specific prior written permission.
7419cf921bSrearnsha  *
7519cf921bSrearnsha  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
7619cf921bSrearnsha  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
7719cf921bSrearnsha  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
7819cf921bSrearnsha  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
7919cf921bSrearnsha  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
8019cf921bSrearnsha  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
8119cf921bSrearnsha  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
8219cf921bSrearnsha  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
8319cf921bSrearnsha  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
8419cf921bSrearnsha  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
8519cf921bSrearnsha  * SUCH DAMAGE.
8619cf921bSrearnsha  *
8719cf921bSrearnsha  *	@(#)com.c	7.5 (Berkeley) 5/16/91
8819cf921bSrearnsha  */
8919cf921bSrearnsha 
9019cf921bSrearnsha /*
914c5a9380Sskrll  * COM driver for the Prime Cell PL010 and PL011 UARTs. Both are is similar to
924c5a9380Sskrll  * the 16C550, but have a completely different programmer's model.
9319cf921bSrearnsha  * Derived from the NS16550AF com driver.
94210924e6Sriastradh  *
95210924e6Sriastradh  * Lock order:
96*5e632a59Sriastradh  *	ttylock (IPL_VM)
97210924e6Sriastradh  *	-> sc->sc_lock (IPL_HIGH)
98210924e6Sriastradh  *	-> timecounter_lock (IPL_HIGH)
9919cf921bSrearnsha  */
10019cf921bSrearnsha 
10108716eaeSlukem #include <sys/cdefs.h>
102*5e632a59Sriastradh __KERNEL_RCSID(0, "$NetBSD: plcom.c,v 1.69 2023/04/11 12:58:03 riastradh Exp $");
10308716eaeSlukem 
10419cf921bSrearnsha #include "opt_plcom.h"
10519cf921bSrearnsha #include "opt_kgdb.h"
106d505b189Smartin #include "opt_lockdebug.h"
107d505b189Smartin #include "opt_multiprocessor.h"
10819cf921bSrearnsha 
10919cf921bSrearnsha /*
11019cf921bSrearnsha  * Override cnmagic(9) macro before including <sys/systm.h>.
11119cf921bSrearnsha  * We need to know if cn_check_magic triggered debugger, so set a flag.
11219cf921bSrearnsha  * Callers of cn_check_magic must declare int cn_trapped = 0;
11319cf921bSrearnsha  * XXX: this is *ugly*!
11419cf921bSrearnsha  */
11519cf921bSrearnsha #define cn_trap()				\
11619cf921bSrearnsha 	do {					\
11719cf921bSrearnsha 		console_debugger();		\
11819cf921bSrearnsha 		cn_trapped = 1;			\
11919cf921bSrearnsha 	} while (/* CONSTCOND */ 0)
12019cf921bSrearnsha 
12119cf921bSrearnsha #include <sys/param.h>
12219cf921bSrearnsha #include <sys/systm.h>
12319cf921bSrearnsha #include <sys/ioctl.h>
12419cf921bSrearnsha #include <sys/select.h>
12519cf921bSrearnsha #include <sys/tty.h>
12619cf921bSrearnsha #include <sys/proc.h>
12719cf921bSrearnsha #include <sys/conf.h>
12819cf921bSrearnsha #include <sys/file.h>
12919cf921bSrearnsha #include <sys/uio.h>
13019cf921bSrearnsha #include <sys/kernel.h>
13119cf921bSrearnsha #include <sys/syslog.h>
13219cf921bSrearnsha #include <sys/types.h>
13319cf921bSrearnsha #include <sys/device.h>
134cb5a6287Sskrll #include <sys/kmem.h>
13519cf921bSrearnsha #include <sys/timepps.h>
13619cf921bSrearnsha #include <sys/vnode.h>
1378ccb6c93Selad #include <sys/kauth.h>
138fbed3be2Sad #include <sys/intr.h>
139fbed3be2Sad #include <sys/bus.h>
1404c5a9380Sskrll #ifdef RND_COM
141445478ceSriastradh #include <sys/rndsource.h>
1424c5a9380Sskrll #endif
14319cf921bSrearnsha 
144dbfa10e5Sriastradh #include <ddb/db_active.h>
145dbfa10e5Sriastradh 
14619cf921bSrearnsha #include <evbarm/dev/plcomreg.h>
14719cf921bSrearnsha #include <evbarm/dev/plcomvar.h>
14819cf921bSrearnsha 
14919cf921bSrearnsha #include <dev/cons.h>
15019cf921bSrearnsha 
15119cf921bSrearnsha static void plcom_enable_debugport (struct plcom_softc *);
15219cf921bSrearnsha 
15319cf921bSrearnsha void	plcom_config	(struct plcom_softc *);
15419cf921bSrearnsha void	plcom_shutdown	(struct plcom_softc *);
1554c5a9380Sskrll int	pl010comspeed	(long, long);
1564c5a9380Sskrll int	pl011comspeed	(long, long);
157820c39ceSmlelstv static	uint32_t	cflag2lcr (tcflag_t);
15819cf921bSrearnsha int	plcomparam	(struct tty *, struct termios *);
15919cf921bSrearnsha void	plcomstart	(struct tty *);
16019cf921bSrearnsha int	plcomhwiflow	(struct tty *, int);
16119cf921bSrearnsha 
16219cf921bSrearnsha void	plcom_loadchannelregs (struct plcom_softc *);
16319cf921bSrearnsha void	plcom_hwiflow	(struct plcom_softc *);
16419cf921bSrearnsha void	plcom_break	(struct plcom_softc *, int);
16519cf921bSrearnsha void	plcom_modem	(struct plcom_softc *, int);
16619cf921bSrearnsha void	tiocm_to_plcom	(struct plcom_softc *, u_long, int);
16719cf921bSrearnsha int	plcom_to_tiocm	(struct plcom_softc *);
16819cf921bSrearnsha void	plcom_iflush	(struct plcom_softc *);
16919cf921bSrearnsha 
1704c5a9380Sskrll int	plcom_common_getc (dev_t, struct plcom_instance *);
1714c5a9380Sskrll void	plcom_common_putc (dev_t, struct plcom_instance *, int);
17219cf921bSrearnsha 
1734c5a9380Sskrll int	plcominit	(struct plcom_instance *, int, int, tcflag_t);
17419cf921bSrearnsha 
17577a6b82bSgehenna dev_type_open(plcomopen);
17677a6b82bSgehenna dev_type_close(plcomclose);
17777a6b82bSgehenna dev_type_read(plcomread);
17877a6b82bSgehenna dev_type_write(plcomwrite);
17977a6b82bSgehenna dev_type_ioctl(plcomioctl);
18077a6b82bSgehenna dev_type_stop(plcomstop);
18177a6b82bSgehenna dev_type_tty(plcomtty);
18277a6b82bSgehenna dev_type_poll(plcompoll);
18319cf921bSrearnsha 
18419cf921bSrearnsha int	plcomcngetc	(dev_t);
18519cf921bSrearnsha void	plcomcnputc	(dev_t, int);
18619cf921bSrearnsha void	plcomcnpollc	(dev_t, int);
187e84b77e3Sjmcneill void	plcomcnhalt	(dev_t);
18819cf921bSrearnsha 
18919cf921bSrearnsha void 	plcomsoft	(void *);
190210924e6Sriastradh static inline void plcom_rxsoft	(struct plcom_softc *, struct tty *);
191210924e6Sriastradh static inline void plcom_txsoft	(struct plcom_softc *, struct tty *);
192210924e6Sriastradh static inline void plcom_stsoft	(struct plcom_softc *, struct tty *);
193210924e6Sriastradh static inline void plcom_schedrx	(struct plcom_softc *);
19419cf921bSrearnsha void	plcomdiag		(void *);
19519cf921bSrearnsha 
1964c5a9380Sskrll bool	plcom_intstatus(struct plcom_instance *, u_int *);
1974c5a9380Sskrll 
19819cf921bSrearnsha extern struct cfdriver plcom_cd;
19919cf921bSrearnsha 
20077a6b82bSgehenna const struct cdevsw plcom_cdevsw = {
201a68f9396Sdholland 	.d_open = plcomopen,
202a68f9396Sdholland 	.d_close = plcomclose,
203a68f9396Sdholland 	.d_read = plcomread,
204a68f9396Sdholland 	.d_write = plcomwrite,
205a68f9396Sdholland 	.d_ioctl = plcomioctl,
206a68f9396Sdholland 	.d_stop = plcomstop,
207a68f9396Sdholland 	.d_tty = plcomtty,
208a68f9396Sdholland 	.d_poll = plcompoll,
209a68f9396Sdholland 	.d_mmap = nommap,
210a68f9396Sdholland 	.d_kqfilter = ttykqfilter,
211f9228f42Sdholland 	.d_discard = nodiscard,
212a68f9396Sdholland 	.d_flag = D_TTY
21377a6b82bSgehenna };
21477a6b82bSgehenna 
21519cf921bSrearnsha /*
21619cf921bSrearnsha  * Make this an option variable one can patch.
21719cf921bSrearnsha  * But be warned:  this must be a power of 2!
21819cf921bSrearnsha  */
21919cf921bSrearnsha u_int plcom_rbuf_size = PLCOM_RING_SIZE;
22019cf921bSrearnsha 
22119cf921bSrearnsha /* Stop input when 3/4 of the ring is full; restart when only 1/4 is full. */
22219cf921bSrearnsha u_int plcom_rbuf_hiwat = (PLCOM_RING_SIZE * 1) / 4;
22319cf921bSrearnsha u_int plcom_rbuf_lowat = (PLCOM_RING_SIZE * 3) / 4;
22419cf921bSrearnsha 
22519cf921bSrearnsha static int	plcomconsunit = -1;
2264c5a9380Sskrll static struct plcom_instance plcomcons_info;
2274c5a9380Sskrll 
22819cf921bSrearnsha static int plcomconsattached;
22919cf921bSrearnsha static int plcomconsrate;
23019cf921bSrearnsha static tcflag_t plcomconscflag;
23119cf921bSrearnsha static struct cnm_state plcom_cnm_state;
23219cf921bSrearnsha 
23319cf921bSrearnsha static int ppscap =
23419cf921bSrearnsha 	PPS_TSFMT_TSPEC |
23519cf921bSrearnsha 	PPS_CAPTUREASSERT |
23619cf921bSrearnsha 	PPS_CAPTURECLEAR |
23719cf921bSrearnsha #ifdef  PPS_SYNC
23819cf921bSrearnsha 	PPS_HARDPPSONASSERT | PPS_HARDPPSONCLEAR |
23919cf921bSrearnsha #endif	/* PPS_SYNC */
24019cf921bSrearnsha 	PPS_OFFSETASSERT | PPS_OFFSETCLEAR;
24119cf921bSrearnsha 
24219cf921bSrearnsha #ifdef KGDB
24319cf921bSrearnsha #include <sys/kgdb.h>
24419cf921bSrearnsha 
2454c5a9380Sskrll static struct plcom_instance plcomkgdb_info;
24619cf921bSrearnsha static int plcom_kgdb_attached;
24719cf921bSrearnsha 
24819cf921bSrearnsha int	plcom_kgdb_getc (void *);
24919cf921bSrearnsha void	plcom_kgdb_putc (void *, int);
25019cf921bSrearnsha #endif /* KGDB */
25119cf921bSrearnsha 
252a0a6c85fSchristos #define	PLCOMDIALOUT_MASK	TTDIALOUT_MASK
25319cf921bSrearnsha 
254a0a6c85fSchristos #define	PLCOMUNIT(x)	TTUNIT(x)
255a0a6c85fSchristos #define	PLCOMDIALOUT(x)	TTDIALOUT(x)
25619cf921bSrearnsha 
25719cf921bSrearnsha #define	PLCOM_ISALIVE(sc)	((sc)->enabled != 0 && \
258b8905ef0Sskrll 				 device_is_active((sc)->sc_dev))
25919cf921bSrearnsha 
26019cf921bSrearnsha #define	BR	BUS_SPACE_BARRIER_READ
26119cf921bSrearnsha #define	BW	BUS_SPACE_BARRIER_WRITE
2624c5a9380Sskrll #define PLCOM_BARRIER(pi, f)	\
2634c5a9380Sskrll     bus_space_barrier((pi)->pi_iot, (pi)->pi_ioh, 0, (pi)->pi_size, (f))
2644c5a9380Sskrll 
2654c5a9380Sskrll static uint8_t
pread1(struct plcom_instance * pi,bus_size_t reg)2664c5a9380Sskrll pread1(struct plcom_instance *pi, bus_size_t reg)
2674c5a9380Sskrll {
2684c5a9380Sskrll 	if (!ISSET(pi->pi_flags, PLC_FLAG_32BIT_ACCESS))
2694c5a9380Sskrll 		return bus_space_read_1(pi->pi_iot, pi->pi_ioh, reg);
2704c5a9380Sskrll 
2714c5a9380Sskrll 	return bus_space_read_4(pi->pi_iot, pi->pi_ioh, reg & -4) >>
2724c5a9380Sskrll 	    (8 * (reg & 3));
2734c5a9380Sskrll }
274820c39ceSmlelstv 
275820c39ceSmlelstv static uint16_t
pread2(struct plcom_instance * pi,bus_size_t reg)276820c39ceSmlelstv pread2(struct plcom_instance *pi, bus_size_t reg)
277820c39ceSmlelstv {
278820c39ceSmlelstv 	if (!ISSET(pi->pi_flags, PLC_FLAG_32BIT_ACCESS))
279820c39ceSmlelstv 		return bus_space_read_2(pi->pi_iot, pi->pi_ioh, reg);
280820c39ceSmlelstv 
281820c39ceSmlelstv 	return bus_space_read_4(pi->pi_iot, pi->pi_ioh, reg & -4) >>
282820c39ceSmlelstv 	    (8 * (reg & 3));
283820c39ceSmlelstv }
284820c39ceSmlelstv 
2854c5a9380Sskrll static void
pwrite1(struct plcom_instance * pi,bus_size_t o,uint8_t val)2864c5a9380Sskrll pwrite1(struct plcom_instance *pi, bus_size_t o, uint8_t val)
2874c5a9380Sskrll {
2884c5a9380Sskrll 	if (!ISSET(pi->pi_flags, PLC_FLAG_32BIT_ACCESS)) {
2894c5a9380Sskrll 		bus_space_write_1(pi->pi_iot, pi->pi_ioh, o, val);
2904c5a9380Sskrll 	} else {
2914c5a9380Sskrll 		const size_t shift = 8 * (o & 3);
2924c5a9380Sskrll 		o &= -4;
2934c5a9380Sskrll 		uint32_t tmp = bus_space_read_4(pi->pi_iot, pi->pi_ioh, o);
2944c5a9380Sskrll 		tmp = (val << shift) | (tmp & ~(0xff << shift));
2954c5a9380Sskrll 		bus_space_write_4(pi->pi_iot, pi->pi_ioh, o, tmp);
2964c5a9380Sskrll 	}
2974c5a9380Sskrll }
2984c5a9380Sskrll 
2994c5a9380Sskrll static void
pwrite2(struct plcom_instance * pi,bus_size_t o,uint16_t val)300820c39ceSmlelstv pwrite2(struct plcom_instance *pi, bus_size_t o, uint16_t val)
301820c39ceSmlelstv {
302820c39ceSmlelstv 	if (!ISSET(pi->pi_flags, PLC_FLAG_32BIT_ACCESS)) {
303820c39ceSmlelstv 		bus_space_write_2(pi->pi_iot, pi->pi_ioh, o, val);
304820c39ceSmlelstv 	} else {
305820c39ceSmlelstv 		const size_t shift = 8 * (o & 3);
306820c39ceSmlelstv 		o &= -4;
307820c39ceSmlelstv 		uint32_t tmp = bus_space_read_4(pi->pi_iot, pi->pi_ioh, o);
308820c39ceSmlelstv 		tmp = (val << shift) | (tmp & ~(0xffff << shift));
309820c39ceSmlelstv 		bus_space_write_4(pi->pi_iot, pi->pi_ioh, o, tmp);
310820c39ceSmlelstv 	}
311820c39ceSmlelstv }
312820c39ceSmlelstv 
313820c39ceSmlelstv static void
pwritem1(struct plcom_instance * pi,bus_size_t o,const uint8_t * datap,bus_size_t count)3144c5a9380Sskrll pwritem1(struct plcom_instance *pi, bus_size_t o, const uint8_t *datap,
3154c5a9380Sskrll     bus_size_t count)
3164c5a9380Sskrll {
3174c5a9380Sskrll 	if (!ISSET(pi->pi_flags, PLC_FLAG_32BIT_ACCESS)) {
3184c5a9380Sskrll 		bus_space_write_multi_1(pi->pi_iot, pi->pi_ioh, o, datap, count);
3194c5a9380Sskrll 	} else {
3204c5a9380Sskrll 		KASSERT((o & 3) == 0);
3214c5a9380Sskrll 		while (count--) {
3224c5a9380Sskrll 			bus_space_write_4(pi->pi_iot, pi->pi_ioh, o, *datap++);
3234c5a9380Sskrll 		};
3244c5a9380Sskrll 	}
3254c5a9380Sskrll }
3264c5a9380Sskrll 
3274c5a9380Sskrll #define	PREAD1(pi, reg)		pread1(pi, reg)
328820c39ceSmlelstv #define	PREAD2(pi, reg)		pread2(pi, reg)
3294c5a9380Sskrll #define	PREAD4(pi, reg)		\
33020dcec26Stnn 	bus_space_read_4((pi)->pi_iot, (pi)->pi_ioh, (reg))
3314c5a9380Sskrll 
3324c5a9380Sskrll #define	PWRITE1(pi, reg, val)	pwrite1(pi, reg, val)
3334c5a9380Sskrll #define	PWRITEM1(pi, reg, d, c)	pwritem1(pi, reg, d, c)
334820c39ceSmlelstv #define	PWRITE2(pi, reg, val)	pwrite2(pi, reg, val)
3354c5a9380Sskrll #define	PWRITE4(pi, reg, val)	\
33620dcec26Stnn 	bus_space_write_4((pi)->pi_iot, (pi)->pi_ioh, (reg), (val))
33719cf921bSrearnsha 
33819cf921bSrearnsha int
pl010comspeed(long speed,long frequency)3394c5a9380Sskrll pl010comspeed(long speed, long frequency)
34019cf921bSrearnsha {
34119cf921bSrearnsha #define	divrnd(n, q)	(((n)*2/(q)+1)/2)	/* divide and round off */
34219cf921bSrearnsha 
34319cf921bSrearnsha 	int x, err;
34419cf921bSrearnsha 
34519cf921bSrearnsha #if 0
34619cf921bSrearnsha 	if (speed == 0)
34719cf921bSrearnsha 		return 0;
34819cf921bSrearnsha #endif
34919cf921bSrearnsha 	if (speed <= 0)
35019cf921bSrearnsha 		return -1;
35119cf921bSrearnsha 	x = divrnd(frequency / 16, speed);
35219cf921bSrearnsha 	if (x <= 0)
35319cf921bSrearnsha 		return -1;
35419cf921bSrearnsha 	err = divrnd(((quad_t)frequency) * 1000 / 16, speed * x) - 1000;
35519cf921bSrearnsha 	if (err < 0)
35619cf921bSrearnsha 		err = -err;
35719cf921bSrearnsha 	if (err > PLCOM_TOLERANCE)
35819cf921bSrearnsha 		return -1;
35919cf921bSrearnsha 	return x;
36019cf921bSrearnsha 
36119cf921bSrearnsha #undef	divrnd
36219cf921bSrearnsha }
36319cf921bSrearnsha 
3644c5a9380Sskrll int
pl011comspeed(long speed,long frequency)3654c5a9380Sskrll pl011comspeed(long speed, long frequency)
3664c5a9380Sskrll {
3674c5a9380Sskrll 	int denom = 16 * speed;
3684c5a9380Sskrll 	int div = frequency / denom;
3694c5a9380Sskrll 	int rem = frequency % denom;
3704c5a9380Sskrll 
3714c5a9380Sskrll 	int ibrd = div << 6;
3724c5a9380Sskrll 	int fbrd = (((8 * rem) / speed) + 1) / 2;
3734c5a9380Sskrll 
3744c5a9380Sskrll 	/* Tolerance? */
3754c5a9380Sskrll 	return ibrd | fbrd;
3764c5a9380Sskrll }
3774c5a9380Sskrll 
37819cf921bSrearnsha #ifdef PLCOM_DEBUG
37919cf921bSrearnsha int	plcom_debug = 0;
38019cf921bSrearnsha 
381cb55528aSbsh void plcomstatus (struct plcom_softc *, const char *);
38219cf921bSrearnsha void
plcomstatus(struct plcom_softc * sc,const char * str)383cb55528aSbsh plcomstatus(struct plcom_softc *sc, const char *str)
38419cf921bSrearnsha {
38519cf921bSrearnsha 	struct tty *tp = sc->sc_tty;
38619cf921bSrearnsha 
38719cf921bSrearnsha 	printf("%s: %s %sclocal  %sdcd %sts_carr_on %sdtr %stx_stopped\n",
388b8905ef0Sskrll 	    device_xname(sc->sc_dev), str,
38919cf921bSrearnsha 	    ISSET(tp->t_cflag, CLOCAL) ? "+" : "-",
390b9703c3dSskrll 	    ISSET(sc->sc_msr, PL01X_MSR_DCD) ? "+" : "-",
39119cf921bSrearnsha 	    ISSET(tp->t_state, TS_CARR_ON) ? "+" : "-",
392b9703c3dSskrll 	    ISSET(sc->sc_mcr, PL01X_MCR_DTR) ? "+" : "-",
39319cf921bSrearnsha 	    sc->sc_tx_stopped ? "+" : "-");
39419cf921bSrearnsha 
39519cf921bSrearnsha 	printf("%s: %s %scrtscts %scts %sts_ttstop  %srts %xrx_flags\n",
396b8905ef0Sskrll 	    device_xname(sc->sc_dev), str,
39719cf921bSrearnsha 	    ISSET(tp->t_cflag, CRTSCTS) ? "+" : "-",
398b9703c3dSskrll 	    ISSET(sc->sc_msr, PL01X_MSR_CTS) ? "+" : "-",
39919cf921bSrearnsha 	    ISSET(tp->t_state, TS_TTSTOP) ? "+" : "-",
400b9703c3dSskrll 	    ISSET(sc->sc_mcr, PL01X_MCR_RTS) ? "+" : "-",
40119cf921bSrearnsha 	    sc->sc_rx_flags);
40219cf921bSrearnsha }
40319cf921bSrearnsha #endif
40419cf921bSrearnsha 
4054c5a9380Sskrll #if 0
40619cf921bSrearnsha int
40719cf921bSrearnsha plcomprobe1(bus_space_tag_t iot, bus_space_handle_t ioh)
40819cf921bSrearnsha {
40919cf921bSrearnsha 	int data;
41019cf921bSrearnsha 
41119cf921bSrearnsha 	/* Disable the UART.  */
41219cf921bSrearnsha 	bus_space_write_1(iot, ioh, plcom_cr, 0);
41319cf921bSrearnsha 	/* Make sure the FIFO is off.  */
414820c39ceSmlelstv 	bus_space_write_4(iot, ioh, plcom_lcr, PL01X_LCR_8BITS);
41519cf921bSrearnsha 	/* Disable interrupts.  */
41619cf921bSrearnsha 	bus_space_write_1(iot, ioh, plcom_iir, 0);
41719cf921bSrearnsha 
41819cf921bSrearnsha 	/* Make sure we swallow anything in the receiving register.  */
41919cf921bSrearnsha 	data = bus_space_read_1(iot, ioh, plcom_dr);
42019cf921bSrearnsha 
421820c39ceSmlelstv 	if (bus_space_read_4(iot, ioh, plcom_lcr) != PL01X_LCR_8BITS)
42219cf921bSrearnsha 		return 0;
42319cf921bSrearnsha 
424b9703c3dSskrll 	data = bus_space_read_1(iot, ioh, plcom_fr) & (PL01X_FR_RXFF | PL01X_FR_RXFE);
42519cf921bSrearnsha 
426b9703c3dSskrll 	if (data != PL01X_FR_RXFE)
42719cf921bSrearnsha 		return 0;
42819cf921bSrearnsha 
42919cf921bSrearnsha 	return 1;
43019cf921bSrearnsha }
4314c5a9380Sskrll #endif
43219cf921bSrearnsha 
433fc0cd919Sskrll /*
434fc0cd919Sskrll  * No locking in this routine; it is only called during attach,
435fc0cd919Sskrll  * or with the port already locked.
436fc0cd919Sskrll  */
43719cf921bSrearnsha static void
plcom_enable_debugport(struct plcom_softc * sc)43819cf921bSrearnsha plcom_enable_debugport(struct plcom_softc *sc)
43919cf921bSrearnsha {
4404c5a9380Sskrll 	struct plcom_instance *pi = &sc->sc_pi;
4414c5a9380Sskrll 
4424c5a9380Sskrll 	sc->sc_cr = PL01X_CR_UARTEN;
4434c5a9380Sskrll 	SET(sc->sc_mcr, PL01X_MCR_DTR | PL01X_MCR_RTS);
44419cf921bSrearnsha 
44519cf921bSrearnsha 	/* Turn on line break interrupt, set carrier. */
4464c5a9380Sskrll 	switch (pi->pi_type) {
4474c5a9380Sskrll 	case PLCOM_TYPE_PL010:
4484c5a9380Sskrll 		SET(sc->sc_cr, PL010_CR_RIE | PL010_CR_RTIE);
4494c5a9380Sskrll 		PWRITE1(pi, PL010COM_CR, sc->sc_cr);
4504c5a9380Sskrll 		if (sc->sc_set_mcr) {
45139cd836eSthorpej 			/* XXX device_unit() abuse */
4524c5a9380Sskrll 			sc->sc_set_mcr(sc->sc_set_mcr_arg,
4534c5a9380Sskrll 			    device_unit(sc->sc_dev), sc->sc_mcr);
4544c5a9380Sskrll 		}
4554c5a9380Sskrll 		break;
4564c5a9380Sskrll 	case PLCOM_TYPE_PL011:
457820c39ceSmlelstv 	case PLCOM_TYPE_GENERIC_UART:
4584c5a9380Sskrll 		sc->sc_imsc = PL011_INT_RX | PL011_INT_RT;
4594c5a9380Sskrll 		SET(sc->sc_cr, PL011_CR_RXE | PL011_CR_TXE);
4604c5a9380Sskrll 		SET(sc->sc_cr, PL011_MCR(sc->sc_mcr));
461820c39ceSmlelstv 		PWRITE2(pi, PL011COM_CR, sc->sc_cr);
4624c5a9380Sskrll 		PWRITE4(pi, PL011COM_IMSC, sc->sc_imsc);
4634c5a9380Sskrll 		break;
4644c5a9380Sskrll 	}
4654c5a9380Sskrll 
46619cf921bSrearnsha }
46719cf921bSrearnsha 
46819cf921bSrearnsha void
plcom_attach_subr(struct plcom_softc * sc)46919cf921bSrearnsha plcom_attach_subr(struct plcom_softc *sc)
47019cf921bSrearnsha {
4714c5a9380Sskrll 	struct plcom_instance *pi = &sc->sc_pi;
47219cf921bSrearnsha 	struct tty *tp;
47319cf921bSrearnsha 
47488ab7da9Sad 	callout_init(&sc->sc_diag_callout, 0);
475fc0cd919Sskrll 	mutex_init(&sc->sc_lock, MUTEX_DEFAULT, IPL_HIGH);
47619cf921bSrearnsha 
4774c5a9380Sskrll 	switch (pi->pi_type) {
4784c5a9380Sskrll 	case PLCOM_TYPE_PL010:
4794c5a9380Sskrll 	case PLCOM_TYPE_PL011:
480820c39ceSmlelstv 	case PLCOM_TYPE_GENERIC_UART:
4814c5a9380Sskrll 		break;
4824c5a9380Sskrll 	default:
4834c5a9380Sskrll 		aprint_error_dev(sc->sc_dev,
4844c5a9380Sskrll 		    "Unknown plcom type: %d\n", pi->pi_type);
4854c5a9380Sskrll 		return;
4864c5a9380Sskrll 	}
4874c5a9380Sskrll 
48819cf921bSrearnsha 	/* Disable interrupts before configuring the device. */
48919cf921bSrearnsha 	sc->sc_cr = 0;
4904c5a9380Sskrll 	sc->sc_imsc = 0;
49119cf921bSrearnsha 
4924c5a9380Sskrll 	if (bus_space_is_equal(pi->pi_iot, plcomcons_info.pi_iot) &&
4934c5a9380Sskrll 	    pi->pi_iobase == plcomcons_info.pi_iobase) {
49419cf921bSrearnsha 		plcomconsattached = 1;
49519cf921bSrearnsha 
49619cf921bSrearnsha 		/* Make sure the console is always "hardwired". */
49719cf921bSrearnsha 		delay(1000);			/* wait for output to finish */
49819cf921bSrearnsha 		SET(sc->sc_hwflags, PLCOM_HW_CONSOLE);
49919cf921bSrearnsha 		SET(sc->sc_swflags, TIOCFLAG_SOFTCAR);
5004c5a9380Sskrll 		/*
5014c5a9380Sskrll 		 * Must re-enable the console immediately, or we will
5024c5a9380Sskrll 		 * hang when trying to print.
5034c5a9380Sskrll 		 */
504b9703c3dSskrll 		sc->sc_cr = PL01X_CR_UARTEN;
505820c39ceSmlelstv 		switch (pi->pi_type) {
506820c39ceSmlelstv 		case PLCOM_TYPE_PL011:
507820c39ceSmlelstv 		case PLCOM_TYPE_GENERIC_UART:
5084c5a9380Sskrll 			SET(sc->sc_cr, PL011_CR_RXE | PL011_CR_TXE);
509820c39ceSmlelstv 			break;
510820c39ceSmlelstv 		}
511820c39ceSmlelstv 	}
512820c39ceSmlelstv 
513820c39ceSmlelstv 	switch (pi->pi_type) {
514820c39ceSmlelstv 	case PLCOM_TYPE_PL011:
515820c39ceSmlelstv 		if (pi->pi_periphid == 0) {
516820c39ceSmlelstv 			pi->pi_periphid = PREAD1(pi, PL011COM_PID0) << 0
517820c39ceSmlelstv 				| PREAD1(pi, PL011COM_PID1) << 8
518820c39ceSmlelstv 				| PREAD1(pi, PL011COM_PID2) << 16
519820c39ceSmlelstv 				| PREAD1(pi, PL011COM_PID3) << 24;
520820c39ceSmlelstv 		}
521820c39ceSmlelstv 		aprint_debug_dev(sc->sc_dev, "PID %08x\n", pi->pi_periphid);
522820c39ceSmlelstv 		break;
52319cf921bSrearnsha 	}
52419cf921bSrearnsha 
5254c5a9380Sskrll 	switch (pi->pi_type) {
5264c5a9380Sskrll 	case PLCOM_TYPE_PL010:
5274c5a9380Sskrll 		PWRITE1(pi, PL010COM_CR, sc->sc_cr);
5284c5a9380Sskrll 		break;
52919cf921bSrearnsha 
5304c5a9380Sskrll 	case PLCOM_TYPE_PL011:
531820c39ceSmlelstv 	case PLCOM_TYPE_GENERIC_UART:
532820c39ceSmlelstv 		PWRITE2(pi, PL011COM_CR, sc->sc_cr);
5334c5a9380Sskrll 		PWRITE4(pi, PL011COM_IMSC, sc->sc_imsc);
5344c5a9380Sskrll 		break;
5354c5a9380Sskrll 	}
5364c5a9380Sskrll 
5374c5a9380Sskrll 	if (sc->sc_fifolen == 0) {
5384c5a9380Sskrll 		switch (pi->pi_type) {
5394c5a9380Sskrll 		case PLCOM_TYPE_PL010:
5404c5a9380Sskrll 			/*
5414c5a9380Sskrll 			 * The PL010 has a 16-byte fifo, but the tx interrupt
5424c5a9380Sskrll 			 * triggers when there is space for 8 more bytes.
5434c5a9380Sskrll 			*/
5442c14b448Sskrll 			sc->sc_fifolen = 8;
5454c5a9380Sskrll 			break;
5464c5a9380Sskrll 		case PLCOM_TYPE_PL011:
5474c5a9380Sskrll 			/* Some revisions have a 32 byte TX FIFO */
548820c39ceSmlelstv 			switch (pi->pi_periphid & (PL011_DESIGNER_MASK | PL011_HWREV_MASK)) {
549820c39ceSmlelstv 			case PL011_DESIGNER_ARM | __SHIFTIN(0, PL011_HWREV_MASK):
550820c39ceSmlelstv 			case PL011_DESIGNER_ARM | __SHIFTIN(1, PL011_HWREV_MASK):
551820c39ceSmlelstv 			case PL011_DESIGNER_ARM | __SHIFTIN(2, PL011_HWREV_MASK):
5524c5a9380Sskrll 				sc->sc_fifolen = 16;
5534c5a9380Sskrll 				break;
554820c39ceSmlelstv 			default:
555820c39ceSmlelstv 				sc->sc_fifolen = 32;
556820c39ceSmlelstv 				break;
557820c39ceSmlelstv 			}
558820c39ceSmlelstv 			break;
559820c39ceSmlelstv 		case PLCOM_TYPE_GENERIC_UART:
560820c39ceSmlelstv 			/* At least 32 byte TX FIFO */
561820c39ceSmlelstv 			sc->sc_fifolen = 32;
562820c39ceSmlelstv 			break;
5634c5a9380Sskrll 		}
5644c5a9380Sskrll 	}
56519cf921bSrearnsha 
566820c39ceSmlelstv 	/* Safe amount of bytes to fill when TX FIFO signals empty */
567820c39ceSmlelstv 	sc->sc_burstlen = 1;
568820c39ceSmlelstv 
56919cf921bSrearnsha 	if (ISSET(sc->sc_hwflags, PLCOM_HW_TXFIFO_DISABLE)) {
57019cf921bSrearnsha 		sc->sc_fifolen = 1;
5714c5a9380Sskrll 		aprint_normal_dev(sc->sc_dev, "txfifo disabled\n");
57219cf921bSrearnsha 	}
57319cf921bSrearnsha 
574820c39ceSmlelstv 	if (sc->sc_fifolen > 1) {
57519cf921bSrearnsha 		SET(sc->sc_hwflags, PLCOM_HW_FIFO);
576820c39ceSmlelstv 		aprint_normal_dev(sc->sc_dev, "txfifo %u bytes\n", sc->sc_fifolen);
577820c39ceSmlelstv 	}
57819cf921bSrearnsha 
5792626d576Srmind 	tp = tty_alloc();
58019cf921bSrearnsha 	tp->t_oproc = plcomstart;
58119cf921bSrearnsha 	tp->t_param = plcomparam;
58219cf921bSrearnsha 	tp->t_hwiflow = plcomhwiflow;
58319cf921bSrearnsha 
58419cf921bSrearnsha 	sc->sc_tty = tp;
585cb5a6287Sskrll 	sc->sc_rbuf = kmem_alloc(plcom_rbuf_size << 1, KM_SLEEP);
58619cf921bSrearnsha 	sc->sc_rbput = sc->sc_rbget = sc->sc_rbuf;
58719cf921bSrearnsha 	sc->sc_rbavail = plcom_rbuf_size;
58819cf921bSrearnsha 	sc->sc_ebuf = sc->sc_rbuf + (plcom_rbuf_size << 1);
58919cf921bSrearnsha 
59019cf921bSrearnsha 	tty_attach(tp);
59119cf921bSrearnsha 
59219cf921bSrearnsha 	if (ISSET(sc->sc_hwflags, PLCOM_HW_CONSOLE)) {
59319cf921bSrearnsha 		int maj;
59419cf921bSrearnsha 
59519cf921bSrearnsha 		/* locate the major number */
59677a6b82bSgehenna 		maj = cdevsw_lookup_major(&plcom_cdevsw);
59719cf921bSrearnsha 
5984c5a9380Sskrll 		tp->t_dev = cn_tab->cn_dev = makedev(maj, device_unit(sc->sc_dev));
59919cf921bSrearnsha 
6004c5a9380Sskrll 		aprint_normal_dev(sc->sc_dev, "console\n");
60119cf921bSrearnsha 	}
60219cf921bSrearnsha 
60319cf921bSrearnsha #ifdef KGDB
60419cf921bSrearnsha 	/*
60519cf921bSrearnsha 	 * Allow kgdb to "take over" this port.  If this is
60619cf921bSrearnsha 	 * the kgdb device, it has exclusive use.
60719cf921bSrearnsha 	 */
608882558bdSmlelstv 	if (bus_space_is_equal(pi->pi_iot, plcomkgdb_info.pi_iot) &&
6094c5a9380Sskrll 	    pi->pi_iobase == plcomkgdb_info.pi_iobase) {
6104c5a9380Sskrll 		if (!ISSET(sc->sc_hwflags, PLCOM_HW_CONSOLE)) {
61119cf921bSrearnsha 			plcom_kgdb_attached = 1;
61219cf921bSrearnsha 
61319cf921bSrearnsha 			SET(sc->sc_hwflags, PLCOM_HW_KGDB);
6144c5a9380Sskrll 		}
6154c5a9380Sskrll 		aprint_normal_dev(sc->sc_dev, "kgdb\n");
61619cf921bSrearnsha 	}
61719cf921bSrearnsha #endif
61819cf921bSrearnsha 
619fbed3be2Sad 	sc->sc_si = softint_establish(SOFTINT_SERIAL, plcomsoft, sc);
62019cf921bSrearnsha 
6217b0b7dedStls #ifdef RND_COM
6224c5a9380Sskrll 	rnd_attach_source(&sc->rnd_source, device_xname(sc->sc_dev),
623ea6af427Stls 	    RND_TYPE_TTY, RND_FLAG_DEFAULT);
62419cf921bSrearnsha #endif
62519cf921bSrearnsha 
6264c5a9380Sskrll 	/*
6274c5a9380Sskrll 	 * if there are no enable/disable functions, assume the device
6284c5a9380Sskrll 	 * is always enabled
6294c5a9380Sskrll 	 */
63019cf921bSrearnsha 	if (!sc->enable)
63119cf921bSrearnsha 		sc->enabled = 1;
63219cf921bSrearnsha 
63319cf921bSrearnsha 	plcom_config(sc);
63419cf921bSrearnsha 
63519cf921bSrearnsha 	SET(sc->sc_hwflags, PLCOM_HW_DEV_OK);
63619cf921bSrearnsha }
63719cf921bSrearnsha 
63819cf921bSrearnsha void
plcom_config(struct plcom_softc * sc)63919cf921bSrearnsha plcom_config(struct plcom_softc *sc)
64019cf921bSrearnsha {
6414c5a9380Sskrll 	struct plcom_instance *pi = &sc->sc_pi;
64219cf921bSrearnsha 
64319cf921bSrearnsha 	/* Disable interrupts before configuring the device. */
64419cf921bSrearnsha 	sc->sc_cr = 0;
6454c5a9380Sskrll 	sc->sc_imsc = 0;
6464c5a9380Sskrll 	switch (pi->pi_type) {
6474c5a9380Sskrll 	case PLCOM_TYPE_PL010:
6484c5a9380Sskrll 		PWRITE1(pi, PL010COM_CR, sc->sc_cr);
6494c5a9380Sskrll 		break;
6504c5a9380Sskrll 
6514c5a9380Sskrll 	case PLCOM_TYPE_PL011:
652820c39ceSmlelstv 	case PLCOM_TYPE_GENERIC_UART:
653820c39ceSmlelstv 		PWRITE2(pi, PL011COM_CR, sc->sc_cr);
6544c5a9380Sskrll 		PWRITE4(pi, PL011COM_IMSC, sc->sc_imsc);
6554c5a9380Sskrll 		break;
6564c5a9380Sskrll 	}
65719cf921bSrearnsha 
65819cf921bSrearnsha 	if (ISSET(sc->sc_hwflags, PLCOM_HW_CONSOLE|PLCOM_HW_KGDB))
65919cf921bSrearnsha 		plcom_enable_debugport(sc);
66019cf921bSrearnsha }
66119cf921bSrearnsha 
66219cf921bSrearnsha int
plcom_detach(device_t self,int flags)663b8905ef0Sskrll plcom_detach(device_t self, int flags)
66419cf921bSrearnsha {
665b8905ef0Sskrll 	struct plcom_softc *sc = device_private(self);
66619cf921bSrearnsha 	int maj, mn;
66719cf921bSrearnsha 
6683dedfc00Sdyoung 	if (sc->sc_hwflags & (PLCOM_HW_CONSOLE|PLCOM_HW_KGDB))
6693dedfc00Sdyoung 		return EBUSY;
6703dedfc00Sdyoung 
6713dedfc00Sdyoung 	if (sc->disable != NULL && sc->enabled != 0) {
6723dedfc00Sdyoung 		(*sc->disable)(sc);
6733dedfc00Sdyoung 		sc->enabled = 0;
6743dedfc00Sdyoung 	}
6753dedfc00Sdyoung 
67619cf921bSrearnsha 	/* locate the major number */
67777a6b82bSgehenna 	maj = cdevsw_lookup_major(&plcom_cdevsw);
67819cf921bSrearnsha 
67919cf921bSrearnsha 	/* Nuke the vnodes for any open instances. */
68039cd836eSthorpej 	mn = device_unit(self);
68119cf921bSrearnsha 	vdevgone(maj, mn, mn, VCHR);
68219cf921bSrearnsha 
68319cf921bSrearnsha 	mn |= PLCOMDIALOUT_MASK;
68419cf921bSrearnsha 	vdevgone(maj, mn, mn, VCHR);
68519cf921bSrearnsha 
6864c5a9380Sskrll 	if (sc->sc_rbuf == NULL) {
6874c5a9380Sskrll 		/*
6884c5a9380Sskrll 		 * Ring buffer allocation failed in the plcom_attach_subr,
6894c5a9380Sskrll 		 * only the tty is allocated, and nothing else.
6904c5a9380Sskrll 		 */
6914c5a9380Sskrll 		tty_free(sc->sc_tty);
6924c5a9380Sskrll 		return 0;
6934c5a9380Sskrll 	}
6944c5a9380Sskrll 
69519cf921bSrearnsha 	/* Free the receive buffer. */
696cb5a6287Sskrll 	kmem_free(sc->sc_rbuf, sc->sc_ebuf - sc->sc_rbuf);
69719cf921bSrearnsha 
69819cf921bSrearnsha 	/* Detach and free the tty. */
69919cf921bSrearnsha 	tty_detach(sc->sc_tty);
7002626d576Srmind 	tty_free(sc->sc_tty);
70119cf921bSrearnsha 
70219cf921bSrearnsha 	/* Unhook the soft interrupt handler. */
703fbed3be2Sad 	softint_disestablish(sc->sc_si);
70419cf921bSrearnsha 
7057b0b7dedStls #ifdef RND_COM
70619cf921bSrearnsha 	/* Unhook the entropy source. */
70719cf921bSrearnsha 	rnd_detach_source(&sc->rnd_source);
70819cf921bSrearnsha #endif
7094c5a9380Sskrll 	callout_destroy(&sc->sc_diag_callout);
71019cf921bSrearnsha 
711fc0cd919Sskrll 	/* Destroy the lock. */
712fc0cd919Sskrll 	mutex_destroy(&sc->sc_lock);
713fc0cd919Sskrll 
71419cf921bSrearnsha 	return 0;
71519cf921bSrearnsha }
71619cf921bSrearnsha 
71719cf921bSrearnsha int
plcom_activate(device_t self,enum devact act)7183dedfc00Sdyoung plcom_activate(device_t self, enum devact act)
71919cf921bSrearnsha {
7203dedfc00Sdyoung 	struct plcom_softc *sc = device_private(self);
72119cf921bSrearnsha 
72219cf921bSrearnsha 	switch (act) {
72319cf921bSrearnsha 	case DVACT_DEACTIVATE:
72419cf921bSrearnsha 		sc->enabled = 0;
7253dedfc00Sdyoung 		return 0;
7263dedfc00Sdyoung 	default:
7273dedfc00Sdyoung 		return EOPNOTSUPP;
72819cf921bSrearnsha 	}
72919cf921bSrearnsha }
73019cf921bSrearnsha 
73119cf921bSrearnsha void
plcom_shutdown(struct plcom_softc * sc)73219cf921bSrearnsha plcom_shutdown(struct plcom_softc *sc)
73319cf921bSrearnsha {
7344c5a9380Sskrll 	struct plcom_instance *pi = &sc->sc_pi;
73519cf921bSrearnsha 	struct tty *tp = sc->sc_tty;
736fc0cd919Sskrll 	mutex_spin_enter(&sc->sc_lock);
73719cf921bSrearnsha 
73819cf921bSrearnsha 	/* If we were asserting flow control, then deassert it. */
73919cf921bSrearnsha 	SET(sc->sc_rx_flags, RX_IBUF_BLOCKED);
74019cf921bSrearnsha 	plcom_hwiflow(sc);
74119cf921bSrearnsha 
74219cf921bSrearnsha 	/* Clear any break condition set with TIOCSBRK. */
74319cf921bSrearnsha 	plcom_break(sc, 0);
74419cf921bSrearnsha 
74519cf921bSrearnsha 	/* Turn off PPS capture on last close. */
746a2249ef7Sad 	mutex_spin_enter(&timecounter_lock);
74719cf921bSrearnsha 	sc->sc_ppsmask = 0;
74819cf921bSrearnsha 	sc->ppsparam.mode = 0;
749a2249ef7Sad 	mutex_spin_exit(&timecounter_lock);
75019cf921bSrearnsha 
75119cf921bSrearnsha 	/*
752210924e6Sriastradh 	 * Hang up if necessary.  Record when we hung up, so if we
753210924e6Sriastradh 	 * immediately open the port again, we will wait a bit until
754210924e6Sriastradh 	 * the other side has had time to notice that we hung up.
75519cf921bSrearnsha 	 */
75619cf921bSrearnsha 	if (ISSET(tp->t_cflag, HUPCL)) {
75719cf921bSrearnsha 		plcom_modem(sc, 0);
75833f66e53Sjmcneill 		microuptime(&sc->sc_hup_pending);
759be917db8Sjmcneill 		sc->sc_hup_pending.tv_sec++;
76019cf921bSrearnsha 	}
76119cf921bSrearnsha 
76219cf921bSrearnsha 	sc->sc_cr = 0;
7634c5a9380Sskrll 	sc->sc_imsc = 0;
7644c5a9380Sskrll 	/* Turn off interrupts. */
7654c5a9380Sskrll 	if (ISSET(sc->sc_hwflags, PLCOM_HW_CONSOLE)) {
7664c5a9380Sskrll 		/* interrupt on break */
7674c5a9380Sskrll 
7684c5a9380Sskrll 		sc->sc_cr = PL01X_CR_UARTEN;
7694c5a9380Sskrll 		sc->sc_imsc = 0;
7704c5a9380Sskrll 		switch (pi->pi_type) {
7714c5a9380Sskrll 		case PLCOM_TYPE_PL010:
7724c5a9380Sskrll 			SET(sc->sc_cr, PL010_CR_RIE | PL010_CR_RTIE);
7734c5a9380Sskrll 			break;
7744c5a9380Sskrll 		case PLCOM_TYPE_PL011:
775820c39ceSmlelstv 		case PLCOM_TYPE_GENERIC_UART:
7764c5a9380Sskrll 			SET(sc->sc_cr, PL011_CR_RXE);
7774c5a9380Sskrll 			SET(sc->sc_imsc, PL011_INT_RT | PL011_INT_RX);
7784c5a9380Sskrll 			break;
7794c5a9380Sskrll 		}
7804c5a9380Sskrll 	}
7814c5a9380Sskrll 	switch (pi->pi_type) {
7824c5a9380Sskrll 	case PLCOM_TYPE_PL010:
7834c5a9380Sskrll 		SET(sc->sc_cr, PL010_CR_RIE | PL010_CR_RTIE);
7844c5a9380Sskrll 		PWRITE1(pi, PL010COM_CR, sc->sc_cr);
7854c5a9380Sskrll 		break;
7864c5a9380Sskrll 	case PLCOM_TYPE_PL011:
787820c39ceSmlelstv 	case PLCOM_TYPE_GENERIC_UART:
7884c5a9380Sskrll 		SET(sc->sc_cr, PL011_CR_RXE | PL011_CR_TXE);
7894c5a9380Sskrll 		SET(sc->sc_imsc, PL011_INT_RT | PL011_INT_RX);
790820c39ceSmlelstv 		PWRITE2(pi, PL011COM_CR, sc->sc_cr);
7914c5a9380Sskrll 		PWRITE4(pi, PL011COM_IMSC, sc->sc_imsc);
7924c5a9380Sskrll 		break;
7934c5a9380Sskrll 	}
79419cf921bSrearnsha 
795fc0cd919Sskrll 	mutex_spin_exit(&sc->sc_lock);
79619cf921bSrearnsha 	if (sc->disable) {
79719cf921bSrearnsha #ifdef DIAGNOSTIC
79819cf921bSrearnsha 		if (!sc->enabled)
79919cf921bSrearnsha 			panic("plcom_shutdown: not enabled?");
80019cf921bSrearnsha #endif
80119cf921bSrearnsha 		(*sc->disable)(sc);
80219cf921bSrearnsha 		sc->enabled = 0;
80319cf921bSrearnsha 	}
80419cf921bSrearnsha }
80519cf921bSrearnsha 
80619cf921bSrearnsha int
plcomopen(dev_t dev,int flag,int mode,struct lwp * l)80795e1ffb1Schristos plcomopen(dev_t dev, int flag, int mode, struct lwp *l)
80819cf921bSrearnsha {
80919cf921bSrearnsha 	struct plcom_softc *sc;
8104c5a9380Sskrll 	struct plcom_instance *pi;
81119cf921bSrearnsha 	struct tty *tp;
812fc0cd919Sskrll 	int s;
81319cf921bSrearnsha 	int error;
81419cf921bSrearnsha 
81563495428Scegger 	sc = device_lookup_private(&plcom_cd, PLCOMUNIT(dev));
81619cf921bSrearnsha 	if (sc == NULL || !ISSET(sc->sc_hwflags, PLCOM_HW_DEV_OK) ||
81719cf921bSrearnsha 		sc->sc_rbuf == NULL)
81819cf921bSrearnsha 		return ENXIO;
81919cf921bSrearnsha 
820b8905ef0Sskrll 	if (!device_is_active(sc->sc_dev))
82119cf921bSrearnsha 		return ENXIO;
82219cf921bSrearnsha 
8234c5a9380Sskrll 	pi = &sc->sc_pi;
8244c5a9380Sskrll 
82519cf921bSrearnsha #ifdef KGDB
82619cf921bSrearnsha 	/*
82719cf921bSrearnsha 	 * If this is the kgdb port, no other use is permitted.
82819cf921bSrearnsha 	 */
82919cf921bSrearnsha 	if (ISSET(sc->sc_hwflags, PLCOM_HW_KGDB))
83019cf921bSrearnsha 		return EBUSY;
83119cf921bSrearnsha #endif
83219cf921bSrearnsha 
83319cf921bSrearnsha 	tp = sc->sc_tty;
83419cf921bSrearnsha 
835e8373398Selad 	if (kauth_authorize_device_tty(l->l_cred, KAUTH_DEVICE_TTY_OPEN, tp))
836e8373398Selad 		return (EBUSY);
83719cf921bSrearnsha 
83819cf921bSrearnsha 	s = spltty();
83919cf921bSrearnsha 
84019cf921bSrearnsha 	/*
84119cf921bSrearnsha 	 * Do the following iff this is a first open.
84219cf921bSrearnsha 	 */
84319cf921bSrearnsha 	if (!ISSET(tp->t_state, TS_ISOPEN) && tp->t_wopen == 0) {
84419cf921bSrearnsha 		struct termios t;
845be917db8Sjmcneill 		struct timeval now, diff;
84619cf921bSrearnsha 
84719cf921bSrearnsha 		tp->t_dev = dev;
84819cf921bSrearnsha 
84919cf921bSrearnsha 		if (sc->enable) {
85019cf921bSrearnsha 			if ((*sc->enable)(sc)) {
85119cf921bSrearnsha 				splx(s);
8524c5a9380Sskrll 				aprint_error_dev(sc->sc_dev,
8534c5a9380Sskrll 				    "device enable failed\n");
85419cf921bSrearnsha 				return EIO;
85519cf921bSrearnsha 			}
856fc0cd919Sskrll 			mutex_spin_enter(&sc->sc_lock);
85719cf921bSrearnsha 			sc->enabled = 1;
85819cf921bSrearnsha 			plcom_config(sc);
859fc0cd919Sskrll 		} else {
860fc0cd919Sskrll 			mutex_spin_enter(&sc->sc_lock);
86119cf921bSrearnsha 		}
86219cf921bSrearnsha 
863be917db8Sjmcneill 		if (timerisset(&sc->sc_hup_pending)) {
86433f66e53Sjmcneill 			microuptime(&now);
865be917db8Sjmcneill 			while (timercmp(&now, &sc->sc_hup_pending, <)) {
866be917db8Sjmcneill 				timersub(&sc->sc_hup_pending, &now, &diff);
867be917db8Sjmcneill 				const int ms = diff.tv_sec * 100 +
86833f66e53Sjmcneill 				    diff.tv_usec / 1000;
86933f66e53Sjmcneill 				kpause(ttclos, false, uimax(mstohz(ms), 1),
87033f66e53Sjmcneill 				    &sc->sc_lock);
87133f66e53Sjmcneill 				microuptime(&now);
872be917db8Sjmcneill 			}
873be917db8Sjmcneill 			timerclear(&sc->sc_hup_pending);
874be917db8Sjmcneill 		}
875be917db8Sjmcneill 
87619cf921bSrearnsha 		/* Turn on interrupts. */
87719cf921bSrearnsha 		/* IER_ERXRDY | IER_ERLS | IER_EMSC;  */
87819cf921bSrearnsha 		/* Fetch the current modem control status, needed later. */
8794c5a9380Sskrll 		sc->sc_cr = PL01X_CR_UARTEN;
8804c5a9380Sskrll 		switch (pi->pi_type) {
8814c5a9380Sskrll 		case PLCOM_TYPE_PL010:
8824c5a9380Sskrll 			SET(sc->sc_cr,
8834c5a9380Sskrll 			    PL010_CR_RIE | PL010_CR_RTIE | PL010_CR_MSIE);
8844c5a9380Sskrll 			PWRITE1(pi, PL010COM_CR, sc->sc_cr);
8854c5a9380Sskrll 			sc->sc_msr = PREAD1(pi, PL01XCOM_FR);
8864c5a9380Sskrll 			break;
8874c5a9380Sskrll 		case PLCOM_TYPE_PL011:
888820c39ceSmlelstv 		case PLCOM_TYPE_GENERIC_UART:
8894c5a9380Sskrll 			SET(sc->sc_cr, PL011_CR_RXE | PL011_CR_TXE);
8904c5a9380Sskrll 			SET(sc->sc_imsc, PL011_INT_RT | PL011_INT_RX |
8914c5a9380Sskrll 			    PL011_INT_MSMASK);
8924c5a9380Sskrll 			PWRITE4(pi, PL011COM_IMSC, sc->sc_imsc);
8934c5a9380Sskrll 			sc->sc_msr = PREAD4(pi, PL01XCOM_FR);
8944c5a9380Sskrll 			break;
8954c5a9380Sskrll 		}
89619cf921bSrearnsha 
89719cf921bSrearnsha 		/* Clear PPS capture state on first open. */
898a2249ef7Sad 
899a2249ef7Sad 		mutex_spin_enter(&timecounter_lock);
90019cf921bSrearnsha 		sc->sc_ppsmask = 0;
90119cf921bSrearnsha 		sc->ppsparam.mode = 0;
902a2249ef7Sad 		mutex_spin_exit(&timecounter_lock);
90319cf921bSrearnsha 
904346c2c54Sskrll 		mutex_spin_exit(&sc->sc_lock);
90519cf921bSrearnsha 
90619cf921bSrearnsha 		/*
90719cf921bSrearnsha 		 * Initialize the termios status to the defaults.  Add in the
90819cf921bSrearnsha 		 * sticky bits from TIOCSFLAGS.
90919cf921bSrearnsha 		 */
91019cf921bSrearnsha 		if (ISSET(sc->sc_hwflags, PLCOM_HW_CONSOLE)) {
91119cf921bSrearnsha 			t.c_ospeed = plcomconsrate;
91219cf921bSrearnsha 			t.c_cflag = plcomconscflag;
91319cf921bSrearnsha 		} else {
91419cf921bSrearnsha 			t.c_ospeed = TTYDEF_SPEED;
91519cf921bSrearnsha 			t.c_cflag = TTYDEF_CFLAG;
91619cf921bSrearnsha 		}
9174c5a9380Sskrll 		t.c_ispeed = t.c_ospeed;
9184c5a9380Sskrll 
91919cf921bSrearnsha 		if (ISSET(sc->sc_swflags, TIOCFLAG_CLOCAL))
92019cf921bSrearnsha 			SET(t.c_cflag, CLOCAL);
92119cf921bSrearnsha 		if (ISSET(sc->sc_swflags, TIOCFLAG_CRTSCTS))
92219cf921bSrearnsha 			SET(t.c_cflag, CRTSCTS);
92319cf921bSrearnsha 		if (ISSET(sc->sc_swflags, TIOCFLAG_MDMBUF))
92419cf921bSrearnsha 			SET(t.c_cflag, MDMBUF);
92519cf921bSrearnsha 		/* Make sure plcomparam() will do something. */
92619cf921bSrearnsha 		tp->t_ospeed = 0;
92719cf921bSrearnsha 		(void) plcomparam(tp, &t);
92819cf921bSrearnsha 		tp->t_iflag = TTYDEF_IFLAG;
92919cf921bSrearnsha 		tp->t_oflag = TTYDEF_OFLAG;
93019cf921bSrearnsha 		tp->t_lflag = TTYDEF_LFLAG;
93119cf921bSrearnsha 		ttychars(tp);
93219cf921bSrearnsha 		ttsetwater(tp);
93319cf921bSrearnsha 
934fc0cd919Sskrll 		mutex_spin_enter(&sc->sc_lock);
93519cf921bSrearnsha 
93619cf921bSrearnsha 		/*
93719cf921bSrearnsha 		 * Turn on DTR.  We must always do this, even if carrier is not
93819cf921bSrearnsha 		 * present, because otherwise we'd have to use TIOCSDTR
93919cf921bSrearnsha 		 * immediately after setting CLOCAL, which applications do not
94019cf921bSrearnsha 		 * expect.  We always assert DTR while the device is open
94119cf921bSrearnsha 		 * unless explicitly requested to deassert it.
94219cf921bSrearnsha 		 */
94319cf921bSrearnsha 		plcom_modem(sc, 1);
94419cf921bSrearnsha 
94519cf921bSrearnsha 		/* Clear the input ring, and unblock. */
94619cf921bSrearnsha 		sc->sc_rbput = sc->sc_rbget = sc->sc_rbuf;
94719cf921bSrearnsha 		sc->sc_rbavail = plcom_rbuf_size;
94819cf921bSrearnsha 		plcom_iflush(sc);
94919cf921bSrearnsha 		CLR(sc->sc_rx_flags, RX_ANY_BLOCK);
95019cf921bSrearnsha 		plcom_hwiflow(sc);
95119cf921bSrearnsha 
95219cf921bSrearnsha #ifdef PLCOM_DEBUG
95319cf921bSrearnsha 		if (plcom_debug)
95419cf921bSrearnsha 			plcomstatus(sc, "plcomopen  ");
95519cf921bSrearnsha #endif
95619cf921bSrearnsha 
957fc0cd919Sskrll 		mutex_spin_exit(&sc->sc_lock);
95819cf921bSrearnsha 	}
95919cf921bSrearnsha 
96019cf921bSrearnsha 	splx(s);
96119cf921bSrearnsha 
96219cf921bSrearnsha 	error = ttyopen(tp, PLCOMDIALOUT(dev), ISSET(flag, O_NONBLOCK));
96319cf921bSrearnsha 	if (error)
96419cf921bSrearnsha 		goto bad;
96519cf921bSrearnsha 
96619cf921bSrearnsha 	error = (*tp->t_linesw->l_open)(dev, tp);
96719cf921bSrearnsha 	if (error)
96819cf921bSrearnsha 		goto bad;
96919cf921bSrearnsha 
97019cf921bSrearnsha 	return 0;
97119cf921bSrearnsha 
97219cf921bSrearnsha bad:
97319cf921bSrearnsha 	if (!ISSET(tp->t_state, TS_ISOPEN) && tp->t_wopen == 0) {
97419cf921bSrearnsha 		/*
97519cf921bSrearnsha 		 * We failed to open the device, and nobody else had it opened.
97619cf921bSrearnsha 		 * Clean up the state as appropriate.
97719cf921bSrearnsha 		 */
97819cf921bSrearnsha 		plcom_shutdown(sc);
97919cf921bSrearnsha 	}
98019cf921bSrearnsha 
98119cf921bSrearnsha 	return error;
98219cf921bSrearnsha }
98319cf921bSrearnsha 
98419cf921bSrearnsha int
plcomclose(dev_t dev,int flag,int mode,struct lwp * l)98595e1ffb1Schristos plcomclose(dev_t dev, int flag, int mode, struct lwp *l)
98619cf921bSrearnsha {
98763495428Scegger 	struct plcom_softc *sc =
98863495428Scegger 		device_lookup_private(&plcom_cd, PLCOMUNIT(dev));
98919cf921bSrearnsha 	struct tty *tp = sc->sc_tty;
99019cf921bSrearnsha 
99119cf921bSrearnsha 	/* XXX This is for cons.c. */
99219cf921bSrearnsha 	if (!ISSET(tp->t_state, TS_ISOPEN))
99319cf921bSrearnsha 		return 0;
99419cf921bSrearnsha 
99519cf921bSrearnsha 	(*tp->t_linesw->l_close)(tp, flag);
99619cf921bSrearnsha 	ttyclose(tp);
99719cf921bSrearnsha 
99819cf921bSrearnsha 	if (PLCOM_ISALIVE(sc) == 0)
99919cf921bSrearnsha 		return 0;
100019cf921bSrearnsha 
100119cf921bSrearnsha 	if (!ISSET(tp->t_state, TS_ISOPEN) && tp->t_wopen == 0) {
100219cf921bSrearnsha 		/*
100319cf921bSrearnsha 		 * Although we got a last close, the device may still be in
100419cf921bSrearnsha 		 * use; e.g. if this was the dialout node, and there are still
100519cf921bSrearnsha 		 * processes waiting for carrier on the non-dialout node.
100619cf921bSrearnsha 		 */
100719cf921bSrearnsha 		plcom_shutdown(sc);
100819cf921bSrearnsha 	}
100919cf921bSrearnsha 
101019cf921bSrearnsha 	return 0;
101119cf921bSrearnsha }
101219cf921bSrearnsha 
101319cf921bSrearnsha int
plcomread(dev_t dev,struct uio * uio,int flag)101419cf921bSrearnsha plcomread(dev_t dev, struct uio *uio, int flag)
101519cf921bSrearnsha {
101663495428Scegger 	struct plcom_softc *sc =
101763495428Scegger 		device_lookup_private(&plcom_cd, PLCOMUNIT(dev));
101819cf921bSrearnsha 	struct tty *tp = sc->sc_tty;
101919cf921bSrearnsha 
102019cf921bSrearnsha 	if (PLCOM_ISALIVE(sc) == 0)
102119cf921bSrearnsha 		return EIO;
102219cf921bSrearnsha 
102319cf921bSrearnsha 	return (*tp->t_linesw->l_read)(tp, uio, flag);
102419cf921bSrearnsha }
102519cf921bSrearnsha 
102619cf921bSrearnsha int
plcomwrite(dev_t dev,struct uio * uio,int flag)102719cf921bSrearnsha plcomwrite(dev_t dev, struct uio *uio, int flag)
102819cf921bSrearnsha {
102963495428Scegger 	struct plcom_softc *sc =
103063495428Scegger 		device_lookup_private(&plcom_cd, PLCOMUNIT(dev));
103119cf921bSrearnsha 	struct tty *tp = sc->sc_tty;
103219cf921bSrearnsha 
103319cf921bSrearnsha 	if (PLCOM_ISALIVE(sc) == 0)
103419cf921bSrearnsha 		return EIO;
103519cf921bSrearnsha 
103619cf921bSrearnsha 	return (*tp->t_linesw->l_write)(tp, uio, flag);
103719cf921bSrearnsha }
103819cf921bSrearnsha 
103919cf921bSrearnsha int
plcompoll(dev_t dev,int events,struct lwp * l)104095e1ffb1Schristos plcompoll(dev_t dev, int events, struct lwp *l)
104119cf921bSrearnsha {
104263495428Scegger 	struct plcom_softc *sc =
104363495428Scegger 		device_lookup_private(&plcom_cd, PLCOMUNIT(dev));
104419cf921bSrearnsha 	struct tty *tp = sc->sc_tty;
104519cf921bSrearnsha 
104619cf921bSrearnsha 	if (PLCOM_ISALIVE(sc) == 0)
104719cf921bSrearnsha 		return EIO;
104819cf921bSrearnsha 
104995e1ffb1Schristos 	return (*tp->t_linesw->l_poll)(tp, events, l);
105019cf921bSrearnsha }
105119cf921bSrearnsha 
105219cf921bSrearnsha struct tty *
plcomtty(dev_t dev)105319cf921bSrearnsha plcomtty(dev_t dev)
105419cf921bSrearnsha {
105563495428Scegger 	struct plcom_softc *sc =
105663495428Scegger 		device_lookup_private(&plcom_cd, PLCOMUNIT(dev));
105719cf921bSrearnsha 	struct tty *tp = sc->sc_tty;
105819cf921bSrearnsha 
105919cf921bSrearnsha 	return tp;
106019cf921bSrearnsha }
106119cf921bSrearnsha 
106219cf921bSrearnsha int
plcomioctl(dev_t dev,u_long cmd,void * data,int flag,struct lwp * l)106353524e44Schristos plcomioctl(dev_t dev, u_long cmd, void *data, int flag, struct lwp *l)
106419cf921bSrearnsha {
106563495428Scegger 	struct plcom_softc *sc =
106663495428Scegger 		device_lookup_private(&plcom_cd, PLCOMUNIT(dev));
10674c5a9380Sskrll 	struct tty *tp;
106819cf921bSrearnsha 	int error;
106919cf921bSrearnsha 
10704c5a9380Sskrll 	if (sc == NULL)
10714c5a9380Sskrll 		return ENXIO;
107219cf921bSrearnsha 	if (PLCOM_ISALIVE(sc) == 0)
107319cf921bSrearnsha 		return EIO;
107419cf921bSrearnsha 
10754c5a9380Sskrll 	tp = sc->sc_tty;
10764c5a9380Sskrll 
107795e1ffb1Schristos 	error = (*tp->t_linesw->l_ioctl)(tp, cmd, data, flag, l);
107831144d99Satatat 	if (error != EPASSTHROUGH)
107919cf921bSrearnsha 		return error;
108019cf921bSrearnsha 
108195e1ffb1Schristos 	error = ttioctl(tp, cmd, data, flag, l);
108231144d99Satatat 	if (error != EPASSTHROUGH)
108319cf921bSrearnsha 		return error;
108419cf921bSrearnsha 
108519cf921bSrearnsha 	error = 0;
10864c5a9380Sskrll 	switch (cmd) {
10874c5a9380Sskrll 	case TIOCSFLAGS:
10884c5a9380Sskrll 		error = kauth_authorize_device_tty(l->l_cred,
10894c5a9380Sskrll 		    KAUTH_DEVICE_TTY_PRIVSET, tp);
10904c5a9380Sskrll 		break;
10914c5a9380Sskrll 	default:
10924c5a9380Sskrll 		/* nothing */
10934c5a9380Sskrll 		break;
10944c5a9380Sskrll 	}
10954c5a9380Sskrll 	if (error) {
10964c5a9380Sskrll 		return error;
10974c5a9380Sskrll 	}
109819cf921bSrearnsha 
1099fc0cd919Sskrll 	mutex_spin_enter(&sc->sc_lock);
110019cf921bSrearnsha 	switch (cmd) {
110119cf921bSrearnsha 	case TIOCSBRK:
110219cf921bSrearnsha 		plcom_break(sc, 1);
110319cf921bSrearnsha 		break;
110419cf921bSrearnsha 
110519cf921bSrearnsha 	case TIOCCBRK:
110619cf921bSrearnsha 		plcom_break(sc, 0);
110719cf921bSrearnsha 		break;
110819cf921bSrearnsha 
110919cf921bSrearnsha 	case TIOCSDTR:
111019cf921bSrearnsha 		plcom_modem(sc, 1);
111119cf921bSrearnsha 		break;
111219cf921bSrearnsha 
111319cf921bSrearnsha 	case TIOCCDTR:
111419cf921bSrearnsha 		plcom_modem(sc, 0);
111519cf921bSrearnsha 		break;
111619cf921bSrearnsha 
111719cf921bSrearnsha 	case TIOCGFLAGS:
111819cf921bSrearnsha 		*(int *)data = sc->sc_swflags;
111919cf921bSrearnsha 		break;
112019cf921bSrearnsha 
112119cf921bSrearnsha 	case TIOCSFLAGS:
112219cf921bSrearnsha 		sc->sc_swflags = *(int *)data;
112319cf921bSrearnsha 		break;
112419cf921bSrearnsha 
112519cf921bSrearnsha 	case TIOCMSET:
112619cf921bSrearnsha 	case TIOCMBIS:
112719cf921bSrearnsha 	case TIOCMBIC:
112819cf921bSrearnsha 		tiocm_to_plcom(sc, cmd, *(int *)data);
112919cf921bSrearnsha 		break;
113019cf921bSrearnsha 
113119cf921bSrearnsha 	case TIOCMGET:
113219cf921bSrearnsha 		*(int *)data = plcom_to_tiocm(sc);
113319cf921bSrearnsha 		break;
113419cf921bSrearnsha 
113519cf921bSrearnsha 	case PPS_IOC_CREATE:
113619cf921bSrearnsha 		break;
113719cf921bSrearnsha 
113819cf921bSrearnsha 	case PPS_IOC_DESTROY:
113919cf921bSrearnsha 		break;
114019cf921bSrearnsha 
114119cf921bSrearnsha 	case PPS_IOC_GETPARAMS: {
114219cf921bSrearnsha 		pps_params_t *pp;
114319cf921bSrearnsha 		pp = (pps_params_t *)data;
1144a2249ef7Sad 		mutex_spin_enter(&timecounter_lock);
114519cf921bSrearnsha 		*pp = sc->ppsparam;
1146a2249ef7Sad 		mutex_spin_exit(&timecounter_lock);
114719cf921bSrearnsha 		break;
114819cf921bSrearnsha 	}
114919cf921bSrearnsha 
115019cf921bSrearnsha 	case PPS_IOC_SETPARAMS: {
115119cf921bSrearnsha 	  	pps_params_t *pp;
115219cf921bSrearnsha 		int mode;
115319cf921bSrearnsha 		pp = (pps_params_t *)data;
1154a2249ef7Sad 		mutex_spin_enter(&timecounter_lock);
115519cf921bSrearnsha 		if (pp->mode & ~ppscap) {
115619cf921bSrearnsha 			error = EINVAL;
1157a2249ef7Sad 			mutex_spin_exit(&timecounter_lock);
115819cf921bSrearnsha 			break;
115919cf921bSrearnsha 		}
116019cf921bSrearnsha 		sc->ppsparam = *pp;
116119cf921bSrearnsha 	 	/*
116219cf921bSrearnsha 		 * Compute msr masks from user-specified timestamp state.
116319cf921bSrearnsha 		 */
116419cf921bSrearnsha 		mode = sc->ppsparam.mode;
116519cf921bSrearnsha #ifdef	PPS_SYNC
116619cf921bSrearnsha 		if (mode & PPS_HARDPPSONASSERT) {
116719cf921bSrearnsha 			mode |= PPS_CAPTUREASSERT;
116819cf921bSrearnsha 			/* XXX revoke any previous HARDPPS source */
116919cf921bSrearnsha 		}
117019cf921bSrearnsha 		if (mode & PPS_HARDPPSONCLEAR) {
117119cf921bSrearnsha 			mode |= PPS_CAPTURECLEAR;
117219cf921bSrearnsha 			/* XXX revoke any previous HARDPPS source */
117319cf921bSrearnsha 		}
117419cf921bSrearnsha #endif	/* PPS_SYNC */
117519cf921bSrearnsha 		switch (mode & PPS_CAPTUREBOTH) {
117619cf921bSrearnsha 		case 0:
117719cf921bSrearnsha 			sc->sc_ppsmask = 0;
117819cf921bSrearnsha 			break;
117919cf921bSrearnsha 
118019cf921bSrearnsha 		case PPS_CAPTUREASSERT:
1181b9703c3dSskrll 			sc->sc_ppsmask = PL01X_MSR_DCD;
1182b9703c3dSskrll 			sc->sc_ppsassert = PL01X_MSR_DCD;
118319cf921bSrearnsha 			sc->sc_ppsclear = -1;
118419cf921bSrearnsha 			break;
118519cf921bSrearnsha 
118619cf921bSrearnsha 		case PPS_CAPTURECLEAR:
1187b9703c3dSskrll 			sc->sc_ppsmask = PL01X_MSR_DCD;
118819cf921bSrearnsha 			sc->sc_ppsassert = -1;
118919cf921bSrearnsha 			sc->sc_ppsclear = 0;
119019cf921bSrearnsha 			break;
119119cf921bSrearnsha 
119219cf921bSrearnsha 		case PPS_CAPTUREBOTH:
1193b9703c3dSskrll 			sc->sc_ppsmask = PL01X_MSR_DCD;
1194b9703c3dSskrll 			sc->sc_ppsassert = PL01X_MSR_DCD;
119519cf921bSrearnsha 			sc->sc_ppsclear = 0;
119619cf921bSrearnsha 			break;
119719cf921bSrearnsha 
119819cf921bSrearnsha 		default:
119919cf921bSrearnsha 			error = EINVAL;
120019cf921bSrearnsha 			break;
120119cf921bSrearnsha 		}
1202a2249ef7Sad 		mutex_spin_exit(&timecounter_lock);
120319cf921bSrearnsha 		break;
120419cf921bSrearnsha 	}
120519cf921bSrearnsha 
120619cf921bSrearnsha 	case PPS_IOC_GETCAP:
120719cf921bSrearnsha 		*(int*)data = ppscap;
120819cf921bSrearnsha 		break;
120919cf921bSrearnsha 
121019cf921bSrearnsha 	case PPS_IOC_FETCH: {
121119cf921bSrearnsha 		pps_info_t *pi;
121219cf921bSrearnsha 		pi = (pps_info_t *)data;
1213a2249ef7Sad 		mutex_spin_enter(&timecounter_lock);
121419cf921bSrearnsha 		*pi = sc->ppsinfo;
1215a2249ef7Sad 		mutex_spin_exit(&timecounter_lock);
121619cf921bSrearnsha 		break;
121719cf921bSrearnsha 	}
121819cf921bSrearnsha 
121919cf921bSrearnsha 	case TIOCDCDTIMESTAMP:	/* XXX old, overloaded  API used by xntpd v3 */
122019cf921bSrearnsha 		/*
122119cf921bSrearnsha 		 * Some GPS clocks models use the falling rather than
122219cf921bSrearnsha 		 * rising edge as the on-the-second signal.
122319cf921bSrearnsha 		 * The old API has no way to specify PPS polarity.
122419cf921bSrearnsha 		 */
1225a2249ef7Sad 		mutex_spin_enter(&timecounter_lock);
1226b9703c3dSskrll 		sc->sc_ppsmask = PL01X_MSR_DCD;
122719cf921bSrearnsha #ifndef PPS_TRAILING_EDGE
1228b9703c3dSskrll 		sc->sc_ppsassert = PL01X_MSR_DCD;
122919cf921bSrearnsha 		sc->sc_ppsclear = -1;
123019cf921bSrearnsha 		TIMESPEC_TO_TIMEVAL((struct timeval *)data,
123119cf921bSrearnsha 		    &sc->ppsinfo.assert_timestamp);
123219cf921bSrearnsha #else
123319cf921bSrearnsha 		sc->sc_ppsassert = -1
123419cf921bSrearnsha 		sc->sc_ppsclear = 0;
123519cf921bSrearnsha 		TIMESPEC_TO_TIMEVAL((struct timeval *)data,
123619cf921bSrearnsha 		    &sc->ppsinfo.clear_timestamp);
123719cf921bSrearnsha #endif
1238a2249ef7Sad 		mutex_spin_exit(&timecounter_lock);
123919cf921bSrearnsha 		break;
124019cf921bSrearnsha 
124119cf921bSrearnsha 	default:
124231144d99Satatat 		error = EPASSTHROUGH;
124319cf921bSrearnsha 		break;
124419cf921bSrearnsha 	}
124519cf921bSrearnsha 
1246fc0cd919Sskrll 	mutex_spin_exit(&sc->sc_lock);
124719cf921bSrearnsha 
124819cf921bSrearnsha #ifdef PLCOM_DEBUG
124919cf921bSrearnsha 	if (plcom_debug)
125019cf921bSrearnsha 		plcomstatus(sc, "plcomioctl ");
125119cf921bSrearnsha #endif
125219cf921bSrearnsha 
125319cf921bSrearnsha 	return error;
125419cf921bSrearnsha }
125519cf921bSrearnsha 
1256210924e6Sriastradh static inline void
plcom_schedrx(struct plcom_softc * sc)125719cf921bSrearnsha plcom_schedrx(struct plcom_softc *sc)
125819cf921bSrearnsha {
125919cf921bSrearnsha 
126019cf921bSrearnsha 	sc->sc_rx_ready = 1;
126119cf921bSrearnsha 
126219cf921bSrearnsha 	/* Wake up the poller. */
1263fbed3be2Sad 	softint_schedule(sc->sc_si);
126419cf921bSrearnsha }
126519cf921bSrearnsha 
126619cf921bSrearnsha void
plcom_break(struct plcom_softc * sc,int onoff)126719cf921bSrearnsha plcom_break(struct plcom_softc *sc, int onoff)
126819cf921bSrearnsha {
126919cf921bSrearnsha 
127019cf921bSrearnsha 	if (onoff)
1271b9703c3dSskrll 		SET(sc->sc_lcr, PL01X_LCR_BRK);
127219cf921bSrearnsha 	else
1273b9703c3dSskrll 		CLR(sc->sc_lcr, PL01X_LCR_BRK);
127419cf921bSrearnsha 
127519cf921bSrearnsha 	if (!sc->sc_heldchange) {
127619cf921bSrearnsha 		if (sc->sc_tx_busy) {
127719cf921bSrearnsha 			sc->sc_heldtbc = sc->sc_tbc;
127819cf921bSrearnsha 			sc->sc_tbc = 0;
127919cf921bSrearnsha 			sc->sc_heldchange = 1;
128019cf921bSrearnsha 		} else
128119cf921bSrearnsha 			plcom_loadchannelregs(sc);
128219cf921bSrearnsha 	}
128319cf921bSrearnsha }
128419cf921bSrearnsha 
128519cf921bSrearnsha void
plcom_modem(struct plcom_softc * sc,int onoff)128619cf921bSrearnsha plcom_modem(struct plcom_softc *sc, int onoff)
128719cf921bSrearnsha {
128819cf921bSrearnsha 
128919cf921bSrearnsha 	if (sc->sc_mcr_dtr == 0)
129019cf921bSrearnsha 		return;
129119cf921bSrearnsha 
129219cf921bSrearnsha 	if (onoff)
129319cf921bSrearnsha 		SET(sc->sc_mcr, sc->sc_mcr_dtr);
129419cf921bSrearnsha 	else
129519cf921bSrearnsha 		CLR(sc->sc_mcr, sc->sc_mcr_dtr);
129619cf921bSrearnsha 
129719cf921bSrearnsha 	if (!sc->sc_heldchange) {
129819cf921bSrearnsha 		if (sc->sc_tx_busy) {
129919cf921bSrearnsha 			sc->sc_heldtbc = sc->sc_tbc;
130019cf921bSrearnsha 			sc->sc_tbc = 0;
130119cf921bSrearnsha 			sc->sc_heldchange = 1;
130219cf921bSrearnsha 		} else
130319cf921bSrearnsha 			plcom_loadchannelregs(sc);
130419cf921bSrearnsha 	}
130519cf921bSrearnsha }
130619cf921bSrearnsha 
130719cf921bSrearnsha void
tiocm_to_plcom(struct plcom_softc * sc,u_long how,int ttybits)130819cf921bSrearnsha tiocm_to_plcom(struct plcom_softc *sc, u_long how, int ttybits)
130919cf921bSrearnsha {
131019cf921bSrearnsha 	u_char plcombits;
131119cf921bSrearnsha 
131219cf921bSrearnsha 	plcombits = 0;
131319cf921bSrearnsha 	if (ISSET(ttybits, TIOCM_DTR))
1314b9703c3dSskrll 		SET(plcombits, PL01X_MCR_DTR);
131519cf921bSrearnsha 	if (ISSET(ttybits, TIOCM_RTS))
1316b9703c3dSskrll 		SET(plcombits, PL01X_MCR_RTS);
131719cf921bSrearnsha 
131819cf921bSrearnsha 	switch (how) {
131919cf921bSrearnsha 	case TIOCMBIC:
132019cf921bSrearnsha 		CLR(sc->sc_mcr, plcombits);
132119cf921bSrearnsha 		break;
132219cf921bSrearnsha 
132319cf921bSrearnsha 	case TIOCMBIS:
132419cf921bSrearnsha 		SET(sc->sc_mcr, plcombits);
132519cf921bSrearnsha 		break;
132619cf921bSrearnsha 
132719cf921bSrearnsha 	case TIOCMSET:
1328b9703c3dSskrll 		CLR(sc->sc_mcr, PL01X_MCR_DTR | PL01X_MCR_RTS);
132919cf921bSrearnsha 		SET(sc->sc_mcr, plcombits);
133019cf921bSrearnsha 		break;
133119cf921bSrearnsha 	}
133219cf921bSrearnsha 
133319cf921bSrearnsha 	if (!sc->sc_heldchange) {
133419cf921bSrearnsha 		if (sc->sc_tx_busy) {
133519cf921bSrearnsha 			sc->sc_heldtbc = sc->sc_tbc;
133619cf921bSrearnsha 			sc->sc_tbc = 0;
133719cf921bSrearnsha 			sc->sc_heldchange = 1;
133819cf921bSrearnsha 		} else
133919cf921bSrearnsha 			plcom_loadchannelregs(sc);
134019cf921bSrearnsha 	}
134119cf921bSrearnsha }
134219cf921bSrearnsha 
134319cf921bSrearnsha int
plcom_to_tiocm(struct plcom_softc * sc)134419cf921bSrearnsha plcom_to_tiocm(struct plcom_softc *sc)
134519cf921bSrearnsha {
134619cf921bSrearnsha 	u_char plcombits;
134719cf921bSrearnsha 	int ttybits = 0;
134819cf921bSrearnsha 
134919cf921bSrearnsha 	plcombits = sc->sc_mcr;
1350b9703c3dSskrll 	if (ISSET(plcombits, PL01X_MCR_DTR))
135119cf921bSrearnsha 		SET(ttybits, TIOCM_DTR);
1352b9703c3dSskrll 	if (ISSET(plcombits, PL01X_MCR_RTS))
135319cf921bSrearnsha 		SET(ttybits, TIOCM_RTS);
135419cf921bSrearnsha 
135519cf921bSrearnsha 	plcombits = sc->sc_msr;
1356b9703c3dSskrll 	if (ISSET(plcombits, PL01X_MSR_DCD))
135719cf921bSrearnsha 		SET(ttybits, TIOCM_CD);
1358b9703c3dSskrll 	if (ISSET(plcombits, PL01X_MSR_CTS))
135919cf921bSrearnsha 		SET(ttybits, TIOCM_CTS);
1360b9703c3dSskrll 	if (ISSET(plcombits, PL01X_MSR_DSR))
136119cf921bSrearnsha 		SET(ttybits, TIOCM_DSR);
13624c5a9380Sskrll 	if (ISSET(plcombits, PL011_MSR_RI))
13634c5a9380Sskrll 		SET(ttybits, TIOCM_RI);
136419cf921bSrearnsha 
136519cf921bSrearnsha 	if (sc->sc_cr != 0)
136619cf921bSrearnsha 		SET(ttybits, TIOCM_LE);
136719cf921bSrearnsha 
136819cf921bSrearnsha 	return ttybits;
136919cf921bSrearnsha }
137019cf921bSrearnsha 
1371820c39ceSmlelstv static uint32_t
cflag2lcr(tcflag_t cflag)137219cf921bSrearnsha cflag2lcr(tcflag_t cflag)
137319cf921bSrearnsha {
1374820c39ceSmlelstv 	uint32_t lcr = 0;
137519cf921bSrearnsha 
137619cf921bSrearnsha 	switch (ISSET(cflag, CSIZE)) {
137719cf921bSrearnsha 	case CS5:
1378b9703c3dSskrll 		SET(lcr, PL01X_LCR_5BITS);
137919cf921bSrearnsha 		break;
138019cf921bSrearnsha 	case CS6:
1381b9703c3dSskrll 		SET(lcr, PL01X_LCR_6BITS);
138219cf921bSrearnsha 		break;
138319cf921bSrearnsha 	case CS7:
1384b9703c3dSskrll 		SET(lcr, PL01X_LCR_7BITS);
138519cf921bSrearnsha 		break;
138619cf921bSrearnsha 	case CS8:
1387b9703c3dSskrll 		SET(lcr, PL01X_LCR_8BITS);
138819cf921bSrearnsha 		break;
138919cf921bSrearnsha 	}
139019cf921bSrearnsha 	if (ISSET(cflag, PARENB)) {
1391b9703c3dSskrll 		SET(lcr, PL01X_LCR_PEN);
139219cf921bSrearnsha 		if (!ISSET(cflag, PARODD))
1393b9703c3dSskrll 			SET(lcr, PL01X_LCR_EPS);
139419cf921bSrearnsha 	}
139519cf921bSrearnsha 	if (ISSET(cflag, CSTOPB))
1396b9703c3dSskrll 		SET(lcr, PL01X_LCR_STP2);
139719cf921bSrearnsha 
139819cf921bSrearnsha 	return lcr;
139919cf921bSrearnsha }
140019cf921bSrearnsha 
140119cf921bSrearnsha int
plcomparam(struct tty * tp,struct termios * t)140219cf921bSrearnsha plcomparam(struct tty *tp, struct termios *t)
140319cf921bSrearnsha {
140463495428Scegger 	struct plcom_softc *sc =
140563495428Scegger 		device_lookup_private(&plcom_cd, PLCOMUNIT(tp->t_dev));
14064c5a9380Sskrll 	struct plcom_instance *pi = &sc->sc_pi;
1407820c39ceSmlelstv 	int ospeed = -1, lvl;
1408820c39ceSmlelstv 	uint32_t lcr;
140919cf921bSrearnsha 
141019cf921bSrearnsha 	if (PLCOM_ISALIVE(sc) == 0)
141119cf921bSrearnsha 		return EIO;
141219cf921bSrearnsha 
14134c5a9380Sskrll 	switch (pi->pi_type) {
14144c5a9380Sskrll 	case PLCOM_TYPE_PL010:
14154c5a9380Sskrll 		ospeed = pl010comspeed(t->c_ospeed, sc->sc_frequency);
14164c5a9380Sskrll 		break;
14174c5a9380Sskrll 	case PLCOM_TYPE_PL011:
1418820c39ceSmlelstv 	case PLCOM_TYPE_GENERIC_UART:
14194c5a9380Sskrll 		ospeed = pl011comspeed(t->c_ospeed, sc->sc_frequency);
14204c5a9380Sskrll 		break;
14214c5a9380Sskrll 	}
142219cf921bSrearnsha 
142319cf921bSrearnsha 	/* Check requested parameters. */
142419cf921bSrearnsha 	if (ospeed < 0)
142519cf921bSrearnsha 		return EINVAL;
142619cf921bSrearnsha 	if (t->c_ispeed && t->c_ispeed != t->c_ospeed)
142719cf921bSrearnsha 		return EINVAL;
142819cf921bSrearnsha 
142919cf921bSrearnsha 	/*
143019cf921bSrearnsha 	 * For the console, always force CLOCAL and !HUPCL, so that the port
143119cf921bSrearnsha 	 * is always active.
143219cf921bSrearnsha 	 */
143319cf921bSrearnsha 	if (ISSET(sc->sc_swflags, TIOCFLAG_SOFTCAR) ||
143419cf921bSrearnsha 	    ISSET(sc->sc_hwflags, PLCOM_HW_CONSOLE)) {
143519cf921bSrearnsha 		SET(t->c_cflag, CLOCAL);
143619cf921bSrearnsha 		CLR(t->c_cflag, HUPCL);
143719cf921bSrearnsha 	}
143819cf921bSrearnsha 
143919cf921bSrearnsha 	/*
144019cf921bSrearnsha 	 * If there were no changes, don't do anything.  This avoids dropping
144119cf921bSrearnsha 	 * input and improves performance when all we did was frob things like
144219cf921bSrearnsha 	 * VMIN and VTIME.
144319cf921bSrearnsha 	 */
144419cf921bSrearnsha 	if (tp->t_ospeed == t->c_ospeed &&
144519cf921bSrearnsha 	    tp->t_cflag == t->c_cflag)
144619cf921bSrearnsha 		return 0;
144719cf921bSrearnsha 
1448b9703c3dSskrll 	lcr = ISSET(sc->sc_lcr, PL01X_LCR_BRK) | cflag2lcr(t->c_cflag);
144919cf921bSrearnsha 
1450fc0cd919Sskrll 	mutex_spin_enter(&sc->sc_lock);
145119cf921bSrearnsha 
145219cf921bSrearnsha 	sc->sc_lcr = lcr;
145319cf921bSrearnsha 
145419cf921bSrearnsha 	/*
145519cf921bSrearnsha 	 * PL010 has a fixed-length FIFO trigger point.
145619cf921bSrearnsha 	 */
145719cf921bSrearnsha 	if (ISSET(sc->sc_hwflags, PLCOM_HW_FIFO))
145819cf921bSrearnsha 		sc->sc_fifo = 1;
145919cf921bSrearnsha 	else
146019cf921bSrearnsha 		sc->sc_fifo = 0;
146119cf921bSrearnsha 
1462820c39ceSmlelstv 	if (sc->sc_fifo) {
1463b9703c3dSskrll 		SET(sc->sc_lcr, PL01X_LCR_FEN);
146419cf921bSrearnsha 
1465820c39ceSmlelstv 		switch (pi->pi_type) {
1466820c39ceSmlelstv 		case PLCOM_TYPE_PL010:
1467820c39ceSmlelstv 			sc->sc_ifls = 0;
1468820c39ceSmlelstv 			break;
1469820c39ceSmlelstv 		case PLCOM_TYPE_PL011:
1470820c39ceSmlelstv 		case PLCOM_TYPE_GENERIC_UART:
1471820c39ceSmlelstv 			lvl = PL011_IFLS_3QUARTERS;
1472820c39ceSmlelstv 			sc->sc_ifls = PL011_IFLS_RXIFLS(lvl);
1473820c39ceSmlelstv 
1474820c39ceSmlelstv 			lvl = PL011_IFLS_1QUARTER;
1475820c39ceSmlelstv 			sc->sc_ifls |= PL011_IFLS_TXIFLS(lvl);
1476820c39ceSmlelstv 			sc->sc_burstlen = uimin(sc->sc_fifolen * 3 / 4, 1);
1477820c39ceSmlelstv 			break;
1478820c39ceSmlelstv 		}
1479820c39ceSmlelstv 	} else
1480820c39ceSmlelstv 		sc->sc_ifls = 0;
1481820c39ceSmlelstv 
148219cf921bSrearnsha 	/*
148319cf921bSrearnsha 	 * If we're not in a mode that assumes a connection is present, then
148419cf921bSrearnsha 	 * ignore carrier changes.
148519cf921bSrearnsha 	 */
148619cf921bSrearnsha 	if (ISSET(t->c_cflag, CLOCAL | MDMBUF))
148719cf921bSrearnsha 		sc->sc_msr_dcd = 0;
148819cf921bSrearnsha 	else
1489b9703c3dSskrll 		sc->sc_msr_dcd = PL01X_MSR_DCD;
149019cf921bSrearnsha 	/*
149119cf921bSrearnsha 	 * Set the flow control pins depending on the current flow control
149219cf921bSrearnsha 	 * mode.
149319cf921bSrearnsha 	 */
149419cf921bSrearnsha 	if (ISSET(t->c_cflag, CRTSCTS)) {
1495b9703c3dSskrll 		sc->sc_mcr_dtr = PL01X_MCR_DTR;
1496820c39ceSmlelstv 
1497820c39ceSmlelstv 		switch (pi->pi_type) {
1498820c39ceSmlelstv 		case PLCOM_TYPE_PL010:
1499b9703c3dSskrll 			sc->sc_mcr_rts = PL01X_MCR_RTS;
1500b9703c3dSskrll 			sc->sc_msr_cts = PL01X_MSR_CTS;
1501820c39ceSmlelstv 			break;
1502820c39ceSmlelstv 		case PLCOM_TYPE_PL011:
1503820c39ceSmlelstv 		case PLCOM_TYPE_GENERIC_UART:
1504820c39ceSmlelstv 			sc->sc_mcr_rts = 0;
1505820c39ceSmlelstv 			sc->sc_msr_cts = 0;
1506820c39ceSmlelstv 			SET(sc->sc_cr, PL011_CR_CTSEN | PL011_CR_RTSEN);
1507820c39ceSmlelstv 			break;
1508820c39ceSmlelstv 		}
150919cf921bSrearnsha 	} else if (ISSET(t->c_cflag, MDMBUF)) {
151019cf921bSrearnsha 		/*
151119cf921bSrearnsha 		 * For DTR/DCD flow control, make sure we don't toggle DTR for
151219cf921bSrearnsha 		 * carrier detection.
151319cf921bSrearnsha 		 */
151419cf921bSrearnsha 		sc->sc_mcr_dtr = 0;
1515b9703c3dSskrll 		sc->sc_mcr_rts = PL01X_MCR_DTR;
1516b9703c3dSskrll 		sc->sc_msr_cts = PL01X_MSR_DCD;
1517820c39ceSmlelstv 
1518820c39ceSmlelstv 		switch (pi->pi_type) {
1519820c39ceSmlelstv 		case PLCOM_TYPE_PL011:
1520820c39ceSmlelstv 			CLR(sc->sc_cr, PL011_CR_CTSEN | PL011_CR_RTSEN);
1521820c39ceSmlelstv 			break;
1522820c39ceSmlelstv 		}
152319cf921bSrearnsha 	} else {
152419cf921bSrearnsha 		/*
152519cf921bSrearnsha 		 * If no flow control, then always set RTS.  This will make
152619cf921bSrearnsha 		 * the other side happy if it mistakenly thinks we're doing
152719cf921bSrearnsha 		 * RTS/CTS flow control.
152819cf921bSrearnsha 		 */
1529b9703c3dSskrll 		sc->sc_mcr_dtr = PL01X_MCR_DTR | PL01X_MCR_RTS;
153019cf921bSrearnsha 		sc->sc_mcr_rts = 0;
153119cf921bSrearnsha 		sc->sc_msr_cts = 0;
1532b9703c3dSskrll 		if (ISSET(sc->sc_mcr, PL01X_MCR_DTR))
1533b9703c3dSskrll 			SET(sc->sc_mcr, PL01X_MCR_RTS);
153419cf921bSrearnsha 		else
1535b9703c3dSskrll 			CLR(sc->sc_mcr, PL01X_MCR_RTS);
1536820c39ceSmlelstv 		switch (pi->pi_type) {
1537820c39ceSmlelstv 		case PLCOM_TYPE_PL011:
1538820c39ceSmlelstv 		case PLCOM_TYPE_GENERIC_UART:
1539820c39ceSmlelstv 			CLR(sc->sc_cr, PL011_CR_CTSEN | PL011_CR_RTSEN);
1540820c39ceSmlelstv 			break;
1541820c39ceSmlelstv 		}
154219cf921bSrearnsha 	}
154319cf921bSrearnsha 	sc->sc_msr_mask = sc->sc_msr_cts | sc->sc_msr_dcd;
154419cf921bSrearnsha 
154519cf921bSrearnsha #if 0
154619cf921bSrearnsha 	if (ospeed == 0)
154719cf921bSrearnsha 		CLR(sc->sc_mcr, sc->sc_mcr_dtr);
154819cf921bSrearnsha 	else
154919cf921bSrearnsha 		SET(sc->sc_mcr, sc->sc_mcr_dtr);
155019cf921bSrearnsha #endif
155119cf921bSrearnsha 
15524c5a9380Sskrll 	switch (pi->pi_type) {
15534c5a9380Sskrll 	case PLCOM_TYPE_PL010:
15544c5a9380Sskrll 		sc->sc_ratel = ospeed & 0xff;
15554c5a9380Sskrll 		sc->sc_rateh = (ospeed >> 8) & 0xff;
15564c5a9380Sskrll 		break;
15574c5a9380Sskrll 	case PLCOM_TYPE_PL011:
1558820c39ceSmlelstv 	case PLCOM_TYPE_GENERIC_UART:
15594c5a9380Sskrll 		sc->sc_ratel = ospeed & ((1 << 6) - 1);
15604c5a9380Sskrll 		sc->sc_rateh = ospeed >> 6;
15614c5a9380Sskrll 		break;
15624c5a9380Sskrll 	}
156319cf921bSrearnsha 
156419cf921bSrearnsha 	/* And copy to tty. */
15654c5a9380Sskrll 	tp->t_ispeed = t->c_ospeed;
156619cf921bSrearnsha 	tp->t_ospeed = t->c_ospeed;
156719cf921bSrearnsha 	tp->t_cflag = t->c_cflag;
156819cf921bSrearnsha 
156919cf921bSrearnsha 	if (!sc->sc_heldchange) {
157019cf921bSrearnsha 		if (sc->sc_tx_busy) {
157119cf921bSrearnsha 			sc->sc_heldtbc = sc->sc_tbc;
157219cf921bSrearnsha 			sc->sc_tbc = 0;
157319cf921bSrearnsha 			sc->sc_heldchange = 1;
157419cf921bSrearnsha 		} else
157519cf921bSrearnsha 			plcom_loadchannelregs(sc);
157619cf921bSrearnsha 	}
157719cf921bSrearnsha 
157819cf921bSrearnsha 	if (!ISSET(t->c_cflag, CHWFLOW)) {
157919cf921bSrearnsha 		/* Disable the high water mark. */
158019cf921bSrearnsha 		sc->sc_r_hiwat = 0;
158119cf921bSrearnsha 		sc->sc_r_lowat = 0;
158219cf921bSrearnsha 		if (ISSET(sc->sc_rx_flags, RX_TTY_OVERFLOWED)) {
158319cf921bSrearnsha 			CLR(sc->sc_rx_flags, RX_TTY_OVERFLOWED);
158419cf921bSrearnsha 			plcom_schedrx(sc);
158519cf921bSrearnsha 		}
158619cf921bSrearnsha 		if (ISSET(sc->sc_rx_flags, RX_TTY_BLOCKED|RX_IBUF_BLOCKED)) {
158719cf921bSrearnsha 			CLR(sc->sc_rx_flags, RX_TTY_BLOCKED|RX_IBUF_BLOCKED);
158819cf921bSrearnsha 			plcom_hwiflow(sc);
158919cf921bSrearnsha 		}
159019cf921bSrearnsha 	} else {
159119cf921bSrearnsha 		sc->sc_r_hiwat = plcom_rbuf_hiwat;
159219cf921bSrearnsha 		sc->sc_r_lowat = plcom_rbuf_lowat;
159319cf921bSrearnsha 	}
159419cf921bSrearnsha 
1595fc0cd919Sskrll 	mutex_spin_exit(&sc->sc_lock);
159619cf921bSrearnsha 
159719cf921bSrearnsha 	/*
159819cf921bSrearnsha 	 * Update the tty layer's idea of the carrier bit, in case we changed
159919cf921bSrearnsha 	 * CLOCAL or MDMBUF.  We don't hang up here; we only do that by
160019cf921bSrearnsha 	 * explicit request.
160119cf921bSrearnsha 	 */
1602b9703c3dSskrll 	(void) (*tp->t_linesw->l_modem)(tp, ISSET(sc->sc_msr, PL01X_MSR_DCD));
160319cf921bSrearnsha 
160419cf921bSrearnsha #ifdef PLCOM_DEBUG
160519cf921bSrearnsha 	if (plcom_debug)
160619cf921bSrearnsha 		plcomstatus(sc, "plcomparam ");
160719cf921bSrearnsha #endif
160819cf921bSrearnsha 
160919cf921bSrearnsha 	if (!ISSET(t->c_cflag, CHWFLOW)) {
161019cf921bSrearnsha 		if (sc->sc_tx_stopped) {
161119cf921bSrearnsha 			sc->sc_tx_stopped = 0;
161219cf921bSrearnsha 			plcomstart(tp);
161319cf921bSrearnsha 		}
161419cf921bSrearnsha 	}
161519cf921bSrearnsha 
161619cf921bSrearnsha 	return 0;
161719cf921bSrearnsha }
161819cf921bSrearnsha 
161919cf921bSrearnsha void
plcom_iflush(struct plcom_softc * sc)162019cf921bSrearnsha plcom_iflush(struct plcom_softc *sc)
162119cf921bSrearnsha {
16224c5a9380Sskrll 	struct plcom_instance *pi = &sc->sc_pi;
162319cf921bSrearnsha #ifdef DIAGNOSTIC
162419cf921bSrearnsha 	int reg;
162519cf921bSrearnsha #endif
162619cf921bSrearnsha 	int timo;
162719cf921bSrearnsha 
162819cf921bSrearnsha #ifdef DIAGNOSTIC
162919cf921bSrearnsha 	reg = 0xffff;
163019cf921bSrearnsha #endif
163119cf921bSrearnsha 	timo = 50000;
163219cf921bSrearnsha 	/* flush any pending I/O */
16334c5a9380Sskrll 	while (! ISSET(PREAD1(pi, PL01XCOM_FR), PL01X_FR_RXFE)
163419cf921bSrearnsha 	    && --timo)
163519cf921bSrearnsha #ifdef DIAGNOSTIC
163619cf921bSrearnsha 		reg =
163719cf921bSrearnsha #else
163819cf921bSrearnsha 		    (void)
163919cf921bSrearnsha #endif
16404c5a9380Sskrll 		    PREAD1(pi, PL01XCOM_DR);
164119cf921bSrearnsha #ifdef DIAGNOSTIC
164219cf921bSrearnsha 	if (!timo)
16434c5a9380Sskrll 		aprint_error_dev(sc->sc_dev, ": %s timeout %02x\n", __func__,
164419cf921bSrearnsha 		    reg);
164519cf921bSrearnsha #endif
164619cf921bSrearnsha }
164719cf921bSrearnsha 
164819cf921bSrearnsha void
plcom_loadchannelregs(struct plcom_softc * sc)164919cf921bSrearnsha plcom_loadchannelregs(struct plcom_softc *sc)
165019cf921bSrearnsha {
16514c5a9380Sskrll 	struct plcom_instance *pi = &sc->sc_pi;
1652820c39ceSmlelstv 	uint16_t ifls;
165319cf921bSrearnsha 
165419cf921bSrearnsha 	/* XXXXX necessary? */
165519cf921bSrearnsha 	plcom_iflush(sc);
165619cf921bSrearnsha 
16574c5a9380Sskrll 	switch (pi->pi_type) {
16584c5a9380Sskrll 	case PLCOM_TYPE_PL010:
16594c5a9380Sskrll 		PWRITE1(pi, PL010COM_CR, 0);
166078423b7cSjmcneill 		if (sc->sc_frequency != 0) {
16614c5a9380Sskrll 			PWRITE1(pi, PL010COM_DLBL, sc->sc_ratel);
16624c5a9380Sskrll 			PWRITE1(pi, PL010COM_DLBH, sc->sc_rateh);
166378423b7cSjmcneill 		}
16644c5a9380Sskrll 		PWRITE1(pi, PL010COM_LCR, sc->sc_lcr);
166519cf921bSrearnsha 
166639cd836eSthorpej 		/* XXX device_unit() abuse */
16674c5a9380Sskrll 		if (sc->sc_set_mcr)
16684c5a9380Sskrll 			sc->sc_set_mcr(sc->sc_set_mcr_arg,
16694c5a9380Sskrll 			    device_unit(sc->sc_dev),
167019cf921bSrearnsha 			    sc->sc_mcr_active = sc->sc_mcr);
167119cf921bSrearnsha 
16724c5a9380Sskrll 		PWRITE1(pi, PL010COM_CR, sc->sc_cr);
16734c5a9380Sskrll 		break;
16744c5a9380Sskrll 
16754c5a9380Sskrll 	case PLCOM_TYPE_PL011:
1676820c39ceSmlelstv 	case PLCOM_TYPE_GENERIC_UART:
1677820c39ceSmlelstv 		PWRITE2(pi, PL011COM_CR, 0);
167878423b7cSjmcneill 		if (sc->sc_frequency != 0) {
16794c5a9380Sskrll 			PWRITE1(pi, PL011COM_FBRD, sc->sc_ratel);
16804c5a9380Sskrll 			PWRITE4(pi, PL011COM_IBRD, sc->sc_rateh);
168178423b7cSjmcneill 		}
1682820c39ceSmlelstv 
1683820c39ceSmlelstv 		/* Bits 6..15 are reserved, don't modify, read as zero */
1684820c39ceSmlelstv 		ifls = PREAD2(pi, PL011COM_IFLS) & ~PL011_IFLS_MASK;
1685820c39ceSmlelstv 		ifls |= sc->sc_ifls & PL011_IFLS_MASK;
1686820c39ceSmlelstv 		PWRITE2(pi, PL011COM_IFLS, ifls);
1687820c39ceSmlelstv 
16884c5a9380Sskrll 		PWRITE1(pi, PL011COM_LCRH, sc->sc_lcr);
16894c5a9380Sskrll 		sc->sc_mcr_active = sc->sc_mcr;
16904c5a9380Sskrll 		CLR(sc->sc_cr, PL011_MCR(PL01X_MCR_RTS | PL01X_MCR_DTR));
16914c5a9380Sskrll 		SET(sc->sc_cr, PL011_MCR(sc->sc_mcr_active));
1692820c39ceSmlelstv 		PWRITE2(pi, PL011COM_CR, sc->sc_cr);
16934c5a9380Sskrll 		break;
16944c5a9380Sskrll 	}
169519cf921bSrearnsha }
169619cf921bSrearnsha 
169719cf921bSrearnsha int
plcomhwiflow(struct tty * tp,int block)169819cf921bSrearnsha plcomhwiflow(struct tty *tp, int block)
169919cf921bSrearnsha {
170063495428Scegger 	struct plcom_softc *sc =
170163495428Scegger 		device_lookup_private(&plcom_cd, PLCOMUNIT(tp->t_dev));
170219cf921bSrearnsha 
170319cf921bSrearnsha 	if (PLCOM_ISALIVE(sc) == 0)
170419cf921bSrearnsha 		return 0;
170519cf921bSrearnsha 
170619cf921bSrearnsha 	if (sc->sc_mcr_rts == 0)
170719cf921bSrearnsha 		return 0;
170819cf921bSrearnsha 
1709fc0cd919Sskrll 	mutex_spin_enter(&sc->sc_lock);
171019cf921bSrearnsha 
171119cf921bSrearnsha 	if (block) {
171219cf921bSrearnsha 		if (!ISSET(sc->sc_rx_flags, RX_TTY_BLOCKED)) {
171319cf921bSrearnsha 			SET(sc->sc_rx_flags, RX_TTY_BLOCKED);
171419cf921bSrearnsha 			plcom_hwiflow(sc);
171519cf921bSrearnsha 		}
171619cf921bSrearnsha 	} else {
171719cf921bSrearnsha 		if (ISSET(sc->sc_rx_flags, RX_TTY_OVERFLOWED)) {
171819cf921bSrearnsha 			CLR(sc->sc_rx_flags, RX_TTY_OVERFLOWED);
171919cf921bSrearnsha 			plcom_schedrx(sc);
172019cf921bSrearnsha 		}
172119cf921bSrearnsha 		if (ISSET(sc->sc_rx_flags, RX_TTY_BLOCKED)) {
172219cf921bSrearnsha 			CLR(sc->sc_rx_flags, RX_TTY_BLOCKED);
172319cf921bSrearnsha 			plcom_hwiflow(sc);
172419cf921bSrearnsha 		}
172519cf921bSrearnsha 	}
172619cf921bSrearnsha 
1727fc0cd919Sskrll 	mutex_spin_exit(&sc->sc_lock);
172819cf921bSrearnsha 	return 1;
172919cf921bSrearnsha }
173019cf921bSrearnsha 
173119cf921bSrearnsha /*
173219cf921bSrearnsha  * (un)block input via hw flowcontrol
173319cf921bSrearnsha  */
173419cf921bSrearnsha void
plcom_hwiflow(struct plcom_softc * sc)173519cf921bSrearnsha plcom_hwiflow(struct plcom_softc *sc)
173619cf921bSrearnsha {
17374c5a9380Sskrll 	struct plcom_instance *pi = &sc->sc_pi;
17384c5a9380Sskrll 
173919cf921bSrearnsha 	if (sc->sc_mcr_rts == 0)
174019cf921bSrearnsha 		return;
174119cf921bSrearnsha 
174219cf921bSrearnsha 	if (ISSET(sc->sc_rx_flags, RX_ANY_BLOCK)) {
174319cf921bSrearnsha 		CLR(sc->sc_mcr, sc->sc_mcr_rts);
174419cf921bSrearnsha 		CLR(sc->sc_mcr_active, sc->sc_mcr_rts);
174519cf921bSrearnsha 	} else {
174619cf921bSrearnsha 		SET(sc->sc_mcr, sc->sc_mcr_rts);
174719cf921bSrearnsha 		SET(sc->sc_mcr_active, sc->sc_mcr_rts);
174819cf921bSrearnsha 	}
17494c5a9380Sskrll 	switch (pi->pi_type) {
17504c5a9380Sskrll 	case PLCOM_TYPE_PL010:
17514c5a9380Sskrll 		if (sc->sc_set_mcr)
175239cd836eSthorpej 			/* XXX device_unit() abuse */
17534c5a9380Sskrll 			sc->sc_set_mcr(sc->sc_set_mcr_arg,
17544c5a9380Sskrll 			     device_unit(sc->sc_dev), sc->sc_mcr_active);
17554c5a9380Sskrll 		break;
17564c5a9380Sskrll 	case PLCOM_TYPE_PL011:
1757820c39ceSmlelstv 	case PLCOM_TYPE_GENERIC_UART:
17584c5a9380Sskrll 		CLR(sc->sc_cr, PL011_MCR(PL01X_MCR_RTS | PL01X_MCR_DTR));
17594c5a9380Sskrll 		SET(sc->sc_cr, PL011_MCR(sc->sc_mcr_active));
1760820c39ceSmlelstv 		PWRITE2(pi, PL011COM_CR, sc->sc_cr);
17614c5a9380Sskrll 		break;
17624c5a9380Sskrll 	}
176319cf921bSrearnsha }
176419cf921bSrearnsha 
176519cf921bSrearnsha 
176619cf921bSrearnsha void
plcomstart(struct tty * tp)176719cf921bSrearnsha plcomstart(struct tty *tp)
176819cf921bSrearnsha {
176963495428Scegger 	struct plcom_softc *sc =
177063495428Scegger 		device_lookup_private(&plcom_cd, PLCOMUNIT(tp->t_dev));
17714c5a9380Sskrll 	struct plcom_instance *pi = &sc->sc_pi;
177219cf921bSrearnsha 
177319cf921bSrearnsha 	if (PLCOM_ISALIVE(sc) == 0)
177419cf921bSrearnsha 		return;
177519cf921bSrearnsha 
177619cf921bSrearnsha 	if (ISSET(tp->t_state, TS_BUSY | TS_TIMEOUT | TS_TTSTOP))
1777210924e6Sriastradh 		return;
177819cf921bSrearnsha 	if (sc->sc_tx_stopped)
1779210924e6Sriastradh 		return;
178019cf921bSrearnsha 
1781dc26833bSad 	if (!ttypull(tp))
1782210924e6Sriastradh 		return;
178319cf921bSrearnsha 
178419cf921bSrearnsha 	/* Grab the first contiguous region of buffer space. */
178519cf921bSrearnsha 	{
178619cf921bSrearnsha 		u_char *tba;
178719cf921bSrearnsha 		int tbc;
178819cf921bSrearnsha 
178919cf921bSrearnsha 		tba = tp->t_outq.c_cf;
179019cf921bSrearnsha 		tbc = ndqb(&tp->t_outq, 0);
179119cf921bSrearnsha 
1792fc0cd919Sskrll 		mutex_spin_enter(&sc->sc_lock);
179319cf921bSrearnsha 
179419cf921bSrearnsha 		sc->sc_tba = tba;
179519cf921bSrearnsha 		sc->sc_tbc = tbc;
179619cf921bSrearnsha 	}
179719cf921bSrearnsha 
179819cf921bSrearnsha 	SET(tp->t_state, TS_BUSY);
179919cf921bSrearnsha 	sc->sc_tx_busy = 1;
180019cf921bSrearnsha 
180119cf921bSrearnsha 	/* Enable transmit completion interrupts if necessary. */
18024c5a9380Sskrll 	switch (pi->pi_type) {
18034c5a9380Sskrll 	case PLCOM_TYPE_PL010:
1804b9703c3dSskrll 		if (!ISSET(sc->sc_cr, PL010_CR_TIE)) {
1805b9703c3dSskrll 			SET(sc->sc_cr, PL010_CR_TIE);
18064c5a9380Sskrll 			PWRITE1(pi, PL010COM_CR, sc->sc_cr);
18074c5a9380Sskrll 		}
18084c5a9380Sskrll 		break;
18094c5a9380Sskrll 	case PLCOM_TYPE_PL011:
1810820c39ceSmlelstv 	case PLCOM_TYPE_GENERIC_UART:
18114c5a9380Sskrll 		if (!ISSET(sc->sc_imsc, PL011_INT_TX)) {
18124c5a9380Sskrll 			SET(sc->sc_imsc, PL011_INT_TX);
18134c5a9380Sskrll 			PWRITE4(pi, PL011COM_IMSC, sc->sc_imsc);
18144c5a9380Sskrll 		}
18154c5a9380Sskrll 		break;
181619cf921bSrearnsha 	}
181719cf921bSrearnsha 
181819cf921bSrearnsha 	/* Output the first chunk of the contiguous buffer. */
181919cf921bSrearnsha 	{
18202c14b448Sskrll 		int n;
182119cf921bSrearnsha 
182219cf921bSrearnsha 		n = sc->sc_tbc;
1823820c39ceSmlelstv 		if (n > sc->sc_burstlen)
1824820c39ceSmlelstv 			n = sc->sc_burstlen;
18254c5a9380Sskrll 		PWRITEM1(pi, PL01XCOM_DR, sc->sc_tba, n);
182619cf921bSrearnsha 		sc->sc_tbc -= n;
182719cf921bSrearnsha 		sc->sc_tba += n;
182819cf921bSrearnsha 	}
1829fc0cd919Sskrll 	mutex_spin_exit(&sc->sc_lock);
183019cf921bSrearnsha }
183119cf921bSrearnsha 
183219cf921bSrearnsha /*
183319cf921bSrearnsha  * Stop output on a line.
183419cf921bSrearnsha  */
183519cf921bSrearnsha void
plcomstop(struct tty * tp,int flag)183619cf921bSrearnsha plcomstop(struct tty *tp, int flag)
183719cf921bSrearnsha {
183863495428Scegger 	struct plcom_softc *sc =
183963495428Scegger 		device_lookup_private(&plcom_cd, PLCOMUNIT(tp->t_dev));
184019cf921bSrearnsha 
1841fc0cd919Sskrll 	mutex_spin_enter(&sc->sc_lock);
184219cf921bSrearnsha 	if (ISSET(tp->t_state, TS_BUSY)) {
184319cf921bSrearnsha 		/* Stop transmitting at the next chunk. */
184419cf921bSrearnsha 		sc->sc_tbc = 0;
184519cf921bSrearnsha 		sc->sc_heldtbc = 0;
184619cf921bSrearnsha 		if (!ISSET(tp->t_state, TS_TTSTOP))
184719cf921bSrearnsha 			SET(tp->t_state, TS_FLUSH);
184819cf921bSrearnsha 	}
1849fc0cd919Sskrll 	mutex_spin_exit(&sc->sc_lock);
185019cf921bSrearnsha }
185119cf921bSrearnsha 
185219cf921bSrearnsha void
plcomdiag(void * arg)185319cf921bSrearnsha plcomdiag(void *arg)
185419cf921bSrearnsha {
185519cf921bSrearnsha 	struct plcom_softc *sc = arg;
185619cf921bSrearnsha 	int overflows, floods;
185719cf921bSrearnsha 
1858fc0cd919Sskrll 	mutex_spin_enter(&sc->sc_lock);
185919cf921bSrearnsha 	overflows = sc->sc_overflows;
186019cf921bSrearnsha 	sc->sc_overflows = 0;
186119cf921bSrearnsha 	floods = sc->sc_floods;
186219cf921bSrearnsha 	sc->sc_floods = 0;
186319cf921bSrearnsha 	sc->sc_errors = 0;
1864fc0cd919Sskrll 	mutex_spin_exit(&sc->sc_lock);
186519cf921bSrearnsha 
186619cf921bSrearnsha 	log(LOG_WARNING, "%s: %d silo overflow%s, %d ibuf flood%s\n",
1867b8905ef0Sskrll 	    device_xname(sc->sc_dev),
186819cf921bSrearnsha 	    overflows, overflows == 1 ? "" : "s",
186919cf921bSrearnsha 	    floods, floods == 1 ? "" : "s");
187019cf921bSrearnsha }
187119cf921bSrearnsha 
1872210924e6Sriastradh static inline void
plcom_rxsoft(struct plcom_softc * sc,struct tty * tp)187319cf921bSrearnsha plcom_rxsoft(struct plcom_softc *sc, struct tty *tp)
187419cf921bSrearnsha {
187519cf921bSrearnsha 	int (*rint) (int, struct tty *) = tp->t_linesw->l_rint;
18764c5a9380Sskrll 	struct plcom_instance *pi = &sc->sc_pi;
187719cf921bSrearnsha 	u_char *get, *end;
187819cf921bSrearnsha 	u_int cc, scc;
187919cf921bSrearnsha 	u_char rsr;
188019cf921bSrearnsha 	int code;
188119cf921bSrearnsha 
188219cf921bSrearnsha 	end = sc->sc_ebuf;
188319cf921bSrearnsha 	get = sc->sc_rbget;
188419cf921bSrearnsha 	scc = cc = plcom_rbuf_size - sc->sc_rbavail;
188519cf921bSrearnsha 
188619cf921bSrearnsha 	if (cc == plcom_rbuf_size) {
188719cf921bSrearnsha 		sc->sc_floods++;
188819cf921bSrearnsha 		if (sc->sc_errors++ == 0)
188919cf921bSrearnsha 			callout_reset(&sc->sc_diag_callout, 60 * hz,
189019cf921bSrearnsha 			    plcomdiag, sc);
189119cf921bSrearnsha 	}
189219cf921bSrearnsha 
189319cf921bSrearnsha 	while (cc) {
189419cf921bSrearnsha 		code = get[0];
189519cf921bSrearnsha 		rsr = get[1];
1896b9703c3dSskrll 		if (ISSET(rsr, PL01X_RSR_ERROR)) {
1897b9703c3dSskrll 			if (ISSET(rsr, PL01X_RSR_OE)) {
189819cf921bSrearnsha 				sc->sc_overflows++;
189919cf921bSrearnsha 				if (sc->sc_errors++ == 0)
190019cf921bSrearnsha 					callout_reset(&sc->sc_diag_callout,
190119cf921bSrearnsha 					    60 * hz, plcomdiag, sc);
190219cf921bSrearnsha 			}
1903b9703c3dSskrll 			if (ISSET(rsr, PL01X_RSR_BE | PL01X_RSR_FE))
190419cf921bSrearnsha 				SET(code, TTY_FE);
1905b9703c3dSskrll 			if (ISSET(rsr, PL01X_RSR_PE))
190619cf921bSrearnsha 				SET(code, TTY_PE);
190719cf921bSrearnsha 		}
190819cf921bSrearnsha 		if ((*rint)(code, tp) == -1) {
190919cf921bSrearnsha 			/*
191019cf921bSrearnsha 			 * The line discipline's buffer is out of space.
191119cf921bSrearnsha 			 */
191219cf921bSrearnsha 			if (!ISSET(sc->sc_rx_flags, RX_TTY_BLOCKED)) {
191319cf921bSrearnsha 				/*
191419cf921bSrearnsha 				 * We're either not using flow control, or the
191519cf921bSrearnsha 				 * line discipline didn't tell us to block for
191619cf921bSrearnsha 				 * some reason.  Either way, we have no way to
191719cf921bSrearnsha 				 * know when there's more space available, so
191819cf921bSrearnsha 				 * just drop the rest of the data.
191919cf921bSrearnsha 				 */
192019cf921bSrearnsha 				get += cc << 1;
192119cf921bSrearnsha 				if (get >= end)
192219cf921bSrearnsha 					get -= plcom_rbuf_size << 1;
192319cf921bSrearnsha 				cc = 0;
192419cf921bSrearnsha 			} else {
192519cf921bSrearnsha 				/*
192619cf921bSrearnsha 				 * Don't schedule any more receive processing
192719cf921bSrearnsha 				 * until the line discipline tells us there's
192819cf921bSrearnsha 				 * space available (through plcomhwiflow()).
192919cf921bSrearnsha 				 * Leave the rest of the data in the input
193019cf921bSrearnsha 				 * buffer.
193119cf921bSrearnsha 				 */
193219cf921bSrearnsha 				SET(sc->sc_rx_flags, RX_TTY_OVERFLOWED);
193319cf921bSrearnsha 			}
193419cf921bSrearnsha 			break;
193519cf921bSrearnsha 		}
193619cf921bSrearnsha 		get += 2;
193719cf921bSrearnsha 		if (get >= end)
193819cf921bSrearnsha 			get = sc->sc_rbuf;
193919cf921bSrearnsha 		cc--;
194019cf921bSrearnsha 	}
194119cf921bSrearnsha 	if (cc != scc) {
194219cf921bSrearnsha 		sc->sc_rbget = get;
1943fc0cd919Sskrll 		mutex_spin_enter(&sc->sc_lock);
194419cf921bSrearnsha 
194519cf921bSrearnsha 		cc = sc->sc_rbavail += scc - cc;
194619cf921bSrearnsha 		/* Buffers should be ok again, release possible block. */
194719cf921bSrearnsha 		if (cc >= sc->sc_r_lowat) {
194819cf921bSrearnsha 			if (ISSET(sc->sc_rx_flags, RX_IBUF_OVERFLOWED)) {
194919cf921bSrearnsha 				CLR(sc->sc_rx_flags, RX_IBUF_OVERFLOWED);
19504c5a9380Sskrll 				switch (pi->pi_type) {
19514c5a9380Sskrll 				case PLCOM_TYPE_PL010:
19524c5a9380Sskrll 					SET(sc->sc_cr,
19534c5a9380Sskrll 					    PL010_CR_RIE | PL010_CR_RTIE);
19544c5a9380Sskrll 					PWRITE1(pi, PL010COM_CR, sc->sc_cr);
19554c5a9380Sskrll 					break;
19564c5a9380Sskrll 				case PLCOM_TYPE_PL011:
1957820c39ceSmlelstv 				case PLCOM_TYPE_GENERIC_UART:
19584c5a9380Sskrll 					SET(sc->sc_imsc,
19594c5a9380Sskrll 					    PL011_INT_RX | PL011_INT_RT);
19604c5a9380Sskrll 					PWRITE4(pi, PL011COM_IMSC, sc->sc_imsc);
19614c5a9380Sskrll 					break;
19624c5a9380Sskrll 				}
196319cf921bSrearnsha 			}
196419cf921bSrearnsha 			if (ISSET(sc->sc_rx_flags, RX_IBUF_BLOCKED)) {
196519cf921bSrearnsha 				CLR(sc->sc_rx_flags, RX_IBUF_BLOCKED);
196619cf921bSrearnsha 				plcom_hwiflow(sc);
196719cf921bSrearnsha 			}
196819cf921bSrearnsha 		}
1969fc0cd919Sskrll 		mutex_spin_exit(&sc->sc_lock);
197019cf921bSrearnsha 	}
197119cf921bSrearnsha }
197219cf921bSrearnsha 
1973210924e6Sriastradh static inline void
plcom_txsoft(struct plcom_softc * sc,struct tty * tp)197419cf921bSrearnsha plcom_txsoft(struct plcom_softc *sc, struct tty *tp)
197519cf921bSrearnsha {
197619cf921bSrearnsha 
197719cf921bSrearnsha 	CLR(tp->t_state, TS_BUSY);
197819cf921bSrearnsha 	if (ISSET(tp->t_state, TS_FLUSH))
197919cf921bSrearnsha 		CLR(tp->t_state, TS_FLUSH);
198019cf921bSrearnsha 	else
198119cf921bSrearnsha 		ndflush(&tp->t_outq, (int)(sc->sc_tba - tp->t_outq.c_cf));
198219cf921bSrearnsha 	(*tp->t_linesw->l_start)(tp);
198319cf921bSrearnsha }
198419cf921bSrearnsha 
1985210924e6Sriastradh static inline void
plcom_stsoft(struct plcom_softc * sc,struct tty * tp)198619cf921bSrearnsha plcom_stsoft(struct plcom_softc *sc, struct tty *tp)
198719cf921bSrearnsha {
198819cf921bSrearnsha 	u_char msr, delta;
198919cf921bSrearnsha 
1990fc0cd919Sskrll 	mutex_spin_enter(&sc->sc_lock);
199119cf921bSrearnsha 	msr = sc->sc_msr;
199219cf921bSrearnsha 	delta = sc->sc_msr_delta;
199319cf921bSrearnsha 	sc->sc_msr_delta = 0;
1994fc0cd919Sskrll 	mutex_spin_exit(&sc->sc_lock);
199519cf921bSrearnsha 
199619cf921bSrearnsha 	if (ISSET(delta, sc->sc_msr_dcd)) {
199719cf921bSrearnsha 		/*
199819cf921bSrearnsha 		 * Inform the tty layer that carrier detect changed.
199919cf921bSrearnsha 		 */
2000b9703c3dSskrll 		(void) (*tp->t_linesw->l_modem)(tp, ISSET(msr, PL01X_MSR_DCD));
200119cf921bSrearnsha 	}
200219cf921bSrearnsha 
200319cf921bSrearnsha 	if (ISSET(delta, sc->sc_msr_cts)) {
200419cf921bSrearnsha 		/* Block or unblock output according to flow control. */
200519cf921bSrearnsha 		if (ISSET(msr, sc->sc_msr_cts)) {
200619cf921bSrearnsha 			sc->sc_tx_stopped = 0;
200719cf921bSrearnsha 			(*tp->t_linesw->l_start)(tp);
200819cf921bSrearnsha 		} else {
200919cf921bSrearnsha 			sc->sc_tx_stopped = 1;
201019cf921bSrearnsha 		}
201119cf921bSrearnsha 	}
201219cf921bSrearnsha 
201319cf921bSrearnsha #ifdef PLCOM_DEBUG
201419cf921bSrearnsha 	if (plcom_debug)
201519cf921bSrearnsha 		plcomstatus(sc, "plcom_stsoft");
201619cf921bSrearnsha #endif
201719cf921bSrearnsha }
201819cf921bSrearnsha 
201919cf921bSrearnsha void
plcomsoft(void * arg)202019cf921bSrearnsha plcomsoft(void *arg)
202119cf921bSrearnsha {
202219cf921bSrearnsha 	struct plcom_softc *sc = arg;
202319cf921bSrearnsha 	struct tty *tp;
202419cf921bSrearnsha 
202519cf921bSrearnsha 	if (PLCOM_ISALIVE(sc) == 0)
202619cf921bSrearnsha 		return;
202719cf921bSrearnsha 
202819cf921bSrearnsha 	tp = sc->sc_tty;
202919cf921bSrearnsha 
203019cf921bSrearnsha 	if (sc->sc_rx_ready) {
203119cf921bSrearnsha 		sc->sc_rx_ready = 0;
203219cf921bSrearnsha 		plcom_rxsoft(sc, tp);
203319cf921bSrearnsha 	}
203419cf921bSrearnsha 
203519cf921bSrearnsha 	if (sc->sc_st_check) {
203619cf921bSrearnsha 		sc->sc_st_check = 0;
203719cf921bSrearnsha 		plcom_stsoft(sc, tp);
203819cf921bSrearnsha 	}
203919cf921bSrearnsha 
204019cf921bSrearnsha 	if (sc->sc_tx_done) {
204119cf921bSrearnsha 		sc->sc_tx_done = 0;
204219cf921bSrearnsha 		plcom_txsoft(sc, tp);
204319cf921bSrearnsha 	}
204419cf921bSrearnsha }
204519cf921bSrearnsha 
20464c5a9380Sskrll bool
plcom_intstatus(struct plcom_instance * pi,u_int * istatus)20474c5a9380Sskrll plcom_intstatus(struct plcom_instance *pi, u_int *istatus)
20484c5a9380Sskrll {
20494c5a9380Sskrll 	bool ret = false;
20504c5a9380Sskrll 	u_int stat = 0;
20514c5a9380Sskrll 
20524c5a9380Sskrll 	switch (pi->pi_type) {
20534c5a9380Sskrll 	case PLCOM_TYPE_PL010:
20544c5a9380Sskrll 		stat = PREAD1(pi, PL010COM_IIR);
20554c5a9380Sskrll 		ret = ISSET(stat, PL010_IIR_IMASK);
20564c5a9380Sskrll 		break;
20574c5a9380Sskrll 	case PLCOM_TYPE_PL011:
2058820c39ceSmlelstv 	case PLCOM_TYPE_GENERIC_UART:
20594c5a9380Sskrll 		stat = PREAD4(pi, PL011COM_MIS);
20604c5a9380Sskrll 		ret = ISSET(stat, PL011_INT_ALLMASK);
20614c5a9380Sskrll 		break;
20624c5a9380Sskrll 	}
20634c5a9380Sskrll 	*istatus = stat;
20644c5a9380Sskrll 
20654c5a9380Sskrll 	return ret;
20664c5a9380Sskrll }
20674c5a9380Sskrll 
206819cf921bSrearnsha int
plcomintr(void * arg)206919cf921bSrearnsha plcomintr(void *arg)
207019cf921bSrearnsha {
207119cf921bSrearnsha 	struct plcom_softc *sc = arg;
20724c5a9380Sskrll 	struct plcom_instance *pi = &sc->sc_pi;
207319cf921bSrearnsha 	u_char *put, *end;
207419cf921bSrearnsha 	u_int cc;
20754c5a9380Sskrll 	u_int istatus = 0;
20764c5a9380Sskrll 	u_char rsr;
20774c5a9380Sskrll 	bool intr = false;
20784c5a9380Sskrll 
20794c5a9380Sskrll 	PLCOM_BARRIER(pi, BR | BW);
208019cf921bSrearnsha 
208119cf921bSrearnsha 	if (PLCOM_ISALIVE(sc) == 0)
208219cf921bSrearnsha 		return 0;
208319cf921bSrearnsha 
2084fc0cd919Sskrll 	mutex_spin_enter(&sc->sc_lock);
20854c5a9380Sskrll 	intr = plcom_intstatus(pi, &istatus);
20864c5a9380Sskrll 	if (!intr) {
2087fc0cd919Sskrll 		mutex_spin_exit(&sc->sc_lock);
208819cf921bSrearnsha 		return 0;
208919cf921bSrearnsha 	}
209019cf921bSrearnsha 
209119cf921bSrearnsha 	end = sc->sc_ebuf;
209219cf921bSrearnsha 	put = sc->sc_rbput;
209319cf921bSrearnsha 	cc = sc->sc_rbavail;
209419cf921bSrearnsha 
209519cf921bSrearnsha 	do {
20964c5a9380Sskrll 		u_int msr = 0, delta, fr;
20974c5a9380Sskrll 		bool rxintr = false, txintr = false, msintr;
209819cf921bSrearnsha 
20994c5a9380Sskrll 		/* don't need RI here*/
21004c5a9380Sskrll 		fr = PREAD1(pi, PL01XCOM_FR);
210119cf921bSrearnsha 
2102b9703c3dSskrll 		if (!ISSET(fr, PL01X_FR_RXFE) &&
210319cf921bSrearnsha 		    !ISSET(sc->sc_rx_flags, RX_IBUF_OVERFLOWED)) {
210419cf921bSrearnsha 			while (cc > 0) {
210519cf921bSrearnsha 				int cn_trapped = 0;
21064c5a9380Sskrll 				put[0] = PREAD1(pi, PL01XCOM_DR);
21074c5a9380Sskrll 				rsr = PREAD1(pi, PL01XCOM_RSR);
210819cf921bSrearnsha 				/* Clear any error status.  */
2109b9703c3dSskrll 				if (ISSET(rsr, PL01X_RSR_ERROR))
21104c5a9380Sskrll 					PWRITE1(pi, PL01XCOM_ECR, 0);
2111820c39ceSmlelstv 
2112b9703c3dSskrll 				if (ISSET(rsr, PL01X_RSR_BE)) {
2113b1f70d4dSrearnsha 					cn_trapped = 0;
211419cf921bSrearnsha 					cn_check_magic(sc->sc_tty->t_dev,
211519cf921bSrearnsha 					    CNC_BREAK, plcom_cnm_state);
211619cf921bSrearnsha 					if (cn_trapped)
211719cf921bSrearnsha 						continue;
211819cf921bSrearnsha #if defined(KGDB)
211919cf921bSrearnsha 					if (ISSET(sc->sc_hwflags,
212019cf921bSrearnsha 					    PLCOM_HW_KGDB)) {
212119cf921bSrearnsha 						kgdb_connect(1);
212219cf921bSrearnsha 						continue;
212319cf921bSrearnsha 					}
212419cf921bSrearnsha #endif
212519cf921bSrearnsha 				}
212619cf921bSrearnsha 
212719cf921bSrearnsha 				put[1] = rsr;
2128b1f70d4dSrearnsha 				cn_trapped = 0;
21294c5a9380Sskrll 				cn_check_magic(sc->sc_tty->t_dev, put[0],
21304c5a9380Sskrll 				    plcom_cnm_state);
213119cf921bSrearnsha 				if (cn_trapped) {
21324c5a9380Sskrll 					fr = PREAD1(pi, PL01XCOM_FR);
2133b9703c3dSskrll 					if (ISSET(fr, PL01X_FR_RXFE))
213419cf921bSrearnsha 						break;
213519cf921bSrearnsha 
213619cf921bSrearnsha 					continue;
213719cf921bSrearnsha 				}
213819cf921bSrearnsha 				put += 2;
213919cf921bSrearnsha 				if (put >= end)
214019cf921bSrearnsha 					put = sc->sc_rbuf;
214119cf921bSrearnsha 				cc--;
214219cf921bSrearnsha 
21434c5a9380Sskrll 				/* don't need RI here*/
21444c5a9380Sskrll 				fr = PREAD1(pi, PL01XCOM_FR);
2145b9703c3dSskrll 				if (ISSET(fr, PL01X_FR_RXFE))
214619cf921bSrearnsha 					break;
214719cf921bSrearnsha 			}
214819cf921bSrearnsha 
214919cf921bSrearnsha 			/*
215019cf921bSrearnsha 			 * Current string of incoming characters ended because
215119cf921bSrearnsha 			 * no more data was available or we ran out of space.
215219cf921bSrearnsha 			 * Schedule a receive event if any data was received.
215319cf921bSrearnsha 			 * If we're out of space, turn off receive interrupts.
215419cf921bSrearnsha 			 */
215519cf921bSrearnsha 			sc->sc_rbput = put;
215619cf921bSrearnsha 			sc->sc_rbavail = cc;
215719cf921bSrearnsha 			if (!ISSET(sc->sc_rx_flags, RX_TTY_OVERFLOWED))
215819cf921bSrearnsha 				sc->sc_rx_ready = 1;
215919cf921bSrearnsha 
216019cf921bSrearnsha 			/*
216119cf921bSrearnsha 			 * See if we are in danger of overflowing a buffer. If
216219cf921bSrearnsha 			 * so, use hardware flow control to ease the pressure.
216319cf921bSrearnsha 			 */
216419cf921bSrearnsha 			if (!ISSET(sc->sc_rx_flags, RX_IBUF_BLOCKED) &&
216519cf921bSrearnsha 			    cc < sc->sc_r_hiwat) {
216619cf921bSrearnsha 				SET(sc->sc_rx_flags, RX_IBUF_BLOCKED);
216719cf921bSrearnsha 				plcom_hwiflow(sc);
216819cf921bSrearnsha 			}
216919cf921bSrearnsha 
217019cf921bSrearnsha 			/*
217119cf921bSrearnsha 			 * If we're out of space, disable receive interrupts
217219cf921bSrearnsha 			 * until the queue has drained a bit.
217319cf921bSrearnsha 			 */
217419cf921bSrearnsha 			if (!cc) {
217519cf921bSrearnsha 				SET(sc->sc_rx_flags, RX_IBUF_OVERFLOWED);
21764c5a9380Sskrll 				switch (pi->pi_type) {
21774c5a9380Sskrll 				case PLCOM_TYPE_PL010:
21784c5a9380Sskrll 					CLR(sc->sc_cr,
21794c5a9380Sskrll 					    PL010_CR_RIE | PL010_CR_RTIE);
21804c5a9380Sskrll 					PWRITE1(pi, PL010COM_CR, sc->sc_cr);
21814c5a9380Sskrll 					break;
21824c5a9380Sskrll 				case PLCOM_TYPE_PL011:
2183820c39ceSmlelstv 				case PLCOM_TYPE_GENERIC_UART:
21844c5a9380Sskrll 					CLR(sc->sc_imsc,
21854c5a9380Sskrll 					    PL011_INT_RT | PL011_INT_RX);
21864c5a9380Sskrll 					PWRITE4(pi, PL011COM_IMSC, sc->sc_imsc);
21874c5a9380Sskrll 					break;
21884c5a9380Sskrll 				}
218919cf921bSrearnsha 			}
219019cf921bSrearnsha 		} else {
21914c5a9380Sskrll 			switch (pi->pi_type) {
21924c5a9380Sskrll 			case PLCOM_TYPE_PL010:
21934c5a9380Sskrll 				rxintr = ISSET(istatus, PL010_IIR_RIS);
21944c5a9380Sskrll 				if (rxintr) {
21954c5a9380Sskrll 					PWRITE1(pi, PL010COM_CR, 0);
219619cf921bSrearnsha 					delay(10);
21974c5a9380Sskrll 					PWRITE1(pi, PL010COM_CR, sc->sc_cr);
219819cf921bSrearnsha 					continue;
219919cf921bSrearnsha 				}
22004c5a9380Sskrll 				break;
22014c5a9380Sskrll 			case PLCOM_TYPE_PL011:
2202820c39ceSmlelstv 			case PLCOM_TYPE_GENERIC_UART:
22034c5a9380Sskrll 				rxintr = ISSET(istatus, PL011_INT_RX);
22044c5a9380Sskrll 				if (rxintr) {
2205820c39ceSmlelstv 					PWRITE2(pi, PL011COM_CR, 0);
22064c5a9380Sskrll 					delay(10);
2207820c39ceSmlelstv 					PWRITE2(pi, PL011COM_CR, sc->sc_cr);
22084c5a9380Sskrll 					continue;
22094c5a9380Sskrll 				}
22104c5a9380Sskrll 				break;
22114c5a9380Sskrll 			}
221219cf921bSrearnsha 		}
221319cf921bSrearnsha 
22144c5a9380Sskrll 		switch (pi->pi_type) {
22154c5a9380Sskrll 		case PLCOM_TYPE_PL010:
22164c5a9380Sskrll 			msr = PREAD1(pi, PL01XCOM_FR);
22174c5a9380Sskrll 			break;
22184c5a9380Sskrll 		case PLCOM_TYPE_PL011:
2219820c39ceSmlelstv 		case PLCOM_TYPE_GENERIC_UART:
22204c5a9380Sskrll 			msr = PREAD4(pi, PL01XCOM_FR);
22214c5a9380Sskrll 			break;
22224c5a9380Sskrll 		}
222319cf921bSrearnsha 		delta = msr ^ sc->sc_msr;
222419cf921bSrearnsha 		sc->sc_msr = msr;
22254c5a9380Sskrll 
222619cf921bSrearnsha 		/* Clear any pending modem status interrupt.  */
22274c5a9380Sskrll 		switch (pi->pi_type) {
22284c5a9380Sskrll 		case PLCOM_TYPE_PL010:
22294c5a9380Sskrll 			msintr = ISSET(istatus, PL010_IIR_MIS);
22304c5a9380Sskrll 			if (msintr) {
22314c5a9380Sskrll 				PWRITE1(pi, PL010COM_ICR, 0);
22324c5a9380Sskrll 			}
22334c5a9380Sskrll 			break;
22344c5a9380Sskrll 		case PLCOM_TYPE_PL011:
2235820c39ceSmlelstv 		case PLCOM_TYPE_GENERIC_UART:
22364c5a9380Sskrll 			msintr = ISSET(istatus, PL011_INT_MSMASK);
22374c5a9380Sskrll 			if (msintr) {
22384c5a9380Sskrll 				PWRITE4(pi, PL011COM_ICR, PL011_INT_MSMASK);
22394c5a9380Sskrll 			}
22404c5a9380Sskrll 			break;
22414c5a9380Sskrll 		}
224219cf921bSrearnsha 		/*
224319cf921bSrearnsha 		 * Pulse-per-second (PSS) signals on edge of DCD?
224419cf921bSrearnsha 		 * Process these even if line discipline is ignoring DCD.
224519cf921bSrearnsha 		 */
224619cf921bSrearnsha 		if (delta & sc->sc_ppsmask) {
224719cf921bSrearnsha 			struct timeval tv;
2248a2249ef7Sad 			mutex_spin_enter(&timecounter_lock);
224919cf921bSrearnsha 		    	if ((msr & sc->sc_ppsmask) == sc->sc_ppsassert) {
225019cf921bSrearnsha 				/* XXX nanotime() */
225119cf921bSrearnsha 				microtime(&tv);
225219cf921bSrearnsha 				TIMEVAL_TO_TIMESPEC(&tv,
225319cf921bSrearnsha 				    &sc->ppsinfo.assert_timestamp);
225419cf921bSrearnsha 				if (sc->ppsparam.mode & PPS_OFFSETASSERT) {
225519cf921bSrearnsha 					timespecadd(&sc->ppsinfo.assert_timestamp,
225619cf921bSrearnsha 					    &sc->ppsparam.assert_offset,
225719cf921bSrearnsha 						    &sc->ppsinfo.assert_timestamp);
225819cf921bSrearnsha 				}
225919cf921bSrearnsha 
226019cf921bSrearnsha #ifdef PPS_SYNC
226119cf921bSrearnsha 				if (sc->ppsparam.mode & PPS_HARDPPSONASSERT)
226219cf921bSrearnsha 					hardpps(&tv, tv.tv_usec);
226319cf921bSrearnsha #endif
226419cf921bSrearnsha 				sc->ppsinfo.assert_sequence++;
226519cf921bSrearnsha 				sc->ppsinfo.current_mode = sc->ppsparam.mode;
226619cf921bSrearnsha 
226719cf921bSrearnsha 			} else if ((msr & sc->sc_ppsmask) == sc->sc_ppsclear) {
226819cf921bSrearnsha 				/* XXX nanotime() */
226919cf921bSrearnsha 				microtime(&tv);
227019cf921bSrearnsha 				TIMEVAL_TO_TIMESPEC(&tv,
227119cf921bSrearnsha 				    &sc->ppsinfo.clear_timestamp);
227219cf921bSrearnsha 				if (sc->ppsparam.mode & PPS_OFFSETCLEAR) {
227319cf921bSrearnsha 					timespecadd(&sc->ppsinfo.clear_timestamp,
227419cf921bSrearnsha 					    &sc->ppsparam.clear_offset,
227519cf921bSrearnsha 					    &sc->ppsinfo.clear_timestamp);
227619cf921bSrearnsha 				}
227719cf921bSrearnsha 
227819cf921bSrearnsha #ifdef PPS_SYNC
227919cf921bSrearnsha 				if (sc->ppsparam.mode & PPS_HARDPPSONCLEAR)
228019cf921bSrearnsha 					hardpps(&tv, tv.tv_usec);
228119cf921bSrearnsha #endif
228219cf921bSrearnsha 				sc->ppsinfo.clear_sequence++;
228319cf921bSrearnsha 				sc->ppsinfo.current_mode = sc->ppsparam.mode;
228419cf921bSrearnsha 			}
2285a2249ef7Sad 			mutex_spin_exit(&timecounter_lock);
228619cf921bSrearnsha 		}
228719cf921bSrearnsha 
228819cf921bSrearnsha 		/*
228919cf921bSrearnsha 		 * Process normal status changes
229019cf921bSrearnsha 		 */
229119cf921bSrearnsha 		if (ISSET(delta, sc->sc_msr_mask)) {
229219cf921bSrearnsha 			SET(sc->sc_msr_delta, delta);
229319cf921bSrearnsha 
229419cf921bSrearnsha 			/*
229519cf921bSrearnsha 			 * Stop output immediately if we lose the output
229619cf921bSrearnsha 			 * flow control signal or carrier detect.
229719cf921bSrearnsha 			 */
229819cf921bSrearnsha 			if (ISSET(~msr, sc->sc_msr_mask)) {
229919cf921bSrearnsha 				sc->sc_tbc = 0;
230019cf921bSrearnsha 				sc->sc_heldtbc = 0;
230119cf921bSrearnsha #ifdef PLCOM_DEBUG
230219cf921bSrearnsha 				if (plcom_debug)
230319cf921bSrearnsha 					plcomstatus(sc, "plcomintr  ");
230419cf921bSrearnsha #endif
230519cf921bSrearnsha 			}
230619cf921bSrearnsha 
230719cf921bSrearnsha 			sc->sc_st_check = 1;
230819cf921bSrearnsha 		}
230919cf921bSrearnsha 
231019cf921bSrearnsha 		/*
231119cf921bSrearnsha 		 * Done handling any receive interrupts. See if data
23124c5a9380Sskrll 		 * can be transmitted as well. Schedule tx done
23134c5a9380Sskrll 		 * event if no data left and tty was marked busy.
231419cf921bSrearnsha 		 */
23154c5a9380Sskrll 
23164c5a9380Sskrll 		switch (pi->pi_type) {
23174c5a9380Sskrll 		case PLCOM_TYPE_PL010:
23184c5a9380Sskrll 			txintr = ISSET(istatus, PL010_IIR_TIS);
23194c5a9380Sskrll 			break;
23204c5a9380Sskrll 		case PLCOM_TYPE_PL011:
23214c5a9380Sskrll 			txintr = ISSET(istatus, PL011_INT_TX);
23224c5a9380Sskrll 			break;
23234c5a9380Sskrll 		}
23244c5a9380Sskrll 		if (txintr) {
232519cf921bSrearnsha 			/*
232619cf921bSrearnsha 			 * If we've delayed a parameter change, do it
232719cf921bSrearnsha 			 * now, and restart * output.
232819cf921bSrearnsha 			 */
23294c5a9380Sskrll // PWRITE4(pi, PL011COM_ICR, PL011_INT_TX);
233019cf921bSrearnsha 			if (sc->sc_heldchange) {
233119cf921bSrearnsha 				plcom_loadchannelregs(sc);
233219cf921bSrearnsha 				sc->sc_heldchange = 0;
233319cf921bSrearnsha 				sc->sc_tbc = sc->sc_heldtbc;
233419cf921bSrearnsha 				sc->sc_heldtbc = 0;
233519cf921bSrearnsha 			}
233619cf921bSrearnsha 
233719cf921bSrearnsha 			/*
233819cf921bSrearnsha 			 * Output the next chunk of the contiguous
233919cf921bSrearnsha 			 * buffer, if any.
234019cf921bSrearnsha 			 */
234119cf921bSrearnsha 			if (sc->sc_tbc > 0) {
234219cf921bSrearnsha 				int n;
234319cf921bSrearnsha 
234419cf921bSrearnsha 				n = sc->sc_tbc;
2345820c39ceSmlelstv 				if (n > sc->sc_burstlen)
2346820c39ceSmlelstv 					n = sc->sc_burstlen;
23474c5a9380Sskrll 				PWRITEM1(pi, PL01XCOM_DR, sc->sc_tba, n);
234819cf921bSrearnsha 				sc->sc_tbc -= n;
234919cf921bSrearnsha 				sc->sc_tba += n;
235019cf921bSrearnsha 			} else {
235119cf921bSrearnsha 				/*
23524c5a9380Sskrll 				 * Disable transmit completion
235319cf921bSrearnsha 				 * interrupts if necessary.
235419cf921bSrearnsha 				 */
23554c5a9380Sskrll 				switch (pi->pi_type) {
23564c5a9380Sskrll 				case PLCOM_TYPE_PL010:
2357b9703c3dSskrll 					if (ISSET(sc->sc_cr, PL010_CR_TIE)) {
2358b9703c3dSskrll 						CLR(sc->sc_cr, PL010_CR_TIE);
23594c5a9380Sskrll 						PWRITE1(pi, PL010COM_CR,
236019cf921bSrearnsha 						    sc->sc_cr);
236119cf921bSrearnsha 					}
23624c5a9380Sskrll 					break;
23634c5a9380Sskrll 				case PLCOM_TYPE_PL011:
2364820c39ceSmlelstv 				case PLCOM_TYPE_GENERIC_UART:
23654c5a9380Sskrll 					if (ISSET(sc->sc_imsc, PL011_INT_TX)) {
23664c5a9380Sskrll 						CLR(sc->sc_imsc, PL011_INT_TX);
23674c5a9380Sskrll 						PWRITE4(pi, PL011COM_IMSC,
23684c5a9380Sskrll 						    sc->sc_imsc);
23694c5a9380Sskrll 					}
23704c5a9380Sskrll 					break;
23714c5a9380Sskrll 				}
237219cf921bSrearnsha 				if (sc->sc_tx_busy) {
237319cf921bSrearnsha 					sc->sc_tx_busy = 0;
237419cf921bSrearnsha 					sc->sc_tx_done = 1;
237519cf921bSrearnsha 				}
237619cf921bSrearnsha 			}
237719cf921bSrearnsha 		}
23784c5a9380Sskrll 
23794c5a9380Sskrll 	} while (plcom_intstatus(pi, &istatus));
238019cf921bSrearnsha 
2381fc0cd919Sskrll 	mutex_spin_exit(&sc->sc_lock);
238219cf921bSrearnsha 
238319cf921bSrearnsha 	/* Wake up the poller. */
2384820c39ceSmlelstv 	if ((sc->sc_rx_ready | sc->sc_st_check | sc->sc_tx_done) != 0)
2385fbed3be2Sad 		softint_schedule(sc->sc_si);
238619cf921bSrearnsha 
23877b0b7dedStls #ifdef RND_COM
23884c5a9380Sskrll 	rnd_add_uint32(&sc->rnd_source, istatus | rsr);
238919cf921bSrearnsha #endif
239019cf921bSrearnsha 
23914c5a9380Sskrll 	PLCOM_BARRIER(pi, BR | BW);
23924c5a9380Sskrll 
239319cf921bSrearnsha 	return 1;
239419cf921bSrearnsha }
239519cf921bSrearnsha 
239619cf921bSrearnsha /*
239719cf921bSrearnsha  * The following functions are polled getc and putc routines, shared
239819cf921bSrearnsha  * by the console and kgdb glue.
239919cf921bSrearnsha  *
240019cf921bSrearnsha  * The read-ahead code is so that you can detect pending in-band
240119cf921bSrearnsha  * cn_magic in polled mode while doing output rather than having to
240219cf921bSrearnsha  * wait until the kernel decides it needs input.
240319cf921bSrearnsha  */
240419cf921bSrearnsha 
240519cf921bSrearnsha #define MAX_READAHEAD	20
240619cf921bSrearnsha static int plcom_readahead[MAX_READAHEAD];
240719cf921bSrearnsha static int plcom_readaheadcount = 0;
240819cf921bSrearnsha 
240919cf921bSrearnsha int
plcom_common_getc(dev_t dev,struct plcom_instance * pi)24104c5a9380Sskrll plcom_common_getc(dev_t dev, struct plcom_instance *pi)
241119cf921bSrearnsha {
241219cf921bSrearnsha 	int s = splserial();
2413c644c266Sskrll 	u_char c;
241419cf921bSrearnsha 
241519cf921bSrearnsha 	/* got a character from reading things earlier */
241619cf921bSrearnsha 	if (plcom_readaheadcount > 0) {
241719cf921bSrearnsha 		int i;
241819cf921bSrearnsha 
241919cf921bSrearnsha 		c = plcom_readahead[0];
242019cf921bSrearnsha 		for (i = 1; i < plcom_readaheadcount; i++) {
242119cf921bSrearnsha 			plcom_readahead[i-1] = plcom_readahead[i];
242219cf921bSrearnsha 		}
242319cf921bSrearnsha 		plcom_readaheadcount--;
242419cf921bSrearnsha 		splx(s);
242519cf921bSrearnsha 		return c;
242619cf921bSrearnsha 	}
242719cf921bSrearnsha 
2428c644c266Sskrll 	if (ISSET(PREAD1(pi, PL01XCOM_FR), PL01X_FR_RXFE)) {
2429c644c266Sskrll 		splx(s);
2430c644c266Sskrll 		return -1;
2431c644c266Sskrll 	}
243219cf921bSrearnsha 
24334c5a9380Sskrll 	c = PREAD1(pi, PL01XCOM_DR);
243419cf921bSrearnsha 	{
24357c36e1d9Sskrll 		int cn_trapped __unused = 0;
2436dbfa10e5Sriastradh 
243719cf921bSrearnsha 		if (!db_active)
243819cf921bSrearnsha 			cn_check_magic(dev, c, plcom_cnm_state);
243919cf921bSrearnsha 	}
244019cf921bSrearnsha 	splx(s);
244119cf921bSrearnsha 	return c;
244219cf921bSrearnsha }
244319cf921bSrearnsha 
244419cf921bSrearnsha void
plcom_common_putc(dev_t dev,struct plcom_instance * pi,int c)24454c5a9380Sskrll plcom_common_putc(dev_t dev, struct plcom_instance *pi, int c)
244619cf921bSrearnsha {
244719cf921bSrearnsha 	int s = splserial();
244819cf921bSrearnsha 	int timo;
244919cf921bSrearnsha 
245019cf921bSrearnsha 	int cin, stat;
245119cf921bSrearnsha 	if (plcom_readaheadcount < MAX_READAHEAD
24524c5a9380Sskrll 	     && !ISSET(stat = PREAD1(pi, PL01XCOM_FR), PL01X_FR_RXFE)) {
24537c36e1d9Sskrll 		int cn_trapped __unused = 0;
24544c5a9380Sskrll 		cin = PREAD1(pi, PL01XCOM_DR);
245519cf921bSrearnsha 		cn_check_magic(dev, cin, plcom_cnm_state);
245619cf921bSrearnsha 		plcom_readahead[plcom_readaheadcount++] = cin;
245719cf921bSrearnsha 	}
245819cf921bSrearnsha 
245919cf921bSrearnsha 	/* wait for any pending transmission to finish */
246019cf921bSrearnsha 	timo = 150000;
24613ac55b05Sskrll 	while (ISSET(PREAD1(pi, PL01XCOM_FR), PL01X_FR_TXFF) && --timo)
246219cf921bSrearnsha 		continue;
246319cf921bSrearnsha 
24644c5a9380Sskrll 	PWRITE1(pi, PL01XCOM_DR, c);
24654c5a9380Sskrll 	PLCOM_BARRIER(pi, BR | BW);
246619cf921bSrearnsha 
246719cf921bSrearnsha 	splx(s);
246819cf921bSrearnsha }
246919cf921bSrearnsha 
247019cf921bSrearnsha /*
247119cf921bSrearnsha  * Initialize UART for use as console or KGDB line.
247219cf921bSrearnsha  */
247319cf921bSrearnsha int
plcominit(struct plcom_instance * pi,int rate,int frequency,tcflag_t cflag)24744c5a9380Sskrll plcominit(struct plcom_instance *pi, int rate, int frequency, tcflag_t cflag)
247519cf921bSrearnsha {
2476820c39ceSmlelstv 	uint32_t lcr;
247719cf921bSrearnsha 
24784c5a9380Sskrll 	switch (pi->pi_type) {
24794c5a9380Sskrll 	case PLCOM_TYPE_PL010:
24804c5a9380Sskrll 		if (pi->pi_size == 0)
24814c5a9380Sskrll 			pi->pi_size = PL010COM_UART_SIZE;
24824c5a9380Sskrll 		break;
24834c5a9380Sskrll 	case PLCOM_TYPE_PL011:
2484820c39ceSmlelstv 	case PLCOM_TYPE_GENERIC_UART:
24854c5a9380Sskrll 		if (pi->pi_size == 0)
24864c5a9380Sskrll 			pi->pi_size = PL011COM_UART_SIZE;
24874c5a9380Sskrll 		break;
24884c5a9380Sskrll 	default:
24894c5a9380Sskrll 		panic("Unknown plcom type");
24904c5a9380Sskrll 	}
24914c5a9380Sskrll 
24924c5a9380Sskrll 	if (bus_space_map(pi->pi_iot, pi->pi_iobase, pi->pi_size, 0,
24934c5a9380Sskrll 	    &pi->pi_ioh))
249419cf921bSrearnsha 		return ENOMEM; /* ??? */
249519cf921bSrearnsha 
24964c5a9380Sskrll 	lcr = cflag2lcr(cflag) | PL01X_LCR_FEN;
24974c5a9380Sskrll 	switch (pi->pi_type) {
24984c5a9380Sskrll 	case PLCOM_TYPE_PL010:
24994c5a9380Sskrll 		PWRITE1(pi, PL010COM_CR, 0);
25004c5a9380Sskrll 
2501760fff22Sjmcneill 		if (rate && frequency) {
25024c5a9380Sskrll 			rate = pl010comspeed(rate, frequency);
25034c5a9380Sskrll 			PWRITE1(pi, PL010COM_DLBL, (rate & 0xff));
25044c5a9380Sskrll 			PWRITE1(pi, PL010COM_DLBH, ((rate >> 8) & 0xff));
2505760fff22Sjmcneill 		}
25064c5a9380Sskrll 		PWRITE1(pi, PL010COM_LCR, lcr);
25074c5a9380Sskrll 		PWRITE1(pi, PL010COM_CR, PL01X_CR_UARTEN);
25084c5a9380Sskrll 		break;
25094c5a9380Sskrll 	case PLCOM_TYPE_PL011:
2510820c39ceSmlelstv 	case PLCOM_TYPE_GENERIC_UART:
2511820c39ceSmlelstv 		PWRITE2(pi, PL011COM_CR, 0);
25124c5a9380Sskrll 
2513760fff22Sjmcneill 		if (rate && frequency) {
25144c5a9380Sskrll 			rate = pl011comspeed(rate, frequency);
25154c5a9380Sskrll 			PWRITE1(pi, PL011COM_FBRD, rate & ((1 << 6) - 1));
25164c5a9380Sskrll 			PWRITE4(pi, PL011COM_IBRD, rate >> 6);
2517760fff22Sjmcneill 		}
25184c5a9380Sskrll 		PWRITE1(pi, PL011COM_LCRH, lcr);
2519820c39ceSmlelstv 		PWRITE2(pi, PL011COM_CR,
25204c5a9380Sskrll 		    PL01X_CR_UARTEN | PL011_CR_RXE | PL011_CR_TXE);
25214c5a9380Sskrll 		break;
25224c5a9380Sskrll 	}
252319cf921bSrearnsha 
252419cf921bSrearnsha #if 0
252519cf921bSrearnsha 	/* Ought to do something like this, but we have no sc to
252619cf921bSrearnsha 	   dereference. */
252739cd836eSthorpej 	/* XXX device_unit() abuse */
25287078f952Sskrll 	sc->sc_set_mcr(sc->sc_set_mcr_arg, device_unit(sc->sc_dev),
2529b9703c3dSskrll 	    PL01X_MCR_DTR | PL01X_MCR_RTS);
253019cf921bSrearnsha #endif
253119cf921bSrearnsha 
253219cf921bSrearnsha 	return 0;
253319cf921bSrearnsha }
253419cf921bSrearnsha 
253519cf921bSrearnsha /*
253619cf921bSrearnsha  * Following are all routines needed for PLCOM to act as console
253719cf921bSrearnsha  */
253819cf921bSrearnsha struct consdev plcomcons = {
253919cf921bSrearnsha 	NULL, NULL, plcomcngetc, plcomcnputc, plcomcnpollc, NULL,
2540e84b77e3Sjmcneill 	plcomcnhalt, NULL, NODEV, CN_NORMAL
254119cf921bSrearnsha };
254219cf921bSrearnsha 
254319cf921bSrearnsha int
plcomcnattach(struct plcom_instance * pi,int rate,int frequency,tcflag_t cflag,int unit)25444c5a9380Sskrll plcomcnattach(struct plcom_instance *pi, int rate, int frequency,
254519cf921bSrearnsha     tcflag_t cflag, int unit)
254619cf921bSrearnsha {
254719cf921bSrearnsha 	int res;
254819cf921bSrearnsha 
25494c5a9380Sskrll 	plcomcons_info = *pi;
25504c5a9380Sskrll 
25514c5a9380Sskrll 	res = plcominit(&plcomcons_info, rate, frequency, cflag);
255219cf921bSrearnsha 	if (res)
255319cf921bSrearnsha 		return res;
255419cf921bSrearnsha 
255519cf921bSrearnsha 	cn_tab = &plcomcons;
255619cf921bSrearnsha 	cn_init_magic(&plcom_cnm_state);
255719cf921bSrearnsha 	cn_set_magic("\047\001"); /* default magic is BREAK */
255819cf921bSrearnsha 
255919cf921bSrearnsha 	plcomconsunit = unit;
256019cf921bSrearnsha 	plcomconsrate = rate;
256119cf921bSrearnsha 	plcomconscflag = cflag;
256219cf921bSrearnsha 
256319cf921bSrearnsha 	return 0;
256419cf921bSrearnsha }
256519cf921bSrearnsha 
256619cf921bSrearnsha void
plcomcndetach(void)256719cf921bSrearnsha plcomcndetach(void)
256819cf921bSrearnsha {
25694c5a9380Sskrll 
25704c5a9380Sskrll 	bus_space_unmap(plcomcons_info.pi_iot, plcomcons_info.pi_ioh,
25714c5a9380Sskrll 	    plcomcons_info.pi_size);
25724c5a9380Sskrll 	plcomcons_info.pi_iot = NULL;
257319cf921bSrearnsha 
257419cf921bSrearnsha 	cn_tab = NULL;
257519cf921bSrearnsha }
257619cf921bSrearnsha 
257719cf921bSrearnsha int
plcomcngetc(dev_t dev)257819cf921bSrearnsha plcomcngetc(dev_t dev)
257919cf921bSrearnsha {
25804c5a9380Sskrll 	return plcom_common_getc(dev, &plcomcons_info);
258119cf921bSrearnsha }
258219cf921bSrearnsha 
258319cf921bSrearnsha /*
258419cf921bSrearnsha  * Console kernel output character routine.
258519cf921bSrearnsha  */
258619cf921bSrearnsha void
plcomcnputc(dev_t dev,int c)258719cf921bSrearnsha plcomcnputc(dev_t dev, int c)
258819cf921bSrearnsha {
25894c5a9380Sskrll 	plcom_common_putc(dev, &plcomcons_info, c);
259019cf921bSrearnsha }
259119cf921bSrearnsha 
259219cf921bSrearnsha void
plcomcnpollc(dev_t dev,int on)259319cf921bSrearnsha plcomcnpollc(dev_t dev, int on)
259419cf921bSrearnsha {
259519cf921bSrearnsha 
2596c2b23d53Smlelstv 	plcom_readaheadcount = 0;
259719cf921bSrearnsha }
259819cf921bSrearnsha 
2599e84b77e3Sjmcneill void
plcomcnhalt(dev_t dev)2600e84b77e3Sjmcneill plcomcnhalt(dev_t dev)
2601e84b77e3Sjmcneill {
2602e84b77e3Sjmcneill 	struct plcom_instance *pi = &plcomcons_info;
2603e84b77e3Sjmcneill 
2604e84b77e3Sjmcneill 	switch (pi->pi_type) {
2605e84b77e3Sjmcneill 	case PLCOM_TYPE_PL010:
2606e84b77e3Sjmcneill 		PWRITE1(pi, PL010COM_CR, PL01X_CR_UARTEN);
2607e84b77e3Sjmcneill 		break;
2608e84b77e3Sjmcneill 	case PLCOM_TYPE_PL011:
2609820c39ceSmlelstv 	case PLCOM_TYPE_GENERIC_UART:
2610820c39ceSmlelstv 		PWRITE2(pi, PL011COM_CR,
2611e84b77e3Sjmcneill 		    PL01X_CR_UARTEN | PL011_CR_RXE | PL011_CR_TXE);
2612e84b77e3Sjmcneill 		PWRITE4(pi, PL011COM_IMSC, 0);
2613e84b77e3Sjmcneill 		break;
2614e84b77e3Sjmcneill 	}
2615e84b77e3Sjmcneill }
2616e84b77e3Sjmcneill 
261719cf921bSrearnsha #ifdef KGDB
261819cf921bSrearnsha int
plcom_kgdb_attach(struct plcom_instance * pi,int rate,int frequency,tcflag_t cflag,int unit)26194c5a9380Sskrll plcom_kgdb_attach(struct plcom_instance *pi, int rate, int frequency,
26204c5a9380Sskrll     tcflag_t cflag, int unit)
262119cf921bSrearnsha {
262219cf921bSrearnsha 	int res;
262319cf921bSrearnsha 
26244c5a9380Sskrll 	if (pi->pi_iot == plcomcons_info.pi_iot &&
26254c5a9380Sskrll 	    pi->pi_iobase == plcomcons_info.pi_iobase)
262619cf921bSrearnsha 		return EBUSY; /* cannot share with console */
262719cf921bSrearnsha 
26284c5a9380Sskrll 	res = plcominit(pi, rate, frequency, cflag);
262919cf921bSrearnsha 	if (res)
263019cf921bSrearnsha 		return res;
263119cf921bSrearnsha 
263219cf921bSrearnsha 	kgdb_attach(plcom_kgdb_getc, plcom_kgdb_putc, NULL);
263319cf921bSrearnsha 	kgdb_dev = 123; /* unneeded, only to satisfy some tests */
263419cf921bSrearnsha 
26354c5a9380Sskrll 	plcomkgdb_info.pi_iot = pi->pi_iot;
26364c5a9380Sskrll 	plcomkgdb_info.pi_ioh = pi->pi_ioh;
26374c5a9380Sskrll 	plcomkgdb_info.pi_iobase = pi->pi_iobase;
263819cf921bSrearnsha 
263919cf921bSrearnsha 	return 0;
264019cf921bSrearnsha }
264119cf921bSrearnsha 
264219cf921bSrearnsha /* ARGSUSED */
264319cf921bSrearnsha int
plcom_kgdb_getc(void * arg)264419cf921bSrearnsha plcom_kgdb_getc(void *arg)
264519cf921bSrearnsha {
2646882558bdSmlelstv 	return plcom_common_getc(NODEV, &plcomkgdb_info);
264719cf921bSrearnsha }
264819cf921bSrearnsha 
264919cf921bSrearnsha /* ARGSUSED */
265019cf921bSrearnsha void
plcom_kgdb_putc(void * arg,int c)265119cf921bSrearnsha plcom_kgdb_putc(void *arg, int c)
265219cf921bSrearnsha {
2653882558bdSmlelstv 	plcom_common_putc(NODEV, &plcomkgdb_info, c);
265419cf921bSrearnsha }
265519cf921bSrearnsha #endif /* KGDB */
265619cf921bSrearnsha 
265719cf921bSrearnsha /* helper function to identify the plcom ports used by
265819cf921bSrearnsha  console or KGDB (and not yet autoconf attached) */
265919cf921bSrearnsha int
plcom_is_console(bus_space_tag_t iot,bus_addr_t iobase,bus_space_handle_t * ioh)26604c5a9380Sskrll plcom_is_console(bus_space_tag_t iot, bus_addr_t iobase,
266119cf921bSrearnsha     bus_space_handle_t *ioh)
266219cf921bSrearnsha {
266319cf921bSrearnsha 	bus_space_handle_t help;
266419cf921bSrearnsha 
266519cf921bSrearnsha 	if (!plcomconsattached &&
26664c5a9380Sskrll 	    bus_space_is_equal(iot, plcomcons_info.pi_iot) &&
26674c5a9380Sskrll 	    iobase == plcomcons_info.pi_iobase)
26684c5a9380Sskrll 		help = plcomcons_info.pi_ioh;
266919cf921bSrearnsha #ifdef KGDB
267019cf921bSrearnsha 	else if (!plcom_kgdb_attached &&
26714c5a9380Sskrll 	    bus_space_is_equal(iot, plcomkgdb_info.pi_iot) &&
26724c5a9380Sskrll 	    iobase == plcomkgdb_info.pi_iobase)
2673882558bdSmlelstv 		help = plcomkgdb_info.pi_ioh;
267419cf921bSrearnsha #endif
267519cf921bSrearnsha 	else
267619cf921bSrearnsha 		return 0;
267719cf921bSrearnsha 
267819cf921bSrearnsha 	if (ioh)
267919cf921bSrearnsha 		*ioh = help;
268019cf921bSrearnsha 	return 1;
268119cf921bSrearnsha }
2682