19406Sbde/*-
26261Sjkh * cyclades cyclom-y serial driver
36261Sjkh *	Andrew Herbert <andrew@werple.apana.org.au>, 17 August 1993
46261Sjkh *
56261Sjkh * Copyright (c) 1993 Andrew Herbert.
66261Sjkh * All rights reserved.
76261Sjkh *
86261Sjkh * Redistribution and use in source and binary forms, with or without
96261Sjkh * modification, are permitted provided that the following conditions
106261Sjkh * are met:
116261Sjkh * 1. Redistributions of source code must retain the above copyright
126261Sjkh *    notice, this list of conditions and the following disclaimer.
136261Sjkh * 2. Redistributions in binary form must reproduce the above copyright
146261Sjkh *    notice, this list of conditions and the following disclaimer in the
156261Sjkh *    documentation and/or other materials provided with the distribution.
166261Sjkh * 3. The name Andrew Herbert may not be used to endorse or promote products
176261Sjkh *    derived from this software without specific prior written permission.
186261Sjkh *
196261Sjkh * THIS SOFTWARE IS PROVIDED BY ``AS IS'' AND ANY EXPRESS OR IMPLIED
206261Sjkh * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
216261Sjkh * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN
226261Sjkh * NO EVENT SHALL I BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
236261Sjkh * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
246261Sjkh * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
256261Sjkh * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
266261Sjkh * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
276261Sjkh * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
286261Sjkh * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
296261Sjkh */
306261Sjkh
31115703Sobrien#include <sys/cdefs.h>
32115703Sobrien__FBSDID("$FreeBSD$");
33115703Sobrien
3431778Seivind#include "opt_compat.h"
3529047Sbde
366261Sjkh/*
379406Sbde * TODO:
389406Sbde * Atomic COR change.
399406Sbde * Consoles.
406261Sjkh */
416261Sjkh
426261Sjkh/*
439406Sbde * Temporary compile-time configuration options.
446261Sjkh */
459406Sbde#define	RxFifoThreshold	(CD1400_RX_FIFO_SIZE / 2)
469406Sbde			/* Number of chars in the receiver FIFO before an
479406Sbde			 * an interrupt is generated.  Should depend on
489406Sbde			 * line speed.  Needs to be about 6 on a 486DX33
499406Sbde			 * for 4 active ports at 115200 bps.  Why doesn't
509406Sbde			 * 10 work?
519406Sbde			 */
529406Sbde#define	PollMode	/* Use polling-based irq service routine, not the
539406Sbde			 * hardware svcack lines.  Must be defined for
549406Sbde			 * Cyclom-16Y boards.  Less efficient for Cyclom-8Ys,
559406Sbde			 * and stops 4 * 115200 bps from working.
569406Sbde			 */
579406Sbde#undef	Smarts		/* Enable slightly more CD1400 intelligence.  Mainly
589406Sbde			 * the output CR/LF processing, plus we can avoid a
599406Sbde			 * few checks usually done in ttyinput().
609406Sbde			 *
619406Sbde			 * XXX not fully implemented, and not particularly
629406Sbde			 * worthwhile.
639406Sbde			 */
649406Sbde#undef	CyDebug		/* Include debugging code (not very expensive). */
656261Sjkh
669406Sbde/* These will go away. */
679406Sbde#undef	SOFT_CTS_OFLOW
689406Sbde#define	SOFT_HOTCHAR
696261Sjkh
706261Sjkh#include <sys/param.h>
716261Sjkh#include <sys/systm.h>
7276166Smarkm#include <sys/bus.h>
736261Sjkh#include <sys/conf.h>
7424131Sbde#include <sys/fcntl.h>
7538246Sbde#include <sys/interrupt.h>
766261Sjkh#include <sys/kernel.h>
7776166Smarkm#include <sys/lock.h>
789406Sbde#include <sys/malloc.h>
7971846Sbde#include <sys/mutex.h>
80136139Sphk#include <sys/serial.h>
816261Sjkh#include <sys/syslog.h>
8276166Smarkm#include <sys/tty.h>
8376166Smarkm
8447585Sbde#include <machine/psl.h>
856261Sjkh
86128800Sbde#include <dev/ic/cd1400.h>
876261Sjkh
88128800Sbde#include <dev/cy/cyreg.h>
89128800Sbde#include <dev/cy/cyvar.h>
90128800Sbde
91127885Sbde#define	NCY 10			/* KLUDGE */
9261011Speter
93136139Sphk#define	NPORTS		(NCY * CY_MAX_PORTS)
946261Sjkh
959406Sbde#define	CY_MAX_PORTS		(CD1400_NO_OF_CHANNELS * CY_MAX_CD1400s)
966261Sjkh
979406Sbde/* We encode the cyclom unit number (cyu) in spare bits in the IVR's. */
989406Sbde#define	CD1400_xIVR_CHAN_SHIFT	3
9929047Sbde#define	CD1400_xIVR_CHAN	0x1F
1006261Sjkh
10141903Sbde/*
10241903Sbde * ETC states.  com->etc may also contain a hardware ETC command value,
10341903Sbde * meaning that execution of that command is pending.
10441903Sbde */
10541903Sbde#define	ETC_NONE		0	/* we depend on bzero() setting this */
10641903Sbde#define	ETC_BREAK_STARTING	1
10741903Sbde#define	ETC_BREAK_STARTED	2
10841903Sbde#define	ETC_BREAK_ENDING	3
10941903Sbde#define	ETC_BREAK_ENDED		4
11041903Sbde
1119406Sbde#define	LOTS_OF_EVENTS	64	/* helps separate urgent events from input */
1126261Sjkh
11381580Sbde/*
1149406Sbde * com state bits.
1159406Sbde * (CS_BUSY | CS_TTGO) and (CS_BUSY | CS_TTGO | CS_ODEVREADY) must be higher
1169406Sbde * than the other bits so that they can be tested as a group without masking
1179406Sbde * off the low bits.
1189406Sbde *
1199406Sbde * The following com and tty flags correspond closely:
120136139Sphk *	CS_BUSY		= TS_BUSY (maintained by cystart(), cypoll() and
12151654Sphk *				   comstop())
122136139Sphk *	CS_TTGO		= ~TS_TTSTOP (maintained by cyparam() and cystart())
123136139Sphk *	CS_CTS_OFLOW	= CCTS_OFLOW (maintained by cyparam())
124136139Sphk *	CS_RTS_IFLOW	= CRTS_IFLOW (maintained by cyparam())
1259406Sbde * TS_FLUSH is not used.
1269406Sbde * XXX I think TIOCSETA doesn't clear TS_TTSTOP when it clears IXON.
1279406Sbde * XXX CS_*FLOW should be CF_*FLOW in com->flags (control flags not state).
1289406Sbde */
1299406Sbde#define	CS_BUSY		0x80	/* output in progress */
1309406Sbde#define	CS_TTGO		0x40	/* output not stopped by XOFF */
1319406Sbde#define	CS_ODEVREADY	0x20	/* external device h/w ready (CTS) */
1329406Sbde#define	CS_CHECKMSR	1	/* check of MSR scheduled */
1339406Sbde#define	CS_CTS_OFLOW	2	/* use CTS output flow control */
1349406Sbde#define	CS_ODONE	4	/* output completed */
1359406Sbde#define	CS_RTS_IFLOW	8	/* use RTS input flow control */
13641388Sbde#define	CSE_ODONE	1	/* output transmitted */
1376261Sjkh
1389406Sbdestatic	char const * const	error_desc[] = {
1399406Sbde#define	CE_OVERRUN			0
1409406Sbde	"silo overflow",
1419406Sbde#define	CE_INTERRUPT_BUF_OVERFLOW	1
1429406Sbde	"interrupt-level buffer overflow",
1439406Sbde#define	CE_TTY_BUF_OVERFLOW		2
1449406Sbde	"tty-level buffer overflow",
1459406Sbde};
1466261Sjkh
1479406Sbde#define	CE_NTYPES			3
1489406Sbde#define	CE_RECORD(com, errnum)		(++(com)->delta_error_counts[errnum])
1496261Sjkh
150122771Sbde#ifdef SMP
151136139Sphk#define	COM_LOCK()	mtx_lock_spin(&cy_lock)
152136139Sphk#define	COM_UNLOCK()	mtx_unlock_spin(&cy_lock)
153122771Sbde#else
154122771Sbde#define	COM_LOCK()
155122771Sbde#define	COM_UNLOCK()
156122771Sbde#endif
157122771Sbde
1589406Sbde/* types.  XXX - should be elsewhere */
1599406Sbdetypedef u_char	bool_t;		/* boolean */
1609406Sbde
1619406Sbde/* queue of linear buffers */
1629406Sbdestruct lbq {
1639406Sbde	u_char	*l_head;	/* next char to process */
1649406Sbde	u_char	*l_tail;	/* one past the last char to process */
1659406Sbde	struct lbq *l_next;	/* next in queue */
1669406Sbde	bool_t	l_queued;	/* nonzero if queued */
1679406Sbde};
1689406Sbde
1699406Sbde/* com device structure */
1709406Sbdestruct com_s {
1719406Sbde	u_char	state;		/* miscellaneous flag bits */
17241903Sbde	u_char	etc;		/* pending Embedded Transmit Command */
17341388Sbde	u_char	extra_state;	/* more flag bits, separate for order trick */
17438303Sbde	u_char	gfrcr_image;	/* copy of value read from GFRCR */
17538303Sbde	u_char	mcr_dtr;	/* MCR bit that is wired to DTR */
1769406Sbde	u_char	mcr_image;	/* copy of value written to MCR */
17738303Sbde	u_char	mcr_rts;	/* MCR bit that is wired to RTS */
1789406Sbde	int	unit;		/* unit	number */
1796261Sjkh
1809406Sbde	/*
1819406Sbde	 * The high level of the driver never reads status registers directly
1829406Sbde	 * because there would be too many side effects to handle conveniently.
1839406Sbde	 * Instead, it reads copies of the registers stored here by the
1849406Sbde	 * interrupt handler.
1859406Sbde	 */
1869406Sbde	u_char	last_modem_status;	/* last MSR read by intr handler */
1879406Sbde	u_char	prev_modem_status;	/* last MSR handled by high level */
1886261Sjkh
1899406Sbde	u_char	*ibuf;		/* start of input buffer */
1909406Sbde	u_char	*ibufend;	/* end of input buffer */
19143611Sbde	u_char	*ibufold;	/* old input buffer, to be freed */
1929406Sbde	u_char	*ihighwater;	/* threshold in input buffer */
1939406Sbde	u_char	*iptr;		/* next free spot in input buffer */
19443611Sbde	int	ibufsize;	/* size of ibuf (not include error bytes) */
19543611Sbde	int	ierroff;	/* offset of error bytes in ibuf */
1966261Sjkh
1979406Sbde	struct lbq	obufq;	/* head of queue of output buffers */
1989406Sbde	struct lbq	obufs[2];	/* output buffers */
1996261Sjkh
20029047Sbde	int	cy_align;	/* index for register alignment */
2019406Sbde	cy_addr	cy_iobase;	/* base address of this port's cyclom */
2029406Sbde	cy_addr	iobase;		/* base address of this port's cd1400 */
20338303Sbde	int	mcr_rts_reg;	/* cd1400 reg number of reg holding mcr_rts */
2046261Sjkh
2059406Sbde	struct tty	*tp;	/* cross reference */
2069406Sbde
2079406Sbde	u_long	bytes_in;	/* statistics */
2089406Sbde	u_long	bytes_out;
2099406Sbde	u_int	delta_error_counts[CE_NTYPES];
2109406Sbde	u_long	error_counts[CE_NTYPES];
2119406Sbde
2129406Sbde	u_int	recv_exception;	/* exception chars received */
2139406Sbde	u_int	mdm;		/* modem signal changes */
2149406Sbde#ifdef CyDebug
215136139Sphk	u_int	start_count;	/* no. of calls to cystart() */
2169406Sbde	u_int	start_real;	/* no. of calls that did something */
2176261Sjkh#endif
21841293Sbde	u_char	car;		/* CD1400 CAR shadow (if first unit in cd) */
2199406Sbde	u_char	channel_control;/* CD1400 CCR control command shadow */
2209406Sbde	u_char	cor[3];		/* CD1400 COR1-3 shadows */
2219406Sbde	u_char	intr_enable;	/* CD1400 SRER shadow */
2226261Sjkh
2239406Sbde	/*
2249406Sbde	 * Data area for output buffers.  Someday we should build the output
2259406Sbde	 * buffer queue without copying data.
2269406Sbde	 */
2279406Sbde	u_char	obuf1[256];
2289406Sbde	u_char	obuf2[256];
2299406Sbde};
2309406Sbde
231127885Sbdedevclass_t	cy_devclass;
232128800Sbdechar		cy_driver_name[] = "cy";
233127885Sbde
23492765Salfredstatic	void	cd1400_channel_cmd(struct com_s *com, int cmd);
23592765Salfredstatic	void	cd1400_channel_cmd_wait(struct com_s *com);
23693024Sbdestatic	void	cd_etc(struct com_s *com, int etc);
23793024Sbdestatic	int	cd_getreg(struct com_s *com, int reg);
23893024Sbdestatic	void	cd_setreg(struct com_s *com, int reg, int val);
239136139Sphkstatic	void	cyinput(struct com_s *com);
240136139Sphkstatic	int	cyparam(struct tty *tp, struct termios *t);
241136139Sphkstatic	void	cypoll(void *arg);
242136139Sphkstatic	void	cysettimeout(void);
243136139Sphkstatic	int	cysetwater(struct com_s *com, speed_t speed);
244136139Sphkstatic	int	cyspeed(speed_t speed, u_long cy_clock, int *prescaler_io);
245136139Sphkstatic	void	cystart(struct tty *tp);
24693024Sbdestatic	void	comstop(struct tty *tp, int rw);
247136139Sphkstatic	timeout_t cywakeup;
24893024Sbdestatic	void	disc_optim(struct tty *tp, struct termios *t,
24993024Sbde		    struct com_s *com);
2509406Sbde
251136139Sphkstatic t_break_t	cybreak;
252136139Sphkstatic t_modem_t	cymodem;
253136139Sphkstatic t_open_t		cyopen;
254136139Sphkstatic t_close_t	cyclose;
255136139Sphk
2566261Sjkh#ifdef CyDebug
25793024Sbdevoid	cystatus(int unit);
2586261Sjkh#endif
2599406Sbde
260136139Sphkstatic struct	mtx cy_lock;
261136139Sphkstatic int	cy_inited;
26212962Sbde
2639406Sbde/* table and macro for fast conversion from a unit number to its com struct */
264136139Sphkstatic	struct com_s	*p_cy_addr[NPORTS];
265136139Sphk#define	cy_addr(unit)	(p_cy_addr[unit])
2669406Sbde
267136139Sphkstatic	u_int	cy_events;	/* input chars + weighted output completions */
268136139Sphkstatic	void	*cy_fast_ih;
269136139Sphkstatic	void	*cy_slow_ih;
270136139Sphkstatic	int	cy_timeout;
271136139Sphkstatic	int	cy_timeouts_until_log;
272136139Sphkstatic	struct	callout_handle cy_timeout_handle
273136139Sphk    = CALLOUT_HANDLE_INITIALIZER(&cy_timeout_handle);
27412962Sbde
2756261Sjkh#ifdef CyDebug
2769406Sbdestatic	u_int	cd_inbs;
2779406Sbdestatic	u_int	cy_inbs;
2789406Sbdestatic	u_int	cd_outbs;
2799406Sbdestatic	u_int	cy_outbs;
2809406Sbdestatic	u_int	cy_svrr_probes;
2819406Sbdestatic	u_int	cy_timeouts;
2826261Sjkh#endif
2836261Sjkh
28429047Sbdestatic	int	cy_chip_offset[] = {
28531104Sbde	0x0000, 0x0400, 0x0800, 0x0c00, 0x0200, 0x0600, 0x0a00, 0x0e00,
28629047Sbde};
2879406Sbdestatic	int	cy_nr_cd1400s[NCY];
28818901Sdgstatic	int	cy_total_devices;
2899406Sbde#undef	RxFifoThreshold
2909406Sbdestatic	int	volatile RxFifoThreshold = (CD1400_RX_FIFO_SIZE / 2);
2916261Sjkh
292128800Sbdeint
293136139Sphkcy_units(cy_addr cy_iobase, int cy_align)
29418901Sdg{
29529047Sbde	int	cyu;
29629047Sbde	u_char	firmware_version;
29729047Sbde	int	i;
29829047Sbde	cy_addr	iobase;
29918901Sdg
30018679Sdg	for (cyu = 0; cyu < CY_MAX_CD1400s; ++cyu) {
30129047Sbde		iobase = cy_iobase + (cy_chip_offset[cyu] << cy_align);
3026261Sjkh
3039406Sbde		/* wait for chip to become ready for new command */
30411424Sdg		for (i = 0; i < 10; i++) {
3059406Sbde			DELAY(50);
30618901Sdg			if (!cd_inb(iobase, CD1400_CCR, cy_align))
3079406Sbde				break;
3089406Sbde		}
3096261Sjkh
3109406Sbde		/* clear the GFRCR register */
31118901Sdg		cd_outb(iobase, CD1400_GFRCR, cy_align, 0);
3126261Sjkh
3139406Sbde		/* issue a reset command */
31418901Sdg		cd_outb(iobase, CD1400_CCR, cy_align,
3159406Sbde			CD1400_CCR_CMDRESET | CD1400_CCR_FULLRESET);
3166261Sjkh
31791314Sbde		/* XXX bogus initialization to avoid a gcc bug/warning. */
31891314Sbde		firmware_version = 0;
31991314Sbde
3209406Sbde		/* wait for the CD1400 to initialize itself */
32111424Sdg		for (i = 0; i < 200; i++) {
3229406Sbde			DELAY(50);
3239406Sbde
3249406Sbde			/* retrieve firmware version */
32529047Sbde			firmware_version = cd_inb(iobase, CD1400_GFRCR,
32629047Sbde						  cy_align);
32711424Sdg			if ((firmware_version & 0xf0) == 0x40)
3289406Sbde				break;
3299406Sbde		}
3309406Sbde
3319406Sbde		/*
3329406Sbde		 * Anything in the 0x40-0x4F range is fine.
3339406Sbde		 * If one CD1400 is bad then we don't support higher
3349406Sbde		 * numbered good ones on this board.
3359406Sbde		 */
33611424Sdg		if ((firmware_version & 0xf0) != 0x40)
3379406Sbde			break;
3386261Sjkh	}
33918901Sdg	return (cyu);
3406261Sjkh}
3416261Sjkh
342123104Sbdevoid *
343136139Sphkcyattach_common(cy_addr cy_iobase, int cy_align)
34418901Sdg{
34529047Sbde	int	adapter;
34629047Sbde	int	cyu;
34738303Sbde	u_char	firmware_version;
34829047Sbde	cy_addr	iobase;
34929047Sbde	int	ncyu;
35029047Sbde	int	unit;
351136139Sphk	struct tty *tp;
35218901Sdg
353136139Sphk	while (cy_inited != 2)
354136139Sphk		if (atomic_cmpset_int(&cy_inited, 0, 1)) {
355136139Sphk			mtx_init(&cy_lock, cy_driver_name, NULL, MTX_SPIN);
356136139Sphk			atomic_store_rel_int(&cy_inited, 2);
357127883Sbde		}
358127883Sbde
35918901Sdg	adapter = cy_total_devices;
36018901Sdg	if ((u_int)adapter >= NCY) {
36129047Sbde		printf(
36229047Sbde	"cy%d: can't attach adapter: insufficient cy devices configured\n",
36329047Sbde		       adapter);
364123104Sbde		return (NULL);
36518901Sdg	}
36618901Sdg	ncyu = cy_units(cy_iobase, cy_align);
36718901Sdg	if (ncyu == 0)
368123104Sbde		return (NULL);
36918901Sdg	cy_nr_cd1400s[adapter] = ncyu;
37018901Sdg	cy_total_devices++;
37118901Sdg
37218901Sdg	unit = adapter * CY_MAX_PORTS;
37318679Sdg	for (cyu = 0; cyu < ncyu; ++cyu) {
3749406Sbde		int	cdu;
3756261Sjkh
37629047Sbde		iobase = (cy_addr) (cy_iobase
37729047Sbde				    + (cy_chip_offset[cyu] << cy_align));
37838303Sbde		firmware_version = cd_inb(iobase, CD1400_GFRCR, cy_align);
37929047Sbde
3809406Sbde		/* Set up a receive timeout period of than 1+ ms. */
38138303Sbde		cd_outb(iobase, CD1400_PPR, cy_align,
38238303Sbde			howmany(CY_CLOCK(firmware_version)
38338303Sbde				/ CD1400_PPR_PRESCALER, 1000));
38438303Sbde
3859406Sbde		for (cdu = 0; cdu < CD1400_NO_OF_CHANNELS; ++cdu, ++unit) {
3869406Sbde			struct com_s	*com;
3879406Sbde			int		s;
3886261Sjkh
389136139Sphk			com = malloc(sizeof *com, M_DEVBUF, M_NOWAIT | M_ZERO);
390136139Sphk			if (com == NULL)
391136139Sphk				break;
392136139Sphk			com->unit = unit;
39338303Sbde			com->gfrcr_image = firmware_version;
39438303Sbde			if (CY_RTS_DTR_SWAPPED(firmware_version)) {
395136139Sphk				com->mcr_dtr = CD1400_MSVR1_RTS;
396136139Sphk				com->mcr_rts = CD1400_MSVR2_DTR;
39738303Sbde				com->mcr_rts_reg = CD1400_MSVR2;
39838303Sbde			} else {
399136139Sphk				com->mcr_dtr = CD1400_MSVR2_DTR;
400136139Sphk				com->mcr_rts = CD1400_MSVR1_RTS;
40138303Sbde				com->mcr_rts_reg = CD1400_MSVR1;
40238303Sbde			}
403136139Sphk			com->obufs[0].l_head = com->obuf1;
404136139Sphk			com->obufs[1].l_head = com->obuf2;
4056261Sjkh
40629047Sbde			com->cy_align = cy_align;
4079406Sbde			com->cy_iobase = cy_iobase;
408136139Sphk			com->iobase = iobase;
40941293Sbde			com->car = ~CD1400_CAR_CHAN;
4106261Sjkh
411136139Sphk			tp = com->tp = ttyalloc();
412136139Sphk			tp->t_open = cyopen;
413136139Sphk			tp->t_close = cyclose;
414136139Sphk			tp->t_oproc = cystart;
415136139Sphk			tp->t_stop = comstop;
416136139Sphk			tp->t_param = cyparam;
417136139Sphk			tp->t_break = cybreak;
418136139Sphk			tp->t_modem = cymodem;
419136139Sphk			tp->t_sc = com;
4206261Sjkh
421136139Sphk			if (cysetwater(com, tp->t_init_in.c_ispeed) != 0) {
422136139Sphk				free(com, M_DEVBUF);
423136139Sphk				return (NULL);
424136139Sphk			}
42512962Sbde
426136139Sphk			s = spltty();
427136139Sphk			cy_addr(unit) = com;
428136139Sphk			splx(s);
429136139Sphk
430136139Sphk			if (cy_fast_ih == NULL) {
431151690Sru				swi_add(&tty_intr_event, "cy", cypoll, NULL, SWI_TTY, 0,
432136139Sphk					&cy_fast_ih);
433151690Sru				swi_add(&clk_intr_event, "cy", cypoll, NULL, SWI_CLOCK, 0,
434136139Sphk					&cy_slow_ih);
435136139Sphk			}
436151383Sphk			ttycreate(tp, TS_CALLOUT, "c%r%r",
437136139Sphk			    adapter, unit % CY_MAX_PORTS);
4389406Sbde		}
4396261Sjkh	}
4406261Sjkh
4419406Sbde	/* ensure an edge for the next interrupt */
44241309Sbde	cy_outb(cy_iobase, CY_CLEAR_INTR, cy_align, 0);
4436261Sjkh
444136139Sphk	return (cy_addr(adapter * CY_MAX_PORTS));
4456261Sjkh}
4466261Sjkh
44712675Sjulianstatic int
448136139Sphkcyopen(struct tty *tp, struct cdev *dev)
4496261Sjkh{
4509406Sbde	struct com_s	*com;
4519406Sbde	int		s;
4526261Sjkh
453136139Sphk	com = tp->t_sc;
4549406Sbde	s = spltty();
4559406Sbde	/*
4569406Sbde	 * We jump to this label after all non-interrupted sleeps to pick
4579406Sbde	 * up any changes of the device state.
4589406Sbde	 */
45942045Sbde
460136139Sphk	/* Encode per-board unit in LIVR for access in intr routines. */
461136139Sphk	cd_setreg(com, CD1400_LIVR,
462136139Sphk		  (com->unit & CD1400_xIVR_CHAN) << CD1400_xIVR_CHAN_SHIFT);
46342045Sbde
4649406Sbde	/*
465136139Sphk	 * Flush fifos.  This requires a full channel reset which
466136139Sphk	 * also disables the transmitter and receiver.  Recover
467136139Sphk	 * from this.
4689406Sbde	 */
469136139Sphk	cd1400_channel_cmd(com,
470136139Sphk			   CD1400_CCR_CMDRESET | CD1400_CCR_CHANRESET);
471136139Sphk	cd1400_channel_cmd(com, com->channel_control);
4726261Sjkh
473136139Sphk	critical_enter();
474136139Sphk	COM_LOCK();
475136139Sphk	com->prev_modem_status = com->last_modem_status
476136139Sphk	    = cd_getreg(com, CD1400_MSVR2);
477136139Sphk	cd_setreg(com, CD1400_SRER,
478136139Sphk		  com->intr_enable
479136139Sphk		  = CD1400_SRER_MDMCH | CD1400_SRER_RXDATA);
480136139Sphk	COM_UNLOCK();
481136139Sphk	critical_exit();
482136139Sphk	cysettimeout();
4839406Sbde	return (0);
4849406Sbde}
4856261Sjkh
486136139Sphk
4879406Sbdestatic void
488136139Sphkcyclose(struct tty *tp)
4899406Sbde{
4909406Sbde	cy_addr		iobase;
491136139Sphk	struct com_s	*com;
4929406Sbde	int		s;
4939406Sbde	int		unit;
4946261Sjkh
495136139Sphk	com = tp->t_sc;
4969406Sbde	unit = com->unit;
4979406Sbde	iobase = com->iobase;
4989406Sbde	s = spltty();
49941903Sbde	/* XXX */
50088088Sjhb	critical_enter();
50172262Sjhb	COM_LOCK();
50241903Sbde	com->etc = ETC_NONE;
50341903Sbde	cd_setreg(com, CD1400_COR2, com->cor[1] &= ~CD1400_COR2_ETC);
50472262Sjhb	COM_UNLOCK();
50588088Sjhb	critical_exit();
506136139Sphk	cd_etc(com, CD1400_ETC_STOPBREAK);
50741903Sbde	cd1400_channel_cmd(com, CD1400_CCR_CMDRESET | CD1400_CCR_FTF);
5086261Sjkh
5099406Sbde	{
51088088Sjhb		critical_enter();
51172262Sjhb		COM_LOCK();
51241293Sbde		cd_setreg(com, CD1400_SRER, com->intr_enable = 0);
51372262Sjhb		COM_UNLOCK();
51488088Sjhb		critical_exit();
5159406Sbde		tp = com->tp;
51643314Sdillon		if ((tp->t_cflag & HUPCL)
5179406Sbde		    /*
5189406Sbde		     * XXX we will miss any carrier drop between here and the
5199406Sbde		     * next open.  Perhaps we should watch DCD even when the
5209406Sbde		     * port is closed; it is not sufficient to check it at
5219406Sbde		     * the next open because it might go up and down while
5229406Sbde		     * we're not watching.
5239406Sbde		     */
524136139Sphk		    || (!tp->t_actout
525136139Sphk		       && !(com->prev_modem_status & CD1400_MSVR2_CD)
526136139Sphk		       && !(tp->t_init_in.c_cflag & CLOCAL))
5279406Sbde		    || !(tp->t_state & TS_ISOPEN)) {
528136139Sphk			(void)cymodem(tp, 0, SER_DTR);
5296261Sjkh
5309406Sbde			/* Disable receiver (leave transmitter enabled). */
5319406Sbde			com->channel_control = CD1400_CCR_CMDCHANCTL
5329406Sbde					       | CD1400_CCR_XMTEN
5339406Sbde					       | CD1400_CCR_RCVDIS;
53441293Sbde			cd1400_channel_cmd(com, com->channel_control);
5356261Sjkh
536131981Sphk			ttydtrwaitstart(tp);
5379406Sbde		}
5389406Sbde	}
539136139Sphk	tp->t_actout = FALSE;
540136139Sphk	wakeup(&tp->t_actout);
5419406Sbde	wakeup(TSA_CARR_ON(tp));	/* restart any wopeners */
5429406Sbde	splx(s);
5439406Sbde}
5449406Sbde
54565557Sjasone/*
54672262Sjhb * This function:
54772262Sjhb *  a) needs to be called with COM_LOCK() held, and
54872262Sjhb *  b) needs to return with COM_LOCK() held.
54965557Sjasone */
55043611Sbdestatic void
551136139Sphkcyinput(struct com_s *com)
55243611Sbde{
55343611Sbde	u_char		*buf;
55443611Sbde	int		incc;
55543611Sbde	u_char		line_status;
55643611Sbde	int		recv_data;
55743611Sbde	struct tty	*tp;
55843611Sbde
55943611Sbde	buf = com->ibuf;
56043611Sbde	tp = com->tp;
56143611Sbde	if (!(tp->t_state & TS_ISOPEN)) {
562136139Sphk		cy_events -= (com->iptr - com->ibuf);
56343611Sbde		com->iptr = com->ibuf;
56443611Sbde		return;
56543611Sbde	}
56643611Sbde	if (tp->t_state & TS_CAN_BYPASS_L_RINT) {
56743611Sbde		/*
56843611Sbde		 * Avoid the grotesquely inefficient lineswitch routine
56943611Sbde		 * (ttyinput) in "raw" mode.  It usually takes about 450
57043611Sbde		 * instructions (that's without canonical processing or echo!).
57143611Sbde		 * slinput is reasonably fast (usually 40 instructions plus
57243611Sbde		 * call overhead).
57343611Sbde		 */
57465557Sjasone
57543611Sbde		do {
57665557Sjasone			/*
57765557Sjasone			 * This may look odd, but it is using save-and-enable
57865557Sjasone			 * semantics instead of the save-and-disable semantics
57965557Sjasone			 * that are used everywhere else.
58065557Sjasone			 */
58172262Sjhb			COM_UNLOCK();
58288088Sjhb			critical_exit();
58343611Sbde			incc = com->iptr - buf;
58443611Sbde			if (tp->t_rawq.c_cc + incc > tp->t_ihiwat
58543611Sbde			    && (com->state & CS_RTS_IFLOW
58643611Sbde				|| tp->t_iflag & IXOFF)
58743611Sbde			    && !(tp->t_state & TS_TBLOCK))
58843611Sbde				ttyblock(tp);
58943611Sbde			com->delta_error_counts[CE_TTY_BUF_OVERFLOW]
59043611Sbde				+= b_to_q((char *)buf, incc, &tp->t_rawq);
59143611Sbde			buf += incc;
59243611Sbde			tk_nin += incc;
59343611Sbde			tk_rawcc += incc;
59443611Sbde			tp->t_rawcc += incc;
59543611Sbde			ttwakeup(tp);
59643611Sbde			if (tp->t_state & TS_TTSTOP
59743611Sbde			    && (tp->t_iflag & IXANY
59843611Sbde				|| tp->t_cc[VSTART] == tp->t_cc[VSTOP])) {
59943611Sbde				tp->t_state &= ~TS_TTSTOP;
60043611Sbde				tp->t_lflag &= ~FLUSHO;
601136139Sphk				cystart(tp);
60243611Sbde			}
60388088Sjhb			critical_enter();
60472262Sjhb			COM_LOCK();
60543611Sbde		} while (buf < com->iptr);
60643611Sbde	} else {
60743611Sbde		do {
60865557Sjasone			/*
60965557Sjasone			 * This may look odd, but it is using save-and-enable
61065557Sjasone			 * semantics instead of the save-and-disable semantics
61165557Sjasone			 * that are used everywhere else.
61265557Sjasone			 */
61372262Sjhb			COM_UNLOCK();
61488088Sjhb			critical_exit();
61543611Sbde			line_status = buf[com->ierroff];
61643611Sbde			recv_data = *buf++;
61743611Sbde			if (line_status
618136139Sphk			    & (CD1400_RDSR_BREAK | CD1400_RDSR_FE | CD1400_RDSR_OE | CD1400_RDSR_PE)) {
619136139Sphk				if (line_status & CD1400_RDSR_BREAK)
62043611Sbde					recv_data |= TTY_BI;
621136139Sphk				if (line_status & CD1400_RDSR_FE)
62243611Sbde					recv_data |= TTY_FE;
623136139Sphk				if (line_status & CD1400_RDSR_OE)
62443611Sbde					recv_data |= TTY_OE;
625136139Sphk				if (line_status & CD1400_RDSR_PE)
62643611Sbde					recv_data |= TTY_PE;
62743611Sbde			}
628130077Sphk			ttyld_rint(tp, recv_data);
62988088Sjhb			critical_enter();
63072262Sjhb			COM_LOCK();
63143611Sbde		} while (buf < com->iptr);
63243611Sbde	}
633136139Sphk	cy_events -= (com->iptr - com->ibuf);
63443611Sbde	com->iptr = com->ibuf;
63543611Sbde
63643611Sbde	/*
63743611Sbde	 * There is now room for another low-level buffer full of input,
63843611Sbde	 * so enable RTS if it is now disabled and there is room in the
63943611Sbde	 * high-level buffer.
64043611Sbde	 */
64143611Sbde	if ((com->state & CS_RTS_IFLOW) && !(com->mcr_image & com->mcr_rts) &&
64243611Sbde	    !(tp->t_state & TS_TBLOCK))
64343611Sbde		cd_setreg(com, com->mcr_rts_reg,
64443611Sbde			  com->mcr_image |= com->mcr_rts);
64543611Sbde}
64643611Sbde
647166901Spisoint
648136139Sphkcyintr(void *vcom)
6496261Sjkh{
650123104Sbde	struct com_s	*basecom;
65129047Sbde	int	baseu;
65229047Sbde	int	cy_align;
65329047Sbde	cy_addr	cy_iobase;
65429047Sbde	int	cyu;
65529047Sbde	cy_addr	iobase;
65629047Sbde	u_char	status;
657123104Sbde	int	unit;
6586261Sjkh
65972262Sjhb	COM_LOCK();	/* XXX could this be placed down lower in the loop? */
66028921Sfsmp
661123104Sbde	basecom = (struct com_s *)vcom;
662123104Sbde	baseu = basecom->unit;
663123104Sbde	cy_align = basecom->cy_align;
664123104Sbde	cy_iobase = basecom->cy_iobase;
665123104Sbde	unit = baseu / CY_MAX_PORTS;
6666261Sjkh
6679406Sbde	/* check each CD1400 in turn */
66818685Sdg	for (cyu = 0; cyu < cy_nr_cd1400s[unit]; ++cyu) {
66929047Sbde		iobase = (cy_addr) (cy_iobase
67029047Sbde				    + (cy_chip_offset[cyu] << cy_align));
6719406Sbde		/* poll to see if it has any work */
67218901Sdg		status = cd_inb(iobase, CD1400_SVRR, cy_align);
6739406Sbde		if (status == 0)
674166901Spiso			continue; // XXX - FILTER_STRAY?
6756261Sjkh#ifdef CyDebug
6769406Sbde		++cy_svrr_probes;
6776261Sjkh#endif
6789406Sbde		/* service requests as appropriate, giving priority to RX */
6799406Sbde		if (status & CD1400_SVRR_RXRDY) {
6809406Sbde			struct com_s	*com;
6819406Sbde			u_int		count;
6829406Sbde			u_char		*ioptr;
6839406Sbde			u_char		line_status;
6849406Sbde			u_char		recv_data;
6859406Sbde			u_char		serv_type;
6869406Sbde#ifdef PollMode
6879406Sbde			u_char		save_rir;
6886261Sjkh#endif
6896261Sjkh
6909406Sbde#ifdef PollMode
69118901Sdg			save_rir = cd_inb(iobase, CD1400_RIR, cy_align);
6926261Sjkh
6939406Sbde			/* enter rx service */
69418901Sdg			cd_outb(iobase, CD1400_CAR, cy_align, save_rir);
695136139Sphk			cy_addr(baseu + cyu * CD1400_NO_OF_CHANNELS)->car
69641293Sbde			= save_rir & CD1400_CAR_CHAN;
6976261Sjkh
69818901Sdg			serv_type = cd_inb(iobase, CD1400_RIVR, cy_align);
699136139Sphk			com = cy_addr(baseu
7009406Sbde				       + ((serv_type >> CD1400_xIVR_CHAN_SHIFT)
7019406Sbde					  & CD1400_xIVR_CHAN));
7026261Sjkh#else
7039406Sbde			/* ack receive service */
70441309Sbde			serv_type = cy_inb(iobase, CY8_SVCACKR, cy_align);
7056261Sjkh
706136139Sphk			com = cy_addr(baseu +
7079406Sbde				       + ((serv_type >> CD1400_xIVR_CHAN_SHIFT)
7089406Sbde					  & CD1400_xIVR_CHAN));
7096261Sjkh#endif
7106261Sjkh
7119406Sbde		if (serv_type & CD1400_RIVR_EXCEPTION) {
7129406Sbde			++com->recv_exception;
71318901Sdg			line_status = cd_inb(iobase, CD1400_RDSR, cy_align);
7149406Sbde			/* break/unnattached error bits or real input? */
71518901Sdg			recv_data = cd_inb(iobase, CD1400_RDSR, cy_align);
7169406Sbde#ifndef SOFT_HOTCHAR
7179406Sbde			if (line_status & CD1400_RDSR_SPECIAL
718131134Sphk			    && com->tp->t_hotchar != 0)
719136139Sphk				swi_sched(cy_fast_ih, 0);
72067551Sjhb
7216261Sjkh#endif
7229406Sbde#if 1 /* XXX "intelligent" PFO error handling would break O error handling */
723136139Sphk			if (line_status & (CD1400_RDSR_PE|CD1400_RDSR_FE|CD1400_RDSR_BREAK)) {
7249406Sbde				/*
7259406Sbde				  Don't store PE if IGNPAR and BI if IGNBRK,
7269406Sbde				  this hack allows "raw" tty optimization
7279406Sbde				  works even if IGN* is set.
7289406Sbde				*/
7299406Sbde				if (   com->tp == NULL
7309406Sbde				    || !(com->tp->t_state & TS_ISOPEN)
731136139Sphk				    || ((line_status & (CD1400_RDSR_PE|CD1400_RDSR_FE))
73243314Sdillon				    &&  (com->tp->t_iflag & IGNPAR))
733136139Sphk				    || ((line_status & CD1400_RDSR_BREAK)
73443314Sdillon				    &&  (com->tp->t_iflag & IGNBRK)))
7359406Sbde					goto cont;
736136139Sphk				if (   (line_status & (CD1400_RDSR_PE|CD1400_RDSR_FE))
7379406Sbde				    && (com->tp->t_state & TS_CAN_BYPASS_L_RINT)
738136139Sphk				    && ((line_status & CD1400_RDSR_FE)
739136139Sphk				    ||  ((line_status & CD1400_RDSR_PE)
74043314Sdillon				    &&  (com->tp->t_iflag & INPCK))))
7419406Sbde					recv_data = 0;
7429406Sbde			}
7439406Sbde#endif /* 1 */
7449406Sbde			++com->bytes_in;
7459406Sbde#ifdef SOFT_HOTCHAR
746131134Sphk			if (com->tp->t_hotchar != 0 && recv_data == com->tp->t_hotchar)
747136139Sphk				swi_sched(cy_fast_ih, 0);
7486261Sjkh#endif
7499406Sbde			ioptr = com->iptr;
7509406Sbde			if (ioptr >= com->ibufend)
7519406Sbde				CE_RECORD(com, CE_INTERRUPT_BUF_OVERFLOW);
7529406Sbde			else {
753136139Sphk				if (com->tp != NULL && com->tp->t_do_timestamp)
754136139Sphk					microtime(&com->tp->t_timestamp);
755136139Sphk				++cy_events;
7569406Sbde				ioptr[0] = recv_data;
75743611Sbde				ioptr[com->ierroff] = line_status;
7589406Sbde				com->iptr = ++ioptr;
7599406Sbde				if (ioptr == com->ihighwater
76038303Sbde				    && com->state & CS_RTS_IFLOW)
76138303Sbde					cd_outb(iobase, com->mcr_rts_reg,
76238303Sbde						cy_align,
76338303Sbde						com->mcr_image &=
76438303Sbde						~com->mcr_rts);
765136139Sphk				if (line_status & CD1400_RDSR_OE)
7669406Sbde					CE_RECORD(com, CE_OVERRUN);
7679406Sbde			}
7689406Sbde			goto cont;
7699406Sbde		} else {
7709406Sbde			int	ifree;
7716261Sjkh
77218901Sdg			count = cd_inb(iobase, CD1400_RDCR, cy_align);
77334661Sdg			if (!count)
77434661Sdg				goto cont;
7759406Sbde			com->bytes_in += count;
7769406Sbde			ioptr = com->iptr;
7779406Sbde			ifree = com->ibufend - ioptr;
7789406Sbde			if (count > ifree) {
7799406Sbde				count -= ifree;
780136139Sphk				cy_events += ifree;
78117354Sbde				if (ifree != 0) {
782136139Sphk					if (com->tp != NULL && com->tp->t_do_timestamp)
783136139Sphk						microtime(&com->tp->t_timestamp);
78417354Sbde					do {
78517354Sbde						recv_data = cd_inb(iobase,
78629047Sbde								   CD1400_RDSR,
78729047Sbde								   cy_align);
7889406Sbde#ifdef SOFT_HOTCHAR
789131134Sphk						if (com->tp->t_hotchar != 0
79017354Sbde						    && recv_data
791131134Sphk						       == com->tp->t_hotchar)
792136139Sphk							swi_sched(cy_fast_ih,
793122799Sbde								  0);
7946261Sjkh#endif
79517354Sbde						ioptr[0] = recv_data;
79643611Sbde						ioptr[com->ierroff] = 0;
79717354Sbde						++ioptr;
79817354Sbde					} while (--ifree != 0);
7999406Sbde				}
8009406Sbde				com->delta_error_counts
8019406Sbde				    [CE_INTERRUPT_BUF_OVERFLOW] += count;
8029406Sbde				do {
80329047Sbde					recv_data = cd_inb(iobase, CD1400_RDSR,
80429047Sbde							   cy_align);
8059406Sbde#ifdef SOFT_HOTCHAR
806131134Sphk					if (com->tp->t_hotchar != 0
807131134Sphk					    && recv_data == com->tp->t_hotchar)
808136139Sphk						swi_sched(cy_fast_ih, 0);
8096261Sjkh#endif
8109406Sbde				} while (--count != 0);
8119406Sbde			} else {
812136139Sphk				if (com->tp != NULL && com->tp->t_do_timestamp)
813136139Sphk					microtime(&com->tp->t_timestamp);
8149406Sbde				if (ioptr <= com->ihighwater
8159406Sbde				    && ioptr + count > com->ihighwater
8169406Sbde				    && com->state & CS_RTS_IFLOW)
81738303Sbde					cd_outb(iobase, com->mcr_rts_reg,
81838303Sbde						cy_align,
81938303Sbde						com->mcr_image
82038303Sbde						&= ~com->mcr_rts);
821136139Sphk				cy_events += count;
8229406Sbde				do {
82329047Sbde					recv_data = cd_inb(iobase, CD1400_RDSR,
82429047Sbde							   cy_align);
8259406Sbde#ifdef SOFT_HOTCHAR
826131134Sphk					if (com->tp->t_hotchar != 0
827131134Sphk					    && recv_data == com->tp->t_hotchar)
828136139Sphk						swi_sched(cy_fast_ih, 0);
8296261Sjkh#endif
8309406Sbde					ioptr[0] = recv_data;
83143611Sbde					ioptr[com->ierroff] = 0;
8329406Sbde					++ioptr;
8339406Sbde				} while (--count != 0);
8346261Sjkh			}
8359406Sbde			com->iptr = ioptr;
8366261Sjkh		}
8379406Sbdecont:
8386261Sjkh
8399406Sbde			/* terminate service context */
8406261Sjkh#ifdef PollMode
84118901Sdg			cd_outb(iobase, CD1400_RIR, cy_align,
8429406Sbde				save_rir
8439406Sbde				& ~(CD1400_RIR_RDIREQ | CD1400_RIR_RBUSY));
8446261Sjkh#else
84518901Sdg			cd_outb(iobase, CD1400_EOSRR, cy_align, 0);
8466261Sjkh#endif
8479406Sbde		}
8489406Sbde		if (status & CD1400_SVRR_MDMCH) {
8499406Sbde			struct com_s	*com;
8509406Sbde			u_char	modem_status;
8516261Sjkh#ifdef PollMode
8529406Sbde			u_char	save_mir;
8536261Sjkh#else
8549406Sbde			u_char	vector;
8556261Sjkh#endif
8566261Sjkh
8576261Sjkh#ifdef PollMode
85818901Sdg			save_mir = cd_inb(iobase, CD1400_MIR, cy_align);
8596261Sjkh
8609406Sbde			/* enter modem service */
86118901Sdg			cd_outb(iobase, CD1400_CAR, cy_align, save_mir);
862136139Sphk			cy_addr(baseu + cyu * CD1400_NO_OF_CHANNELS)->car
86341293Sbde			= save_mir & CD1400_CAR_CHAN;
8646261Sjkh
865136139Sphk			com = cy_addr(baseu + cyu * CD1400_NO_OF_CHANNELS
8669406Sbde				       + (save_mir & CD1400_MIR_CHAN));
8676261Sjkh#else
8689406Sbde			/* ack modem service */
86941309Sbde			vector = cy_inb(iobase, CY8_SVCACKM, cy_align);
8706261Sjkh
871136139Sphk			com = cy_addr(baseu
8729406Sbde				       + ((vector >> CD1400_xIVR_CHAN_SHIFT)
8739406Sbde					  & CD1400_xIVR_CHAN));
8749406Sbde#endif
8759406Sbde			++com->mdm;
87618901Sdg			modem_status = cd_inb(iobase, CD1400_MSVR2, cy_align);
8779406Sbde		if (modem_status != com->last_modem_status) {
8789406Sbde			/*
8799406Sbde			 * Schedule high level to handle DCD changes.  Note
8809406Sbde			 * that we don't use the delta bits anywhere.  Some
8819406Sbde			 * UARTs mess them up, and it's easy to remember the
8829406Sbde			 * previous bits and calculate the delta.
8839406Sbde			 */
8849406Sbde			com->last_modem_status = modem_status;
8859406Sbde			if (!(com->state & CS_CHECKMSR)) {
886136139Sphk				cy_events += LOTS_OF_EVENTS;
8879406Sbde				com->state |= CS_CHECKMSR;
888136139Sphk				swi_sched(cy_fast_ih, 0);
8899406Sbde			}
8906261Sjkh
8919406Sbde#ifdef SOFT_CTS_OFLOW
8929406Sbde			/* handle CTS change immediately for crisp flow ctl */
8939406Sbde			if (com->state & CS_CTS_OFLOW) {
894136139Sphk				if (modem_status & CD1400_MSVR2_CTS) {
8959406Sbde					com->state |= CS_ODEVREADY;
8969406Sbde					if (com->state >= (CS_BUSY | CS_TTGO
8979406Sbde							   | CS_ODEVREADY)
8989406Sbde					    && !(com->intr_enable
8999406Sbde						 & CD1400_SRER_TXRDY))
90029047Sbde						cd_outb(iobase, CD1400_SRER,
90129047Sbde							cy_align,
9029406Sbde							com->intr_enable
90341388Sbde							= com->intr_enable
90441388Sbde							  & ~CD1400_SRER_TXMPTY
90541388Sbde							  | CD1400_SRER_TXRDY);
9069406Sbde				} else {
9079406Sbde					com->state &= ~CS_ODEVREADY;
90829047Sbde					if (com->intr_enable
90929047Sbde					    & CD1400_SRER_TXRDY)
91029047Sbde						cd_outb(iobase, CD1400_SRER,
91129047Sbde							cy_align,
9129406Sbde							com->intr_enable
91341388Sbde							= com->intr_enable
91441388Sbde							  & ~CD1400_SRER_TXRDY
91541388Sbde							  | CD1400_SRER_TXMPTY);
9169406Sbde				}
9179406Sbde			}
9186261Sjkh#endif
9199406Sbde		}
9206261Sjkh
9219406Sbde			/* terminate service context */
9226261Sjkh#ifdef PollMode
92318901Sdg			cd_outb(iobase, CD1400_MIR, cy_align,
9249406Sbde				save_mir
9259406Sbde				& ~(CD1400_MIR_RDIREQ | CD1400_MIR_RBUSY));
9266261Sjkh#else
92718901Sdg			cd_outb(iobase, CD1400_EOSRR, cy_align, 0);
9286261Sjkh#endif
9299406Sbde		}
9309406Sbde		if (status & CD1400_SVRR_TXRDY) {
9319406Sbde			struct com_s	*com;
9326261Sjkh#ifdef PollMode
9339406Sbde			u_char	save_tir;
9346261Sjkh#else
9359406Sbde			u_char	vector;
9366261Sjkh#endif
9376261Sjkh
9386261Sjkh#ifdef PollMode
93918901Sdg			save_tir = cd_inb(iobase, CD1400_TIR, cy_align);
9409406Sbde
9419406Sbde			/* enter tx service */
94218901Sdg			cd_outb(iobase, CD1400_CAR, cy_align, save_tir);
943136139Sphk			cy_addr(baseu + cyu * CD1400_NO_OF_CHANNELS)->car
94441293Sbde			= save_tir & CD1400_CAR_CHAN;
94541293Sbde
946136139Sphk			com = cy_addr(baseu
9479406Sbde				       + cyu * CD1400_NO_OF_CHANNELS
9489406Sbde				       + (save_tir & CD1400_TIR_CHAN));
9496261Sjkh#else
9509406Sbde			/* ack transmit service */
95141309Sbde			vector = cy_inb(iobase, CY8_SVCACKT, cy_align);
9526261Sjkh
953136139Sphk			com = cy_addr(baseu
9549406Sbde				       + ((vector >> CD1400_xIVR_CHAN_SHIFT)
9559406Sbde					  & CD1400_xIVR_CHAN));
9566261Sjkh#endif
9576261Sjkh
95841903Sbde			if (com->etc != ETC_NONE) {
95941903Sbde				if (com->intr_enable & CD1400_SRER_TXRDY) {
96041903Sbde					/*
96141903Sbde					 * Here due to sloppy SRER_TXRDY
96241903Sbde					 * enabling.  Ignore.  Come back when
96341903Sbde					 * tx is empty.
96441903Sbde					 */
96541903Sbde					cd_outb(iobase, CD1400_SRER, cy_align,
96641903Sbde						com->intr_enable
96743314Sdillon						= (com->intr_enable
96843314Sdillon						  & ~CD1400_SRER_TXRDY)
96941903Sbde						  | CD1400_SRER_TXMPTY);
97041903Sbde					goto terminate_tx_service;
97141903Sbde				}
97241903Sbde				switch (com->etc) {
97341903Sbde				case CD1400_ETC_SENDBREAK:
97441903Sbde				case CD1400_ETC_STOPBREAK:
97541903Sbde					/*
97641903Sbde					 * Start the command.  Come back on
97741903Sbde					 * next tx empty interrupt, hopefully
97841903Sbde					 * after command has been executed.
97941903Sbde					 */
98041903Sbde					cd_outb(iobase, CD1400_COR2, cy_align,
98141903Sbde						com->cor[1] |= CD1400_COR2_ETC);
98241903Sbde					cd_outb(iobase, CD1400_TDR, cy_align,
98341903Sbde						CD1400_ETC_CMD);
98441903Sbde					cd_outb(iobase, CD1400_TDR, cy_align,
98541903Sbde						com->etc);
98641903Sbde					if (com->etc == CD1400_ETC_SENDBREAK)
98741903Sbde						com->etc = ETC_BREAK_STARTING;
98841903Sbde					else
98941903Sbde						com->etc = ETC_BREAK_ENDING;
99041903Sbde					goto terminate_tx_service;
99141903Sbde				case ETC_BREAK_STARTING:
99241903Sbde					/*
99341903Sbde					 * BREAK is now on.  Continue with
99441903Sbde					 * SRER_TXMPTY processing, hopefully
99541903Sbde					 * don't come back.
99641903Sbde					 */
99741903Sbde					com->etc = ETC_BREAK_STARTED;
99841903Sbde					break;
99941903Sbde				case ETC_BREAK_STARTED:
100041903Sbde					/*
100141903Sbde					 * Came back due to sloppy SRER_TXMPTY
100241903Sbde					 * enabling.  Hope again.
100341903Sbde					 */
100441903Sbde					break;
100541903Sbde				case ETC_BREAK_ENDING:
100641903Sbde					/*
100741903Sbde					 * BREAK is now off.  Continue with
100841903Sbde					 * SRER_TXMPTY processing and don't
100941903Sbde					 * come back.  The SWI handler will
101041903Sbde					 * restart tx interrupts if necessary.
101141903Sbde					 */
101241903Sbde					cd_outb(iobase, CD1400_COR2, cy_align,
101341903Sbde						com->cor[1]
101441903Sbde						&= ~CD1400_COR2_ETC);
101541903Sbde					com->etc = ETC_BREAK_ENDED;
101641903Sbde					if (!(com->state & CS_ODONE)) {
1017136139Sphk						cy_events += LOTS_OF_EVENTS;
101841903Sbde						com->state |= CS_ODONE;
1019136139Sphk						swi_sched(cy_fast_ih, 0);
102041903Sbde					}
102141903Sbde					break;
102241903Sbde				case ETC_BREAK_ENDED:
102341903Sbde					/*
102441903Sbde					 * Shouldn't get here.  Hope again.
102541903Sbde					 */
102641903Sbde					break;
102741903Sbde				}
102841903Sbde			}
102941388Sbde			if (com->intr_enable & CD1400_SRER_TXMPTY) {
103041388Sbde				if (!(com->extra_state & CSE_ODONE)) {
1031136139Sphk					cy_events += LOTS_OF_EVENTS;
103241388Sbde					com->extra_state |= CSE_ODONE;
1033136139Sphk					swi_sched(cy_fast_ih, 0);
103441388Sbde				}
103541388Sbde				cd_outb(iobase, CD1400_SRER, cy_align,
103641388Sbde					com->intr_enable
103741388Sbde					&= ~CD1400_SRER_TXMPTY);
103841388Sbde				goto terminate_tx_service;
103941388Sbde			}
10409406Sbde		if (com->state >= (CS_BUSY | CS_TTGO | CS_ODEVREADY)) {
10419406Sbde			u_char	*ioptr;
10429406Sbde			u_int	ocount;
10436261Sjkh
10449406Sbde			ioptr = com->obufq.l_head;
10459406Sbde				ocount = com->obufq.l_tail - ioptr;
10469406Sbde				if (ocount > CD1400_TX_FIFO_SIZE)
10479406Sbde					ocount = CD1400_TX_FIFO_SIZE;
10489406Sbde				com->bytes_out += ocount;
10499406Sbde				do
105029047Sbde					cd_outb(iobase, CD1400_TDR, cy_align,
105129047Sbde						*ioptr++);
10529406Sbde				while (--ocount != 0);
10539406Sbde			com->obufq.l_head = ioptr;
10549406Sbde			if (ioptr >= com->obufq.l_tail) {
10559406Sbde				struct lbq	*qp;
10566261Sjkh
10579406Sbde				qp = com->obufq.l_next;
10589406Sbde				qp->l_queued = FALSE;
10599406Sbde				qp = qp->l_next;
10609406Sbde				if (qp != NULL) {
10619406Sbde					com->obufq.l_head = qp->l_head;
10629406Sbde					com->obufq.l_tail = qp->l_tail;
10639406Sbde					com->obufq.l_next = qp;
10649406Sbde				} else {
10659406Sbde					/* output just completed */
10669406Sbde					com->state &= ~CS_BUSY;
106741388Sbde
106841388Sbde					/*
106941388Sbde					 * The setting of CSE_ODONE may be
107041388Sbde					 * stale here.  We currently only
107141388Sbde					 * use it when CS_BUSY is set, and
107241388Sbde					 * fixing it when we clear CS_BUSY
107341388Sbde					 * is easiest.
107441388Sbde					 */
107541388Sbde					if (com->extra_state & CSE_ODONE) {
1076136139Sphk						cy_events -= LOTS_OF_EVENTS;
107741388Sbde						com->extra_state &= ~CSE_ODONE;
107841388Sbde					}
107941388Sbde
108018901Sdg					cd_outb(iobase, CD1400_SRER, cy_align,
10819406Sbde						com->intr_enable
108243314Sdillon						= (com->intr_enable
108343314Sdillon						  & ~CD1400_SRER_TXRDY)
108441388Sbde						  | CD1400_SRER_TXMPTY);
10859406Sbde				}
10869406Sbde				if (!(com->state & CS_ODONE)) {
1087136139Sphk					cy_events += LOTS_OF_EVENTS;
10889406Sbde					com->state |= CS_ODONE;
108929047Sbde
109029047Sbde					/* handle at high level ASAP */
1091136139Sphk					swi_sched(cy_fast_ih, 0);
10929406Sbde				}
10939406Sbde			}
10949406Sbde		}
10956261Sjkh
10969406Sbde			/* terminate service context */
109741388Sbdeterminate_tx_service:
10986261Sjkh#ifdef PollMode
109918901Sdg			cd_outb(iobase, CD1400_TIR, cy_align,
11009406Sbde				save_tir
11019406Sbde				& ~(CD1400_TIR_RDIREQ | CD1400_TIR_RBUSY));
11026261Sjkh#else
110318901Sdg			cd_outb(iobase, CD1400_EOSRR, cy_align, 0);
11046261Sjkh#endif
11059406Sbde		}
11066261Sjkh	}
11076261Sjkh
11089406Sbde	/* ensure an edge for the next interrupt */
110941309Sbde	cy_outb(cy_iobase, CY_CLEAR_INTR, cy_align, 0);
11106261Sjkh
1111136139Sphk	swi_sched(cy_slow_ih, SWI_DELAY);
111228921Sfsmp
111372262Sjhb	COM_UNLOCK();
1114166901Spiso	return (FILTER_HANDLED);
11159406Sbde}
11166261Sjkh
1117136139Sphkstatic void
1118136139Sphkcybreak(struct tty *tp, int sig)
11196261Sjkh{
11209406Sbde	struct com_s	*com;
11216261Sjkh
1122136139Sphk	com = tp->t_sc;
1123136139Sphk	if (sig)
112441903Sbde		cd_etc(com, CD1400_ETC_SENDBREAK);
1125136139Sphk	else
112641903Sbde		cd_etc(com, CD1400_ETC_STOPBREAK);
11279406Sbde}
11286261Sjkh
112938246Sbdestatic void
1130136139Sphkcypoll(void *arg)
11319406Sbde{
11329406Sbde	int		unit;
11336261Sjkh
11349406Sbde#ifdef CyDebug
11359406Sbde	++cy_timeouts;
11369406Sbde#endif
1137136139Sphk	if (cy_events == 0)
11389406Sbde		return;
11399406Sbderepeat:
1140136139Sphk	for (unit = 0; unit < NPORTS; ++unit) {
11419406Sbde		struct com_s	*com;
11429406Sbde		int		incc;
11439406Sbde		struct tty	*tp;
11446261Sjkh
1145136139Sphk		com = cy_addr(unit);
11469406Sbde		if (com == NULL)
11479406Sbde			continue;
11489406Sbde		tp = com->tp;
11499406Sbde		if (tp == NULL) {
11509406Sbde			/*
11519406Sbde			 * XXX forget any events related to closed devices
11529406Sbde			 * (actually never opened devices) so that we don't
11539406Sbde			 * loop.
11549406Sbde			 */
115588088Sjhb			critical_enter();
115672262Sjhb			COM_LOCK();
11579406Sbde			incc = com->iptr - com->ibuf;
11589406Sbde			com->iptr = com->ibuf;
11599406Sbde			if (com->state & CS_CHECKMSR) {
11609406Sbde				incc += LOTS_OF_EVENTS;
11619406Sbde				com->state &= ~CS_CHECKMSR;
11629406Sbde			}
1163136139Sphk			cy_events -= incc;
116472262Sjhb			COM_UNLOCK();
116588088Sjhb			critical_exit();
11669406Sbde			if (incc != 0)
11679406Sbde				log(LOG_DEBUG,
1168136139Sphk				    "cy%d: %d events for device with no tp\n",
11699406Sbde				    unit, incc);
11709406Sbde			continue;
11719406Sbde		}
117243611Sbde		if (com->iptr != com->ibuf) {
117388088Sjhb			critical_enter();
117472262Sjhb			COM_LOCK();
1175136139Sphk			cyinput(com);
117672262Sjhb			COM_UNLOCK();
117788088Sjhb			critical_exit();
11789406Sbde		}
11799406Sbde		if (com->state & CS_CHECKMSR) {
11809406Sbde			u_char	delta_modem_status;
11816261Sjkh
118288088Sjhb			critical_enter();
118372262Sjhb			COM_LOCK();
1184136139Sphk			cyinput(com);
11859406Sbde			delta_modem_status = com->last_modem_status
11869406Sbde					     ^ com->prev_modem_status;
11879406Sbde			com->prev_modem_status = com->last_modem_status;
1188136139Sphk			cy_events -= LOTS_OF_EVENTS;
11899406Sbde			com->state &= ~CS_CHECKMSR;
119072262Sjhb			COM_UNLOCK();
119188088Sjhb			critical_exit();
1192136139Sphk			if (delta_modem_status & CD1400_MSVR2_CD)
1193130095Sphk				ttyld_modem(tp,
1194136139Sphk				    com->prev_modem_status & CD1400_MSVR2_CD);
11959406Sbde		}
119641388Sbde		if (com->extra_state & CSE_ODONE) {
119788088Sjhb			critical_enter();
119872262Sjhb			COM_LOCK();
1199136139Sphk			cy_events -= LOTS_OF_EVENTS;
120041388Sbde			com->extra_state &= ~CSE_ODONE;
120172262Sjhb			COM_UNLOCK();
120288088Sjhb			critical_exit();
120341388Sbde			if (!(com->state & CS_BUSY)) {
120441388Sbde				tp->t_state &= ~TS_BUSY;
120541388Sbde				ttwwakeup(com->tp);
120641388Sbde			}
120741903Sbde			if (com->etc != ETC_NONE) {
120841903Sbde				if (com->etc == ETC_BREAK_ENDED)
120941903Sbde					com->etc = ETC_NONE;
121041903Sbde				wakeup(&com->etc);
121141903Sbde			}
121241388Sbde		}
121341903Sbde		if (com->state & CS_ODONE) {
121488088Sjhb			critical_enter();
121572262Sjhb			COM_LOCK();
1216136139Sphk			cy_events -= LOTS_OF_EVENTS;
121741903Sbde			com->state &= ~CS_ODONE;
121872262Sjhb			COM_UNLOCK();
121988088Sjhb			critical_exit();
1220130077Sphk			ttyld_start(tp);
122141903Sbde		}
1222136139Sphk		if (cy_events == 0)
12239406Sbde			break;
12249406Sbde	}
1225136139Sphk	if (cy_events >= LOTS_OF_EVENTS)
12269406Sbde		goto repeat;
12279406Sbde}
12286261Sjkh
12299406Sbdestatic int
1230136139Sphkcyparam(struct tty *tp, struct termios *t)
12319406Sbde{
12329406Sbde	int		bits;
12339406Sbde	int		cflag;
12349406Sbde	struct com_s	*com;
12359406Sbde	u_char		cor_change;
123638303Sbde	u_long		cy_clock;
12379406Sbde	int		idivisor;
12389406Sbde	int		iflag;
12399406Sbde	int		iprescaler;
12409406Sbde	int		itimeout;
12419406Sbde	int		odivisor;
12429406Sbde	int		oprescaler;
12439406Sbde	u_char		opt;
12449406Sbde	int		s;
12456261Sjkh
1246136139Sphk	com = tp->t_sc;
124738302Sbde
12489406Sbde	/* check requested parameters */
124938303Sbde	cy_clock = CY_CLOCK(com->gfrcr_image);
1250136139Sphk	idivisor = cyspeed(t->c_ispeed, cy_clock, &iprescaler);
1251120512Sbde	if (idivisor <= 0)
12529406Sbde		return (EINVAL);
1253136139Sphk	odivisor = cyspeed(t->c_ospeed != 0 ? t->c_ospeed : tp->t_ospeed,
1254120512Sbde			    cy_clock, &oprescaler);
1255120512Sbde	if (odivisor <= 0)
12569406Sbde		return (EINVAL);
12576261Sjkh
12589406Sbde	/* parameters are OK, convert them to the com struct and the device */
125938303Sbde	s = spltty();
1260120512Sbde	if (t->c_ospeed == 0)
1261136139Sphk		(void)cymodem(tp, 0, SER_DTR);
12629406Sbde	else
1263136139Sphk		(void)cymodem(tp, SER_DTR, 0);
12646261Sjkh
1265136139Sphk	(void) cysetwater(com, t->c_ispeed);
126643611Sbde
126743611Sbde	/* XXX we don't actually change the speed atomically. */
126843611Sbde
1269120512Sbde	cd_setreg(com, CD1400_RBPR, idivisor);
1270120512Sbde	cd_setreg(com, CD1400_RCOR, iprescaler);
1271120512Sbde	cd_setreg(com, CD1400_TBPR, odivisor);
1272120512Sbde	cd_setreg(com, CD1400_TCOR, oprescaler);
12736261Sjkh
12746261Sjkh	/*
12756261Sjkh	 * channel control
12766261Sjkh	 *	receiver enable
12776261Sjkh	 *	transmitter enable (always set)
12786261Sjkh	 */
12799406Sbde	cflag = t->c_cflag;
12809406Sbde	opt = CD1400_CCR_CMDCHANCTL | CD1400_CCR_XMTEN
12819406Sbde	      | (cflag & CREAD ? CD1400_CCR_RCVEN : CD1400_CCR_RCVDIS);
12829406Sbde	if (opt != com->channel_control) {
12839406Sbde		com->channel_control = opt;
128441293Sbde		cd1400_channel_cmd(com, opt);
12856261Sjkh	}
12866261Sjkh
12876261Sjkh#ifdef Smarts
12886261Sjkh	/* set special chars */
12899406Sbde	/* XXX if one is _POSIX_VDISABLE, can't use some others */
12909406Sbde	if (t->c_cc[VSTOP] != _POSIX_VDISABLE)
129141293Sbde		cd_setreg(com, CD1400_SCHR1, t->c_cc[VSTOP]);
12929406Sbde	if (t->c_cc[VSTART] != _POSIX_VDISABLE)
129341293Sbde		cd_setreg(com, CD1400_SCHR2, t->c_cc[VSTART]);
12949406Sbde	if (t->c_cc[VINTR] != _POSIX_VDISABLE)
129541293Sbde		cd_setreg(com, CD1400_SCHR3, t->c_cc[VINTR]);
12969406Sbde	if (t->c_cc[VSUSP] != _POSIX_VDISABLE)
129741293Sbde		cd_setreg(com, CD1400_SCHR4, t->c_cc[VSUSP]);
12986261Sjkh#endif
12996261Sjkh
13006261Sjkh	/*
13016261Sjkh	 * set channel option register 1 -
13026261Sjkh	 *	parity mode
13036261Sjkh	 *	stop bits
13046261Sjkh	 *	char length
13056261Sjkh	 */
13066261Sjkh	opt = 0;
13076261Sjkh	/* parity */
13086261Sjkh	if (cflag & PARENB) {
13099406Sbde		if (cflag & PARODD)
13109406Sbde			opt |= CD1400_COR1_PARODD;
13119406Sbde		opt |= CD1400_COR1_PARNORMAL;
13126261Sjkh	}
13139406Sbde	iflag = t->c_iflag;
13146261Sjkh	if (!(iflag & INPCK))
13159406Sbde		opt |= CD1400_COR1_NOINPCK;
13169406Sbde	bits = 1 + 1;
13176261Sjkh	/* stop bits */
13189406Sbde	if (cflag & CSTOPB) {
13199406Sbde		++bits;
13209406Sbde		opt |= CD1400_COR1_STOP2;
13219406Sbde	}
13226261Sjkh	/* char length */
13239406Sbde	switch (cflag & CSIZE) {
13249406Sbde	case CS5:
13259406Sbde		bits += 5;
13269406Sbde		opt |= CD1400_COR1_CS5;
13279406Sbde		break;
13289406Sbde	case CS6:
13299406Sbde		bits += 6;
13309406Sbde		opt |= CD1400_COR1_CS6;
13319406Sbde		break;
13329406Sbde	case CS7:
13339406Sbde		bits += 7;
13349406Sbde		opt |= CD1400_COR1_CS7;
13359406Sbde		break;
13369406Sbde	default:
13379406Sbde		bits += 8;
13389406Sbde		opt |= CD1400_COR1_CS8;
13399406Sbde		break;
13406261Sjkh	}
13419406Sbde	cor_change = 0;
13429406Sbde	if (opt != com->cor[0]) {
13439406Sbde		cor_change |= CD1400_CCR_COR1;
134441293Sbde		cd_setreg(com, CD1400_COR1, com->cor[0] = opt);
13459406Sbde	}
13466261Sjkh
13476261Sjkh	/*
13489406Sbde	 * Set receive time-out period, normally to max(one char time, 5 ms).
13499406Sbde	 */
1350120512Sbde	itimeout = (1000 * bits + t->c_ispeed - 1) / t->c_ispeed;
13519406Sbde#ifdef SOFT_HOTCHAR
13529406Sbde#define	MIN_RTP		1
13539406Sbde#else
13549406Sbde#define	MIN_RTP		5
13559406Sbde#endif
1356120512Sbde	if (itimeout < MIN_RTP)
1357120512Sbde		itimeout = MIN_RTP;
13589406Sbde	if (!(t->c_lflag & ICANON) && t->c_cc[VMIN] != 0 && t->c_cc[VTIME] != 0
13599406Sbde	    && t->c_cc[VTIME] * 10 > itimeout)
13609406Sbde		itimeout = t->c_cc[VTIME] * 10;
13619406Sbde	if (itimeout > 255)
13629406Sbde		itimeout = 255;
136341293Sbde	cd_setreg(com, CD1400_RTPR, itimeout);
13649406Sbde
13659406Sbde	/*
13666261Sjkh	 * set channel option register 2 -
13676261Sjkh	 *	flow control
13686261Sjkh	 */
13696261Sjkh	opt = 0;
13706261Sjkh#ifdef Smarts
13716261Sjkh	if (iflag & IXANY)
13729406Sbde		opt |= CD1400_COR2_IXANY;
13736261Sjkh	if (iflag & IXOFF)
13749406Sbde		opt |= CD1400_COR2_IXOFF;
13756261Sjkh#endif
13769406Sbde#ifndef SOFT_CTS_OFLOW
13776261Sjkh	if (cflag & CCTS_OFLOW)
13789406Sbde		opt |= CD1400_COR2_CCTS_OFLOW;
13796261Sjkh#endif
138088088Sjhb	critical_enter();
138172262Sjhb	COM_LOCK();
13829406Sbde	if (opt != com->cor[1]) {
13839406Sbde		cor_change |= CD1400_CCR_COR2;
138441293Sbde		cd_setreg(com, CD1400_COR2, com->cor[1] = opt);
13856261Sjkh	}
138672262Sjhb	COM_UNLOCK();
138788088Sjhb	critical_exit();
13886261Sjkh
13896261Sjkh	/*
13906261Sjkh	 * set channel option register 3 -
13916261Sjkh	 *	receiver FIFO interrupt threshold
13926261Sjkh	 *	flow control
13936261Sjkh	 */
13949406Sbde	opt = RxFifoThreshold;
13956261Sjkh#ifdef Smarts
13966261Sjkh	if (t->c_lflag & ICANON)
13979406Sbde		opt |= CD1400_COR3_SCD34;	/* detect INTR & SUSP chars */
13986261Sjkh	if (iflag & IXOFF)
13999406Sbde		/* detect and transparently handle START and STOP chars */
14009406Sbde		opt |= CD1400_COR3_FCT | CD1400_COR3_SCD12;
14016261Sjkh#endif
14029406Sbde	if (opt != com->cor[2]) {
14039406Sbde		cor_change |= CD1400_CCR_COR3;
140441293Sbde		cd_setreg(com, CD1400_COR3, com->cor[2] = opt);
14056261Sjkh	}
14066261Sjkh
14076261Sjkh	/* notify the CD1400 if COR1-3 have changed */
14089406Sbde	if (cor_change)
140941293Sbde		cd1400_channel_cmd(com, CD1400_CCR_CMDCORCHG | cor_change);
14106261Sjkh
14116261Sjkh	/*
14126261Sjkh	 * set channel option register 4 -
14136261Sjkh	 *	CR/NL processing
14146261Sjkh	 *	break processing
14156261Sjkh	 *	received exception processing
14166261Sjkh	 */
14176261Sjkh	opt = 0;
14186261Sjkh	if (iflag & IGNCR)
14199406Sbde		opt |= CD1400_COR4_IGNCR;
14206261Sjkh#ifdef Smarts
14216261Sjkh	/*
14226261Sjkh	 * we need a new ttyinput() for this, as we don't want to
14236261Sjkh	 * have ICRNL && INLCR being done in both layers, or to have
14246261Sjkh	 * synchronisation problems
14256261Sjkh	 */
14266261Sjkh	if (iflag & ICRNL)
14279406Sbde		opt |= CD1400_COR4_ICRNL;
14286261Sjkh	if (iflag & INLCR)
14299406Sbde		opt |= CD1400_COR4_INLCR;
14306261Sjkh#endif
14316261Sjkh	if (iflag & IGNBRK)
143241905Sbde		opt |= CD1400_COR4_IGNBRK | CD1400_COR4_NOBRKINT;
143341905Sbde	/*
143441905Sbde	 * The `-ignbrk -brkint parmrk' case is not handled by the hardware,
143541905Sbde	 * so only tell the hardware about -brkint if -parmrk.
143641905Sbde	 */
143741905Sbde	if (!(iflag & (BRKINT | PARMRK)))
14389406Sbde		opt |= CD1400_COR4_NOBRKINT;
14399406Sbde#if 0
14409406Sbde	/* XXX using this "intelligence" breaks reporting of overruns. */
14416261Sjkh	if (iflag & IGNPAR)
14429406Sbde		opt |= CD1400_COR4_PFO_DISCARD;
14436261Sjkh	else {
14449406Sbde		if (iflag & PARMRK)
14459406Sbde			opt |= CD1400_COR4_PFO_ESC;
14469406Sbde		else
14479406Sbde			opt |= CD1400_COR4_PFO_NUL;
14489406Sbde	}
14496261Sjkh#else
14509406Sbde	opt |= CD1400_COR4_PFO_EXCEPTION;
14516261Sjkh#endif
145241293Sbde	cd_setreg(com, CD1400_COR4, opt);
14536261Sjkh
14546261Sjkh	/*
14556261Sjkh	 * set channel option register 5 -
14566261Sjkh	 */
14576261Sjkh	opt = 0;
14586261Sjkh	if (iflag & ISTRIP)
14599406Sbde		opt |= CD1400_COR5_ISTRIP;
14609406Sbde	if (t->c_iflag & IEXTEN)
14619406Sbde		/* enable LNEXT (e.g. ctrl-v quoting) handling */
14629406Sbde		opt |= CD1400_COR5_LNEXT;
14636261Sjkh#ifdef Smarts
14646261Sjkh	if (t->c_oflag & ONLCR)
14659406Sbde		opt |= CD1400_COR5_ONLCR;
14666261Sjkh	if (t->c_oflag & OCRNL)
14679406Sbde		opt |= CD1400_COR5_OCRNL;
14686261Sjkh#endif
146941293Sbde	cd_setreg(com, CD1400_COR5, opt);
14706261Sjkh
14716261Sjkh	/*
147220152Sbde	 * We always generate modem status change interrupts for CD changes.
147320152Sbde	 * Among other things, this is necessary to track TS_CARR_ON for
147420152Sbde	 * pstat to print even when the driver doesn't care.  CD changes
147520152Sbde	 * should be rare so interrupts for them are not worth extra code to
147620152Sbde	 * avoid.  We avoid interrupts for other modem status changes (except
147720152Sbde	 * for CTS changes when SOFT_CTS_OFLOW is configured) since this is
147820152Sbde	 * simplest and best.
14799406Sbde	 */
148020152Sbde
14819406Sbde	/*
14826261Sjkh	 * set modem change option register 1
14836261Sjkh	 *	generate modem interrupts on which 1 -> 0 input transitions
14846261Sjkh	 *	also controls auto-DTR output flow-control, which we don't use
14856261Sjkh	 */
148620152Sbde	opt = CD1400_MCOR1_CDzd;
14879406Sbde#ifdef SOFT_CTS_OFLOW
14889406Sbde	if (cflag & CCTS_OFLOW)
14899406Sbde		opt |= CD1400_MCOR1_CTSzd;
14909406Sbde#endif
149141293Sbde	cd_setreg(com, CD1400_MCOR1, opt);
14926261Sjkh
14936261Sjkh	/*
14946261Sjkh	 * set modem change option register 2
14956261Sjkh	 *	generate modem interrupts on specific 0 -> 1 input transitions
14966261Sjkh	 */
149720152Sbde	opt = CD1400_MCOR2_CDod;
14989406Sbde#ifdef SOFT_CTS_OFLOW
14999406Sbde	if (cflag & CCTS_OFLOW)
15009406Sbde		opt |= CD1400_MCOR2_CTSod;
15019406Sbde#endif
150241293Sbde	cd_setreg(com, CD1400_MCOR2, opt);
15036261Sjkh
15049406Sbde	/*
15059406Sbde	 * XXX should have done this long ago, but there is too much state
15069406Sbde	 * to change all atomically.
15079406Sbde	 */
150888088Sjhb	critical_enter();
150972262Sjhb	COM_LOCK();
15106261Sjkh
15119406Sbde	com->state &= ~CS_TTGO;
15129406Sbde	if (!(tp->t_state & TS_TTSTOP))
15139406Sbde		com->state |= CS_TTGO;
151419718Sbde	if (cflag & CRTS_IFLOW) {
151519718Sbde		com->state |= CS_RTS_IFLOW;
151619718Sbde		/*
151719718Sbde		 * If CS_RTS_IFLOW just changed from off to on, the change
1518136139Sphk		 * needs to be propagated to CD1400_MSVR1_RTS.  This isn't urgent,
1519136139Sphk		 * so do it later by calling cystart() instead of repeating
1520136139Sphk		 * a lot of code from cystart() here.
152119718Sbde		 */
152219718Sbde	} else if (com->state & CS_RTS_IFLOW) {
15239406Sbde		com->state &= ~CS_RTS_IFLOW;
152419718Sbde		/*
1525136139Sphk		 * CS_RTS_IFLOW just changed from on to off.  Force CD1400_MSVR1_RTS
1526136139Sphk		 * on here, since cystart() won't do it later.
152719718Sbde		 */
152841293Sbde		cd_setreg(com, com->mcr_rts_reg,
152941293Sbde			  com->mcr_image |= com->mcr_rts);
153019718Sbde	}
15316261Sjkh
15329406Sbde	/*
15339406Sbde	 * Set up state to handle output flow control.
15349406Sbde	 * XXX - worth handling MDMBUF (DCD) flow control at the lowest level?
15359406Sbde	 * Now has 10+ msec latency, while CTS flow has 50- usec latency.
15369406Sbde	 */
15379406Sbde	com->state |= CS_ODEVREADY;
15389406Sbde#ifdef SOFT_CTS_OFLOW
15399406Sbde	com->state &= ~CS_CTS_OFLOW;
15409406Sbde	if (cflag & CCTS_OFLOW) {
15419406Sbde		com->state |= CS_CTS_OFLOW;
1542136139Sphk		if (!(com->last_modem_status & CD1400_MSVR2_CTS))
15439406Sbde			com->state &= ~CS_ODEVREADY;
15449406Sbde	}
15459406Sbde#endif
15469406Sbde	/* XXX shouldn't call functions while intrs are disabled. */
15479406Sbde	disc_optim(tp, t, com);
15489406Sbde#if 0
15499406Sbde	/*
1550136139Sphk	 * Recover from fiddling with CS_TTGO.  We used to call cyintr1()
15519406Sbde	 * unconditionally, but that defeated the careful discarding of
1552136139Sphk	 * stale input in cyopen().
15539406Sbde	 */
15549406Sbde	if (com->state >= (CS_BUSY | CS_TTGO))
1555136139Sphk		cyintr1(com);
15569406Sbde#endif
15579406Sbde	if (com->state >= (CS_BUSY | CS_TTGO | CS_ODEVREADY)) {
15589406Sbde		if (!(com->intr_enable & CD1400_SRER_TXRDY))
155941293Sbde			cd_setreg(com, CD1400_SRER,
156041388Sbde				  com->intr_enable
156143314Sdillon				  = (com->intr_enable & ~CD1400_SRER_TXMPTY)
156241388Sbde				    | CD1400_SRER_TXRDY);
15639406Sbde	} else {
15649406Sbde		if (com->intr_enable & CD1400_SRER_TXRDY)
156541293Sbde			cd_setreg(com, CD1400_SRER,
156641388Sbde				  com->intr_enable
156743314Sdillon				  = (com->intr_enable & ~CD1400_SRER_TXRDY)
156841388Sbde				    | CD1400_SRER_TXMPTY);
15699406Sbde	}
15706261Sjkh
157172262Sjhb	COM_UNLOCK();
157288088Sjhb	critical_exit();
15739406Sbde	splx(s);
1574136139Sphk	cystart(tp);
157543611Sbde	if (com->ibufold != NULL) {
157643611Sbde		free(com->ibufold, M_DEVBUF);
157743611Sbde		com->ibufold = NULL;
157843611Sbde	}
15799406Sbde	return (0);
15809406Sbde}
15819406Sbde
158243611Sbdestatic int
1583136139Sphkcysetwater(struct com_s *com, speed_t speed)
158443611Sbde{
158543611Sbde	int		cp4ticks;
158643611Sbde	u_char		*ibuf;
158743611Sbde	int		ibufsize;
158843611Sbde	struct tty	*tp;
158943611Sbde
159043611Sbde	/*
159143611Sbde	 * Make the buffer size large enough to handle a softtty interrupt
159243611Sbde	 * latency of about 2 ticks without loss of throughput or data
159343611Sbde	 * (about 3 ticks if input flow control is not used or not honoured,
159443611Sbde	 * but a bit less for CS5-CS7 modes).
159543611Sbde	 */
159643611Sbde	cp4ticks = speed / 10 / hz * 4;
159743611Sbde	for (ibufsize = 128; ibufsize < cp4ticks;)
159843611Sbde		ibufsize <<= 1;
159943611Sbde	if (ibufsize == com->ibufsize) {
160043611Sbde		return (0);
160143611Sbde	}
160243611Sbde
160343611Sbde	/*
160443611Sbde	 * Allocate input buffer.  The extra factor of 2 in the size is
160543611Sbde	 * to allow for an error byte for each input byte.
160643611Sbde	 */
160743611Sbde	ibuf = malloc(2 * ibufsize, M_DEVBUF, M_NOWAIT);
160843611Sbde	if (ibuf == NULL) {
160943611Sbde		return (ENOMEM);
161043611Sbde	}
161143611Sbde
161243611Sbde	/* Initialize non-critical variables. */
161343611Sbde	com->ibufold = com->ibuf;
161443611Sbde	com->ibufsize = ibufsize;
161543611Sbde	tp = com->tp;
161643611Sbde	if (tp != NULL) {
161743611Sbde		tp->t_ififosize = 2 * ibufsize;
161843611Sbde		tp->t_ispeedwat = (speed_t)-1;
161943611Sbde		tp->t_ospeedwat = (speed_t)-1;
162043611Sbde	}
162143611Sbde
162243611Sbde	/*
162343611Sbde	 * Read current input buffer, if any.  Continue with interrupts
162443611Sbde	 * disabled.
162543611Sbde	 */
162688088Sjhb	critical_enter();
162772262Sjhb	COM_LOCK();
162843611Sbde	if (com->iptr != com->ibuf)
1629136139Sphk		cyinput(com);
163043611Sbde
163143611Sbde	/*-
163243611Sbde	 * Initialize critical variables, including input buffer watermarks.
163343611Sbde	 * The external device is asked to stop sending when the buffer
163443611Sbde	 * exactly reaches high water, or when the high level requests it.
163543611Sbde	 * The high level is notified immediately (rather than at a later
163643611Sbde	 * clock tick) when this watermark is reached.
163743611Sbde	 * The buffer size is chosen so the watermark should almost never
163843611Sbde	 * be reached.
163943611Sbde	 * The low watermark is invisibly 0 since the buffer is always
164043611Sbde	 * emptied all at once.
164143611Sbde	 */
164243611Sbde	com->iptr = com->ibuf = ibuf;
164343611Sbde	com->ibufend = ibuf + ibufsize;
164443611Sbde	com->ierroff = ibufsize;
164543611Sbde	com->ihighwater = ibuf + 3 * ibufsize / 4;
164665557Sjasone
164772262Sjhb	COM_UNLOCK();
164888088Sjhb	critical_exit();
164943611Sbde	return (0);
165043611Sbde}
165143611Sbde
16529406Sbdestatic void
1653136139Sphkcystart(struct tty *tp)
16546261Sjkh{
16559406Sbde	struct com_s	*com;
16566261Sjkh	int		s;
16579406Sbde#ifdef CyDebug
16589406Sbde	bool_t		started;
16599406Sbde#endif
16606261Sjkh
1661136139Sphk	com = tp->t_sc;
16629406Sbde	s = spltty();
16639406Sbde
16646261Sjkh#ifdef CyDebug
16659406Sbde	++com->start_count;
16669406Sbde	started = FALSE;
16676261Sjkh#endif
16686261Sjkh
166988088Sjhb	critical_enter();
167072262Sjhb	COM_LOCK();
16719406Sbde	if (tp->t_state & TS_TTSTOP) {
16729406Sbde		com->state &= ~CS_TTGO;
16739406Sbde		if (com->intr_enable & CD1400_SRER_TXRDY)
167441293Sbde			cd_setreg(com, CD1400_SRER,
167541388Sbde				  com->intr_enable
167643314Sdillon				  = (com->intr_enable & ~CD1400_SRER_TXRDY)
167741388Sbde				    | CD1400_SRER_TXMPTY);
16789406Sbde	} else {
16799406Sbde		com->state |= CS_TTGO;
16809406Sbde		if (com->state >= (CS_BUSY | CS_TTGO | CS_ODEVREADY)
16819406Sbde		    && !(com->intr_enable & CD1400_SRER_TXRDY))
168241293Sbde			cd_setreg(com, CD1400_SRER,
168341388Sbde				  com->intr_enable
168443314Sdillon				  = (com->intr_enable & ~CD1400_SRER_TXMPTY)
168541388Sbde				    | CD1400_SRER_TXRDY);
16869406Sbde	}
16879406Sbde	if (tp->t_state & TS_TBLOCK) {
168838303Sbde		if (com->mcr_image & com->mcr_rts && com->state & CS_RTS_IFLOW)
16899406Sbde#if 0
1690136139Sphk			outb(com->modem_ctl_port, com->mcr_image &= ~CD1400_MSVR1_RTS);
16919406Sbde#else
169241293Sbde			cd_setreg(com, com->mcr_rts_reg,
169341293Sbde				  com->mcr_image &= ~com->mcr_rts);
16949406Sbde#endif
16959406Sbde	} else {
169638303Sbde		if (!(com->mcr_image & com->mcr_rts)
169738302Sbde		    && com->iptr < com->ihighwater
169819718Sbde		    && com->state & CS_RTS_IFLOW)
16999406Sbde#if 0
1700136139Sphk			outb(com->modem_ctl_port, com->mcr_image |= CD1400_MSVR1_RTS);
17019406Sbde#else
170241293Sbde			cd_setreg(com, com->mcr_rts_reg,
170341293Sbde				  com->mcr_image |= com->mcr_rts);
17049406Sbde#endif
17059406Sbde	}
170672262Sjhb	COM_UNLOCK();
170788088Sjhb	critical_exit();
17089406Sbde	if (tp->t_state & (TS_TIMEOUT | TS_TTSTOP)) {
170932045Sbde		ttwwakeup(tp);
17109406Sbde		splx(s);
17116261Sjkh		return;
17129406Sbde	}
17139406Sbde	if (tp->t_outq.c_cc != 0) {
17149406Sbde		struct lbq	*qp;
17159406Sbde		struct lbq	*next;
17166261Sjkh
17179406Sbde		if (!com->obufs[0].l_queued) {
17189406Sbde#ifdef CyDebug
17199406Sbde			started = TRUE;
17209406Sbde#endif
17219406Sbde			com->obufs[0].l_tail
17229406Sbde			    = com->obuf1 + q_to_b(&tp->t_outq, com->obuf1,
17239406Sbde						  sizeof com->obuf1);
17249406Sbde			com->obufs[0].l_next = NULL;
17259406Sbde			com->obufs[0].l_queued = TRUE;
172688088Sjhb			critical_enter();
172772262Sjhb			COM_LOCK();
17289406Sbde			if (com->state & CS_BUSY) {
17299406Sbde				qp = com->obufq.l_next;
17309406Sbde				while ((next = qp->l_next) != NULL)
17319406Sbde					qp = next;
17329406Sbde				qp->l_next = &com->obufs[0];
17339406Sbde			} else {
17349406Sbde				com->obufq.l_head = com->obufs[0].l_head;
17359406Sbde				com->obufq.l_tail = com->obufs[0].l_tail;
17369406Sbde				com->obufq.l_next = &com->obufs[0];
17379406Sbde				com->state |= CS_BUSY;
17389406Sbde				if (com->state >= (CS_BUSY | CS_TTGO
17399406Sbde						   | CS_ODEVREADY))
174041293Sbde					cd_setreg(com, CD1400_SRER,
174141293Sbde						  com->intr_enable
174243314Sdillon						  = (com->intr_enable
174343314Sdillon						    & ~CD1400_SRER_TXMPTY)
174441388Sbde						    | CD1400_SRER_TXRDY);
17459406Sbde			}
174672262Sjhb			COM_UNLOCK();
174788088Sjhb			critical_exit();
17489406Sbde		}
17499406Sbde		if (tp->t_outq.c_cc != 0 && !com->obufs[1].l_queued) {
17509406Sbde#ifdef CyDebug
17519406Sbde			started = TRUE;
17529406Sbde#endif
17539406Sbde			com->obufs[1].l_tail
17549406Sbde			    = com->obuf2 + q_to_b(&tp->t_outq, com->obuf2,
17559406Sbde						  sizeof com->obuf2);
17569406Sbde			com->obufs[1].l_next = NULL;
17579406Sbde			com->obufs[1].l_queued = TRUE;
175888088Sjhb			critical_enter();
175972262Sjhb			COM_LOCK();
17609406Sbde			if (com->state & CS_BUSY) {
17619406Sbde				qp = com->obufq.l_next;
17629406Sbde				while ((next = qp->l_next) != NULL)
17639406Sbde					qp = next;
17649406Sbde				qp->l_next = &com->obufs[1];
17659406Sbde			} else {
17669406Sbde				com->obufq.l_head = com->obufs[1].l_head;
17679406Sbde				com->obufq.l_tail = com->obufs[1].l_tail;
17689406Sbde				com->obufq.l_next = &com->obufs[1];
17699406Sbde				com->state |= CS_BUSY;
17709406Sbde				if (com->state >= (CS_BUSY | CS_TTGO
17719406Sbde						   | CS_ODEVREADY))
177241293Sbde					cd_setreg(com, CD1400_SRER,
177341293Sbde						  com->intr_enable
177443314Sdillon						  = (com->intr_enable
177543314Sdillon						    & ~CD1400_SRER_TXMPTY)
177641388Sbde						    | CD1400_SRER_TXRDY);
17779406Sbde			}
177872262Sjhb			COM_UNLOCK();
177988088Sjhb			critical_exit();
17809406Sbde		}
17819406Sbde		tp->t_state |= TS_BUSY;
17829406Sbde	}
17839406Sbde#ifdef CyDebug
17849406Sbde	if (started)
17859406Sbde		++com->start_real;
17869406Sbde#endif
17879406Sbde#if 0
178888088Sjhb	critical_enter();
178972262Sjhb	COM_LOCK();
179012962Sbde	if (com->state >= (CS_BUSY | CS_TTGO))
1791136139Sphk		cyintr1(com);	/* fake interrupt to start output */
179272262Sjhb	COM_UNLOCK();
179388088Sjhb	critical_exit();
17949406Sbde#endif
17959626Sbde	ttwwakeup(tp);
17969406Sbde	splx(s);
17979406Sbde}
17986261Sjkh
179912675Sjulianstatic void
1800136139Sphkcomstop(struct tty *tp, int rw)
18019406Sbde{
18029406Sbde	struct com_s	*com;
180341903Sbde	bool_t		wakeup_etc;
18046261Sjkh
1805136139Sphk	com = tp->t_sc;
180641903Sbde	wakeup_etc = FALSE;
180788088Sjhb	critical_enter();
180872262Sjhb	COM_LOCK();
18099406Sbde	if (rw & FWRITE) {
18109406Sbde		com->obufs[0].l_queued = FALSE;
18119406Sbde		com->obufs[1].l_queued = FALSE;
181241388Sbde		if (com->extra_state & CSE_ODONE) {
1813136139Sphk			cy_events -= LOTS_OF_EVENTS;
181441388Sbde			com->extra_state &= ~CSE_ODONE;
181541903Sbde			if (com->etc != ETC_NONE) {
181641903Sbde				if (com->etc == ETC_BREAK_ENDED)
181741903Sbde					com->etc = ETC_NONE;
181841903Sbde				wakeup_etc = TRUE;
181941903Sbde			}
182041388Sbde		}
18219406Sbde		com->tp->t_state &= ~TS_BUSY;
182241903Sbde		if (com->state & CS_ODONE)
1823136139Sphk			cy_events -= LOTS_OF_EVENTS;
182441903Sbde		com->state &= ~(CS_ODONE | CS_BUSY);
18259406Sbde	}
18269406Sbde	if (rw & FREAD) {
182741908Sbde		/* XXX no way to reset only input fifo. */
1828136139Sphk		cy_events -= (com->iptr - com->ibuf);
18299406Sbde		com->iptr = com->ibuf;
18309406Sbde	}
183172262Sjhb	COM_UNLOCK();
183288088Sjhb	critical_exit();
183341903Sbde	if (wakeup_etc)
183441903Sbde		wakeup(&com->etc);
183541908Sbde	if (rw & FWRITE && com->etc == ETC_NONE)
183641908Sbde		cd1400_channel_cmd(com, CD1400_CCR_CMDRESET | CD1400_CCR_FTF);
1837136139Sphk	cystart(tp);
18389406Sbde}
18399406Sbde
18409406Sbdestatic int
1841136139Sphkcymodem(struct tty *tp, int sigon, int sigoff)
1842136139Sphk{
18439406Sbde	struct com_s	*com;
18449406Sbde	int	mcr;
18459406Sbde	int	msr;
18469406Sbde
1847136139Sphk	com = tp->t_sc;
1848136139Sphk	if (sigon == 0 && sigoff == 0) {
1849136139Sphk		sigon = 0;
18509406Sbde		mcr = com->mcr_image;
185138303Sbde		if (mcr & com->mcr_dtr)
1852136139Sphk			sigon |= SER_DTR;
185338303Sbde		if (mcr & com->mcr_rts)
18549406Sbde			/* XXX wired on for Cyclom-8Ys */
1855136139Sphk			sigon |= SER_RTS;
185620152Sbde
185720152Sbde		/*
185820152Sbde		 * We must read the modem status from the hardware because
185920152Sbde		 * we don't generate modem status change interrupts for all
186020152Sbde		 * changes, so com->prev_modem_status is not guaranteed to
186120152Sbde		 * be up to date.  This is safe, unlike for sio, because
186220152Sbde		 * reading the status register doesn't clear pending modem
186320152Sbde		 * status change interrupts.
186420152Sbde		 */
186541293Sbde		msr = cd_getreg(com, CD1400_MSVR2);
186620152Sbde
1867136139Sphk		if (msr & CD1400_MSVR2_CTS)
1868136139Sphk			sigon |= SER_CTS;
1869136139Sphk		if (msr & CD1400_MSVR2_CD)
1870136139Sphk			sigon |= SER_DCD;
1871136139Sphk		if (msr & CD1400_MSVR2_DSR)
1872136139Sphk			sigon |= SER_DSR;
1873136139Sphk		if (msr & CD1400_MSVR2_RI)
18749406Sbde			/* XXX not connected except for Cyclom-16Y? */
1875136139Sphk			sigon |= SER_RI;
1876136139Sphk		return (sigon);
18776261Sjkh	}
1878136139Sphk	mcr = com->mcr_image;
1879136139Sphk	if (sigon & SER_DTR)
188038303Sbde		mcr |= com->mcr_dtr;
1881136139Sphk	if (sigoff & SER_DTR)
1882136139Sphk		mcr &= ~com->mcr_dtr;
1883136139Sphk	if (sigon & SER_RTS)
188438303Sbde		mcr |= com->mcr_rts;
1885136139Sphk	if (sigoff & SER_RTS)
1886136139Sphk		mcr &= ~com->mcr_rts;
188788088Sjhb	critical_enter();
188872262Sjhb	COM_LOCK();
1889136139Sphk	com->mcr_image = mcr;
1890136139Sphk	cd_setreg(com, CD1400_MSVR1, mcr);
1891136139Sphk	cd_setreg(com, CD1400_MSVR2, mcr);
189272262Sjhb	COM_UNLOCK();
189388088Sjhb	critical_exit();
18949406Sbde	return (0);
18959406Sbde}
18966261Sjkh
18979406Sbdestatic void
1898136139Sphkcysettimeout()
18999406Sbde{
19009406Sbde	struct com_s	*com;
19019406Sbde	bool_t		someopen;
19029406Sbde	int		unit;
19036261Sjkh
19049406Sbde	/*
19059406Sbde	 * Set our timeout period to 1 second if no polled devices are open.
19069406Sbde	 * Otherwise set it to max(1/200, 1/hz).
19079406Sbde	 * Enable timeouts iff some device is open.
19089406Sbde	 */
1909136139Sphk	untimeout(cywakeup, (void *)NULL, cy_timeout_handle);
1910136139Sphk	cy_timeout = hz;
19119406Sbde	someopen = FALSE;
1912136139Sphk	for (unit = 0; unit < NPORTS; ++unit) {
1913136139Sphk		com = cy_addr(unit);
19149406Sbde		if (com != NULL && com->tp != NULL
19159406Sbde		    && com->tp->t_state & TS_ISOPEN) {
19169406Sbde			someopen = TRUE;
19179406Sbde		}
19189406Sbde	}
19199406Sbde	if (someopen) {
1920136139Sphk		cy_timeouts_until_log = hz / cy_timeout;
1921136139Sphk		cy_timeout_handle = timeout(cywakeup, (void *)NULL,
1922136139Sphk					     cy_timeout);
19239406Sbde	} else {
19249406Sbde		/* Flush error messages, if any. */
1925136139Sphk		cy_timeouts_until_log = 1;
1926136139Sphk		cywakeup((void *)NULL);
1927136139Sphk		untimeout(cywakeup, (void *)NULL, cy_timeout_handle);
19289406Sbde	}
19299406Sbde}
19306261Sjkh
19319406Sbdestatic void
1932136139Sphkcywakeup(void *chan)
19336261Sjkh{
19349406Sbde	struct com_s	*com;
19359406Sbde	int		unit;
19366261Sjkh
1937136139Sphk	cy_timeout_handle = timeout(cywakeup, (void *)NULL, cy_timeout);
19386261Sjkh
19399406Sbde	/*
19409406Sbde	 * Check for and log errors, but not too often.
19419406Sbde	 */
1942136139Sphk	if (--cy_timeouts_until_log > 0)
19439406Sbde		return;
1944136139Sphk	cy_timeouts_until_log = hz / cy_timeout;
1945136139Sphk	for (unit = 0; unit < NPORTS; ++unit) {
19469406Sbde		int	errnum;
19476261Sjkh
1948136139Sphk		com = cy_addr(unit);
19499406Sbde		if (com == NULL)
19509406Sbde			continue;
19519406Sbde		for (errnum = 0; errnum < CE_NTYPES; ++errnum) {
19529406Sbde			u_int	delta;
19539406Sbde			u_long	total;
19546261Sjkh
195588088Sjhb			critical_enter();
195672262Sjhb			COM_LOCK();
19579406Sbde			delta = com->delta_error_counts[errnum];
19589406Sbde			com->delta_error_counts[errnum] = 0;
195972262Sjhb			COM_UNLOCK();
196088088Sjhb			critical_exit();
19619406Sbde			if (delta == 0)
19629406Sbde				continue;
19639406Sbde			total = com->error_counts[errnum] += delta;
19649406Sbde			log(LOG_ERR, "cy%d: %u more %s%s (total %lu)\n",
19659406Sbde			    unit, delta, error_desc[errnum],
19669406Sbde			    delta == 1 ? "" : "s", total);
19679406Sbde		}
19689406Sbde	}
19696261Sjkh}
19706261Sjkh
19719406Sbdestatic void
1972136139Sphkdisc_optim(struct tty *tp, struct termios *t, struct com_s *com)
19736712Spst{
19749406Sbde#ifndef SOFT_HOTCHAR
19759406Sbde	u_char	opt;
19769406Sbde#endif
19776261Sjkh
1978131134Sphk	ttyldoptim(tp);
19799406Sbde#ifndef SOFT_HOTCHAR
19809406Sbde	opt = com->cor[2] & ~CD1400_COR3_SCD34;
1981131134Sphk	if (com->tp->t_hotchar != 0) {
1982131134Sphk		cd_setreg(com, CD1400_SCHR3, com->tp->t_hotchar);
1983131134Sphk		cd_setreg(com, CD1400_SCHR4, com->tp->t_hotchar);
19849406Sbde		opt |= CD1400_COR3_SCD34;
19859406Sbde	}
19869406Sbde	if (opt != com->cor[2]) {
198741293Sbde		cd_setreg(com, CD1400_COR3, com->cor[2] = opt);
198841293Sbde		cd1400_channel_cmd(com, CD1400_CCR_CMDCORCHG | CD1400_CCR_COR3);
19899406Sbde	}
19909406Sbde#endif
19916712Spst}
19926712Spst
19939406Sbde#ifdef Smarts
19949406Sbde/* standard line discipline input routine */
19956261Sjkhint
1996136139Sphkcyinput(int c, struct tty *tp)
19976261Sjkh{
19989406Sbde	/* XXX duplicate ttyinput(), but without the IXOFF/IXON/ISTRIP/IPARMRK
19999406Sbde	 * bits, as they are done by the CD1400.  Hardly worth the effort,
2000136139Sphk	 * given that high-throughput session are raw anyhow.
20019406Sbde	 */
20029406Sbde}
20039406Sbde#endif /* Smarts */
20046261Sjkh
20059406Sbdestatic int
2006136139Sphkcyspeed(speed_t speed, u_long cy_clock, int *prescaler_io)
20079406Sbde{
20089406Sbde	int	actual;
20099406Sbde	int	error;
20109406Sbde	int	divider;
20119406Sbde	int	prescaler;
20129406Sbde	int	prescaler_unit;
20136261Sjkh
20149406Sbde	if (speed == 0)
20159406Sbde		return (0);
20169406Sbde	if (speed < 0 || speed > 150000)
20179406Sbde		return (-1);
20186261Sjkh
20199406Sbde	/* determine which prescaler to use */
20209406Sbde	for (prescaler_unit = 4, prescaler = 2048; prescaler_unit;
20219406Sbde		prescaler_unit--, prescaler >>= 2) {
202238303Sbde		if (cy_clock / prescaler / speed > 63)
20239406Sbde			break;
20249406Sbde	}
20256261Sjkh
202638303Sbde	divider = (cy_clock / prescaler * 2 / speed + 1) / 2; /* round off */
20279406Sbde	if (divider > 255)
20289406Sbde		divider = 255;
202938303Sbde	actual = cy_clock/prescaler/divider;
20306261Sjkh
203137959Sbde	/* 10 times error in percent: */
203237959Sbde	error = ((actual - (long)speed) * 2000 / (long)speed + 1) / 2;
203337959Sbde
20349406Sbde	/* 3.0% max error tolerance */
20359406Sbde	if (error < -30 || error > 30)
20369406Sbde		return (-1);
20376261Sjkh
20389406Sbde	*prescaler_io = prescaler_unit;
20399406Sbde	return (divider);
20409406Sbde}
20416261Sjkh
20426261Sjkhstatic void
2043136139Sphkcd1400_channel_cmd(struct com_s *com, int cmd)
20446261Sjkh{
204541940Sbde	cd1400_channel_cmd_wait(com);
204641940Sbde	cd_setreg(com, CD1400_CCR, cmd);
204741940Sbde	cd1400_channel_cmd_wait(com);
204841940Sbde}
20496261Sjkh
205041940Sbdestatic void
2051136139Sphkcd1400_channel_cmd_wait(struct com_s *com)
205241940Sbde{
205341940Sbde	struct timeval	start;
205441940Sbde	struct timeval	tv;
205541940Sbde	long		usec;
20568876Srgrimes
205741940Sbde	if (cd_getreg(com, CD1400_CCR) == 0)
205841940Sbde		return;
205941940Sbde	microtime(&start);
206041940Sbde	for (;;) {
206141940Sbde		if (cd_getreg(com, CD1400_CCR) == 0)
206241940Sbde			return;
206341940Sbde		microtime(&tv);
206441940Sbde		usec = 1000000 * (tv.tv_sec - start.tv_sec) +
206541940Sbde		    tv.tv_usec - start.tv_usec;
206641940Sbde		if (usec >= 5000) {
206741940Sbde			log(LOG_ERR,
206841940Sbde			    "cy%d: channel command timeout (%ld usec)\n",
206941940Sbde			    com->unit, usec);
207041940Sbde			return;
207141940Sbde		}
207241940Sbde	}
20739406Sbde}
20746261Sjkh
207541903Sbdestatic void
2076136139Sphkcd_etc(struct com_s *com, int etc)
207741903Sbde{
207865557Sjasone
207941903Sbde	/*
208041903Sbde	 * We can't change the hardware's ETC state while there are any
208141903Sbde	 * characters in the tx fifo, since those characters would be
208241903Sbde	 * interpreted as commands!  Unputting characters from the fifo
208341903Sbde	 * is difficult, so we wait up to 12 character times for the fifo
208441903Sbde	 * to drain.  The command will be delayed for up to 2 character
208541903Sbde	 * times for the tx to become empty.  Unputting characters from
208641903Sbde	 * the tx holding and shift registers is impossible, so we wait
208741903Sbde	 * for the tx to become empty so that the command is sure to be
208841903Sbde	 * executed soon after we issue it.
208941903Sbde	 */
209088088Sjhb	critical_enter();
209172262Sjhb	COM_LOCK();
209265557Sjasone	if (com->etc == etc)
209341903Sbde		goto wait;
209443314Sdillon	if ((etc == CD1400_ETC_SENDBREAK
209541903Sbde	    && (com->etc == ETC_BREAK_STARTING
209643314Sdillon		|| com->etc == ETC_BREAK_STARTED))
209743314Sdillon	    || (etc == CD1400_ETC_STOPBREAK
209841903Sbde	       && (com->etc == ETC_BREAK_ENDING || com->etc == ETC_BREAK_ENDED
209943314Sdillon		   || com->etc == ETC_NONE))) {
210072262Sjhb		COM_UNLOCK();
210188088Sjhb		critical_exit();
210241903Sbde		return;
210341903Sbde	}
210441903Sbde	com->etc = etc;
210541903Sbde	cd_setreg(com, CD1400_SRER,
210641903Sbde		  com->intr_enable
210743314Sdillon		  = (com->intr_enable & ~CD1400_SRER_TXRDY) | CD1400_SRER_TXMPTY);
210841903Sbdewait:
210972262Sjhb	COM_UNLOCK();
211088088Sjhb	critical_exit();
211141903Sbde	while (com->etc == etc
211241903Sbde	       && tsleep(&com->etc, TTIPRI | PCATCH, "cyetc", 0) == 0)
211341903Sbde		continue;
211441903Sbde}
211541903Sbde
211641293Sbdestatic int
2117136139Sphkcd_getreg(struct com_s *com, int reg)
211841293Sbde{
211941293Sbde	struct com_s	*basecom;
212041293Sbde	u_char	car;
212141293Sbde	int	cy_align;
212241293Sbde	cy_addr	iobase;
2123120504Sbde#ifdef SMP
2124120504Sbde	int	need_unlock;
2125120504Sbde#endif
212641293Sbde	int	val;
212741293Sbde
2128136139Sphk	basecom = cy_addr(com->unit & ~(CD1400_NO_OF_CHANNELS - 1));
212941293Sbde	car = com->unit & CD1400_CAR_CHAN;
213041293Sbde	cy_align = com->cy_align;
213141293Sbde	iobase = com->iobase;
213288088Sjhb	critical_enter();
2133120504Sbde#ifdef SMP
2134120504Sbde	need_unlock = 0;
2135136139Sphk	if (!mtx_owned(&cy_lock)) {
213672262Sjhb		COM_LOCK();
2137120504Sbde		need_unlock = 1;
2138120504Sbde	}
2139120504Sbde#endif
214041293Sbde	if (basecom->car != car)
214141293Sbde		cd_outb(iobase, CD1400_CAR, cy_align, basecom->car = car);
214241293Sbde	val = cd_inb(iobase, reg, cy_align);
2143120504Sbde#ifdef SMP
2144120504Sbde	if (need_unlock)
214572262Sjhb		COM_UNLOCK();
2146120504Sbde#endif
214788088Sjhb	critical_exit();
214841293Sbde	return (val);
214941293Sbde}
215041293Sbde
215141293Sbdestatic void
2152136139Sphkcd_setreg(struct com_s *com, int reg, int val)
215341293Sbde{
215441293Sbde	struct com_s	*basecom;
215541293Sbde	u_char	car;
215641293Sbde	int	cy_align;
215741293Sbde	cy_addr	iobase;
2158120504Sbde#ifdef SMP
2159120504Sbde	int	need_unlock;
2160120504Sbde#endif
216141293Sbde
2162136139Sphk	basecom = cy_addr(com->unit & ~(CD1400_NO_OF_CHANNELS - 1));
216341293Sbde	car = com->unit & CD1400_CAR_CHAN;
216441293Sbde	cy_align = com->cy_align;
216541293Sbde	iobase = com->iobase;
216688088Sjhb	critical_enter();
2167120504Sbde#ifdef SMP
2168120504Sbde	need_unlock = 0;
2169136139Sphk	if (!mtx_owned(&cy_lock)) {
217072262Sjhb		COM_LOCK();
2171120504Sbde		need_unlock = 1;
2172120504Sbde	}
2173120504Sbde#endif
217441293Sbde	if (basecom->car != car)
217541293Sbde		cd_outb(iobase, CD1400_CAR, cy_align, basecom->car = car);
217641293Sbde	cd_outb(iobase, reg, cy_align, val);
2177120504Sbde#ifdef SMP
2178120504Sbde	if (need_unlock)
217972262Sjhb		COM_UNLOCK();
2180120504Sbde#endif
218188088Sjhb	critical_exit();
218241293Sbde}
218341293Sbde
21846261Sjkh#ifdef CyDebug
21856261Sjkh/* useful in ddb */
21866261Sjkhvoid
2187136139Sphkcystatus(int unit)
21886261Sjkh{
21899406Sbde	struct com_s	*com;
21909406Sbde	cy_addr		iobase;
21919406Sbde	u_int		ocount;
21929406Sbde	struct tty	*tp;
21936261Sjkh
2194136139Sphk	com = cy_addr(unit);
21956261Sjkh	printf("info for channel %d\n", unit);
21966261Sjkh	printf("------------------\n");
21979406Sbde	printf("total cyclom service probes:\t%d\n", cy_svrr_probes);
21989406Sbde	printf("calls to upper layer:\t\t%d\n", cy_timeouts);
21999406Sbde	if (com == NULL)
22009406Sbde		return;
22019406Sbde	iobase = com->iobase;
22026261Sjkh	printf("\n");
22039406Sbde	printf("cd1400 base address:\\tt%p\n", iobase);
22049406Sbde	printf("saved channel_control:\t\t0x%02x\n", com->channel_control);
22059406Sbde	printf("saved cor1-3:\t\t\t0x%02x 0x%02x 0x%02x\n",
22069406Sbde	       com->cor[0], com->cor[1], com->cor[2]);
22079406Sbde	printf("service request enable reg:\t0x%02x (0x%02x cached)\n",
220841293Sbde	       cd_getreg(com, CD1400_SRER), com->intr_enable);
22099406Sbde	printf("service request register:\t0x%02x\n",
221018925Sdg	       cd_inb(iobase, CD1400_SVRR, com->cy_align));
22119406Sbde	printf("modem status:\t\t\t0x%02x (0x%02x cached)\n",
221241293Sbde	       cd_getreg(com, CD1400_MSVR2), com->prev_modem_status);
22139406Sbde	printf("rx/tx/mdm interrupt registers:\t0x%02x 0x%02x 0x%02x\n",
221418925Sdg	       cd_inb(iobase, CD1400_RIR, com->cy_align),
221518925Sdg	       cd_inb(iobase, CD1400_TIR, com->cy_align),
221618925Sdg	       cd_inb(iobase, CD1400_MIR, com->cy_align));
22176261Sjkh	printf("\n");
22189406Sbde	printf("com state:\t\t\t0x%02x\n", com->state);
2219136139Sphk	printf("calls to cystart():\t\t%d (%d useful)\n",
22209406Sbde	       com->start_count, com->start_real);
22219406Sbde	printf("rx buffer chars free:\t\t%d\n", com->iptr - com->ibuf);
22229406Sbde	ocount = 0;
22239406Sbde	if (com->obufs[0].l_queued)
22249406Sbde		ocount += com->obufs[0].l_tail - com->obufs[0].l_head;
22259406Sbde	if (com->obufs[1].l_queued)
22269406Sbde		ocount += com->obufs[1].l_tail - com->obufs[1].l_head;
22279406Sbde	printf("tx buffer chars:\t\t%u\n", ocount);
22289406Sbde	printf("received chars:\t\t\t%d\n", com->bytes_in);
22299406Sbde	printf("received exceptions:\t\t%d\n", com->recv_exception);
22309406Sbde	printf("modem signal deltas:\t\t%d\n", com->mdm);
22319406Sbde	printf("transmitted chars:\t\t%d\n", com->bytes_out);
22329406Sbde	printf("\n");
22339406Sbde	tp = com->tp;
22349406Sbde	if (tp != NULL) {
22359406Sbde		printf("tty state:\t\t\t0x%08x\n", tp->t_state);
223629047Sbde		printf(
223729047Sbde		"upper layer queue lengths:\t%d raw, %d canon, %d output\n",
22389406Sbde		       tp->t_rawq.c_cc, tp->t_canq.c_cc, tp->t_outq.c_cc);
22399406Sbde	} else
22406261Sjkh		printf("tty state:\t\t\tclosed\n");
22419406Sbde}
22429406Sbde#endif /* CyDebug */
2243