1*d47bcd29Schs /* $NetBSD: clmpcc.c,v 1.54 2019/11/10 21:16:35 chs Exp $ */
294719c7bSscw
394719c7bSscw /*-
494719c7bSscw * Copyright (c) 1999 The NetBSD Foundation, Inc.
594719c7bSscw * All rights reserved.
694719c7bSscw *
794719c7bSscw * This code is derived from software contributed to The NetBSD Foundation
894719c7bSscw * by Steve C. Woodford.
994719c7bSscw *
1094719c7bSscw * Redistribution and use in source and binary forms, with or without
1194719c7bSscw * modification, are permitted provided that the following conditions
1294719c7bSscw * are met:
1394719c7bSscw * 1. Redistributions of source code must retain the above copyright
1494719c7bSscw * notice, this list of conditions and the following disclaimer.
1594719c7bSscw * 2. Redistributions in binary form must reproduce the above copyright
1694719c7bSscw * notice, this list of conditions and the following disclaimer in the
1794719c7bSscw * documentation and/or other materials provided with the distribution.
1894719c7bSscw *
1994719c7bSscw * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
2094719c7bSscw * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
2194719c7bSscw * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
2294719c7bSscw * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
2394719c7bSscw * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
2494719c7bSscw * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
2594719c7bSscw * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
2694719c7bSscw * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
2794719c7bSscw * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
2894719c7bSscw * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
2994719c7bSscw * POSSIBILITY OF SUCH DAMAGE.
3094719c7bSscw */
3194719c7bSscw
3294719c7bSscw /*
3394719c7bSscw * Cirrus Logic CD2400/CD2401 Four Channel Multi-Protocol Comms. Controller.
3494719c7bSscw */
3594719c7bSscw
36a4bae8b0Slukem #include <sys/cdefs.h>
37*d47bcd29Schs __KERNEL_RCSID(0, "$NetBSD: clmpcc.c,v 1.54 2019/11/10 21:16:35 chs Exp $");
38a4bae8b0Slukem
3994719c7bSscw #include "opt_ddb.h"
4094719c7bSscw
4194719c7bSscw #include <sys/param.h>
4294719c7bSscw #include <sys/systm.h>
4394719c7bSscw #include <sys/ioctl.h>
4494719c7bSscw #include <sys/select.h>
4594719c7bSscw #include <sys/tty.h>
4694719c7bSscw #include <sys/proc.h>
4794719c7bSscw #include <sys/conf.h>
4894719c7bSscw #include <sys/file.h>
4994719c7bSscw #include <sys/uio.h>
5094719c7bSscw #include <sys/kernel.h>
5194719c7bSscw #include <sys/syslog.h>
5294719c7bSscw #include <sys/device.h>
5394719c7bSscw #include <sys/malloc.h>
542867b68bSelad #include <sys/kauth.h>
5546ed8f7dSad #include <sys/intr.h>
5694719c7bSscw
57a2a38285Sad #include <sys/bus.h>
583a5b0d88Sscw #include <machine/param.h>
5994719c7bSscw
6094719c7bSscw #include <dev/ic/clmpccreg.h>
6194719c7bSscw #include <dev/ic/clmpccvar.h>
6294719c7bSscw #include <dev/cons.h>
6394719c7bSscw
6482b8cabaSriastradh #include "ioconf.h"
6594719c7bSscw
6694719c7bSscw #if defined(CLMPCC_ONLY_BYTESWAP_LOW) && defined(CLMPCC_ONLY_BYTESWAP_HIGH)
6794719c7bSscw #error "CLMPCC_ONLY_BYTESWAP_LOW and CLMPCC_ONLY_BYTESWAP_HIGH are mutually exclusive."
6894719c7bSscw #endif
6994719c7bSscw
7094719c7bSscw
7118db93c7Sperry static int clmpcc_init(struct clmpcc_softc *sc);
7218db93c7Sperry static void clmpcc_shutdown(struct clmpcc_chan *);
7318db93c7Sperry static int clmpcc_speed(struct clmpcc_softc *, speed_t, int *, int *);
7418db93c7Sperry static int clmpcc_param(struct tty *, struct termios *);
7518db93c7Sperry static void clmpcc_set_params(struct clmpcc_chan *);
7618db93c7Sperry static void clmpcc_start(struct tty *);
7718db93c7Sperry static int clmpcc_modem_control(struct clmpcc_chan *, int, int);
7894719c7bSscw
796ca6d5d6Schristos #define CLMPCCUNIT(x) (TTUNIT(x) & ~0x3) // XXX >> 2?
806ca6d5d6Schristos #define CLMPCCCHAN(x) (TTUNIT(x) & 0x3)
816ca6d5d6Schristos #define CLMPCCDIALOUT(x) TTDIALOUT(x)
8294719c7bSscw
8394719c7bSscw /*
8494719c7bSscw * These should be in a header file somewhere...
8594719c7bSscw */
8694719c7bSscw #define ISCLR(v, f) (((v) & (f)) == 0)
8794719c7bSscw
8877a6b82bSgehenna dev_type_open(clmpccopen);
8977a6b82bSgehenna dev_type_close(clmpccclose);
9077a6b82bSgehenna dev_type_read(clmpccread);
9177a6b82bSgehenna dev_type_write(clmpccwrite);
9277a6b82bSgehenna dev_type_ioctl(clmpccioctl);
9377a6b82bSgehenna dev_type_stop(clmpccstop);
9477a6b82bSgehenna dev_type_tty(clmpcctty);
9577a6b82bSgehenna dev_type_poll(clmpccpoll);
9677a6b82bSgehenna
9777a6b82bSgehenna const struct cdevsw clmpcc_cdevsw = {
98a68f9396Sdholland .d_open = clmpccopen,
99a68f9396Sdholland .d_close = clmpccclose,
100a68f9396Sdholland .d_read = clmpccread,
101a68f9396Sdholland .d_write = clmpccwrite,
102a68f9396Sdholland .d_ioctl = clmpccioctl,
103a68f9396Sdholland .d_stop = clmpccstop,
104a68f9396Sdholland .d_tty = clmpcctty,
105a68f9396Sdholland .d_poll = clmpccpoll,
106a68f9396Sdholland .d_mmap = nommap,
107a68f9396Sdholland .d_kqfilter = ttykqfilter,
108f9228f42Sdholland .d_discard = nodiscard,
109a68f9396Sdholland .d_flag = D_TTY
11077a6b82bSgehenna };
11194719c7bSscw
11294719c7bSscw /*
11394719c7bSscw * Make this an option variable one can patch.
11494719c7bSscw */
11594719c7bSscw u_int clmpcc_ibuf_size = CLMPCC_RING_SIZE;
11694719c7bSscw
11794719c7bSscw
11894719c7bSscw /*
11994719c7bSscw * Things needed when the device is used as a console
12094719c7bSscw */
12194719c7bSscw static struct clmpcc_softc *cons_sc = NULL;
12294719c7bSscw static int cons_chan;
12394719c7bSscw static int cons_rate;
12494719c7bSscw
12518db93c7Sperry static int clmpcc_common_getc(struct clmpcc_softc *, int);
12618db93c7Sperry static void clmpcc_common_putc(struct clmpcc_softc *, int, int);
12718db93c7Sperry int clmpcccngetc(dev_t);
12818db93c7Sperry void clmpcccnputc(dev_t, int);
12994719c7bSscw
13094719c7bSscw
13194719c7bSscw /*
13294719c7bSscw * Convenience functions, inlined for speed
13394719c7bSscw */
13494719c7bSscw #define integrate static inline
13518db93c7Sperry integrate u_int8_t clmpcc_rdreg(struct clmpcc_softc *, u_int);
13618db93c7Sperry integrate void clmpcc_wrreg(struct clmpcc_softc *, u_int, u_int);
13718db93c7Sperry integrate u_int8_t clmpcc_rdreg_odd(struct clmpcc_softc *, u_int);
13818db93c7Sperry integrate void clmpcc_wrreg_odd(struct clmpcc_softc *, u_int, u_int);
13918db93c7Sperry integrate void clmpcc_wrtx_multi(struct clmpcc_softc *, u_int8_t *,
14018db93c7Sperry u_int);
14118db93c7Sperry integrate u_int8_t clmpcc_select_channel(struct clmpcc_softc *, u_int);
14218db93c7Sperry integrate void clmpcc_channel_cmd(struct clmpcc_softc *,int,int);
14318db93c7Sperry integrate void clmpcc_enable_transmitter(struct clmpcc_chan *);
14494719c7bSscw
14594719c7bSscw #define clmpcc_rd_msvr(s) clmpcc_rdreg_odd(s,CLMPCC_REG_MSVR)
14694719c7bSscw #define clmpcc_wr_msvr(s,r,v) clmpcc_wrreg_odd(s,r,v)
14794719c7bSscw #define clmpcc_wr_pilr(s,r,v) clmpcc_wrreg_odd(s,r,v)
14894719c7bSscw #define clmpcc_rd_rxdata(s) clmpcc_rdreg_odd(s,CLMPCC_REG_RDR)
14994719c7bSscw #define clmpcc_wr_txdata(s,v) clmpcc_wrreg_odd(s,CLMPCC_REG_TDR,v)
15094719c7bSscw
15194719c7bSscw
15294719c7bSscw integrate u_int8_t
clmpcc_rdreg(struct clmpcc_softc * sc,u_int offset)153454af1c0Sdsl clmpcc_rdreg(struct clmpcc_softc *sc, u_int offset)
15494719c7bSscw {
15594719c7bSscw #if !defined(CLMPCC_ONLY_BYTESWAP_LOW) && !defined(CLMPCC_ONLY_BYTESWAP_HIGH)
15694719c7bSscw offset ^= sc->sc_byteswap;
15794719c7bSscw #elif defined(CLMPCC_ONLY_BYTESWAP_HIGH)
15894719c7bSscw offset ^= CLMPCC_BYTESWAP_HIGH;
15994719c7bSscw #endif
16094719c7bSscw return bus_space_read_1(sc->sc_iot, sc->sc_ioh, offset);
16194719c7bSscw }
16294719c7bSscw
16394719c7bSscw integrate void
clmpcc_wrreg(struct clmpcc_softc * sc,u_int offset,u_int val)164454af1c0Sdsl clmpcc_wrreg(struct clmpcc_softc *sc, u_int offset, u_int val)
16594719c7bSscw {
16694719c7bSscw #if !defined(CLMPCC_ONLY_BYTESWAP_LOW) && !defined(CLMPCC_ONLY_BYTESWAP_HIGH)
16794719c7bSscw offset ^= sc->sc_byteswap;
16894719c7bSscw #elif defined(CLMPCC_ONLY_BYTESWAP_HIGH)
16994719c7bSscw offset ^= CLMPCC_BYTESWAP_HIGH;
17094719c7bSscw #endif
17194719c7bSscw bus_space_write_1(sc->sc_iot, sc->sc_ioh, offset, val);
17294719c7bSscw }
17394719c7bSscw
17494719c7bSscw integrate u_int8_t
clmpcc_rdreg_odd(struct clmpcc_softc * sc,u_int offset)175454af1c0Sdsl clmpcc_rdreg_odd(struct clmpcc_softc *sc, u_int offset)
17694719c7bSscw {
17794719c7bSscw #if !defined(CLMPCC_ONLY_BYTESWAP_LOW) && !defined(CLMPCC_ONLY_BYTESWAP_HIGH)
17894719c7bSscw offset ^= (sc->sc_byteswap & 2);
17994719c7bSscw #elif defined(CLMPCC_ONLY_BYTESWAP_HIGH)
18094719c7bSscw offset ^= (CLMPCC_BYTESWAP_HIGH & 2);
18194719c7bSscw #endif
18294719c7bSscw return bus_space_read_1(sc->sc_iot, sc->sc_ioh, offset);
18394719c7bSscw }
18494719c7bSscw
18594719c7bSscw integrate void
clmpcc_wrreg_odd(struct clmpcc_softc * sc,u_int offset,u_int val)186454af1c0Sdsl clmpcc_wrreg_odd(struct clmpcc_softc *sc, u_int offset, u_int val)
18794719c7bSscw {
18894719c7bSscw #if !defined(CLMPCC_ONLY_BYTESWAP_LOW) && !defined(CLMPCC_ONLY_BYTESWAP_HIGH)
18994719c7bSscw offset ^= (sc->sc_byteswap & 2);
19094719c7bSscw #elif defined(CLMPCC_ONLY_BYTESWAP_HIGH)
19194719c7bSscw offset ^= (CLMPCC_BYTESWAP_HIGH & 2);
19294719c7bSscw #endif
19394719c7bSscw bus_space_write_1(sc->sc_iot, sc->sc_ioh, offset, val);
19494719c7bSscw }
19594719c7bSscw
196ba922d31Sscw integrate void
clmpcc_wrtx_multi(struct clmpcc_softc * sc,u_int8_t * buff,u_int count)197454af1c0Sdsl clmpcc_wrtx_multi(struct clmpcc_softc *sc, u_int8_t *buff, u_int count)
198ba922d31Sscw {
199ba922d31Sscw u_int offset = CLMPCC_REG_TDR;
200ba922d31Sscw
201ba922d31Sscw #if !defined(CLMPCC_ONLY_BYTESWAP_LOW) && !defined(CLMPCC_ONLY_BYTESWAP_HIGH)
202ba922d31Sscw offset ^= (sc->sc_byteswap & 2);
203ba922d31Sscw #elif defined(CLMPCC_ONLY_BYTESWAP_HIGH)
204ba922d31Sscw offset ^= (CLMPCC_BYTESWAP_HIGH & 2);
205ba922d31Sscw #endif
206ba922d31Sscw bus_space_write_multi_1(sc->sc_iot, sc->sc_ioh, offset, buff, count);
207ba922d31Sscw }
208ba922d31Sscw
20994719c7bSscw integrate u_int8_t
clmpcc_select_channel(struct clmpcc_softc * sc,u_int new_chan)210454af1c0Sdsl clmpcc_select_channel(struct clmpcc_softc *sc, u_int new_chan)
21194719c7bSscw {
21294719c7bSscw u_int old_chan = clmpcc_rdreg_odd(sc, CLMPCC_REG_CAR);
21394719c7bSscw
21494719c7bSscw clmpcc_wrreg_odd(sc, CLMPCC_REG_CAR, new_chan);
21594719c7bSscw
21694719c7bSscw return old_chan;
21794719c7bSscw }
21894719c7bSscw
21994719c7bSscw integrate void
clmpcc_channel_cmd(struct clmpcc_softc * sc,int chan,int cmd)220454af1c0Sdsl clmpcc_channel_cmd(struct clmpcc_softc *sc, int chan, int cmd)
22194719c7bSscw {
22294719c7bSscw int i;
22394719c7bSscw
22494719c7bSscw for (i = 5000; i; i--) {
22594719c7bSscw if ( clmpcc_rdreg(sc, CLMPCC_REG_CCR) == 0 )
22694719c7bSscw break;
22794719c7bSscw delay(1);
22894719c7bSscw }
22994719c7bSscw
23094719c7bSscw if ( i == 0 )
23194719c7bSscw printf("%s: channel %d command timeout (idle)\n",
232cbab9cadSchs device_xname(sc->sc_dev), chan);
23394719c7bSscw
23494719c7bSscw clmpcc_wrreg(sc, CLMPCC_REG_CCR, cmd);
23594719c7bSscw }
23694719c7bSscw
23794719c7bSscw integrate void
clmpcc_enable_transmitter(struct clmpcc_chan * ch)238454af1c0Sdsl clmpcc_enable_transmitter(struct clmpcc_chan *ch)
23994719c7bSscw {
24094719c7bSscw u_int old;
2410ed9dc11Sscw int s;
24294719c7bSscw
24394719c7bSscw old = clmpcc_select_channel(ch->ch_sc, ch->ch_car);
24494719c7bSscw
2450ed9dc11Sscw s = splserial();
24694719c7bSscw clmpcc_wrreg(ch->ch_sc, CLMPCC_REG_IER,
24794719c7bSscw clmpcc_rdreg(ch->ch_sc, CLMPCC_REG_IER) | CLMPCC_IER_TX_EMPTY);
2480ed9dc11Sscw SET(ch->ch_tty->t_state, TS_BUSY);
2490ed9dc11Sscw splx(s);
2500ed9dc11Sscw
25194719c7bSscw clmpcc_select_channel(ch->ch_sc, old);
25294719c7bSscw }
25394719c7bSscw
25494719c7bSscw static int
clmpcc_speed(struct clmpcc_softc * sc,speed_t speed,int * cor,int * bpr)25582357f6dSdsl clmpcc_speed(struct clmpcc_softc *sc, speed_t speed, int *cor, int *bpr)
25694719c7bSscw {
25794719c7bSscw int c, co, br;
25894719c7bSscw
25994719c7bSscw for (co = 0, c = 8; c <= 2048; co++, c *= 4) {
26094719c7bSscw br = ((sc->sc_clk / c) / speed) - 1;
26194719c7bSscw if ( br < 0x100 ) {
26294719c7bSscw *cor = co;
26394719c7bSscw *bpr = br;
26494719c7bSscw return 0;
26594719c7bSscw }
26694719c7bSscw }
26794719c7bSscw
26894719c7bSscw return -1;
26994719c7bSscw }
27094719c7bSscw
27194719c7bSscw void
clmpcc_attach(struct clmpcc_softc * sc)272454af1c0Sdsl clmpcc_attach(struct clmpcc_softc *sc)
27394719c7bSscw {
27494719c7bSscw struct clmpcc_chan *ch;
27594719c7bSscw struct tty *tp;
27694719c7bSscw int chan;
27794719c7bSscw
27894719c7bSscw if ( cons_sc != NULL &&
27994719c7bSscw sc->sc_iot == cons_sc->sc_iot && sc->sc_ioh == cons_sc->sc_ioh )
28094719c7bSscw cons_sc = sc;
28194719c7bSscw
28294719c7bSscw /* Initialise the chip */
28394719c7bSscw clmpcc_init(sc);
28494719c7bSscw
28594719c7bSscw printf(": Cirrus Logic CD240%c Serial Controller\n",
28694719c7bSscw (clmpcc_rd_msvr(sc) & CLMPCC_MSVR_PORT_ID) ? '0' : '1');
28794719c7bSscw
288a1b8e79bSscw sc->sc_softintr_cookie =
28946ed8f7dSad softint_establish(SOFTINT_SERIAL, clmpcc_softintr, sc);
290a1b8e79bSscw if (sc->sc_softintr_cookie == NULL)
291a1b8e79bSscw panic("clmpcc_attach: softintr_establish");
29294719c7bSscw memset(&(sc->sc_chans[0]), 0, sizeof(sc->sc_chans));
29394719c7bSscw
29494719c7bSscw for (chan = 0; chan < CLMPCC_NUM_CHANS; chan++) {
29594719c7bSscw ch = &sc->sc_chans[chan];
29694719c7bSscw
29794719c7bSscw ch->ch_sc = sc;
29894719c7bSscw ch->ch_car = chan;
29994719c7bSscw
3002626d576Srmind tp = tty_alloc();
30194719c7bSscw tp->t_oproc = clmpcc_start;
30294719c7bSscw tp->t_param = clmpcc_param;
30394719c7bSscw
30494719c7bSscw ch->ch_tty = tp;
30594719c7bSscw
306*d47bcd29Schs ch->ch_ibuf = malloc(clmpcc_ibuf_size * 2, M_DEVBUF, M_WAITOK);
30794719c7bSscw ch->ch_ibuf_end = &(ch->ch_ibuf[clmpcc_ibuf_size * 2]);
30894719c7bSscw ch->ch_ibuf_rd = ch->ch_ibuf_wr = ch->ch_ibuf;
30994719c7bSscw
31094719c7bSscw tty_attach(tp);
31194719c7bSscw }
31294719c7bSscw
313cbab9cadSchs aprint_error_dev(sc->sc_dev, "%d channels available",
31494719c7bSscw CLMPCC_NUM_CHANS);
31594719c7bSscw if ( cons_sc == sc ) {
31694719c7bSscw printf(", console on channel %d.\n", cons_chan);
31794719c7bSscw SET(sc->sc_chans[cons_chan].ch_flags, CLMPCC_FLG_IS_CONSOLE);
31894719c7bSscw SET(sc->sc_chans[cons_chan].ch_openflags, TIOCFLAG_SOFTCAR);
31994719c7bSscw } else
32094719c7bSscw printf(".\n");
32194719c7bSscw }
32294719c7bSscw
32394719c7bSscw static int
clmpcc_init(struct clmpcc_softc * sc)324454af1c0Sdsl clmpcc_init(struct clmpcc_softc *sc)
32594719c7bSscw {
32674d943f3Schristos u_int tcor = 0, tbpr = 0;
32774d943f3Schristos u_int rcor = 0, rbpr = 0;
32894719c7bSscw u_int msvr_rts, msvr_dtr;
32994719c7bSscw u_int ccr;
33094719c7bSscw int is_console;
33194719c7bSscw int i;
33294719c7bSscw
33394719c7bSscw /*
33494719c7bSscw * All we're really concerned about here is putting the chip
33594719c7bSscw * into a quiescent state so that it won't do anything until
33694719c7bSscw * clmpccopen() is called. (Except the console channel.)
33794719c7bSscw */
33894719c7bSscw
33994719c7bSscw /*
34094719c7bSscw * If the chip is acting as console, set all channels to the supplied
34194719c7bSscw * console baud rate. Otherwise, plump for 9600.
34294719c7bSscw */
34394719c7bSscw if ( cons_sc &&
34494719c7bSscw sc->sc_ioh == cons_sc->sc_ioh && sc->sc_iot == cons_sc->sc_iot ) {
34594719c7bSscw clmpcc_speed(sc, cons_rate, &tcor, &tbpr);
34694719c7bSscw clmpcc_speed(sc, cons_rate, &rcor, &rbpr);
34794719c7bSscw is_console = 1;
34894719c7bSscw } else {
34994719c7bSscw clmpcc_speed(sc, 9600, &tcor, &tbpr);
35094719c7bSscw clmpcc_speed(sc, 9600, &rcor, &rbpr);
35194719c7bSscw is_console = 0;
35294719c7bSscw }
35394719c7bSscw
35494719c7bSscw /* Allow any pending output to be sent */
35594719c7bSscw delay(10000);
35694719c7bSscw
35794719c7bSscw /* Send the Reset All command to channel 0 (resets all channels!) */
35894719c7bSscw clmpcc_channel_cmd(sc, 0, CLMPCC_CCR_T0_RESET_ALL);
35994719c7bSscw
36094719c7bSscw delay(1000);
36194719c7bSscw
36294719c7bSscw /*
363f0a7346dSsnj * The chip will set its firmware revision register to a non-zero
36494719c7bSscw * value to indicate completion of reset.
36594719c7bSscw */
36694719c7bSscw for (i = 10000; clmpcc_rdreg(sc, CLMPCC_REG_GFRCR) == 0 && i; i--)
36794719c7bSscw delay(1);
36894719c7bSscw
36994719c7bSscw if ( i == 0 ) {
37094719c7bSscw /*
37194719c7bSscw * Watch out... If this chip is console, the message
37294719c7bSscw * probably won't be sent since we just reset it!
37394719c7bSscw */
374cbab9cadSchs aprint_error_dev(sc->sc_dev, "Failed to reset chip\n");
37594719c7bSscw return -1;
37694719c7bSscw }
37794719c7bSscw
37894719c7bSscw for (i = 0; i < CLMPCC_NUM_CHANS; i++) {
37994719c7bSscw clmpcc_select_channel(sc, i);
38094719c7bSscw
38194719c7bSscw /* All interrupts are disabled to begin with */
38294719c7bSscw clmpcc_wrreg(sc, CLMPCC_REG_IER, 0);
38394719c7bSscw
38494719c7bSscw /* Make sure the channel interrupts on the correct vectors */
38594719c7bSscw clmpcc_wrreg(sc, CLMPCC_REG_LIVR, sc->sc_vector_base);
38694719c7bSscw clmpcc_wr_pilr(sc, CLMPCC_REG_RPILR, sc->sc_rpilr);
38794719c7bSscw clmpcc_wr_pilr(sc, CLMPCC_REG_TPILR, sc->sc_tpilr);
38894719c7bSscw clmpcc_wr_pilr(sc, CLMPCC_REG_MPILR, sc->sc_mpilr);
38994719c7bSscw
39094719c7bSscw /* Receive timer prescaler set to 1ms */
39194719c7bSscw clmpcc_wrreg(sc, CLMPCC_REG_TPR,
39294719c7bSscw CLMPCC_MSEC_TO_TPR(sc->sc_clk, 1));
39394719c7bSscw
39494719c7bSscw /* We support Async mode only */
39594719c7bSscw clmpcc_wrreg(sc, CLMPCC_REG_CMR, CLMPCC_CMR_ASYNC);
39694719c7bSscw
39794719c7bSscw /* Set the required baud rate */
39894719c7bSscw clmpcc_wrreg(sc, CLMPCC_REG_TCOR, CLMPCC_TCOR_CLK(tcor));
39994719c7bSscw clmpcc_wrreg(sc, CLMPCC_REG_TBPR, tbpr);
40094719c7bSscw clmpcc_wrreg(sc, CLMPCC_REG_RCOR, CLMPCC_RCOR_CLK(rcor));
40194719c7bSscw clmpcc_wrreg(sc, CLMPCC_REG_RBPR, rbpr);
40294719c7bSscw
40394719c7bSscw /* Always default to 8N1 (XXX what about console?) */
40494719c7bSscw clmpcc_wrreg(sc, CLMPCC_REG_COR1, CLMPCC_COR1_CHAR_8BITS |
40594719c7bSscw CLMPCC_COR1_NO_PARITY |
40694719c7bSscw CLMPCC_COR1_IGNORE_PAR);
40794719c7bSscw
40894719c7bSscw clmpcc_wrreg(sc, CLMPCC_REG_COR2, 0);
40994719c7bSscw
41094719c7bSscw clmpcc_wrreg(sc, CLMPCC_REG_COR3, CLMPCC_COR3_STOP_1);
41194719c7bSscw
41294719c7bSscw clmpcc_wrreg(sc, CLMPCC_REG_COR4, CLMPCC_COR4_DSRzd |
41394719c7bSscw CLMPCC_COR4_CDzd |
41494719c7bSscw CLMPCC_COR4_CTSzd);
41594719c7bSscw
41694719c7bSscw clmpcc_wrreg(sc, CLMPCC_REG_COR5, CLMPCC_COR5_DSRod |
41794719c7bSscw CLMPCC_COR5_CDod |
41894719c7bSscw CLMPCC_COR5_CTSod |
41994719c7bSscw CLMPCC_COR5_FLOW_NORM);
42094719c7bSscw
42194719c7bSscw clmpcc_wrreg(sc, CLMPCC_REG_COR6, 0);
42294719c7bSscw clmpcc_wrreg(sc, CLMPCC_REG_COR7, 0);
42394719c7bSscw
42494719c7bSscw /* Set the receive FIFO timeout */
42594719c7bSscw clmpcc_wrreg(sc, CLMPCC_REG_RTPRl, CLMPCC_RTPR_DEFAULT);
42694719c7bSscw clmpcc_wrreg(sc, CLMPCC_REG_RTPRh, 0);
42794719c7bSscw
42894719c7bSscw /* At this point, we set up the console differently */
42994719c7bSscw if ( is_console && i == cons_chan ) {
43094719c7bSscw msvr_rts = CLMPCC_MSVR_RTS;
43194719c7bSscw msvr_dtr = CLMPCC_MSVR_DTR;
43294719c7bSscw ccr = CLMPCC_CCR_T0_RX_EN | CLMPCC_CCR_T0_TX_EN;
43394719c7bSscw } else {
43494719c7bSscw msvr_rts = 0;
43594719c7bSscw msvr_dtr = 0;
43694719c7bSscw ccr = CLMPCC_CCR_T0_RX_DIS | CLMPCC_CCR_T0_TX_DIS;
43794719c7bSscw }
43894719c7bSscw
43994719c7bSscw clmpcc_wrreg(sc, CLMPCC_REG_MSVR_RTS, msvr_rts);
44094719c7bSscw clmpcc_wrreg(sc, CLMPCC_REG_MSVR_DTR, msvr_dtr);
44194719c7bSscw clmpcc_channel_cmd(sc, i, CLMPCC_CCR_T0_INIT | ccr);
44294719c7bSscw delay(100);
44394719c7bSscw }
44494719c7bSscw
44594719c7bSscw return 0;
44694719c7bSscw }
44794719c7bSscw
44894719c7bSscw static void
clmpcc_shutdown(struct clmpcc_chan * ch)449454af1c0Sdsl clmpcc_shutdown(struct clmpcc_chan *ch)
45094719c7bSscw {
45194719c7bSscw int oldch;
45294719c7bSscw
45394719c7bSscw oldch = clmpcc_select_channel(ch->ch_sc, ch->ch_car);
45494719c7bSscw
45594719c7bSscw /* Turn off interrupts. */
45694719c7bSscw clmpcc_wrreg(ch->ch_sc, CLMPCC_REG_IER, 0);
45794719c7bSscw
45894719c7bSscw if ( ISCLR(ch->ch_flags, CLMPCC_FLG_IS_CONSOLE) ) {
45994719c7bSscw /* Disable the transmitter and receiver */
46094719c7bSscw clmpcc_channel_cmd(ch->ch_sc, ch->ch_car, CLMPCC_CCR_T0_RX_DIS |
46194719c7bSscw CLMPCC_CCR_T0_TX_DIS);
46294719c7bSscw
46394719c7bSscw /* Drop RTS and DTR */
46494719c7bSscw clmpcc_modem_control(ch, TIOCM_RTS | TIOCM_DTR, DMBIS);
46594719c7bSscw }
46694719c7bSscw
46794719c7bSscw clmpcc_select_channel(ch->ch_sc, oldch);
46894719c7bSscw }
46994719c7bSscw
47094719c7bSscw int
clmpccopen(dev_t dev,int flag,int mode,struct lwp * l)471669c3672Scegger clmpccopen(dev_t dev, int flag, int mode, struct lwp *l)
47294719c7bSscw {
47394719c7bSscw struct clmpcc_softc *sc;
47494719c7bSscw struct clmpcc_chan *ch;
47594719c7bSscw struct tty *tp;
47694719c7bSscw int oldch;
47794719c7bSscw int error;
47894719c7bSscw
479669c3672Scegger sc = device_lookup_private(&clmpcc_cd, CLMPCCUNIT(dev));
480cb450cc4Sthorpej if (sc == NULL)
481cb450cc4Sthorpej return (ENXIO);
48294719c7bSscw
48394719c7bSscw ch = &sc->sc_chans[CLMPCCCHAN(dev)];
48494719c7bSscw
48594719c7bSscw tp = ch->ch_tty;
48694719c7bSscw
487bdc51baeSelad if (kauth_authorize_device_tty(l->l_cred, KAUTH_DEVICE_TTY_OPEN, tp))
48894719c7bSscw return EBUSY;
48994719c7bSscw
49094719c7bSscw /*
49194719c7bSscw * Do the following iff this is a first open.
49294719c7bSscw */
49394719c7bSscw if ( ISCLR(tp->t_state, TS_ISOPEN) && tp->t_wopen == 0 ) {
49494719c7bSscw
49594719c7bSscw ttychars(tp);
49694719c7bSscw
49794719c7bSscw tp->t_dev = dev;
49894719c7bSscw tp->t_iflag = TTYDEF_IFLAG;
49994719c7bSscw tp->t_oflag = TTYDEF_OFLAG;
50094719c7bSscw tp->t_lflag = TTYDEF_LFLAG;
50194719c7bSscw tp->t_cflag = TTYDEF_CFLAG;
50294719c7bSscw tp->t_ospeed = tp->t_ispeed = TTYDEF_SPEED;
50394719c7bSscw
50494719c7bSscw if ( ISSET(ch->ch_openflags, TIOCFLAG_CLOCAL) )
50594719c7bSscw SET(tp->t_cflag, CLOCAL);
50694719c7bSscw if ( ISSET(ch->ch_openflags, TIOCFLAG_CRTSCTS) )
50794719c7bSscw SET(tp->t_cflag, CRTSCTS);
50894719c7bSscw if ( ISSET(ch->ch_openflags, TIOCFLAG_MDMBUF) )
50994719c7bSscw SET(tp->t_cflag, MDMBUF);
51094719c7bSscw
51194719c7bSscw /*
51294719c7bSscw * Override some settings if the channel is being
51394719c7bSscw * used as the console.
51494719c7bSscw */
51594719c7bSscw if ( ISSET(ch->ch_flags, CLMPCC_FLG_IS_CONSOLE) ) {
51694719c7bSscw tp->t_ospeed = tp->t_ispeed = cons_rate;
51794719c7bSscw SET(tp->t_cflag, CLOCAL);
51894719c7bSscw CLR(tp->t_cflag, CRTSCTS);
51994719c7bSscw CLR(tp->t_cflag, HUPCL);
52094719c7bSscw }
52194719c7bSscw
52294719c7bSscw ch->ch_control = 0;
52394719c7bSscw
52494719c7bSscw clmpcc_param(tp, &tp->t_termios);
52594719c7bSscw ttsetwater(tp);
52694719c7bSscw
52794719c7bSscw /* Clear the input ring */
52894719c7bSscw ch->ch_ibuf_rd = ch->ch_ibuf_wr = ch->ch_ibuf;
52994719c7bSscw
53094719c7bSscw /* Select the channel */
53194719c7bSscw oldch = clmpcc_select_channel(sc, ch->ch_car);
53294719c7bSscw
53394719c7bSscw /* Reset it */
53494719c7bSscw clmpcc_channel_cmd(sc, ch->ch_car, CLMPCC_CCR_T0_CLEAR |
53594719c7bSscw CLMPCC_CCR_T0_RX_EN |
53694719c7bSscw CLMPCC_CCR_T0_TX_EN);
53794719c7bSscw
53894719c7bSscw /* Enable receiver and modem change interrupts. */
53994719c7bSscw clmpcc_wrreg(sc, CLMPCC_REG_IER, CLMPCC_IER_MODEM |
54094719c7bSscw CLMPCC_IER_RET |
54194719c7bSscw CLMPCC_IER_RX_FIFO);
54294719c7bSscw
54394719c7bSscw /* Raise RTS and DTR */
54494719c7bSscw clmpcc_modem_control(ch, TIOCM_RTS | TIOCM_DTR, DMBIS);
54594719c7bSscw
54694719c7bSscw clmpcc_select_channel(sc, oldch);
5476fa3ab87Skleink }
54894719c7bSscw
54994719c7bSscw error = ttyopen(tp, CLMPCCDIALOUT(dev), ISSET(flag, O_NONBLOCK));
55094719c7bSscw if (error)
55194719c7bSscw goto bad;
55294719c7bSscw
5532a860a3dSeeh error = (*tp->t_linesw->l_open)(dev, tp);
55494719c7bSscw if (error)
55594719c7bSscw goto bad;
55694719c7bSscw
55794719c7bSscw return 0;
55894719c7bSscw
55994719c7bSscw bad:
56094719c7bSscw if ( ISCLR(tp->t_state, TS_ISOPEN) && tp->t_wopen == 0 ) {
56194719c7bSscw /*
56294719c7bSscw * We failed to open the device, and nobody else had it opened.
56394719c7bSscw * Clean up the state as appropriate.
56494719c7bSscw */
56594719c7bSscw clmpcc_shutdown(ch);
56694719c7bSscw }
56794719c7bSscw
56894719c7bSscw return error;
56994719c7bSscw }
57094719c7bSscw
57194719c7bSscw int
clmpccclose(dev_t dev,int flag,int mode,struct lwp * l)572669c3672Scegger clmpccclose(dev_t dev, int flag, int mode, struct lwp *l)
57394719c7bSscw {
574cb450cc4Sthorpej struct clmpcc_softc *sc =
575669c3672Scegger device_lookup_private(&clmpcc_cd, CLMPCCUNIT(dev));
57694719c7bSscw struct clmpcc_chan *ch = &sc->sc_chans[CLMPCCCHAN(dev)];
57794719c7bSscw struct tty *tp = ch->ch_tty;
57894719c7bSscw int s;
57994719c7bSscw
58094719c7bSscw if ( ISCLR(tp->t_state, TS_ISOPEN) )
58194719c7bSscw return 0;
58294719c7bSscw
5832a860a3dSeeh (*tp->t_linesw->l_close)(tp, flag);
58494719c7bSscw
58594719c7bSscw s = spltty();
58694719c7bSscw
58794719c7bSscw if ( ISCLR(tp->t_state, TS_ISOPEN) && tp->t_wopen == 0 ) {
58894719c7bSscw /*
58994719c7bSscw * Although we got a last close, the device may still be in
59094719c7bSscw * use; e.g. if this was the dialout node, and there are still
59194719c7bSscw * processes waiting for carrier on the non-dialout node.
59294719c7bSscw */
59394719c7bSscw clmpcc_shutdown(ch);
59494719c7bSscw }
59594719c7bSscw
59694719c7bSscw ttyclose(tp);
59794719c7bSscw
59894719c7bSscw splx(s);
59994719c7bSscw
60094719c7bSscw return 0;
60194719c7bSscw }
60294719c7bSscw
60394719c7bSscw int
clmpccread(dev_t dev,struct uio * uio,int flag)604669c3672Scegger clmpccread(dev_t dev, struct uio *uio, int flag)
60594719c7bSscw {
606669c3672Scegger struct clmpcc_softc *sc = device_lookup_private(&clmpcc_cd, CLMPCCUNIT(dev));
60794719c7bSscw struct tty *tp = sc->sc_chans[CLMPCCCHAN(dev)].ch_tty;
60894719c7bSscw
6092a860a3dSeeh return ((*tp->t_linesw->l_read)(tp, uio, flag));
61094719c7bSscw }
61194719c7bSscw
61294719c7bSscw int
clmpccwrite(dev_t dev,struct uio * uio,int flag)613669c3672Scegger clmpccwrite(dev_t dev, struct uio *uio, int flag)
61494719c7bSscw {
615669c3672Scegger struct clmpcc_softc *sc = device_lookup_private(&clmpcc_cd, CLMPCCUNIT(dev));
61694719c7bSscw struct tty *tp = sc->sc_chans[CLMPCCCHAN(dev)].ch_tty;
61794719c7bSscw
6182a860a3dSeeh return ((*tp->t_linesw->l_write)(tp, uio, flag));
61994719c7bSscw }
62094719c7bSscw
6212963ff5cSscw int
clmpccpoll(dev_t dev,int events,struct lwp * l)622669c3672Scegger clmpccpoll(dev_t dev, int events, struct lwp *l)
6232963ff5cSscw {
624669c3672Scegger struct clmpcc_softc *sc = device_lookup_private(&clmpcc_cd, CLMPCCUNIT(dev));
6252963ff5cSscw struct tty *tp = sc->sc_chans[CLMPCCCHAN(dev)].ch_tty;
6262963ff5cSscw
62795e1ffb1Schristos return ((*tp->t_linesw->l_poll)(tp, events, l));
6282963ff5cSscw }
6292963ff5cSscw
63094719c7bSscw struct tty *
clmpcctty(dev_t dev)631669c3672Scegger clmpcctty(dev_t dev)
63294719c7bSscw {
633669c3672Scegger struct clmpcc_softc *sc = device_lookup_private(&clmpcc_cd, CLMPCCUNIT(dev));
63494719c7bSscw
63594719c7bSscw return (sc->sc_chans[CLMPCCCHAN(dev)].ch_tty);
63694719c7bSscw }
63794719c7bSscw
63894719c7bSscw int
clmpccioctl(dev_t dev,u_long cmd,void * data,int flag,struct lwp * l)639669c3672Scegger clmpccioctl(dev_t dev, u_long cmd, void *data, int flag, struct lwp *l)
64094719c7bSscw {
641669c3672Scegger struct clmpcc_softc *sc = device_lookup_private(&clmpcc_cd, CLMPCCUNIT(dev));
64294719c7bSscw struct clmpcc_chan *ch = &sc->sc_chans[CLMPCCCHAN(dev)];
64394719c7bSscw struct tty *tp = ch->ch_tty;
64494719c7bSscw int error;
64594719c7bSscw
64695e1ffb1Schristos error = (*tp->t_linesw->l_ioctl)(tp, cmd, data, flag, l);
64731144d99Satatat if (error != EPASSTHROUGH)
64894719c7bSscw return error;
64994719c7bSscw
65095e1ffb1Schristos error = ttioctl(tp, cmd, data, flag, l);
65131144d99Satatat if (error != EPASSTHROUGH)
65294719c7bSscw return error;
65394719c7bSscw
65494719c7bSscw error = 0;
65594719c7bSscw
65694719c7bSscw switch (cmd) {
65794719c7bSscw case TIOCSBRK:
65894719c7bSscw SET(ch->ch_flags, CLMPCC_FLG_START_BREAK);
65994719c7bSscw clmpcc_enable_transmitter(ch);
66094719c7bSscw break;
66194719c7bSscw
66294719c7bSscw case TIOCCBRK:
66394719c7bSscw SET(ch->ch_flags, CLMPCC_FLG_END_BREAK);
66494719c7bSscw clmpcc_enable_transmitter(ch);
66594719c7bSscw break;
66694719c7bSscw
66794719c7bSscw case TIOCSDTR:
66894719c7bSscw clmpcc_modem_control(ch, TIOCM_DTR, DMBIS);
66994719c7bSscw break;
67094719c7bSscw
67194719c7bSscw case TIOCCDTR:
67294719c7bSscw clmpcc_modem_control(ch, TIOCM_DTR, DMBIC);
67394719c7bSscw break;
67494719c7bSscw
67594719c7bSscw case TIOCMSET:
67694719c7bSscw clmpcc_modem_control(ch, *((int *)data), DMSET);
67794719c7bSscw break;
67894719c7bSscw
67994719c7bSscw case TIOCMBIS:
68094719c7bSscw clmpcc_modem_control(ch, *((int *)data), DMBIS);
68194719c7bSscw break;
68294719c7bSscw
68394719c7bSscw case TIOCMBIC:
68494719c7bSscw clmpcc_modem_control(ch, *((int *)data), DMBIC);
68594719c7bSscw break;
68694719c7bSscw
68794719c7bSscw case TIOCMGET:
68894719c7bSscw *((int *)data) = clmpcc_modem_control(ch, 0, DMGET);
68994719c7bSscw break;
69094719c7bSscw
69194719c7bSscw case TIOCGFLAGS:
69294719c7bSscw *((int *)data) = ch->ch_openflags;
69394719c7bSscw break;
69494719c7bSscw
69594719c7bSscw case TIOCSFLAGS:
69665792a03Selad error = kauth_authorize_device_tty(l->l_cred,
69765792a03Selad KAUTH_DEVICE_TTY_PRIVSET, tp);
69894719c7bSscw if ( error )
69994719c7bSscw break;
70094719c7bSscw ch->ch_openflags = *((int *)data) &
70194719c7bSscw (TIOCFLAG_SOFTCAR | TIOCFLAG_CLOCAL |
70294719c7bSscw TIOCFLAG_CRTSCTS | TIOCFLAG_MDMBUF);
70394719c7bSscw if ( ISSET(ch->ch_flags, CLMPCC_FLG_IS_CONSOLE) )
70494719c7bSscw SET(ch->ch_openflags, TIOCFLAG_SOFTCAR);
70594719c7bSscw break;
70694719c7bSscw
70794719c7bSscw default:
70831144d99Satatat error = EPASSTHROUGH;
70994719c7bSscw break;
71094719c7bSscw }
71194719c7bSscw
71294719c7bSscw return error;
71394719c7bSscw }
71494719c7bSscw
71594719c7bSscw int
clmpcc_modem_control(struct clmpcc_chan * ch,int bits,int howto)716454af1c0Sdsl clmpcc_modem_control(struct clmpcc_chan *ch, int bits, int howto)
71794719c7bSscw {
71894719c7bSscw struct clmpcc_softc *sc = ch->ch_sc;
71994719c7bSscw struct tty *tp = ch->ch_tty;
72094719c7bSscw int oldch;
72194719c7bSscw int msvr;
72294719c7bSscw int rbits = 0;
72394719c7bSscw
72494719c7bSscw oldch = clmpcc_select_channel(sc, ch->ch_car);
72594719c7bSscw
72694719c7bSscw switch ( howto ) {
72794719c7bSscw case DMGET:
72894719c7bSscw msvr = clmpcc_rd_msvr(sc);
72994719c7bSscw
73094719c7bSscw if ( sc->sc_swaprtsdtr ) {
73194719c7bSscw rbits |= (msvr & CLMPCC_MSVR_RTS) ? TIOCM_DTR : 0;
73294719c7bSscw rbits |= (msvr & CLMPCC_MSVR_DTR) ? TIOCM_RTS : 0;
73394719c7bSscw } else {
73494719c7bSscw rbits |= (msvr & CLMPCC_MSVR_RTS) ? TIOCM_RTS : 0;
73594719c7bSscw rbits |= (msvr & CLMPCC_MSVR_DTR) ? TIOCM_DTR : 0;
73694719c7bSscw }
73794719c7bSscw
73894719c7bSscw rbits |= (msvr & CLMPCC_MSVR_CTS) ? TIOCM_CTS : 0;
73994719c7bSscw rbits |= (msvr & CLMPCC_MSVR_CD) ? TIOCM_CD : 0;
74094719c7bSscw rbits |= (msvr & CLMPCC_MSVR_DSR) ? TIOCM_DSR : 0;
74194719c7bSscw break;
74294719c7bSscw
74394719c7bSscw case DMSET:
74494719c7bSscw if ( sc->sc_swaprtsdtr ) {
74594719c7bSscw if ( ISCLR(tp->t_cflag, CRTSCTS) )
74694719c7bSscw clmpcc_wr_msvr(sc, CLMPCC_REG_MSVR_DTR,
74794719c7bSscw bits & TIOCM_RTS ? CLMPCC_MSVR_DTR : 0);
74894719c7bSscw clmpcc_wr_msvr(sc, CLMPCC_REG_MSVR_RTS,
74994719c7bSscw bits & TIOCM_DTR ? CLMPCC_MSVR_RTS : 0);
75094719c7bSscw } else {
75194719c7bSscw if ( ISCLR(tp->t_cflag, CRTSCTS) )
75294719c7bSscw clmpcc_wr_msvr(sc, CLMPCC_REG_MSVR_RTS,
75394719c7bSscw bits & TIOCM_RTS ? CLMPCC_MSVR_RTS : 0);
75494719c7bSscw clmpcc_wr_msvr(sc, CLMPCC_REG_MSVR_DTR,
75594719c7bSscw bits & TIOCM_DTR ? CLMPCC_MSVR_DTR : 0);
75694719c7bSscw }
75794719c7bSscw break;
75894719c7bSscw
75994719c7bSscw case DMBIS:
76094719c7bSscw if ( sc->sc_swaprtsdtr ) {
76194719c7bSscw if ( ISCLR(tp->t_cflag, CRTSCTS) && ISSET(bits, TIOCM_RTS) )
76294719c7bSscw clmpcc_wr_msvr(sc,CLMPCC_REG_MSVR_DTR, CLMPCC_MSVR_DTR);
76394719c7bSscw if ( ISSET(bits, TIOCM_DTR) )
76494719c7bSscw clmpcc_wr_msvr(sc,CLMPCC_REG_MSVR_RTS, CLMPCC_MSVR_RTS);
76594719c7bSscw } else {
76694719c7bSscw if ( ISCLR(tp->t_cflag, CRTSCTS) && ISSET(bits, TIOCM_RTS) )
76794719c7bSscw clmpcc_wr_msvr(sc,CLMPCC_REG_MSVR_RTS, CLMPCC_MSVR_RTS);
76894719c7bSscw if ( ISSET(bits, TIOCM_DTR) )
76994719c7bSscw clmpcc_wr_msvr(sc,CLMPCC_REG_MSVR_DTR, CLMPCC_MSVR_DTR);
77094719c7bSscw }
77194719c7bSscw break;
77294719c7bSscw
77394719c7bSscw case DMBIC:
77494719c7bSscw if ( sc->sc_swaprtsdtr ) {
77594719c7bSscw if ( ISCLR(tp->t_cflag, CRTSCTS) && ISCLR(bits, TIOCM_RTS) )
77694719c7bSscw clmpcc_wr_msvr(sc, CLMPCC_REG_MSVR_DTR, 0);
77794719c7bSscw if ( ISCLR(bits, TIOCM_DTR) )
77894719c7bSscw clmpcc_wr_msvr(sc, CLMPCC_REG_MSVR_RTS, 0);
77994719c7bSscw } else {
78094719c7bSscw if ( ISCLR(tp->t_cflag, CRTSCTS) && ISCLR(bits, TIOCM_RTS) )
78194719c7bSscw clmpcc_wr_msvr(sc, CLMPCC_REG_MSVR_RTS, 0);
78294719c7bSscw if ( ISCLR(bits, TIOCM_DTR) )
78394719c7bSscw clmpcc_wr_msvr(sc, CLMPCC_REG_MSVR_DTR, 0);
78494719c7bSscw }
78594719c7bSscw break;
78694719c7bSscw }
78794719c7bSscw
78894719c7bSscw clmpcc_select_channel(sc, oldch);
78994719c7bSscw
79094719c7bSscw return rbits;
79194719c7bSscw }
79294719c7bSscw
79394719c7bSscw static int
clmpcc_param(struct tty * tp,struct termios * t)794bc897d8eShe clmpcc_param(struct tty *tp, struct termios *t)
79594719c7bSscw {
796cb450cc4Sthorpej struct clmpcc_softc *sc =
797669c3672Scegger device_lookup_private(&clmpcc_cd, CLMPCCUNIT(tp->t_dev));
79894719c7bSscw struct clmpcc_chan *ch = &sc->sc_chans[CLMPCCCHAN(tp->t_dev)];
7990ed9dc11Sscw u_char cor;
8009737cf76Sscw u_char oldch;
8017cb12714Schs int oclk = 0, obpr = 0;
8027cb12714Schs int iclk = 0, ibpr = 0;
80394719c7bSscw int s;
80494719c7bSscw
80594719c7bSscw /* Check requested parameters. */
80694719c7bSscw if ( t->c_ospeed && clmpcc_speed(sc, t->c_ospeed, &oclk, &obpr) < 0 )
80794719c7bSscw return EINVAL;
80894719c7bSscw
80994719c7bSscw if ( t->c_ispeed && clmpcc_speed(sc, t->c_ispeed, &iclk, &ibpr) < 0 )
81094719c7bSscw return EINVAL;
81194719c7bSscw
81294719c7bSscw /*
81394719c7bSscw * For the console, always force CLOCAL and !HUPCL, so that the port
81494719c7bSscw * is always active.
81594719c7bSscw */
81694719c7bSscw if ( ISSET(ch->ch_openflags, TIOCFLAG_SOFTCAR) ||
81794719c7bSscw ISSET(ch->ch_flags, CLMPCC_FLG_IS_CONSOLE) ) {
81894719c7bSscw SET(t->c_cflag, CLOCAL);
81994719c7bSscw CLR(t->c_cflag, HUPCL);
82094719c7bSscw }
82194719c7bSscw
8220ed9dc11Sscw CLR(ch->ch_flags, CLMPCC_FLG_UPDATE_PARMS);
8230ed9dc11Sscw
82494719c7bSscw /* If ospeed it zero, hangup the line */
82594719c7bSscw clmpcc_modem_control(ch, TIOCM_DTR, t->c_ospeed == 0 ? DMBIC : DMBIS);
82694719c7bSscw
82794719c7bSscw if ( t->c_ospeed ) {
8280ed9dc11Sscw ch->ch_tcor = CLMPCC_TCOR_CLK(oclk);
8290ed9dc11Sscw ch->ch_tbpr = obpr;
8300ed9dc11Sscw } else {
8310ed9dc11Sscw ch->ch_tcor = 0;
8320ed9dc11Sscw ch->ch_tbpr = 0;
83394719c7bSscw }
83494719c7bSscw
83594719c7bSscw if ( t->c_ispeed ) {
8360ed9dc11Sscw ch->ch_rcor = CLMPCC_RCOR_CLK(iclk);
8370ed9dc11Sscw ch->ch_rbpr = ibpr;
8380ed9dc11Sscw } else {
8390ed9dc11Sscw ch->ch_rcor = 0;
8400ed9dc11Sscw ch->ch_rbpr = 0;
84194719c7bSscw }
84294719c7bSscw
84394719c7bSscw /* Work out value to use for COR1 */
84494719c7bSscw cor = 0;
84594719c7bSscw if ( ISSET(t->c_cflag, PARENB) ) {
84694719c7bSscw cor |= CLMPCC_COR1_NORM_PARITY;
84794719c7bSscw if ( ISSET(t->c_cflag, PARODD) )
84894719c7bSscw cor |= CLMPCC_COR1_ODD_PARITY;
84994719c7bSscw }
85094719c7bSscw
85194719c7bSscw if ( ISCLR(t->c_cflag, INPCK) )
85294719c7bSscw cor |= CLMPCC_COR1_IGNORE_PAR;
85394719c7bSscw
85494719c7bSscw switch ( t->c_cflag & CSIZE ) {
85594719c7bSscw case CS5:
85694719c7bSscw cor |= CLMPCC_COR1_CHAR_5BITS;
85794719c7bSscw break;
85894719c7bSscw
85994719c7bSscw case CS6:
86094719c7bSscw cor |= CLMPCC_COR1_CHAR_6BITS;
86194719c7bSscw break;
86294719c7bSscw
86394719c7bSscw case CS7:
86494719c7bSscw cor |= CLMPCC_COR1_CHAR_7BITS;
86594719c7bSscw break;
86694719c7bSscw
86794719c7bSscw case CS8:
86894719c7bSscw cor |= CLMPCC_COR1_CHAR_8BITS;
86994719c7bSscw break;
87094719c7bSscw }
87194719c7bSscw
8720ed9dc11Sscw ch->ch_cor1 = cor;
87394719c7bSscw
87494719c7bSscw /*
87594719c7bSscw * The only interesting bit in COR2 is 'CTS Automatic Enable'
87694719c7bSscw * when hardware flow control is in effect.
87794719c7bSscw */
8780ed9dc11Sscw ch->ch_cor2 = ISSET(t->c_cflag, CRTSCTS) ? CLMPCC_COR2_CtsAE : 0;
87994719c7bSscw
88094719c7bSscw /* COR3 needs to be set to the number of stop bits... */
8810ed9dc11Sscw ch->ch_cor3 = ISSET(t->c_cflag, CSTOPB) ? CLMPCC_COR3_STOP_2 :
88294719c7bSscw CLMPCC_COR3_STOP_1;
88394719c7bSscw
88494719c7bSscw /*
88594719c7bSscw * COR4 contains the FIFO threshold setting.
88694719c7bSscw * We adjust the threshold depending on the input speed...
88794719c7bSscw */
88894719c7bSscw if ( t->c_ispeed <= 1200 )
8890ed9dc11Sscw ch->ch_cor4 = CLMPCC_COR4_FIFO_LOW;
89094719c7bSscw else if ( t->c_ispeed <= 19200 )
8910ed9dc11Sscw ch->ch_cor4 = CLMPCC_COR4_FIFO_MED;
89294719c7bSscw else
8930ed9dc11Sscw ch->ch_cor4 = CLMPCC_COR4_FIFO_HIGH;
89494719c7bSscw
89594719c7bSscw /*
89694719c7bSscw * If chip is used with CTS and DTR swapped, we can enable
89794719c7bSscw * automatic hardware flow control.
89894719c7bSscw */
89994719c7bSscw if ( sc->sc_swaprtsdtr && ISSET(t->c_cflag, CRTSCTS) )
9000ed9dc11Sscw ch->ch_cor5 = CLMPCC_COR5_FLOW_NORM;
9010ed9dc11Sscw else
9020ed9dc11Sscw ch->ch_cor5 = 0;
90394719c7bSscw
9040ed9dc11Sscw s = splserial();
9050ed9dc11Sscw oldch = clmpcc_select_channel(sc, ch->ch_car);
9069737cf76Sscw
9079737cf76Sscw /*
9089737cf76Sscw * COR2 needs to be set immediately otherwise we might never get
9099737cf76Sscw * a Tx EMPTY interrupt to change the other parameters.
9109737cf76Sscw */
9119737cf76Sscw if ( clmpcc_rdreg(sc, CLMPCC_REG_COR2) != ch->ch_cor2 )
9129737cf76Sscw clmpcc_wrreg(sc, CLMPCC_REG_COR2, ch->ch_cor2);
9139737cf76Sscw
9149737cf76Sscw if ( ISCLR(ch->ch_tty->t_state, TS_BUSY) )
9150ed9dc11Sscw clmpcc_set_params(ch);
9169737cf76Sscw else
9170ed9dc11Sscw SET(ch->ch_flags, CLMPCC_FLG_UPDATE_PARMS);
9189737cf76Sscw
9199737cf76Sscw clmpcc_select_channel(sc, oldch);
9209737cf76Sscw
9210ed9dc11Sscw splx(s);
92294719c7bSscw
92394719c7bSscw return 0;
92494719c7bSscw }
92594719c7bSscw
92694719c7bSscw static void
clmpcc_set_params(struct clmpcc_chan * ch)927454af1c0Sdsl clmpcc_set_params(struct clmpcc_chan *ch)
9280ed9dc11Sscw {
9290ed9dc11Sscw struct clmpcc_softc *sc = ch->ch_sc;
930fc658e2dSscw u_char r1;
931fc658e2dSscw u_char r2;
9320ed9dc11Sscw
9333981e235Sscw if ( ch->ch_tcor || ch->ch_tbpr ) {
934fc658e2dSscw r1 = clmpcc_rdreg(sc, CLMPCC_REG_TCOR);
935fc658e2dSscw r2 = clmpcc_rdreg(sc, CLMPCC_REG_TBPR);
936fc658e2dSscw /* Only write Tx rate if it really has changed */
937fc658e2dSscw if ( ch->ch_tcor != r1 || ch->ch_tbpr != r2 ) {
9380ed9dc11Sscw clmpcc_wrreg(sc, CLMPCC_REG_TCOR, ch->ch_tcor);
9390ed9dc11Sscw clmpcc_wrreg(sc, CLMPCC_REG_TBPR, ch->ch_tbpr);
9400ed9dc11Sscw }
941fc658e2dSscw }
9420ed9dc11Sscw
9433981e235Sscw if ( ch->ch_rcor || ch->ch_rbpr ) {
944fc658e2dSscw r1 = clmpcc_rdreg(sc, CLMPCC_REG_RCOR);
945fc658e2dSscw r2 = clmpcc_rdreg(sc, CLMPCC_REG_RBPR);
946fc658e2dSscw /* Only write Rx rate if it really has changed */
947fc658e2dSscw if ( ch->ch_rcor != r1 || ch->ch_rbpr != r2 ) {
9480ed9dc11Sscw clmpcc_wrreg(sc, CLMPCC_REG_RCOR, ch->ch_rcor);
9490ed9dc11Sscw clmpcc_wrreg(sc, CLMPCC_REG_RBPR, ch->ch_rbpr);
9500ed9dc11Sscw }
951fc658e2dSscw }
9520ed9dc11Sscw
953fc658e2dSscw if ( clmpcc_rdreg(sc, CLMPCC_REG_COR1) != ch->ch_cor1 ) {
9540ed9dc11Sscw clmpcc_wrreg(sc, CLMPCC_REG_COR1, ch->ch_cor1);
955fc658e2dSscw /* Any change to COR1 requires an INIT command */
956fc658e2dSscw SET(ch->ch_flags, CLMPCC_FLG_NEED_INIT);
957fc658e2dSscw }
958fc658e2dSscw
959fc658e2dSscw if ( clmpcc_rdreg(sc, CLMPCC_REG_COR3) != ch->ch_cor3 )
9600ed9dc11Sscw clmpcc_wrreg(sc, CLMPCC_REG_COR3, ch->ch_cor3);
9610ed9dc11Sscw
962fc658e2dSscw r1 = clmpcc_rdreg(sc, CLMPCC_REG_COR4);
963fc658e2dSscw if ( ch->ch_cor4 != (r1 & CLMPCC_COR4_FIFO_MASK) ) {
964fc658e2dSscw /*
96554f42f17Sscw * Note: If the FIFO has changed, we always set it to
966fc658e2dSscw * zero here and disable the Receive Timeout interrupt.
967fc658e2dSscw * It's up to the Rx Interrupt handler to pick the
96854f42f17Sscw * appropriate moment to write the new FIFO length.
969fc658e2dSscw */
970fc658e2dSscw clmpcc_wrreg(sc, CLMPCC_REG_COR4, r1 & ~CLMPCC_COR4_FIFO_MASK);
971fc658e2dSscw r1 = clmpcc_rdreg(sc, CLMPCC_REG_IER);
972fc658e2dSscw clmpcc_wrreg(sc, CLMPCC_REG_IER, r1 & ~CLMPCC_IER_RET);
9730ed9dc11Sscw SET(ch->ch_flags, CLMPCC_FLG_FIFO_CLEAR);
974fc658e2dSscw }
9750ed9dc11Sscw
976fc658e2dSscw r1 = clmpcc_rdreg(sc, CLMPCC_REG_COR5);
977fc658e2dSscw if ( ch->ch_cor5 != (r1 & CLMPCC_COR5_FLOW_MASK) ) {
978fc658e2dSscw r1 &= ~CLMPCC_COR5_FLOW_MASK;
979fc658e2dSscw clmpcc_wrreg(sc, CLMPCC_REG_COR5, r1 | ch->ch_cor5);
980fc658e2dSscw }
9810ed9dc11Sscw }
9820ed9dc11Sscw
9830ed9dc11Sscw static void
clmpcc_start(struct tty * tp)984669c3672Scegger clmpcc_start(struct tty *tp)
98594719c7bSscw {
986cb450cc4Sthorpej struct clmpcc_softc *sc =
987669c3672Scegger device_lookup_private(&clmpcc_cd, CLMPCCUNIT(tp->t_dev));
98894719c7bSscw struct clmpcc_chan *ch = &sc->sc_chans[CLMPCCCHAN(tp->t_dev)];
989ba922d31Sscw u_int oldch;
99094719c7bSscw int s;
99194719c7bSscw
99294719c7bSscw s = spltty();
99394719c7bSscw
994ba922d31Sscw if ( ISCLR(tp->t_state, TS_TTSTOP | TS_TIMEOUT | TS_BUSY) ) {
995dc26833bSad ttypull(tp);
99654f42f17Sscw if ( ISSET(ch->ch_flags, CLMPCC_FLG_START_BREAK |
99754f42f17Sscw CLMPCC_FLG_END_BREAK) ||
99854f42f17Sscw tp->t_outq.c_cc > 0 ) {
99954f42f17Sscw
100054f42f17Sscw if ( ISCLR(ch->ch_flags, CLMPCC_FLG_START_BREAK |
100154f42f17Sscw CLMPCC_FLG_END_BREAK) ) {
1002ba922d31Sscw ch->ch_obuf_addr = tp->t_outq.c_cf;
1003ba922d31Sscw ch->ch_obuf_size = ndqb(&tp->t_outq, 0);
100454f42f17Sscw }
1005ba922d31Sscw
1006ba922d31Sscw /* Enable TX empty interrupts */
1007ba922d31Sscw oldch = clmpcc_select_channel(ch->ch_sc, ch->ch_car);
1008ba922d31Sscw clmpcc_wrreg(ch->ch_sc, CLMPCC_REG_IER,
1009ba922d31Sscw clmpcc_rdreg(ch->ch_sc, CLMPCC_REG_IER) |
1010ba922d31Sscw CLMPCC_IER_TX_EMPTY);
1011ba922d31Sscw clmpcc_select_channel(ch->ch_sc, oldch);
101294719c7bSscw SET(tp->t_state, TS_BUSY);
1013ba922d31Sscw }
101494719c7bSscw }
101594719c7bSscw
101694719c7bSscw splx(s);
101794719c7bSscw }
101894719c7bSscw
101994719c7bSscw /*
102094719c7bSscw * Stop output on a line.
102194719c7bSscw */
102294719c7bSscw void
clmpccstop(struct tty * tp,int flag)1023bc897d8eShe clmpccstop(struct tty *tp, int flag)
102494719c7bSscw {
1025cb450cc4Sthorpej struct clmpcc_softc *sc =
1026669c3672Scegger device_lookup_private(&clmpcc_cd, CLMPCCUNIT(tp->t_dev));
102794719c7bSscw struct clmpcc_chan *ch = &sc->sc_chans[CLMPCCCHAN(tp->t_dev)];
102894719c7bSscw int s;
102994719c7bSscw
1030ba922d31Sscw s = splserial();
103194719c7bSscw
103294719c7bSscw if ( ISSET(tp->t_state, TS_BUSY) ) {
103394719c7bSscw if ( ISCLR(tp->t_state, TS_TTSTOP) )
103494719c7bSscw SET(tp->t_state, TS_FLUSH);
1035ba922d31Sscw ch->ch_obuf_size = 0;
103694719c7bSscw }
103794719c7bSscw splx(s);
103894719c7bSscw }
103994719c7bSscw
104094719c7bSscw /*
104194719c7bSscw * RX interrupt routine
104294719c7bSscw */
104394719c7bSscw int
clmpcc_rxintr(void * arg)1044454af1c0Sdsl clmpcc_rxintr(void *arg)
104594719c7bSscw {
104694719c7bSscw struct clmpcc_softc *sc = (struct clmpcc_softc *)arg;
104794719c7bSscw struct clmpcc_chan *ch;
104894719c7bSscw u_int8_t *put, *end, rxd;
104994719c7bSscw u_char errstat;
10500ed9dc11Sscw u_char fc, tc;
10510ed9dc11Sscw u_char risr;
10520ed9dc11Sscw u_char rir;
105394719c7bSscw #ifdef DDB
105494719c7bSscw int saw_break = 0;
105594719c7bSscw #endif
105694719c7bSscw
105794719c7bSscw /* Receive interrupt active? */
105894719c7bSscw rir = clmpcc_rdreg(sc, CLMPCC_REG_RIR);
105994719c7bSscw
106094719c7bSscw /*
106194719c7bSscw * If we're using auto-vectored interrupts, we have to
106294719c7bSscw * verify if the chip is generating the interrupt.
106394719c7bSscw */
106494719c7bSscw if ( sc->sc_vector_base == 0 && (rir & CLMPCC_RIR_RACT) == 0 )
106594719c7bSscw return 0;
106694719c7bSscw
106794719c7bSscw /* Get pointer to interrupting channel's data structure */
106894719c7bSscw ch = &sc->sc_chans[rir & CLMPCC_RIR_RCN_MASK];
106994719c7bSscw
107094719c7bSscw /* Get the interrupt status register */
107194719c7bSscw risr = clmpcc_rdreg(sc, CLMPCC_REG_RISRl);
107294719c7bSscw if ( risr & CLMPCC_RISR_TIMEOUT ) {
107394719c7bSscw u_char reg;
107494719c7bSscw /*
107594719c7bSscw * Set the FIFO threshold to zero, and disable
107694719c7bSscw * further receive timeout interrupts.
107794719c7bSscw */
107894719c7bSscw reg = clmpcc_rdreg(sc, CLMPCC_REG_COR4);
10793981e235Sscw clmpcc_wrreg(sc, CLMPCC_REG_COR4, reg & ~CLMPCC_COR4_FIFO_MASK);
108094719c7bSscw reg = clmpcc_rdreg(sc, CLMPCC_REG_IER);
108194719c7bSscw clmpcc_wrreg(sc, CLMPCC_REG_IER, reg & ~CLMPCC_IER_RET);
108294719c7bSscw clmpcc_wrreg(sc, CLMPCC_REG_REOIR, CLMPCC_REOIR_NO_TRANS);
108394719c7bSscw SET(ch->ch_flags, CLMPCC_FLG_FIFO_CLEAR);
108494719c7bSscw return 1;
108594719c7bSscw }
108694719c7bSscw
108794719c7bSscw /* How many bytes are waiting in the FIFO? */
108894719c7bSscw fc = tc = clmpcc_rdreg(sc, CLMPCC_REG_RFOC) & CLMPCC_RFOC_MASK;
108994719c7bSscw
109094719c7bSscw #ifdef DDB
109194719c7bSscw /*
109294719c7bSscw * Allow BREAK on the console to drop to the debugger.
109394719c7bSscw */
109494719c7bSscw if ( ISSET(ch->ch_flags, CLMPCC_FLG_IS_CONSOLE) &&
109594719c7bSscw risr & CLMPCC_RISR_BREAK ) {
109694719c7bSscw saw_break = 1;
109794719c7bSscw }
109894719c7bSscw #endif
109994719c7bSscw
110094719c7bSscw if ( ISCLR(ch->ch_tty->t_state, TS_ISOPEN) && fc ) {
110194719c7bSscw /* Just get rid of the data */
110294719c7bSscw while ( fc-- )
110394719c7bSscw (void) clmpcc_rd_rxdata(sc);
110494719c7bSscw goto rx_done;
110594719c7bSscw }
110694719c7bSscw
110794719c7bSscw put = ch->ch_ibuf_wr;
110894719c7bSscw end = ch->ch_ibuf_end;
110994719c7bSscw
111094719c7bSscw /*
111194719c7bSscw * Note: The chip is completely hosed WRT these error
111294719c7bSscw * conditions; there seems to be no way to associate
111394719c7bSscw * the error with the correct character in the FIFO.
111494719c7bSscw * We compromise by tagging the first character we read
111594719c7bSscw * with the error. Not perfect, but there's no other way.
111694719c7bSscw */
111794719c7bSscw errstat = 0;
111894719c7bSscw if ( risr & CLMPCC_RISR_PARITY )
111994719c7bSscw errstat |= TTY_PE;
112094719c7bSscw if ( risr & (CLMPCC_RISR_FRAMING | CLMPCC_RISR_BREAK) )
112194719c7bSscw errstat |= TTY_FE;
112294719c7bSscw
112394719c7bSscw /*
112494719c7bSscw * As long as there are characters in the FIFO, and we
112594719c7bSscw * have space for them...
112694719c7bSscw */
112794719c7bSscw while ( fc > 0 ) {
112894719c7bSscw
112994719c7bSscw *put++ = rxd = clmpcc_rd_rxdata(sc);
113094719c7bSscw *put++ = errstat;
113194719c7bSscw
113294719c7bSscw if ( put >= end )
113394719c7bSscw put = ch->ch_ibuf;
113494719c7bSscw
113594719c7bSscw if ( put == ch->ch_ibuf_rd ) {
113694719c7bSscw put -= 2;
113794719c7bSscw if ( put < ch->ch_ibuf )
113894719c7bSscw put = end - 2;
113994719c7bSscw }
114094719c7bSscw
114194719c7bSscw errstat = 0;
114294719c7bSscw fc--;
114394719c7bSscw }
114494719c7bSscw
114594719c7bSscw ch->ch_ibuf_wr = put;
114694719c7bSscw
114794719c7bSscw #if 0
114894719c7bSscw if ( sc->sc_swaprtsdtr == 0 &&
114994719c7bSscw ISSET(cy->cy_tty->t_cflag, CRTSCTS) && cc < ch->ch_r_hiwat) {
115094719c7bSscw /*
115194719c7bSscw * If RTS/DTR are not physically swapped, we have to
115294719c7bSscw * do hardware flow control manually
115394719c7bSscw */
115494719c7bSscw clmpcc_wr_msvr(sc, CLMPCC_MSVR_RTS, 0);
115594719c7bSscw }
115694719c7bSscw #endif
115794719c7bSscw
115894719c7bSscw rx_done:
115994719c7bSscw if ( fc != tc ) {
116094719c7bSscw if ( ISSET(ch->ch_flags, CLMPCC_FLG_FIFO_CLEAR) ) {
116194719c7bSscw u_char reg;
116294719c7bSscw /*
116394719c7bSscw * Set the FIFO threshold to the preset value,
116494719c7bSscw * and enable receive timeout interrupts.
116594719c7bSscw */
116694719c7bSscw reg = clmpcc_rdreg(sc, CLMPCC_REG_COR4);
11670ed9dc11Sscw reg = (reg & ~CLMPCC_COR4_FIFO_MASK) | ch->ch_cor4;
116894719c7bSscw clmpcc_wrreg(sc, CLMPCC_REG_COR4, reg);
116994719c7bSscw reg = clmpcc_rdreg(sc, CLMPCC_REG_IER);
117094719c7bSscw clmpcc_wrreg(sc, CLMPCC_REG_IER, reg | CLMPCC_IER_RET);
117194719c7bSscw CLR(ch->ch_flags, CLMPCC_FLG_FIFO_CLEAR);
117294719c7bSscw }
117394719c7bSscw
117494719c7bSscw clmpcc_wrreg(sc, CLMPCC_REG_REOIR, 0);
117546ed8f7dSad softint_schedule(sc->sc_softintr_cookie);
117694719c7bSscw } else
117794719c7bSscw clmpcc_wrreg(sc, CLMPCC_REG_REOIR, CLMPCC_REOIR_NO_TRANS);
117894719c7bSscw
117994719c7bSscw #ifdef DDB
118094719c7bSscw /*
118194719c7bSscw * Only =after= we write REOIR is it safe to drop to the debugger.
118294719c7bSscw */
118394719c7bSscw if ( saw_break )
118494719c7bSscw Debugger();
118594719c7bSscw #endif
118694719c7bSscw
118794719c7bSscw return 1;
118894719c7bSscw }
118994719c7bSscw
119094719c7bSscw /*
119194719c7bSscw * Tx interrupt routine
119294719c7bSscw */
119394719c7bSscw int
clmpcc_txintr(void * arg)1194454af1c0Sdsl clmpcc_txintr(void *arg)
119594719c7bSscw {
119694719c7bSscw struct clmpcc_softc *sc = (struct clmpcc_softc *)arg;
119794719c7bSscw struct clmpcc_chan *ch;
11980ed9dc11Sscw u_char ftc, oftc;
119954f42f17Sscw u_char tir, teoir;
120054f42f17Sscw int etcmode = 0;
120194719c7bSscw
120294719c7bSscw /* Tx interrupt active? */
120394719c7bSscw tir = clmpcc_rdreg(sc, CLMPCC_REG_TIR);
120494719c7bSscw
120594719c7bSscw /*
120694719c7bSscw * If we're using auto-vectored interrupts, we have to
120794719c7bSscw * verify if the chip is generating the interrupt.
120894719c7bSscw */
120994719c7bSscw if ( sc->sc_vector_base == 0 && (tir & CLMPCC_TIR_TACT) == 0 )
121094719c7bSscw return 0;
121194719c7bSscw
121294719c7bSscw /* Get pointer to interrupting channel's data structure */
121394719c7bSscw ch = &sc->sc_chans[tir & CLMPCC_TIR_TCN_MASK];
121494719c7bSscw
121594719c7bSscw /* Dummy read of the interrupt status register */
121694719c7bSscw (void) clmpcc_rdreg(sc, CLMPCC_REG_TISR);
121794719c7bSscw
121854f42f17Sscw /* Make sure embedded transmit commands are disabled */
121954f42f17Sscw clmpcc_wrreg(sc, CLMPCC_REG_COR2, ch->ch_cor2);
122054f42f17Sscw
122194719c7bSscw ftc = oftc = clmpcc_rdreg(sc, CLMPCC_REG_TFTC);
122294719c7bSscw
12230ed9dc11Sscw /* Handle a delayed parameter change */
12240ed9dc11Sscw if ( ISSET(ch->ch_flags, CLMPCC_FLG_UPDATE_PARMS) ) {
1225ba922d31Sscw CLR(ch->ch_flags, CLMPCC_FLG_UPDATE_PARMS);
12260ed9dc11Sscw clmpcc_set_params(ch);
12270ed9dc11Sscw }
12280ed9dc11Sscw
1229ba922d31Sscw if ( ch->ch_obuf_size > 0 ) {
1230d1579b2dSriastradh u_int n = uimin(ch->ch_obuf_size, ftc);
123194719c7bSscw
1232ba922d31Sscw clmpcc_wrtx_multi(sc, ch->ch_obuf_addr, n);
12330ed9dc11Sscw
1234ba922d31Sscw ftc -= n;
1235ba922d31Sscw ch->ch_obuf_size -= n;
1236ba922d31Sscw ch->ch_obuf_addr += n;
123754f42f17Sscw
123894719c7bSscw } else {
123994719c7bSscw /*
124054f42f17Sscw * Check if we should start/stop a break
124194719c7bSscw */
124294719c7bSscw if ( ISSET(ch->ch_flags, CLMPCC_FLG_START_BREAK) ) {
124394719c7bSscw CLR(ch->ch_flags, CLMPCC_FLG_START_BREAK);
124454f42f17Sscw /* Enable embedded transmit commands */
124554f42f17Sscw clmpcc_wrreg(sc, CLMPCC_REG_COR2,
124654f42f17Sscw ch->ch_cor2 | CLMPCC_COR2_ETC);
124754f42f17Sscw clmpcc_wr_txdata(sc, CLMPCC_ETC_MAGIC);
124854f42f17Sscw clmpcc_wr_txdata(sc, CLMPCC_ETC_SEND_BREAK);
124954f42f17Sscw ftc -= 2;
125054f42f17Sscw etcmode = 1;
125194719c7bSscw }
125294719c7bSscw
125394719c7bSscw if ( ISSET(ch->ch_flags, CLMPCC_FLG_END_BREAK) ) {
125494719c7bSscw CLR(ch->ch_flags, CLMPCC_FLG_END_BREAK);
125554f42f17Sscw /* Enable embedded transmit commands */
125654f42f17Sscw clmpcc_wrreg(sc, CLMPCC_REG_COR2,
125754f42f17Sscw ch->ch_cor2 | CLMPCC_COR2_ETC);
125854f42f17Sscw clmpcc_wr_txdata(sc, CLMPCC_ETC_MAGIC);
125954f42f17Sscw clmpcc_wr_txdata(sc, CLMPCC_ETC_STOP_BREAK);
126054f42f17Sscw ftc -= 2;
126154f42f17Sscw etcmode = 1;
126254f42f17Sscw }
126394719c7bSscw }
126494719c7bSscw
126554f42f17Sscw tir = clmpcc_rdreg(sc, CLMPCC_REG_IER);
126654f42f17Sscw
126754f42f17Sscw if ( ftc != oftc ) {
126894719c7bSscw /*
126954f42f17Sscw * Enable/disable the Tx FIFO threshold interrupt
127054f42f17Sscw * according to how much data is in the FIFO.
127154f42f17Sscw * However, always disable the FIFO threshold if
127254f42f17Sscw * we've left the channel in 'Embedded Transmit
127354f42f17Sscw * Command' mode.
127494719c7bSscw */
127554f42f17Sscw if ( etcmode || ftc >= ch->ch_cor4 )
127654f42f17Sscw tir &= ~CLMPCC_IER_TX_FIFO;
127754f42f17Sscw else
127854f42f17Sscw tir |= CLMPCC_IER_TX_FIFO;
127954f42f17Sscw teoir = 0;
128054f42f17Sscw } else {
128154f42f17Sscw /*
128254f42f17Sscw * No data was sent.
128354f42f17Sscw * Disable transmit interrupt.
128454f42f17Sscw */
128554f42f17Sscw tir &= ~(CLMPCC_IER_TX_EMPTY|CLMPCC_IER_TX_FIFO);
128654f42f17Sscw teoir = CLMPCC_TEOIR_NO_TRANS;
128794719c7bSscw
1288ba922d31Sscw /*
1289ba922d31Sscw * Request Tx processing in the soft interrupt handler
1290ba922d31Sscw */
1291ba922d31Sscw ch->ch_tx_done = 1;
129246ed8f7dSad softint_schedule(sc->sc_softintr_cookie);
1293ba922d31Sscw }
12940ed9dc11Sscw
129554f42f17Sscw clmpcc_wrreg(sc, CLMPCC_REG_IER, tir);
129654f42f17Sscw clmpcc_wrreg(sc, CLMPCC_REG_TEOIR, teoir);
129794719c7bSscw
129894719c7bSscw return 1;
129994719c7bSscw }
130094719c7bSscw
130194719c7bSscw /*
130294719c7bSscw * Modem change interrupt routine
130394719c7bSscw */
130494719c7bSscw int
clmpcc_mdintr(void * arg)1305454af1c0Sdsl clmpcc_mdintr(void *arg)
130694719c7bSscw {
130794719c7bSscw struct clmpcc_softc *sc = (struct clmpcc_softc *)arg;
13080ed9dc11Sscw u_char mir;
130994719c7bSscw
131094719c7bSscw /* Modem status interrupt active? */
131194719c7bSscw mir = clmpcc_rdreg(sc, CLMPCC_REG_MIR);
131294719c7bSscw
131394719c7bSscw /*
131494719c7bSscw * If we're using auto-vectored interrupts, we have to
131594719c7bSscw * verify if the chip is generating the interrupt.
131694719c7bSscw */
131794719c7bSscw if ( sc->sc_vector_base == 0 && (mir & CLMPCC_MIR_MACT) == 0 )
131894719c7bSscw return 0;
131994719c7bSscw
132094719c7bSscw /* Dummy read of the interrupt status register */
132194719c7bSscw (void) clmpcc_rdreg(sc, CLMPCC_REG_MISR);
132294719c7bSscw
132394719c7bSscw /* Retrieve current status of modem lines. */
132494719c7bSscw sc->sc_chans[mir & CLMPCC_MIR_MCN_MASK].ch_control |=
132594719c7bSscw clmpcc_rd_msvr(sc) & CLMPCC_MSVR_CD;
132694719c7bSscw
132794719c7bSscw clmpcc_wrreg(sc, CLMPCC_REG_MEOIR, 0);
132846ed8f7dSad softint_schedule(sc->sc_softintr_cookie);
132994719c7bSscw
133094719c7bSscw return 1;
133194719c7bSscw }
133294719c7bSscw
1333b0ca3fa9Sscw void
clmpcc_softintr(void * arg)1334454af1c0Sdsl clmpcc_softintr(void *arg)
133594719c7bSscw {
133694719c7bSscw struct clmpcc_softc *sc = (struct clmpcc_softc *)arg;
133794719c7bSscw struct clmpcc_chan *ch;
13380ed9dc11Sscw struct tty *tp;
133918db93c7Sperry int (*rint)(int, struct tty *);
134094719c7bSscw u_char *get;
13410ed9dc11Sscw u_char reg;
134294719c7bSscw u_int c;
134394719c7bSscw int chan;
134494719c7bSscw
134594719c7bSscw /* Handle Modem state changes too... */
134694719c7bSscw
134794719c7bSscw for (chan = 0; chan < CLMPCC_NUM_CHANS; chan++) {
134894719c7bSscw ch = &sc->sc_chans[chan];
13490ed9dc11Sscw tp = ch->ch_tty;
13500ed9dc11Sscw
135194719c7bSscw get = ch->ch_ibuf_rd;
13522a860a3dSeeh rint = tp->t_linesw->l_rint;
135394719c7bSscw
135494719c7bSscw /* Squirt buffered incoming data into the tty layer */
135594719c7bSscw while ( get != ch->ch_ibuf_wr ) {
13560ed9dc11Sscw c = get[0];
13570ed9dc11Sscw c |= ((u_int)get[1]) << 8;
13580ed9dc11Sscw if ( (rint)(c, tp) == -1 ) {
13590ed9dc11Sscw ch->ch_ibuf_rd = ch->ch_ibuf_wr;
13600ed9dc11Sscw break;
13610ed9dc11Sscw }
136294719c7bSscw
13630ed9dc11Sscw get += 2;
136494719c7bSscw if ( get == ch->ch_ibuf_end )
136594719c7bSscw get = ch->ch_ibuf;
136694719c7bSscw
136794719c7bSscw ch->ch_ibuf_rd = get;
136894719c7bSscw }
13690ed9dc11Sscw
1370ba922d31Sscw /*
1371ba922d31Sscw * Is the transmitter idle and in need of attention?
1372ba922d31Sscw */
1373ba922d31Sscw if ( ch->ch_tx_done ) {
1374ba922d31Sscw ch->ch_tx_done = 0;
1375ba922d31Sscw
13760ed9dc11Sscw if ( ISSET(ch->ch_flags, CLMPCC_FLG_NEED_INIT) ) {
1377ba922d31Sscw clmpcc_channel_cmd(sc, ch->ch_car,
1378ba922d31Sscw CLMPCC_CCR_T0_INIT |
13790ed9dc11Sscw CLMPCC_CCR_T0_RX_EN |
13800ed9dc11Sscw CLMPCC_CCR_T0_TX_EN);
13810ed9dc11Sscw CLR(ch->ch_flags, CLMPCC_FLG_NEED_INIT);
13820ed9dc11Sscw
13830ed9dc11Sscw /*
1384fc658e2dSscw * Allow time for the channel to initialise.
1385ba922d31Sscw * (Empirically derived duration; there must
1386ba922d31Sscw * be another way to determine the command
1387fc658e2dSscw * has completed without busy-waiting...)
1388fc658e2dSscw */
1389fc658e2dSscw delay(800);
1390fc658e2dSscw
1391fc658e2dSscw /*
1392ba922d31Sscw * Update the tty layer's idea of the carrier
1393ba922d31Sscw * bit, in case we changed CLOCAL or MDMBUF.
1394ba922d31Sscw * We don't hang up here; we only do that by
1395ba922d31Sscw * explicit request.
13960ed9dc11Sscw */
13970ed9dc11Sscw reg = clmpcc_rd_msvr(sc) & CLMPCC_MSVR_CD;
13982a860a3dSeeh (*tp->t_linesw->l_modem)(tp, reg != 0);
13990ed9dc11Sscw }
14000ed9dc11Sscw
1401ba922d31Sscw CLR(tp->t_state, TS_BUSY);
1402ba922d31Sscw if ( ISSET(tp->t_state, TS_FLUSH) )
1403ba922d31Sscw CLR(tp->t_state, TS_FLUSH);
1404ba922d31Sscw else
1405ba922d31Sscw ndflush(&tp->t_outq,
1406ba922d31Sscw (int)(ch->ch_obuf_addr - tp->t_outq.c_cf));
1407ba922d31Sscw
14082a860a3dSeeh (*tp->t_linesw->l_start)(tp);
140994719c7bSscw }
1410ba922d31Sscw }
141194719c7bSscw }
141294719c7bSscw
141394719c7bSscw
141494719c7bSscw /*XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX*/
141594719c7bSscw /*XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX*/
141694719c7bSscw /*XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX*/
141794719c7bSscw /*
141894719c7bSscw * Following are all routines needed for a cd240x channel to act as console
141994719c7bSscw */
142094719c7bSscw int
clmpcc_cnattach(struct clmpcc_softc * sc,int chan,int rate)1421454af1c0Sdsl clmpcc_cnattach(struct clmpcc_softc *sc, int chan, int rate)
142294719c7bSscw {
142394719c7bSscw cons_sc = sc;
142494719c7bSscw cons_chan = chan;
142594719c7bSscw cons_rate = rate;
142694719c7bSscw
142705be817dSscw return (clmpcc_init(sc));
142894719c7bSscw }
142994719c7bSscw
143094719c7bSscw /*
143194719c7bSscw * The following functions are polled getc and putc routines, for console use.
143294719c7bSscw */
143394719c7bSscw static int
clmpcc_common_getc(struct clmpcc_softc * sc,int chan)1434454af1c0Sdsl clmpcc_common_getc(struct clmpcc_softc *sc, int chan)
143594719c7bSscw {
143694719c7bSscw u_char old_chan;
143794719c7bSscw u_char old_ier;
143894719c7bSscw u_char ch, rir, risr;
143994719c7bSscw int s;
144094719c7bSscw
144194719c7bSscw s = splhigh();
144294719c7bSscw
1443fc658e2dSscw /* Save the currently active channel */
144494719c7bSscw old_chan = clmpcc_select_channel(sc, chan);
144594719c7bSscw
144694719c7bSscw /*
144794719c7bSscw * We have to put the channel into RX interrupt mode before
144894719c7bSscw * trying to read the Rx data register. So save the previous
144994719c7bSscw * interrupt mode.
145094719c7bSscw */
145194719c7bSscw old_ier = clmpcc_rdreg(sc, CLMPCC_REG_IER);
145294719c7bSscw clmpcc_wrreg(sc, CLMPCC_REG_IER, CLMPCC_IER_RX_FIFO);
145394719c7bSscw
145494719c7bSscw /* Loop until we get a character */
145594719c7bSscw for (;;) {
145694719c7bSscw /*
145794719c7bSscw * The REN bit will be set in the Receive Interrupt Register
145894719c7bSscw * when the CD240x has a character to process. Remember,
145994719c7bSscw * the RACT bit won't be set until we generate an interrupt
146094719c7bSscw * acknowledge cycle via the MD front-end.
146194719c7bSscw */
146294719c7bSscw rir = clmpcc_rdreg(sc, CLMPCC_REG_RIR);
146394719c7bSscw if ( (rir & CLMPCC_RIR_REN) == 0 )
146494719c7bSscw continue;
146594719c7bSscw
146694719c7bSscw /* Acknowledge the request */
146794719c7bSscw if ( sc->sc_iackhook )
146894719c7bSscw (sc->sc_iackhook)(sc, CLMPCC_IACK_RX);
146994719c7bSscw
147094719c7bSscw /*
147194719c7bSscw * Determine if the interrupt is for the required channel
147294719c7bSscw * and if valid data is available.
147394719c7bSscw */
147494719c7bSscw rir = clmpcc_rdreg(sc, CLMPCC_REG_RIR);
147594719c7bSscw risr = clmpcc_rdreg(sc, CLMPCC_REG_RISR);
147694719c7bSscw if ( (rir & CLMPCC_RIR_RCN_MASK) != chan ||
147794719c7bSscw risr != 0 ) {
147894719c7bSscw /* Rx error, or BREAK */
147994719c7bSscw clmpcc_wrreg(sc, CLMPCC_REG_REOIR,
148094719c7bSscw CLMPCC_REOIR_NO_TRANS);
148194719c7bSscw } else {
148294719c7bSscw /* Dummy read of the FIFO count register */
148394719c7bSscw (void) clmpcc_rdreg(sc, CLMPCC_REG_RFOC);
148494719c7bSscw
148594719c7bSscw /* Fetch the received character */
148694719c7bSscw ch = clmpcc_rd_rxdata(sc);
148794719c7bSscw
148894719c7bSscw clmpcc_wrreg(sc, CLMPCC_REG_REOIR, 0);
148994719c7bSscw break;
149094719c7bSscw }
149194719c7bSscw }
149294719c7bSscw
1493fc658e2dSscw /* Restore the original IER and CAR register contents */
149494719c7bSscw clmpcc_wrreg(sc, CLMPCC_REG_IER, old_ier);
149594719c7bSscw clmpcc_select_channel(sc, old_chan);
149694719c7bSscw
149794719c7bSscw splx(s);
149894719c7bSscw return ch;
149994719c7bSscw }
150094719c7bSscw
150194719c7bSscw
150294719c7bSscw static void
clmpcc_common_putc(struct clmpcc_softc * sc,int chan,int c)1503454af1c0Sdsl clmpcc_common_putc(struct clmpcc_softc *sc, int chan, int c)
150494719c7bSscw {
150594719c7bSscw u_char old_chan;
150694719c7bSscw int s = splhigh();
150794719c7bSscw
1508fc658e2dSscw /* Save the currently active channel */
150994719c7bSscw old_chan = clmpcc_select_channel(sc, chan);
151094719c7bSscw
1511fc658e2dSscw /*
1512fc658e2dSscw * Since we can only access the Tx Data register from within
1513fc658e2dSscw * the interrupt handler, the easiest way to get console data
1514fc658e2dSscw * onto the wire is using one of the Special Transmit Character
1515fc658e2dSscw * registers.
1516fc658e2dSscw */
151794719c7bSscw clmpcc_wrreg(sc, CLMPCC_REG_SCHR4, c);
151894719c7bSscw clmpcc_wrreg(sc, CLMPCC_REG_STCR, CLMPCC_STCR_SSPC(4) |
151994719c7bSscw CLMPCC_STCR_SND_SPC);
152094719c7bSscw
1521fc658e2dSscw /* Wait until the "Send Special Character" command is accepted */
152294719c7bSscw while ( clmpcc_rdreg(sc, CLMPCC_REG_STCR) != 0 )
152394719c7bSscw ;
152494719c7bSscw
1525fc658e2dSscw /* Restore the previous channel selected */
152694719c7bSscw clmpcc_select_channel(sc, old_chan);
152794719c7bSscw
152894719c7bSscw splx(s);
152994719c7bSscw }
153094719c7bSscw
153194719c7bSscw int
clmpcccngetc(dev_t dev)1532454af1c0Sdsl clmpcccngetc(dev_t dev)
153394719c7bSscw {
153494719c7bSscw return clmpcc_common_getc(cons_sc, cons_chan);
153594719c7bSscw }
153694719c7bSscw
153794719c7bSscw /*
153894719c7bSscw * Console kernel output character routine.
153994719c7bSscw */
154094719c7bSscw void
clmpcccnputc(dev_t dev,int c)1541454af1c0Sdsl clmpcccnputc(dev_t dev, int c)
154294719c7bSscw {
154394719c7bSscw if ( c == '\n' )
154494719c7bSscw clmpcc_common_putc(cons_sc, cons_chan, '\r');
154594719c7bSscw
154694719c7bSscw clmpcc_common_putc(cons_sc, cons_chan, c);
154794719c7bSscw }
1548