1139749Simp/*-
28471Sache * Copyright (C) 1995 by Pavel Antonov, Moscow, Russia.
38471Sache * Copyright (C) 1995 by Andrey A. Chernov, Moscow, Russia.
4367457Sdim * All rights reserved.
5105806Sjhb * Copyright (C) 2002 by John Baldwin <jhb@FreeBSD.org>
68471Sache *
78471Sache * Redistribution and use in source and binary forms, with or without
88471Sache * modification, are permitted provided that the following conditions
98471Sache * are met:
108471Sache * 1. Redistributions of source code must retain the above copyright
118471Sache *    notice, this list of conditions and the following disclaimer.
128471Sache * 2. Redistributions in binary form must reproduce the above copyright
138471Sache *    notice, this list of conditions and the following disclaimer in the
148471Sache *    documentation and/or other materials provided with the distribution.
158471Sache *
168471Sache * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND
178471Sache * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
188471Sache * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
198471Sache * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
208471Sache * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
218471Sache * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
228471Sache * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
238471Sache * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
248471Sache * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
258471Sache * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
268471Sache * SUCH DAMAGE.
2751654Sphk *
2851654Sphk * $FreeBSD: stable/11/sys/dev/rc/rc.c 367457 2020-11-07 18:10:59Z dim $
298471Sache */
308471Sache
318471Sache/*
328471Sache * SDL Communications Riscom/8 (based on Cirrus Logic CL-CD180) driver
338471Sache *
348471Sache */
358471Sache
369232Sache/*#define RCDEBUG*/
378471Sache
38111899Sdas#include "opt_tty.h"
39111899Sdas
408471Sache#include <sys/param.h>
418471Sache#include <sys/systm.h>
42105806Sjhb#include <sys/bus.h>
438471Sache#include <sys/conf.h>
4424131Sbde#include <sys/fcntl.h>
4538246Sbde#include <sys/interrupt.h>
46105806Sjhb#include <sys/kernel.h>
47105806Sjhb#include <sys/malloc.h>
48129879Sphk#include <sys/module.h>
49131096Sphk#include <sys/serial.h>
50105806Sjhb#include <sys/tty.h>
51105806Sjhb#include <machine/bus.h>
52105806Sjhb#include <machine/resource.h>
53105806Sjhb#include <sys/rman.h>
5466824Sbde
55105806Sjhb#include <dev/ic/cd180.h>
56105806Sjhb#include <dev/rc/rcreg.h>
57105806Sjhb#include <isa/isavar.h>
588471Sache
59105806Sjhb#define	IOBASE_ADDRS	14
608471Sache
61105806Sjhb#define rcin(sc, port)		RC_IN(sc, port)
62105806Sjhb#define rcout(sc, port, v)	RC_OUT(sc, port, v)
638471Sache
64105806Sjhb#define WAITFORCCR(sc, chan)	rc_wait0((sc), (chan), __LINE__)
658471Sache
66105806Sjhb#define CCRCMD(sc, chan, cmd) do {					\
67105806Sjhb	WAITFORCCR((sc), (chan));					\
68105806Sjhb	rcout((sc), CD180_CCR, (cmd));					\
69105806Sjhb} while (0)
708471Sache
719232Sache#define RC_IBUFSIZE     256
729232Sache#define RB_I_HIGH_WATER (TTYHOG - 2 * RC_IBUFSIZE)
739232Sache#define RC_OBUFSIZE     512
748471Sache#define RC_IHIGHWATER   (3 * RC_IBUFSIZE / 4)
758471Sache#define INPUT_FLAGS_SHIFT (2 * RC_IBUFSIZE)
768471Sache#define LOTS_OF_EVENTS  64
778471Sache
788471Sache#define RC_FAKEID       0x10
798471Sache
80105806Sjhb/* Per-channel structure */
81105806Sjhbstruct rc_chans  {
82105806Sjhb	struct rc_softc *rc_rcb;                /* back ptr             */
83105806Sjhb	u_short          rc_flags;              /* Misc. flags          */
84105806Sjhb	int              rc_chan;               /* Channel #            */
85105806Sjhb	u_char           rc_ier;                /* intr. enable reg     */
86105806Sjhb	u_char           rc_msvr;               /* modem sig. status    */
87105806Sjhb	u_char           rc_cor2;               /* options reg          */
88105806Sjhb	u_char           rc_pendcmd;            /* special cmd pending  */
89105806Sjhb	u_int            rc_dcdwaits;           /* how many waits DCD in open */
90136210Sphk	struct tty      *rc_tp;                 /* tty struct           */
91105806Sjhb	u_char          *rc_iptr;               /* Chars input buffer         */
92105806Sjhb	u_char          *rc_hiwat;              /* hi-water mark        */
93105806Sjhb	u_char          *rc_bufend;             /* end of buffer        */
94105806Sjhb	u_char          *rc_optr;               /* ptr in output buf    */
95105806Sjhb	u_char          *rc_obufend;            /* end of output buf    */
96105806Sjhb	u_char           rc_ibuf[4 * RC_IBUFSIZE];  /* input buffer         */
97105806Sjhb	u_char           rc_obuf[RC_OBUFSIZE];  /* output buffer        */
98105806Sjhb	struct callout	 rc_dtrcallout;
99105806Sjhb};
1008471Sache
101105806Sjhb/* Per-board structure */
102105806Sjhbstruct rc_softc {
103105806Sjhb	device_t	 sc_dev;
104105806Sjhb	struct resource *sc_irq;
105105806Sjhb	struct resource *sc_port[IOBASE_ADDRS];
106105806Sjhb	int		 sc_irqrid;
107105806Sjhb	void		*sc_hwicookie;
108105806Sjhb	bus_space_tag_t  sc_bt;
109105806Sjhb	bus_space_handle_t sc_bh;
110105806Sjhb	u_int            sc_unit;       /* unit #               */
111105806Sjhb	u_char           sc_dtr;        /* DTR status           */
112105806Sjhb	int		 sc_scheduled_event;
113105806Sjhb	void		*sc_swicookie;
114105806Sjhb	struct rc_chans  sc_channels[CD180_NCHAN]; /* channels */
1158471Sache};
1168471Sache
117105806Sjhb/* Static prototypes */
118136210Sphkstatic t_close_t rc_close;
119131373Sphkstatic void rc_break(struct tty *, int);
120105806Sjhbstatic void rc_release_resources(device_t dev);
121105806Sjhbstatic void rc_intr(void *);
122105806Sjhbstatic void rc_hwreset(struct rc_softc *, unsigned int);
123105806Sjhbstatic int  rc_test(struct rc_softc *);
124105806Sjhbstatic void rc_discard_output(struct rc_chans *);
125131096Sphkstatic int  rc_modem(struct tty *, int, int);
126105806Sjhbstatic void rc_start(struct tty *);
127105806Sjhbstatic void rc_stop(struct tty *, int rw);
128105806Sjhbstatic int  rc_param(struct tty *, struct termios *);
129105806Sjhbstatic void rc_pollcard(void *);
130105806Sjhbstatic void rc_reinit(struct rc_softc *);
131105806Sjhb#ifdef RCDEBUG
132105806Sjhbstatic void printrcflags();
133105806Sjhb#endif
134105806Sjhbstatic void rc_wait0(struct rc_softc *sc, int chan, int line);
135105806Sjhb
136105806Sjhbstatic devclass_t rc_devclass;
1378471Sache
1388471Sache/* Flags */
1399232Sache#define RC_DTR_OFF      0x0001          /* DTR wait, for close/open     */
1409232Sache#define RC_ACTOUT       0x0002          /* Dial-out port active         */
1419232Sache#define RC_RTSFLOW      0x0004          /* RTS flow ctl enabled         */
1429232Sache#define RC_CTSFLOW      0x0008          /* CTS flow ctl enabled         */
1439232Sache#define RC_DORXFER      0x0010          /* RXFER event planned          */
1449232Sache#define RC_DOXXFER      0x0020          /* XXFER event planned          */
1459232Sache#define RC_MODCHG       0x0040          /* Modem status changed         */
1469232Sache#define RC_OSUSP        0x0080          /* Output suspended             */
1479232Sache#define RC_OSBUSY       0x0100          /* start() routine in progress  */
1489232Sache#define RC_WAS_BUFOVFL  0x0200          /* low-level buffer ovferflow   */
1499232Sache#define RC_WAS_SILOVFL  0x0400          /* silo buffer overflow         */
1509232Sache#define RC_SEND_RDY     0x0800          /* ready to send */
1518471Sache
1528471Sache/* Table for translation of RCSR status bits to internal form */
1538471Sachestatic int rc_rcsrt[16] = {
1548471Sache	0,             TTY_OE,               TTY_FE,
1558471Sache	TTY_FE|TTY_OE, TTY_PE,               TTY_PE|TTY_OE,
1568471Sache	TTY_PE|TTY_FE, TTY_PE|TTY_FE|TTY_OE, TTY_BI,
1578471Sache	TTY_BI|TTY_OE, TTY_BI|TTY_FE,        TTY_BI|TTY_FE|TTY_OE,
1588471Sache	TTY_BI|TTY_PE, TTY_BI|TTY_PE|TTY_OE, TTY_BI|TTY_PE|TTY_FE,
1598471Sache	TTY_BI|TTY_PE|TTY_FE|TTY_OE
1608471Sache};
1618471Sache
162105806Sjhbstatic int rc_ports[] =
163105806Sjhb    { 0x220, 0x240, 0x250, 0x260, 0x2a0, 0x2b0, 0x300, 0x320 };
164105806Sjhbstatic int iobase_addrs[IOBASE_ADDRS] =
165105806Sjhb    { 0, 0x400, 0x800, 0xc00, 0x1400, 0x1800, 0x1c00, 0x2000,
166105806Sjhb      0x3000, 0x3400, 0x3800, 0x3c00, 0x4000, 0x8000 };
16767551Sjhb
1688471Sache/**********************************************/
1698471Sache
17012724Sphkstatic int
171105806Sjhbrc_probe(device_t dev)
1728471Sache{
173105806Sjhb	u_int port;
174105806Sjhb	int i, found;
1758471Sache
176105806Sjhb	/*
177105806Sjhb	 * We don't know of any PnP ID's for these cards.
178105806Sjhb	 */
179105806Sjhb	if (isa_get_logicalid(dev) != 0)
180105806Sjhb		return (ENXIO);
1819232Sache
182105806Sjhb	/*
183105806Sjhb	 * We have to have an IO port hint that is valid.
184105806Sjhb	 */
185105806Sjhb	port = isa_get_port(dev);
186105806Sjhb	if (port == -1)
187105806Sjhb		return (ENXIO);
188105806Sjhb	found = 0;
189298307Spfg	for (i = 0; i < nitems(rc_ports); i++)
190105806Sjhb		if (rc_ports[i] == port) {
191105806Sjhb			found = 1;
192105806Sjhb			break;
193105806Sjhb		}
194105806Sjhb	if (!found)
195105806Sjhb		return (ENXIO);
196105806Sjhb
197105806Sjhb	/*
198105806Sjhb	 * We have to have an IRQ hint.
199105806Sjhb	 */
200105806Sjhb	if (isa_get_irq(dev) == -1)
201105806Sjhb		return (ENXIO);
202105806Sjhb
203105806Sjhb	device_set_desc(dev, "SDL Riscom/8");
204105806Sjhb	return (0);
2058471Sache}
2068471Sache
20712724Sphkstatic int
208105806Sjhbrc_attach(device_t dev)
2098471Sache{
210105806Sjhb 	struct rc_chans *rc;
211105806Sjhb	struct tty *tp;
212105806Sjhb	struct rc_softc *sc;
213105806Sjhb	u_int port;
214105806Sjhb	int base, chan, error, i, x;
2158471Sache
216105806Sjhb	sc = device_get_softc(dev);
217105806Sjhb	sc->sc_dev = dev;
21840565Sbde
219105806Sjhb	/*
220105806Sjhb	 * We need to have IO ports.  Lots of them.  We need
221105806Sjhb	 * the following ranges relative to the base port:
222105806Sjhb	 * 0x0    -   0x10
223105806Sjhb	 * 0x400  -  0x410
224105806Sjhb	 * 0x800  -  0x810
225105806Sjhb	 * 0xc00  -  0xc10
226105806Sjhb	 * 0x1400 - 0x1410
227105806Sjhb	 * 0x1800 - 0x1810
228105806Sjhb	 * 0x1c00 - 0x1c10
229105806Sjhb	 * 0x2000 - 0x2010
230105806Sjhb	 * 0x3000 - 0x3010
231105806Sjhb	 * 0x3400 - 0x3410
232105806Sjhb	 * 0x3800 - 0x3810
233105806Sjhb	 * 0x3c00 - 0x3c10
234105806Sjhb	 * 0x4000 - 0x4010
235105806Sjhb	 * 0x8000 - 0x8010
236105806Sjhb	 */
237105806Sjhb	port = isa_get_port(dev);
238105806Sjhb	for (i = 0; i < IOBASE_ADDRS; i++)
239105806Sjhb		if (bus_set_resource(dev, SYS_RES_IOPORT, i,
240105806Sjhb		    port + iobase_addrs[i], 0x10) != 0)
241105806Sjhb			return (ENXIO);
242105806Sjhb	error = ENOMEM;
243105806Sjhb	for (i = 0; i < IOBASE_ADDRS; i++) {
244105806Sjhb		x = i;
245296137Sjhibbits		sc->sc_port[i] = bus_alloc_resource_anywhere(dev,
246296137Sjhibbits		    SYS_RES_IOPORT, &x, 0x10, RF_ACTIVE);
247105806Sjhb		if (x != i) {
248105806Sjhb			device_printf(dev, "ioport %d was rid %d\n", i, x);
249105806Sjhb			goto fail;
250105806Sjhb		}
251105806Sjhb		if (sc->sc_port[i] == NULL) {
252105806Sjhb			device_printf(dev, "failed to alloc ioports %x-%x\n",
253105806Sjhb			    port + iobase_addrs[i],
254105806Sjhb			    port + iobase_addrs[i] + 0x10);
255105806Sjhb			goto fail;
256105806Sjhb		}
257105806Sjhb	}
258105806Sjhb	sc->sc_bt = rman_get_bustag(sc->sc_port[0]);
259105806Sjhb	sc->sc_bh = rman_get_bushandle(sc->sc_port[0]);
2608471Sache
261127135Snjl	sc->sc_irq = bus_alloc_resource_any(dev, SYS_RES_IRQ, &sc->sc_irqrid,
262127135Snjl	    RF_ACTIVE);
263105806Sjhb	if (sc->sc_irq == NULL) {
264105806Sjhb		device_printf(dev, "failed to alloc IRQ\n");
265105806Sjhb		goto fail;
266105806Sjhb	}
267105806Sjhb
268105806Sjhb	/*
269105806Sjhb	 * Now do some actual tests to make sure it works.
270105806Sjhb	 */
271105806Sjhb	error = ENXIO;
272105806Sjhb	rcout(sc, CD180_PPRL, 0x22); /* Random values to Prescale reg. */
273105806Sjhb	rcout(sc, CD180_PPRH, 0x11);
274105806Sjhb	if (rcin(sc, CD180_PPRL) != 0x22 || rcin(sc, CD180_PPRH) != 0x11)
275105806Sjhb		goto fail;
276105806Sjhb	if (rc_test(sc))
277105806Sjhb		goto fail;
278105806Sjhb
279105806Sjhb	/*
280105806Sjhb	 * Ok, start actually hooking things up.
281105806Sjhb	 */
282105806Sjhb	sc->sc_unit = device_get_unit(dev);
283105806Sjhb	/*sc->sc_chipid = 0x10 + device_get_unit(dev);*/
284105806Sjhb	device_printf(dev, "%d chans, firmware rev. %c\n",
285105806Sjhb		CD180_NCHAN, (rcin(sc, CD180_GFRCR) & 0xF) + 'A');
286105806Sjhb	rc = sc->sc_channels;
287105806Sjhb	base = CD180_NCHAN * sc->sc_unit;
2888471Sache	for (chan = 0; chan < CD180_NCHAN; chan++, rc++) {
289105806Sjhb		rc->rc_rcb     = sc;
2908471Sache		rc->rc_chan    = chan;
2918471Sache		rc->rc_iptr    = rc->rc_ibuf;
2928471Sache		rc->rc_bufend  = &rc->rc_ibuf[RC_IBUFSIZE];
2938471Sache		rc->rc_hiwat   = &rc->rc_ibuf[RC_IHIGHWATER];
2948471Sache		rc->rc_optr    = rc->rc_obufend  = rc->rc_obuf;
295105806Sjhb		callout_init(&rc->rc_dtrcallout, 0);
296136210Sphk		tp = rc->rc_tp = ttyalloc();
297136210Sphk		tp->t_sc = rc;
298136210Sphk		tp->t_oproc   = rc_start;
299136210Sphk		tp->t_param   = rc_param;
300136210Sphk		tp->t_modem   = rc_modem;
301136210Sphk		tp->t_break   = rc_break;
302136210Sphk		tp->t_close   = rc_close;
303136210Sphk		tp->t_stop    = rc_stop;
304151383Sphk		ttycreate(tp, TS_CALLOUT, "m%d", chan + base);
3058471Sache	}
306105806Sjhb
307166901Spiso	error = bus_setup_intr(dev, sc->sc_irq, INTR_TYPE_TTY, NULL, rc_intr,
308166901Spiso	    sc, &sc->sc_hwicookie);
309105806Sjhb	if (error) {
310105806Sjhb		device_printf(dev, "failed to register interrupt handler\n");
311105806Sjhb		goto fail;
3128471Sache	}
313105806Sjhb
314151700Sjhb	swi_add(&tty_intr_event, "rc", rc_pollcard, sc, SWI_TTY, 0,
315105806Sjhb	    &sc->sc_swicookie);
316105806Sjhb	return (0);
317105806Sjhb
318105806Sjhbfail:
319105806Sjhb	rc_release_resources(dev);
320105806Sjhb	return (error);
3218471Sache}
3228471Sache
323105806Sjhbstatic int
324105806Sjhbrc_detach(device_t dev)
325105806Sjhb{
326105806Sjhb	struct rc_softc *sc;
327105806Sjhb	struct rc_chans *rc;
328131981Sphk	int error, i;
329105806Sjhb
330105806Sjhb	sc = device_get_softc(dev);
331105806Sjhb
332105806Sjhb	rc = sc->sc_channels;
333136210Sphk	for (i = 0; i < CD180_NCHAN; i++, rc++)
334136210Sphk		ttyfree(rc->rc_tp);
335105806Sjhb
336105806Sjhb	error = bus_teardown_intr(dev, sc->sc_irq, sc->sc_hwicookie);
337105806Sjhb	if (error)
338105806Sjhb		device_printf(dev, "failed to deregister interrupt handler\n");
339151700Sjhb	swi_remove(sc->sc_swicookie);
340105806Sjhb	rc_release_resources(dev);
341105806Sjhb
342105806Sjhb	return (0);
343105806Sjhb}
344105806Sjhb
34540565Sbdestatic void
346105806Sjhbrc_release_resources(device_t dev)
3478471Sache{
348105806Sjhb	struct rc_softc *sc;
349105806Sjhb	int i;
3508471Sache
351105806Sjhb	sc = device_get_softc(dev);
352105806Sjhb	if (sc->sc_irq != NULL) {
353105806Sjhb		bus_release_resource(dev, SYS_RES_IRQ, sc->sc_irqrid,
354105806Sjhb		    sc->sc_irq);
355105806Sjhb		sc->sc_irq = NULL;
3569232Sache	}
357105806Sjhb	for (i = 0; i < IOBASE_ADDRS; i++) {
358105806Sjhb		if (sc->sc_port[i] == NULL)
359105806Sjhb			break;
360105806Sjhb		bus_release_resource(dev, SYS_RES_IOPORT, i, sc->sc_port[i]);
361105806Sjhb		sc->sc_port[i] = NULL;
362105806Sjhb	}
363105806Sjhb}
3648471Sache
365105806Sjhb/* RC interrupt handling */
366105806Sjhbstatic void
367105806Sjhbrc_intr(void *arg)
368105806Sjhb{
369105806Sjhb	struct rc_softc        *sc;
370105806Sjhb	struct rc_chans        *rc;
371105806Sjhb	int                    resid, chan;
372105806Sjhb	u_char                 val, iack, bsr, ucnt, *optr;
373105806Sjhb	int                    good_data, t_state;
3748471Sache
375105806Sjhb	sc = (struct rc_softc *)arg;
376105806Sjhb	bsr = ~(rcin(sc, RC_BSR));
3779232Sache	if (!(bsr & (RC_BSR_TOUT|RC_BSR_RXINT|RC_BSR_TXINT|RC_BSR_MOINT))) {
378105806Sjhb		device_printf(sc->sc_dev, "extra interrupt\n");
379105806Sjhb		rcout(sc, CD180_EOIR, 0);
3809232Sache		return;
3819232Sache	}
3829232Sache
3839232Sache	while (bsr & (RC_BSR_TOUT|RC_BSR_RXINT|RC_BSR_TXINT|RC_BSR_MOINT)) {
3849232Sache#ifdef RCDEBUG_DETAILED
385105806Sjhb		device_printf(sc->sc_dev, "intr (%p) %s%s%s%s\n", arg, bsr,
3869232Sache			(bsr & RC_BSR_TOUT)?"TOUT ":"",
3879232Sache			(bsr & RC_BSR_RXINT)?"RXINT ":"",
3889232Sache			(bsr & RC_BSR_TXINT)?"TXINT ":"",
3899232Sache			(bsr & RC_BSR_MOINT)?"MOINT":"");
3908471Sache#endif
3919232Sache		if (bsr & RC_BSR_TOUT) {
392105806Sjhb			device_printf(sc->sc_dev,
393105806Sjhb			    "hardware failure, reset board\n");
394105806Sjhb			rcout(sc, RC_CTOUT, 0);
395105806Sjhb			rc_reinit(sc);
3969232Sache			return;
3978471Sache		}
3989232Sache		if (bsr & RC_BSR_RXINT) {
399105806Sjhb			iack = rcin(sc, RC_PILR_RX);
4009232Sache			good_data = (iack == (GIVR_IT_RGDI | RC_FAKEID));
4019232Sache			if (!good_data && iack != (GIVR_IT_REI | RC_FAKEID)) {
402105806Sjhb				device_printf(sc->sc_dev,
403105806Sjhb				    "fake rxint: %02x\n", iack);
4049232Sache				goto more_intrs;
4059232Sache			}
406105806Sjhb			chan = ((rcin(sc, CD180_GICR) & GICR_CHAN) >> GICR_LSH);
407105806Sjhb			rc = &sc->sc_channels[chan];
408136210Sphk			t_state = rc->rc_tp->t_state;
4099232Sache			/* Do RTS flow control stuff */
4109232Sache			if (  (rc->rc_flags & RC_RTSFLOW)
4119232Sache			    || !(t_state & TS_ISOPEN)
4129232Sache			   ) {
4139232Sache				if (  (   !(t_state & TS_ISOPEN)
4149232Sache				       || (t_state & TS_TBLOCK)
4159232Sache				      )
4169232Sache				    && (rc->rc_msvr & MSVR_RTS)
4179232Sache				   )
418105806Sjhb					rcout(sc, CD180_MSVR,
4199232Sache						rc->rc_msvr &= ~MSVR_RTS);
4209232Sache				else if (!(rc->rc_msvr & MSVR_RTS))
421105806Sjhb					rcout(sc, CD180_MSVR,
4229232Sache						rc->rc_msvr |= MSVR_RTS);
4239232Sache			}
424105806Sjhb			ucnt  = rcin(sc, CD180_RDCR) & 0xF;
4259232Sache			resid = 0;
4268471Sache
4279232Sache			if (t_state & TS_ISOPEN) {
4289232Sache				/* check for input buffer overflow */
4299232Sache				if ((rc->rc_iptr + ucnt) >= rc->rc_bufend) {
4309232Sache					resid  = ucnt;
4319232Sache					ucnt   = rc->rc_bufend - rc->rc_iptr;
4329232Sache					resid -= ucnt;
4339232Sache					if (!(rc->rc_flags & RC_WAS_BUFOVFL)) {
4349232Sache						rc->rc_flags |= RC_WAS_BUFOVFL;
435105806Sjhb						sc->sc_scheduled_event++;
4369232Sache					}
4378471Sache				}
4389232Sache				optr = rc->rc_iptr;
4399232Sache				/* check foor good data */
4409232Sache				if (good_data) {
4419232Sache					while (ucnt-- > 0) {
442105806Sjhb						val = rcin(sc, CD180_RDR);
4439232Sache						optr[0] = val;
4449232Sache						optr[INPUT_FLAGS_SHIFT] = 0;
4459232Sache						optr++;
446105806Sjhb						sc->sc_scheduled_event++;
447136210Sphk						if (val != 0 && val == rc->rc_tp->t_hotchar)
448105806Sjhb							swi_sched(sc->sc_swicookie, 0);
4498471Sache					}
4509232Sache				} else {
4519232Sache					/* Store also status data */
4529232Sache					while (ucnt-- > 0) {
453105806Sjhb						iack = rcin(sc, CD180_RCSR);
4549232Sache						if (iack & RCSR_Timeout)
4559232Sache							break;
4569232Sache						if (   (iack & RCSR_OE)
4579232Sache						    && !(rc->rc_flags & RC_WAS_SILOVFL)) {
4589232Sache							rc->rc_flags |= RC_WAS_SILOVFL;
459105806Sjhb							sc->sc_scheduled_event++;
4609232Sache						}
461105806Sjhb						val = rcin(sc, CD180_RDR);
4629232Sache						/*
4639232Sache						  Don't store PE if IGNPAR and BREAK if IGNBRK,
4649232Sache						  this hack allows "raw" tty optimization
4659232Sache						  works even if IGN* is set.
4669232Sache						*/
4679232Sache						if (   !(iack & (RCSR_PE|RCSR_FE|RCSR_Break))
46846704Speter						    || ((!(iack & (RCSR_PE|RCSR_FE))
469136210Sphk						    ||  !(rc->rc_tp->t_iflag & IGNPAR))
4709232Sache						    && (!(iack & RCSR_Break)
471136210Sphk						    ||  !(rc->rc_tp->t_iflag & IGNBRK)))) {
4729232Sache							if (   (iack & (RCSR_PE|RCSR_FE))
4739232Sache							    && (t_state & TS_CAN_BYPASS_L_RINT)
4749232Sache							    && ((iack & RCSR_FE)
47546704Speter							    ||  ((iack & RCSR_PE)
476136210Sphk							    &&  (rc->rc_tp->t_iflag & INPCK))))
4779232Sache								val = 0;
478136210Sphk							else if (val != 0 && val == rc->rc_tp->t_hotchar)
479105806Sjhb								swi_sched(sc->sc_swicookie, 0);
4809232Sache							optr[0] = val;
4819232Sache							optr[INPUT_FLAGS_SHIFT] = iack;
4829232Sache							optr++;
483105806Sjhb							sc->sc_scheduled_event++;
4849232Sache						}
4859232Sache					}
4868471Sache				}
4879232Sache				rc->rc_iptr = optr;
4889232Sache				rc->rc_flags |= RC_DORXFER;
4899232Sache			} else
4909232Sache				resid = ucnt;
4919232Sache			/* Clear FIFO if necessary */
4929232Sache			while (resid-- > 0) {
4939232Sache				if (!good_data)
494105806Sjhb					iack = rcin(sc, CD180_RCSR);
4959232Sache				else
4969232Sache					iack = 0;
4979232Sache				if (iack & RCSR_Timeout)
4989232Sache					break;
499105806Sjhb				(void) rcin(sc, CD180_RDR);
5008471Sache			}
5019232Sache			goto more_intrs;
5028471Sache		}
5039232Sache		if (bsr & RC_BSR_MOINT) {
504105806Sjhb			iack = rcin(sc, RC_PILR_MODEM);
5059232Sache			if (iack != (GIVR_IT_MSCI | RC_FAKEID)) {
506105806Sjhb				device_printf(sc->sc_dev, "fake moint: %02x\n",
507105806Sjhb				    iack);
5089232Sache				goto more_intrs;
5099232Sache			}
510105806Sjhb			chan = ((rcin(sc, CD180_GICR) & GICR_CHAN) >> GICR_LSH);
511105806Sjhb			rc = &sc->sc_channels[chan];
512105806Sjhb			iack = rcin(sc, CD180_MCR);
513105806Sjhb			rc->rc_msvr = rcin(sc, CD180_MSVR);
514105806Sjhb			rcout(sc, CD180_MCR, 0);
5158471Sache#ifdef RCDEBUG
5169232Sache			printrcflags(rc, "moint");
5178471Sache#endif
5189232Sache			if (rc->rc_flags & RC_CTSFLOW) {
5199232Sache				if (rc->rc_msvr & MSVR_CTS)
5209232Sache					rc->rc_flags |= RC_SEND_RDY;
5219232Sache				else
5229232Sache					rc->rc_flags &= ~RC_SEND_RDY;
5239232Sache			} else
5248471Sache				rc->rc_flags |= RC_SEND_RDY;
5259232Sache			if ((iack & MCR_CDchg) && !(rc->rc_flags & RC_MODCHG)) {
526105806Sjhb				sc->sc_scheduled_event += LOTS_OF_EVENTS;
5279232Sache				rc->rc_flags |= RC_MODCHG;
528105806Sjhb				swi_sched(sc->sc_swicookie, 0);
5299232Sache			}
5309232Sache			goto more_intrs;
5318471Sache		}
5329232Sache		if (bsr & RC_BSR_TXINT) {
533105806Sjhb			iack = rcin(sc, RC_PILR_TX);
5349232Sache			if (iack != (GIVR_IT_TDI | RC_FAKEID)) {
535105806Sjhb				device_printf(sc->sc_dev, "fake txint: %02x\n",
536105806Sjhb				    iack);
5379232Sache				goto more_intrs;
5389232Sache			}
539105806Sjhb			chan = ((rcin(sc, CD180_GICR) & GICR_CHAN) >> GICR_LSH);
540105806Sjhb			rc = &sc->sc_channels[chan];
5419232Sache			if (    (rc->rc_flags & RC_OSUSP)
5429232Sache			    || !(rc->rc_flags & RC_SEND_RDY)
5439232Sache			   )
5449232Sache				goto more_intrs;
5459232Sache			/* Handle breaks and other stuff */
5469232Sache			if (rc->rc_pendcmd) {
547105806Sjhb				rcout(sc, CD180_COR2, rc->rc_cor2 |= COR2_ETC);
548105806Sjhb				rcout(sc, CD180_TDR,  CD180_C_ESC);
549105806Sjhb				rcout(sc, CD180_TDR,  rc->rc_pendcmd);
550105806Sjhb				rcout(sc, CD180_COR2, rc->rc_cor2 &= ~COR2_ETC);
5519232Sache				rc->rc_pendcmd = 0;
5529232Sache				goto more_intrs;
5539232Sache			}
5549232Sache			optr = rc->rc_optr;
5559232Sache			resid = rc->rc_obufend - optr;
5569232Sache			if (resid > CD180_NFIFO)
5579232Sache				resid = CD180_NFIFO;
5589232Sache			while (resid-- > 0)
559105806Sjhb				rcout(sc, CD180_TDR, *optr++);
5609232Sache			rc->rc_optr = optr;
5618471Sache
5629232Sache			/* output completed? */
5639232Sache			if (optr >= rc->rc_obufend) {
564105806Sjhb				rcout(sc, CD180_IER, rc->rc_ier &= ~IER_TxRdy);
5658471Sache#ifdef RCDEBUG
566105806Sjhb				device_printf(sc->sc_dev,
567105806Sjhb				    "channel %d: output completed\n",
568105806Sjhb				    rc->rc_chan);
5698471Sache#endif
5709232Sache				if (!(rc->rc_flags & RC_DOXXFER)) {
571105806Sjhb					sc->sc_scheduled_event += LOTS_OF_EVENTS;
5729232Sache					rc->rc_flags |= RC_DOXXFER;
573105806Sjhb					swi_sched(sc->sc_swicookie, 0);
5749232Sache				}
5759232Sache			}
5768471Sache		}
5779232Sache	more_intrs:
578105806Sjhb		rcout(sc, CD180_EOIR, 0);   /* end of interrupt */
579105806Sjhb		rcout(sc, RC_CTOUT, 0);
580105806Sjhb		bsr = ~(rcin(sc, RC_BSR));
5818471Sache	}
5828471Sache}
5838471Sache
5848471Sache/* Feed characters to output buffer */
585105806Sjhbstatic void
586105806Sjhbrc_start(struct tty *tp)
5878471Sache{
588105806Sjhb	struct rc_softc *sc;
589105806Sjhb	struct rc_chans *rc;
590105806Sjhb	int s;
5918471Sache
592136210Sphk	rc = tp->t_sc;
5938471Sache	if (rc->rc_flags & RC_OSBUSY)
5948471Sache		return;
595105806Sjhb	sc = rc->rc_rcb;
5968471Sache	s = spltty();
5978471Sache	rc->rc_flags |= RC_OSBUSY;
598106653Sjhb	critical_enter();
5998471Sache	if (tp->t_state & TS_TTSTOP)
6008471Sache		rc->rc_flags |= RC_OSUSP;
6018471Sache	else
6028471Sache		rc->rc_flags &= ~RC_OSUSP;
6038471Sache	/* Do RTS flow control stuff */
6049232Sache	if (   (rc->rc_flags & RC_RTSFLOW)
6059232Sache	    && (tp->t_state & TS_TBLOCK)
6069232Sache	    && (rc->rc_msvr & MSVR_RTS)
6079232Sache	   ) {
608105806Sjhb		rcout(sc, CD180_CAR, rc->rc_chan);
609105806Sjhb		rcout(sc, CD180_MSVR, rc->rc_msvr &= ~MSVR_RTS);
6109232Sache	} else if (!(rc->rc_msvr & MSVR_RTS)) {
611105806Sjhb		rcout(sc, CD180_CAR, rc->rc_chan);
612105806Sjhb		rcout(sc, CD180_MSVR, rc->rc_msvr |= MSVR_RTS);
6138471Sache	}
614106653Sjhb	critical_exit();
6158471Sache	if (tp->t_state & (TS_TIMEOUT|TS_TTSTOP))
6168471Sache		goto out;
6178471Sache#ifdef RCDEBUG
6188471Sache	printrcflags(rc, "rcstart");
6198471Sache#endif
6209626Sbde	ttwwakeup(tp);
6218471Sache#ifdef RCDEBUG
6229232Sache	printf("rcstart: outq = %d obuf = %d\n",
6238471Sache		tp->t_outq.c_cc, rc->rc_obufend - rc->rc_optr);
6248471Sache#endif
6259232Sache	if (tp->t_state & TS_BUSY)
626105806Sjhb		goto out;    /* output still in progress ... */
6278471Sache
6288471Sache	if (tp->t_outq.c_cc > 0) {
6298471Sache		u_int   ocnt;
6308471Sache
6318471Sache		tp->t_state |= TS_BUSY;
6328471Sache		ocnt = q_to_b(&tp->t_outq, rc->rc_obuf, sizeof rc->rc_obuf);
633106653Sjhb		critical_enter();
6348471Sache		rc->rc_optr = rc->rc_obuf;
6359232Sache		rc->rc_obufend = rc->rc_optr + ocnt;
636106653Sjhb		critical_exit();
6379232Sache		if (!(rc->rc_ier & IER_TxRdy)) {
6388471Sache#ifdef RCDEBUG
639105806Sjhb			device_printf(sc->sc_dev,
640105806Sjhb			    "channel %d: rcstart enable txint\n", rc->rc_chan);
6418471Sache#endif
642105806Sjhb			rcout(sc, CD180_CAR, rc->rc_chan);
643105806Sjhb			rcout(sc, CD180_IER, rc->rc_ier |= IER_TxRdy);
6448471Sache		}
6458471Sache	}
6468471Sacheout:
6478471Sache	rc->rc_flags &= ~RC_OSBUSY;
6488471Sache	(void) splx(s);
6498471Sache}
6508471Sache
6518471Sache/* Handle delayed events. */
652105806Sjhbvoid
653105806Sjhbrc_pollcard(void *arg)
6548471Sache{
655105806Sjhb	struct rc_softc *sc;
656105806Sjhb	struct rc_chans *rc;
657105806Sjhb	struct tty *tp;
658105806Sjhb	u_char *tptr, *eptr;
659105806Sjhb	int chan, icnt;
6608471Sache
661105806Sjhb	sc = (struct rc_softc *)arg;
662105806Sjhb	if (sc->sc_scheduled_event == 0)
6638471Sache		return;
664105806Sjhb	do {
665105806Sjhb		rc = sc->sc_channels;
6668471Sache		for (chan = 0; chan < CD180_NCHAN; rc++, chan++) {
667136210Sphk			tp = rc->rc_tp;
6688471Sache#ifdef RCDEBUG
6698471Sache			if (rc->rc_flags & (RC_DORXFER|RC_DOXXFER|RC_MODCHG|
6708471Sache			    RC_WAS_BUFOVFL|RC_WAS_SILOVFL))
6718471Sache				printrcflags(rc, "rcevent");
6728471Sache#endif
6738471Sache			if (rc->rc_flags & RC_WAS_BUFOVFL) {
674106653Sjhb				critical_enter();
6758471Sache				rc->rc_flags &= ~RC_WAS_BUFOVFL;
676105806Sjhb				sc->sc_scheduled_event--;
677106653Sjhb				critical_exit();
678105806Sjhb				device_printf(sc->sc_dev,
679105806Sjhb			    "channel %d: interrupt-level buffer overflow\n",
680105806Sjhb				     chan);
6818471Sache			}
6828471Sache			if (rc->rc_flags & RC_WAS_SILOVFL) {
683106653Sjhb				critical_enter();
6848471Sache				rc->rc_flags &= ~RC_WAS_SILOVFL;
685105806Sjhb				sc->sc_scheduled_event--;
686106653Sjhb				critical_exit();
687105806Sjhb				device_printf(sc->sc_dev,
688105806Sjhb				    "channel %d: silo overflow\n", chan);
6898471Sache			}
6908471Sache			if (rc->rc_flags & RC_MODCHG) {
691106653Sjhb				critical_enter();
6928471Sache				rc->rc_flags &= ~RC_MODCHG;
693105806Sjhb				sc->sc_scheduled_event -= LOTS_OF_EVENTS;
694106653Sjhb				critical_exit();
695130077Sphk				ttyld_modem(tp, !!(rc->rc_msvr & MSVR_CD));
6968471Sache			}
6978471Sache			if (rc->rc_flags & RC_DORXFER) {
698106653Sjhb				critical_enter();
6998471Sache				rc->rc_flags &= ~RC_DORXFER;
7008471Sache				eptr = rc->rc_iptr;
7018471Sache				if (rc->rc_bufend == &rc->rc_ibuf[2 * RC_IBUFSIZE])
7028471Sache					tptr = &rc->rc_ibuf[RC_IBUFSIZE];
7038471Sache				else
7048471Sache					tptr = rc->rc_ibuf;
7058471Sache				icnt = eptr - tptr;
7068471Sache				if (icnt > 0) {
7078471Sache					if (rc->rc_bufend == &rc->rc_ibuf[2 * RC_IBUFSIZE]) {
7088471Sache						rc->rc_iptr   = rc->rc_ibuf;
7098471Sache						rc->rc_bufend = &rc->rc_ibuf[RC_IBUFSIZE];
7108471Sache						rc->rc_hiwat  = &rc->rc_ibuf[RC_IHIGHWATER];
7118471Sache					} else {
7128471Sache						rc->rc_iptr   = &rc->rc_ibuf[RC_IBUFSIZE];
7138471Sache						rc->rc_bufend = &rc->rc_ibuf[2 * RC_IBUFSIZE];
7148471Sache						rc->rc_hiwat  =
7158471Sache							&rc->rc_ibuf[RC_IBUFSIZE + RC_IHIGHWATER];
7168471Sache					}
7179232Sache					if (   (rc->rc_flags & RC_RTSFLOW)
7189232Sache					    && (tp->t_state & TS_ISOPEN)
7199232Sache					    && !(tp->t_state & TS_TBLOCK)
7208471Sache					    && !(rc->rc_msvr & MSVR_RTS)
7219232Sache					    ) {
722105806Sjhb						rcout(sc, CD180_CAR, chan);
723105806Sjhb						rcout(sc, CD180_MSVR,
7248471Sache							rc->rc_msvr |= MSVR_RTS);
7258471Sache					}
726105806Sjhb					sc->sc_scheduled_event -= icnt;
7278471Sache				}
728106653Sjhb				critical_exit();
7298471Sache
7309232Sache				if (icnt <= 0 || !(tp->t_state & TS_ISOPEN))
7318471Sache					goto done1;
7328471Sache
7338471Sache				if (   (tp->t_state & TS_CAN_BYPASS_L_RINT)
7348471Sache				    && !(tp->t_state & TS_LOCAL)) {
7359822Sbde					if ((tp->t_rawq.c_cc + icnt) >= RB_I_HIGH_WATER
7369822Sbde					    && ((rc->rc_flags & RC_RTSFLOW) || (tp->t_iflag & IXOFF))
7379822Sbde					    && !(tp->t_state & TS_TBLOCK))
7389822Sbde						ttyblock(tp);
7398471Sache					tk_nin += icnt;
7408471Sache					tk_rawcc += icnt;
7418471Sache					tp->t_rawcc += icnt;
7428471Sache					if (b_to_q(tptr, icnt, &tp->t_rawq))
743105806Sjhb						device_printf(sc->sc_dev,
744105806Sjhb				    "channel %d: tty-level buffer overflow\n",
745105806Sjhb						    chan);
7468471Sache					ttwakeup(tp);
7478471Sache					if ((tp->t_state & TS_TTSTOP) && ((tp->t_iflag & IXANY)
7488471Sache					    || (tp->t_cc[VSTART] == tp->t_cc[VSTOP]))) {
7498471Sache						tp->t_state &= ~TS_TTSTOP;
7508471Sache						tp->t_lflag &= ~FLUSHO;
7519754Sbde						rc_start(tp);
7528471Sache					}
7538471Sache				} else {
7548471Sache					for (; tptr < eptr; tptr++)
755130095Sphk						ttyld_rint(tp,
7568471Sache						    (tptr[0] |
757130095Sphk						    rc_rcsrt[tptr[INPUT_FLAGS_SHIFT] & 0xF]));
7588471Sache				}
75927125Sbdedone1: ;
7608471Sache			}
7618471Sache			if (rc->rc_flags & RC_DOXXFER) {
762106653Sjhb				critical_enter();
763105806Sjhb				sc->sc_scheduled_event -= LOTS_OF_EVENTS;
7649232Sache				rc->rc_flags &= ~RC_DOXXFER;
765136210Sphk				rc->rc_tp->t_state &= ~TS_BUSY;
766106653Sjhb				critical_exit();
767130077Sphk				ttyld_start(tp);
7688471Sache			}
769111564Sjhb			if (sc->sc_scheduled_event == 0)
770111564Sjhb				break;
7718471Sache		}
772111564Sjhb	} while (sc->sc_scheduled_event >= LOTS_OF_EVENTS);
7738471Sache}
7748471Sache
775105806Sjhbstatic void
776105806Sjhbrc_stop(struct tty *tp, int rw)
7778471Sache{
778105806Sjhb	struct rc_softc *sc;
779105806Sjhb	struct rc_chans *rc;
7808471Sache	u_char *tptr, *eptr;
7818471Sache
782136210Sphk	rc = tp->t_sc;
783105806Sjhb	sc = rc->rc_rcb;
7848471Sache#ifdef RCDEBUG
785105806Sjhb	device_printf(sc->sc_dev, "channel %d: rc_stop %s%s\n",
786105806Sjhb	    rc->rc_chan, (rw & FWRITE)?"FWRITE ":"", (rw & FREAD)?"FREAD":"");
7878471Sache#endif
7888471Sache	if (rw & FWRITE)
7898471Sache		rc_discard_output(rc);
790106653Sjhb	critical_enter();
7918471Sache	if (rw & FREAD) {
7929232Sache		rc->rc_flags &= ~RC_DORXFER;
7938471Sache		eptr = rc->rc_iptr;
7948471Sache		if (rc->rc_bufend == &rc->rc_ibuf[2 * RC_IBUFSIZE]) {
7958471Sache			tptr = &rc->rc_ibuf[RC_IBUFSIZE];
7968471Sache			rc->rc_iptr = &rc->rc_ibuf[RC_IBUFSIZE];
7978471Sache		} else {
7988471Sache			tptr = rc->rc_ibuf;
7998471Sache			rc->rc_iptr = rc->rc_ibuf;
8008471Sache		}
801105806Sjhb		sc->sc_scheduled_event -= eptr - tptr;
8028471Sache	}
8038471Sache	if (tp->t_state & TS_TTSTOP)
8048471Sache		rc->rc_flags |= RC_OSUSP;
8058471Sache	else
8068471Sache		rc->rc_flags &= ~RC_OSUSP;
807106653Sjhb	critical_exit();
8088471Sache}
8098471Sache
810136210Sphkstatic void
811136210Sphkrc_close(struct tty *tp)
8128471Sache{
813105806Sjhb	struct rc_chans *rc;
814105806Sjhb	struct rc_softc *sc;
815105806Sjhb	int s;
8168471Sache
817136210Sphk	rc = tp->t_sc;
818105806Sjhb	sc = rc->rc_rcb;
8198471Sache	s = spltty();
820105806Sjhb	rcout(sc, CD180_CAR, rc->rc_chan);
8218471Sache
8229232Sache	/* Disable rx/tx intrs */
823105806Sjhb	rcout(sc, CD180_IER, rc->rc_ier = 0);
8249232Sache	if (   (tp->t_cflag & HUPCL)
82546704Speter	    || (!(rc->rc_flags & RC_ACTOUT)
8268471Sache	       && !(rc->rc_msvr & MSVR_CD)
82746704Speter	       && !(tp->t_cflag & CLOCAL))
8289232Sache	    || !(tp->t_state & TS_ISOPEN)
8299232Sache	   ) {
830105806Sjhb		CCRCMD(sc, rc->rc_chan, CCR_ResetChan);
831105806Sjhb		WAITFORCCR(sc, rc->rc_chan);
832131096Sphk		(void) rc_modem(tp, SER_RTS, 0);
833131981Sphk		ttydtrwaitstart(tp);
8348471Sache	}
8358471Sache	rc->rc_flags &= ~RC_ACTOUT;
836111748Sdes	wakeup( &rc->rc_rcb);  /* wake bi */
8379639Sbde	wakeup(TSA_CARR_ON(tp));
8388471Sache	(void) splx(s);
8398471Sache}
8408471Sache
8418471Sache/* Reset the bastard */
842105806Sjhbstatic void
843118607Sjhbrc_hwreset(struct rc_softc *sc, u_int chipid)
8448471Sache{
845105806Sjhb	CCRCMD(sc, -1, CCR_HWRESET);            /* Hardware reset */
8468471Sache	DELAY(20000);
847105806Sjhb	WAITFORCCR(sc, -1);
8489232Sache
849105806Sjhb	rcout(sc, RC_CTOUT, 0);             /* Clear timeout  */
850105806Sjhb	rcout(sc, CD180_GIVR,  chipid);
851105806Sjhb	rcout(sc, CD180_GICR,  0);
8528471Sache
8538471Sache	/* Set Prescaler Registers (1 msec) */
854105806Sjhb	rcout(sc, CD180_PPRL, ((RC_OSCFREQ + 999) / 1000) & 0xFF);
855105806Sjhb	rcout(sc, CD180_PPRH, ((RC_OSCFREQ + 999) / 1000) >> 8);
8568471Sache
8578471Sache	/* Initialize Priority Interrupt Level Registers */
858105806Sjhb	rcout(sc, CD180_PILR1, RC_PILR_MODEM);
859105806Sjhb	rcout(sc, CD180_PILR2, RC_PILR_TX);
860105806Sjhb	rcout(sc, CD180_PILR3, RC_PILR_RX);
8618471Sache
8628471Sache	/* Reset DTR */
863105806Sjhb	rcout(sc, RC_DTREG, ~0);
8648471Sache}
8658471Sache
8668471Sache/* Set channel parameters */
867105806Sjhbstatic int
868105806Sjhbrc_param(struct tty *tp, struct termios *ts)
8698471Sache{
870105806Sjhb	struct rc_softc *sc;
871105806Sjhb	struct rc_chans *rc;
872105806Sjhb	int idivs, odivs, s, val, cflag, iflag, lflag, inpflow;
8738471Sache
8749855Sache	if (   ts->c_ospeed < 0 || ts->c_ospeed > 76800
8759855Sache	    || ts->c_ispeed < 0 || ts->c_ispeed > 76800
8769855Sache	   )
8779855Sache		return (EINVAL);
8788471Sache	if (ts->c_ispeed == 0)
8798471Sache		ts->c_ispeed = ts->c_ospeed;
8809855Sache	odivs = RC_BRD(ts->c_ospeed);
8819855Sache	idivs = RC_BRD(ts->c_ispeed);
8828471Sache
883136210Sphk	rc = tp->t_sc;
884105806Sjhb	sc = rc->rc_rcb;
8858471Sache	s = spltty();
8868471Sache
8879232Sache	/* Select channel */
888105806Sjhb	rcout(sc, CD180_CAR, rc->rc_chan);
8899232Sache
8908471Sache	/* If speed == 0, hangup line */
8919232Sache	if (ts->c_ospeed == 0) {
892105806Sjhb		CCRCMD(sc, rc->rc_chan, CCR_ResetChan);
893105806Sjhb		WAITFORCCR(sc, rc->rc_chan);
894131096Sphk		(void) rc_modem(tp, 0, SER_DTR);
8959232Sache	}
8968471Sache
8978471Sache	tp->t_state &= ~TS_CAN_BYPASS_L_RINT;
8988471Sache	cflag = ts->c_cflag;
8998471Sache	iflag = ts->c_iflag;
9008471Sache	lflag = ts->c_lflag;
9018471Sache
9028471Sache	if (idivs > 0) {
903105806Sjhb		rcout(sc, CD180_RBPRL, idivs & 0xFF);
904105806Sjhb		rcout(sc, CD180_RBPRH, idivs >> 8);
9058471Sache	}
9068471Sache	if (odivs > 0) {
907105806Sjhb		rcout(sc, CD180_TBPRL, odivs & 0xFF);
908105806Sjhb		rcout(sc, CD180_TBPRH, odivs >> 8);
9098471Sache	}
9108471Sache
9118471Sache	/* set timeout value */
9129232Sache	if (ts->c_ispeed > 0) {
9139232Sache		int itm = ts->c_ispeed > 2400 ? 5 : 10000 / ts->c_ispeed + 1;
9148471Sache
9159232Sache		if (   !(lflag & ICANON)
9169232Sache		    && ts->c_cc[VMIN] != 0 && ts->c_cc[VTIME] != 0
9179232Sache		    && ts->c_cc[VTIME] * 10 > itm)
9189232Sache			itm = ts->c_cc[VTIME] * 10;
9199232Sache
920105806Sjhb		rcout(sc, CD180_RTPR, itm <= 255 ? itm : 255);
9219232Sache	}
9229232Sache
9238471Sache	switch (cflag & CSIZE) {
9248471Sache		case CS5:       val = COR1_5BITS;      break;
9258471Sache		case CS6:       val = COR1_6BITS;      break;
9268471Sache		case CS7:       val = COR1_7BITS;      break;
9278471Sache		default:
9288471Sache		case CS8:       val = COR1_8BITS;      break;
9298471Sache	}
9308471Sache	if (cflag & PARENB) {
9318471Sache		val |= COR1_NORMPAR;
9328471Sache		if (cflag & PARODD)
9338471Sache			val |= COR1_ODDP;
9349232Sache		if (!(cflag & INPCK))
9359232Sache			val |= COR1_Ignore;
9368471Sache	} else
9379232Sache		val |= COR1_Ignore;
9388471Sache	if (cflag & CSTOPB)
9398471Sache		val |= COR1_2SB;
940105806Sjhb	rcout(sc, CD180_COR1, val);
9418471Sache
9428471Sache	/* Set FIFO threshold */
9439232Sache	val = ts->c_ospeed <= 4800 ? 1 : CD180_NFIFO / 2;
9449232Sache	inpflow = 0;
9459232Sache	if (   (iflag & IXOFF)
9469232Sache	    && (   ts->c_cc[VSTOP] != _POSIX_VDISABLE
9479232Sache		&& (   ts->c_cc[VSTART] != _POSIX_VDISABLE
9489232Sache		    || (iflag & IXANY)
9499232Sache		   )
9509232Sache	       )
9519232Sache	   ) {
9529232Sache		inpflow = 1;
9539232Sache		val |= COR3_SCDE|COR3_FCT;
9549232Sache	}
955105806Sjhb	rcout(sc, CD180_COR3, val);
9568471Sache
9578471Sache	/* Initialize on-chip automatic flow control */
9588471Sache	val = 0;
9599232Sache	rc->rc_flags &= ~(RC_CTSFLOW|RC_SEND_RDY);
9608471Sache	if (cflag & CCTS_OFLOW) {
9618471Sache		rc->rc_flags |= RC_CTSFLOW;
9629232Sache		val |= COR2_CtsAE;
9639232Sache	} else
9649232Sache		rc->rc_flags |= RC_SEND_RDY;
9659232Sache	if (tp->t_state & TS_TTSTOP)
9669232Sache		rc->rc_flags |= RC_OSUSP;
9678471Sache	else
9689232Sache		rc->rc_flags &= ~RC_OSUSP;
9698471Sache	if (cflag & CRTS_IFLOW)
9708471Sache		rc->rc_flags |= RC_RTSFLOW;
9719232Sache	else
9729232Sache		rc->rc_flags &= ~RC_RTSFLOW;
9738471Sache
9749232Sache	if (inpflow) {
9759232Sache		if (ts->c_cc[VSTART] != _POSIX_VDISABLE)
976105806Sjhb			rcout(sc, CD180_SCHR1, ts->c_cc[VSTART]);
977105806Sjhb		rcout(sc, CD180_SCHR2, ts->c_cc[VSTOP]);
9789232Sache		val |= COR2_TxIBE;
9799232Sache		if (iflag & IXANY)
9809232Sache			val |= COR2_IXM;
9818471Sache	}
9828471Sache
983105806Sjhb	rcout(sc, CD180_COR2, rc->rc_cor2 = val);
9848471Sache
985105806Sjhb	CCRCMD(sc, rc->rc_chan, CCR_CORCHG1 | CCR_CORCHG2 | CCR_CORCHG3);
9869232Sache
987131134Sphk	ttyldoptim(tp);
9888471Sache
9898471Sache	/* modem ctl */
9909232Sache	val = cflag & CLOCAL ? 0 : MCOR1_CDzd;
9919232Sache	if (cflag & CCTS_OFLOW)
9929232Sache		val |= MCOR1_CTSzd;
993105806Sjhb	rcout(sc, CD180_MCOR1, val);
9948471Sache
9959232Sache	val = cflag & CLOCAL ? 0 : MCOR2_CDod;
9969232Sache	if (cflag & CCTS_OFLOW)
9979232Sache		val |= MCOR2_CTSod;
998105806Sjhb	rcout(sc, CD180_MCOR2, val);
9999232Sache
10008471Sache	/* enable i/o and interrupts */
1001105806Sjhb	CCRCMD(sc, rc->rc_chan,
10029232Sache		CCR_XMTREN | ((cflag & CREAD) ? CCR_RCVREN : CCR_RCVRDIS));
1003105806Sjhb	WAITFORCCR(sc, rc->rc_chan);
10048471Sache
10059232Sache	rc->rc_ier = cflag & CLOCAL ? 0 : IER_CD;
10069232Sache	if (cflag & CCTS_OFLOW)
10079232Sache		rc->rc_ier |= IER_CTS;
10089232Sache	if (cflag & CREAD)
10099232Sache		rc->rc_ier |= IER_RxData;
10109232Sache	if (tp->t_state & TS_BUSY)
10119232Sache		rc->rc_ier |= IER_TxRdy;
10129232Sache	if (ts->c_ospeed != 0)
1013131096Sphk		rc_modem(tp, SER_DTR, 0);
10149232Sache	if ((cflag & CCTS_OFLOW) && (rc->rc_msvr & MSVR_CTS))
10159232Sache		rc->rc_flags |= RC_SEND_RDY;
1016105806Sjhb	rcout(sc, CD180_IER, rc->rc_ier);
10178471Sache	(void) splx(s);
10188471Sache	return 0;
10198471Sache}
10208471Sache
10219232Sache/* Re-initialize board after bogus interrupts */
1022105806Sjhbstatic void
1023105806Sjhbrc_reinit(struct rc_softc *sc)
10249232Sache{
1025105806Sjhb	struct rc_chans *rc;
1026105806Sjhb	int i;
10279232Sache
1028105806Sjhb	rc_hwreset(sc, RC_FAKEID);
1029105806Sjhb	rc = sc->sc_channels;
1030105806Sjhb	for (i = 0; i < CD180_NCHAN; i++, rc++)
1031136210Sphk		(void) rc_param(rc->rc_tp, &rc->rc_tp->t_termios);
10329232Sache}
10339232Sache
10348471Sache/* Modem control routines */
10358471Sache
1036105806Sjhbstatic int
1037131096Sphkrc_modem(struct tty *tp, int biton, int bitoff)
10388471Sache{
1039131096Sphk	struct rc_chans *rc;
1040105806Sjhb	struct rc_softc *sc;
1041105806Sjhb	u_char *dtr;
1042105806Sjhb	u_char msvr;
10438471Sache
1044136210Sphk	rc = tp->t_sc;
1045105806Sjhb	sc = rc->rc_rcb;
1046105806Sjhb	dtr = &sc->sc_dtr;
1047105806Sjhb	rcout(sc, CD180_CAR, rc->rc_chan);
10488471Sache
1049131096Sphk	if (biton == 0 && bitoff == 0) {
1050105806Sjhb		msvr = rc->rc_msvr = rcin(sc, CD180_MSVR);
10518471Sache
10528471Sache		if (msvr & MSVR_RTS)
1053131096Sphk			biton |= SER_RTS;
10548471Sache		if (msvr & MSVR_CTS)
1055131096Sphk			biton |= SER_CTS;
10568471Sache		if (msvr & MSVR_DSR)
1057131096Sphk			biton |= SER_DSR;
10588471Sache		if (msvr & MSVR_DTR)
1059131096Sphk			biton |= SER_DTR;
10609232Sache		if (msvr & MSVR_CD)
1061131096Sphk			biton |= SER_DCD;
1062105806Sjhb		if (~rcin(sc, RC_RIREG) & (1 << rc->rc_chan))
1063131096Sphk			biton |= SER_RI;
1064131096Sphk		return biton;
10658471Sache	}
1066131096Sphk	if (biton & SER_DTR)
1067131096Sphk		rcout(sc, RC_DTREG, ~(*dtr |= 1 << rc->rc_chan));
1068131096Sphk	if (bitoff & SER_DTR)
1069131096Sphk		rcout(sc, RC_DTREG, ~(*dtr &= ~(1 << rc->rc_chan)));
1070131096Sphk	msvr = rcin(sc, CD180_MSVR);
1071131096Sphk	if (biton & SER_DTR)
1072131096Sphk		msvr |= MSVR_DTR;
1073131096Sphk	if (bitoff & SER_DTR)
1074131096Sphk		msvr &= ~MSVR_DTR;
1075131096Sphk	if (biton & SER_RTS)
1076131096Sphk		msvr |= MSVR_RTS;
1077131096Sphk	if (bitoff & SER_RTS)
1078131096Sphk		msvr &= ~MSVR_RTS;
1079131096Sphk	rcout(sc, CD180_MSVR, msvr);
10808471Sache	return 0;
10818471Sache}
10828471Sache
1083131373Sphkstatic void
1084131096Sphkrc_break(struct tty *tp, int brk)
1085131096Sphk{
1086131096Sphk	struct rc_chans *rc;
1087131096Sphk
1088136210Sphk	rc = tp->t_sc;
1089131096Sphk
1090131096Sphk	if (brk)
1091131096Sphk		rc->rc_pendcmd = CD180_C_SBRK;
1092131096Sphk	else
1093131096Sphk		rc->rc_pendcmd = CD180_C_EBRK;
1094131096Sphk}
1095131096Sphk
1096105806Sjhb#define ERR(s) do {							\
1097105806Sjhb	device_printf(sc->sc_dev, "%s", "");				\
1098105806Sjhb	printf s ;							\
1099105806Sjhb	printf("\n");							\
1100105806Sjhb	(void) splx(old_level);						\
1101105806Sjhb	return 1;							\
1102105806Sjhb} while (0)
1103105806Sjhb
11048471Sache/* Test the board. */
1105105806Sjhbint
1106105806Sjhbrc_test(struct rc_softc *sc)
11078471Sache{
110816322Sgpalmer	int     chan = 0;
11098471Sache	int     i = 0, rcnt, old_level;
11108471Sache	unsigned int    iack, chipid;
11118471Sache	unsigned short  divs;
11128471Sache	static  u_char  ctest[] = "\377\125\252\045\244\0\377";
11138471Sache#define CTLEN   8
11148471Sache
11158471Sache	struct rtest {
11168471Sache		u_char  txbuf[CD180_NFIFO];     /* TX buffer  */
11178471Sache		u_char  rxbuf[CD180_NFIFO];     /* RX buffer  */
11188471Sache		int     rxptr;                  /* RX pointer */
11198471Sache		int     txptr;                  /* TX pointer */
11208471Sache	} tchans[CD180_NCHAN];
11218471Sache
11229232Sache	old_level = spltty();
11238471Sache
11248471Sache	chipid = RC_FAKEID;
11258471Sache
1126298955Spfg	/* First, reset board to initial state */
1127105806Sjhb	rc_hwreset(sc, chipid);
11288471Sache
11299232Sache	divs = RC_BRD(19200);
11309232Sache
11318471Sache	/* Initialize channels */
11328471Sache	for (chan = 0; chan < CD180_NCHAN; chan++) {
11338471Sache
11348471Sache		/* Select and reset channel */
1135105806Sjhb		rcout(sc, CD180_CAR, chan);
1136105806Sjhb		CCRCMD(sc, chan, CCR_ResetChan);
1137105806Sjhb		WAITFORCCR(sc, chan);
11388471Sache
11398471Sache		/* Set speed */
1140105806Sjhb		rcout(sc, CD180_RBPRL, divs & 0xFF);
1141105806Sjhb		rcout(sc, CD180_RBPRH, divs >> 8);
1142105806Sjhb		rcout(sc, CD180_TBPRL, divs & 0xFF);
1143105806Sjhb		rcout(sc, CD180_TBPRH, divs >> 8);
11448471Sache
11458471Sache		/* set timeout value */
1146105806Sjhb		rcout(sc, CD180_RTPR,  0);
11478471Sache
11488471Sache		/* Establish local loopback */
1149105806Sjhb		rcout(sc, CD180_COR1, COR1_NOPAR | COR1_8BITS | COR1_1SB);
1150105806Sjhb		rcout(sc, CD180_COR2, COR2_LLM);
1151105806Sjhb		rcout(sc, CD180_COR3, CD180_NFIFO);
1152105806Sjhb		CCRCMD(sc, chan, CCR_CORCHG1 | CCR_CORCHG2 | CCR_CORCHG3);
1153105806Sjhb		CCRCMD(sc, chan, CCR_RCVREN | CCR_XMTREN);
1154105806Sjhb		WAITFORCCR(sc, chan);
1155105806Sjhb		rcout(sc, CD180_MSVR, MSVR_RTS);
11568471Sache
11578471Sache		/* Fill TXBUF with test data */
11588471Sache		for (i = 0; i < CD180_NFIFO; i++) {
11598471Sache			tchans[chan].txbuf[i] = ctest[i];
11608471Sache			tchans[chan].rxbuf[i] = 0;
11618471Sache		}
11628471Sache		tchans[chan].txptr = tchans[chan].rxptr = 0;
11638471Sache
11648471Sache		/* Now, start transmit */
1165105806Sjhb		rcout(sc, CD180_IER, IER_TxMpty|IER_RxData);
11668471Sache	}
11678471Sache	/* Pseudo-interrupt poll stuff */
11688471Sache	for (rcnt = 10000; rcnt-- > 0; rcnt--) {
1169105806Sjhb		i = ~(rcin(sc, RC_BSR));
11708471Sache		if (i & RC_BSR_TOUT)
1171105806Sjhb			ERR(("BSR timeout bit set\n"));
11729232Sache		else if (i & RC_BSR_TXINT) {
1173105806Sjhb			iack = rcin(sc, RC_PILR_TX);
11748471Sache			if (iack != (GIVR_IT_TDI | chipid))
11758471Sache				ERR(("Bad TX intr ack (%02x != %02x)\n",
11768471Sache					iack, GIVR_IT_TDI | chipid));
1177105806Sjhb			chan = (rcin(sc, CD180_GICR) & GICR_CHAN) >> GICR_LSH;
11788471Sache			/* If no more data to transmit, disable TX intr */
11798471Sache			if (tchans[chan].txptr >= CD180_NFIFO) {
1180105806Sjhb				iack = rcin(sc, CD180_IER);
1181105806Sjhb				rcout(sc, CD180_IER, iack & ~IER_TxMpty);
11828471Sache			} else {
11838471Sache				for (iack = tchans[chan].txptr;
11848471Sache				    iack < CD180_NFIFO; iack++)
1185105806Sjhb					rcout(sc, CD180_TDR,
11868471Sache					    tchans[chan].txbuf[iack]);
11878471Sache				tchans[chan].txptr = iack;
11888471Sache			}
1189105806Sjhb			rcout(sc, CD180_EOIR, 0);
11909232Sache		} else if (i & RC_BSR_RXINT) {
11919232Sache			u_char ucnt;
11928471Sache
1193105806Sjhb			iack = rcin(sc, RC_PILR_RX);
11948471Sache			if (iack != (GIVR_IT_RGDI | chipid) &&
11958471Sache			    iack != (GIVR_IT_REI  | chipid))
11968471Sache				ERR(("Bad RX intr ack (%02x != %02x)\n",
1197105806Sjhb					iack, GIVR_IT_RGDI | chipid));
1198105806Sjhb			chan = (rcin(sc, CD180_GICR) & GICR_CHAN) >> GICR_LSH;
1199105806Sjhb			ucnt = rcin(sc, CD180_RDCR) & 0xF;
12008471Sache			while (ucnt-- > 0) {
1201105806Sjhb				iack = rcin(sc, CD180_RCSR);
12029232Sache				if (iack & RCSR_Timeout)
12038471Sache					break;
12048471Sache				if (iack & 0xF)
12058471Sache					ERR(("Bad char chan %d (RCSR = %02X)\n",
1206105806Sjhb					    chan, iack));
12078471Sache				if (tchans[chan].rxptr > CD180_NFIFO)
12088471Sache					ERR(("Got extra chars chan %d\n",
1209105806Sjhb					    chan));
12108471Sache				tchans[chan].rxbuf[tchans[chan].rxptr++] =
1211105806Sjhb					rcin(sc, CD180_RDR);
12128471Sache			}
1213105806Sjhb			rcout(sc, CD180_EOIR, 0);
12148471Sache		}
1215105806Sjhb		rcout(sc, RC_CTOUT, 0);
12168471Sache		for (iack = chan = 0; chan < CD180_NCHAN; chan++)
12178471Sache			if (tchans[chan].rxptr >= CD180_NFIFO)
12188471Sache				iack++;
12198471Sache		if (iack == CD180_NCHAN)
12208471Sache			break;
12218471Sache	}
12229232Sache	for (chan = 0; chan < CD180_NCHAN; chan++) {
12239232Sache		/* Select and reset channel */
1224105806Sjhb		rcout(sc, CD180_CAR, chan);
1225105806Sjhb		CCRCMD(sc, chan, CCR_ResetChan);
12269232Sache	}
12279232Sache
12288471Sache	if (!rcnt)
1229105806Sjhb		ERR(("looses characters during local loopback\n"));
12308471Sache	/* Now, check data */
12318471Sache	for (chan = 0; chan < CD180_NCHAN; chan++)
12328471Sache		for (i = 0; i < CD180_NFIFO; i++)
12338471Sache			if (ctest[i] != tchans[chan].rxbuf[i])
12348471Sache				ERR(("data mismatch chan %d ptr %d (%d != %d)\n",
1235105806Sjhb				    chan, i, ctest[i], tchans[chan].rxbuf[i]));
12368471Sache	(void) splx(old_level);
12378471Sache	return 0;
12388471Sache}
12398471Sache
12408471Sache#ifdef RCDEBUG
1241105806Sjhbstatic void
1242105806Sjhbprintrcflags(struct rc_chans *rc, char *comment)
12438471Sache{
1244105806Sjhb	struct rc_softc *sc;
12458471Sache	u_short f = rc->rc_flags;
12468471Sache
1247105806Sjhb	sc = rc->rc_rcb;
12489232Sache	printf("rc%d/%d: %s flags: %s%s%s%s%s%s%s%s%s%s%s%s\n",
12498471Sache		rc->rc_rcb->rcb_unit, rc->rc_chan, comment,
12508471Sache		(f & RC_DTR_OFF)?"DTR_OFF " :"",
12519232Sache		(f & RC_ACTOUT) ?"ACTOUT " :"",
12529232Sache		(f & RC_RTSFLOW)?"RTSFLOW " :"",
12539232Sache		(f & RC_CTSFLOW)?"CTSFLOW " :"",
12549232Sache		(f & RC_DORXFER)?"DORXFER " :"",
12559232Sache		(f & RC_DOXXFER)?"DOXXFER " :"",
12569232Sache		(f & RC_MODCHG) ?"MODCHG "  :"",
12579232Sache		(f & RC_OSUSP)  ?"OSUSP " :"",
12589232Sache		(f & RC_OSBUSY) ?"OSBUSY " :"",
12599232Sache		(f & RC_WAS_BUFOVFL) ?"BUFOVFL " :"",
12609232Sache		(f & RC_WAS_SILOVFL) ?"SILOVFL " :"",
12619232Sache		(f & RC_SEND_RDY) ?"SEND_RDY":"");
12629232Sache
1263105806Sjhb	rcout(sc, CD180_CAR, rc->rc_chan);
12649232Sache
12659232Sache	printf("rc%d/%d: msvr %02x ier %02x ccsr %02x\n",
12669232Sache		rc->rc_rcb->rcb_unit, rc->rc_chan,
1267105806Sjhb		rcin(sc, CD180_MSVR),
1268105806Sjhb		rcin(sc, CD180_IER),
1269105806Sjhb		rcin(sc, CD180_CCSR));
12708471Sache}
12718471Sache#endif /* RCDEBUG */
12728471Sache
12738471Sachestatic void
1274105806Sjhbrc_discard_output(struct rc_chans *rc)
12758471Sache{
1276106653Sjhb	critical_enter();
12778471Sache	if (rc->rc_flags & RC_DOXXFER) {
1278105806Sjhb		rc->rc_rcb->sc_scheduled_event -= LOTS_OF_EVENTS;
12798471Sache		rc->rc_flags &= ~RC_DOXXFER;
12808471Sache	}
12818471Sache	rc->rc_optr = rc->rc_obufend;
1282136210Sphk	rc->rc_tp->t_state &= ~TS_BUSY;
1283106653Sjhb	critical_exit();
1284136210Sphk	ttwwakeup(rc->rc_tp);
12858471Sache}
12868471Sache
12878471Sachestatic void
1288105806Sjhbrc_wait0(struct rc_softc *sc, int chan, int line)
12899232Sache{
12909232Sache	int rcnt;
12919232Sache
1292105806Sjhb	for (rcnt = 50; rcnt && rcin(sc, CD180_CCR); rcnt--)
129314441Srgrimes		DELAY(30);
12949232Sache	if (rcnt == 0)
1295105806Sjhb		device_printf(sc->sc_dev,
1296105806Sjhb		    "channel %d command timeout, rc.c line: %d\n", chan, line);
12979232Sache}
1298105806Sjhb
1299105806Sjhbstatic device_method_t rc_methods[] = {
1300105806Sjhb	/* Device interface */
1301105806Sjhb	DEVMETHOD(device_probe,		rc_probe),
1302105806Sjhb	DEVMETHOD(device_attach,	rc_attach),
1303105806Sjhb	DEVMETHOD(device_detach,	rc_detach),
1304105806Sjhb	{ 0, 0 }
1305105806Sjhb};
1306105806Sjhb
1307105806Sjhbstatic driver_t rc_driver = {
1308105806Sjhb	"rc",
1309105806Sjhb	rc_methods, sizeof(struct rc_softc),
1310105806Sjhb};
1311105806Sjhb
1312105806SjhbDRIVER_MODULE(rc, isa, rc_driver, rc_devclass, 0, 0);
1313