1*dbfa10e5Sriastradh /* $NetBSD: epcom.c,v 1.36 2022/10/26 23:38:06 riastradh Exp $ */
24e771f5dSjoff /*
34e771f5dSjoff * Copyright (c) 1998, 1999, 2001, 2002, 2004 The NetBSD Foundation, Inc.
44e771f5dSjoff * All rights reserved.
54e771f5dSjoff *
64e771f5dSjoff * This code is derived from software contributed to The NetBSD Foundation
74e771f5dSjoff * by Jesse Off
84e771f5dSjoff *
94e771f5dSjoff * This code is derived from software contributed to The NetBSD Foundation
104e771f5dSjoff * by Ichiro FUKUHARA and Naoto Shimazaki.
114e771f5dSjoff *
124e771f5dSjoff * This code is derived from software contributed to The NetBSD Foundation
134e771f5dSjoff * by IWAMOTO Toshihiro.
144e771f5dSjoff *
154e771f5dSjoff * This code is derived from software contributed to The NetBSD Foundation
164e771f5dSjoff * by Charles M. Hannum.
174e771f5dSjoff *
184e771f5dSjoff * Redistribution and use in source and binary forms, with or without
194e771f5dSjoff * modification, are permitted provided that the following conditions
204e771f5dSjoff * are met:
214e771f5dSjoff * 1. Redistributions of source code must retain the above copyright
224e771f5dSjoff * notice, this list of conditions and the following disclaimer.
234e771f5dSjoff * 2. Redistributions in binary form must reproduce the above copyright
244e771f5dSjoff * notice, this list of conditions and the following disclaimer in the
254e771f5dSjoff * documentation and/or other materials provided with the distribution.
264e771f5dSjoff *
274e771f5dSjoff * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
284e771f5dSjoff * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
294e771f5dSjoff * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
304e771f5dSjoff * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
314e771f5dSjoff * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
324e771f5dSjoff * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
334e771f5dSjoff * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
344e771f5dSjoff * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
354e771f5dSjoff * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
364e771f5dSjoff * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
374e771f5dSjoff * POSSIBILITY OF SUCH DAMAGE.
384e771f5dSjoff */
394e771f5dSjoff
404e771f5dSjoff /*
414e771f5dSjoff * Copyright (c) 1991 The Regents of the University of California.
424e771f5dSjoff * All rights reserved.
434e771f5dSjoff *
444e771f5dSjoff * Redistribution and use in source and binary forms, with or without
454e771f5dSjoff * modification, are permitted provided that the following conditions
464e771f5dSjoff * are met:
474e771f5dSjoff * 1. Redistributions of source code must retain the above copyright
484e771f5dSjoff * notice, this list of conditions and the following disclaimer.
494e771f5dSjoff * 2. Redistributions in binary form must reproduce the above copyright
504e771f5dSjoff * notice, this list of conditions and the following disclaimer in the
514e771f5dSjoff * documentation and/or other materials provided with the distribution.
524e771f5dSjoff * 3. Neither the name of the University nor the names of its contributors
534e771f5dSjoff * may be used to endorse or promote products derived from this software
544e771f5dSjoff * without specific prior written permission.
554e771f5dSjoff *
564e771f5dSjoff * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
574e771f5dSjoff * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
584e771f5dSjoff * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
594e771f5dSjoff * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
604e771f5dSjoff * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
614e771f5dSjoff * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
624e771f5dSjoff * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
634e771f5dSjoff * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
644e771f5dSjoff * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
654e771f5dSjoff * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
664e771f5dSjoff * SUCH DAMAGE.
674e771f5dSjoff *
684e771f5dSjoff * @(#)com.c 7.5 (Berkeley) 5/16/91
694e771f5dSjoff */
704e771f5dSjoff
714e771f5dSjoff /*
724e771f5dSjoff * TODO: hardware flow control
734e771f5dSjoff */
744e771f5dSjoff
754e771f5dSjoff #include <sys/cdefs.h>
76*dbfa10e5Sriastradh __KERNEL_RCSID(0, "$NetBSD: epcom.c,v 1.36 2022/10/26 23:38:06 riastradh Exp $");
774e771f5dSjoff
784e771f5dSjoff #include "opt_kgdb.h"
794e771f5dSjoff #include "epcom.h"
804e771f5dSjoff
817b0b7dedStls #ifdef RND_COM
82445478ceSriastradh #include <sys/rndsource.h>
834e771f5dSjoff #endif
844e771f5dSjoff
854e771f5dSjoff /*
864e771f5dSjoff * Override cnmagic(9) macro before including <sys/systm.h>.
874e771f5dSjoff * We need to know if cn_check_magic triggered debugger, so set a flag.
884e771f5dSjoff * Callers of cn_check_magic must declare int cn_trapped = 0;
894e771f5dSjoff * XXX: this is *ugly*!
904e771f5dSjoff */
914e771f5dSjoff #define cn_trap() \
924e771f5dSjoff do { \
934e771f5dSjoff console_debugger(); \
944e771f5dSjoff cn_trapped = 1; \
954e771f5dSjoff } while (/* CONSTCOND */ 0)
964e771f5dSjoff
974e771f5dSjoff
984e771f5dSjoff #include <sys/param.h>
994e771f5dSjoff #include <sys/systm.h>
1004e771f5dSjoff #include <sys/types.h>
1014e771f5dSjoff #include <sys/conf.h>
1024e771f5dSjoff #include <sys/file.h>
1034e771f5dSjoff #include <sys/device.h>
1044e771f5dSjoff #include <sys/kernel.h>
10538fdb085Sthorpej #include <sys/kmem.h>
1064e771f5dSjoff #include <sys/tty.h>
1074e771f5dSjoff #include <sys/uio.h>
1084e771f5dSjoff #include <sys/vnode.h>
1098ccb6c93Selad #include <sys/kauth.h>
1104e771f5dSjoff
1114e771f5dSjoff #include <machine/intr.h>
112cf10107dSdyoung #include <sys/bus.h>
1134e771f5dSjoff
114*dbfa10e5Sriastradh #include <ddb/db_active.h>
115*dbfa10e5Sriastradh
1164e771f5dSjoff #include <arm/ep93xx/epcomreg.h>
1174e771f5dSjoff #include <arm/ep93xx/epcomvar.h>
1184e771f5dSjoff #include <arm/ep93xx/ep93xxreg.h>
1194e771f5dSjoff #include <arm/ep93xx/ep93xxvar.h>
1204e771f5dSjoff
1214e771f5dSjoff #include <dev/cons.h>
1224e771f5dSjoff
1234e771f5dSjoff static int epcomparam(struct tty *, struct termios *);
1244e771f5dSjoff static void epcomstart(struct tty *);
1254e771f5dSjoff static int epcomhwiflow(struct tty *, int);
1264e771f5dSjoff
1274e771f5dSjoff static u_int cflag2lcrhi(tcflag_t);
1284e771f5dSjoff static void epcom_iflush(struct epcom_softc *);
1294e771f5dSjoff static void epcom_set(struct epcom_softc *);
1304e771f5dSjoff
1314e771f5dSjoff int epcomcngetc(dev_t);
1324e771f5dSjoff void epcomcnputc(dev_t, int);
1334e771f5dSjoff void epcomcnpollc(dev_t, int);
1344e771f5dSjoff
1354e771f5dSjoff static void epcomsoft(void* arg);
1364e771f5dSjoff inline static void epcom_txsoft(struct epcom_softc *, struct tty *);
1374e771f5dSjoff inline static void epcom_rxsoft(struct epcom_softc *, struct tty *);
1384e771f5dSjoff
1394e771f5dSjoff void epcomcnprobe(struct consdev *);
1404e771f5dSjoff void epcomcninit(struct consdev *);
1414e771f5dSjoff
1424e771f5dSjoff static struct epcom_cons_softc {
1434e771f5dSjoff bus_space_tag_t sc_iot;
1444e771f5dSjoff bus_space_handle_t sc_ioh;
1454e771f5dSjoff bus_addr_t sc_hwbase;
1464e771f5dSjoff int sc_ospeed;
1474e771f5dSjoff tcflag_t sc_cflag;
1484e771f5dSjoff } epcomcn_sc;
1494e771f5dSjoff
1509d134cf5Sskrll static int epcom_common_getc(struct epcom_cons_softc *, dev_t);
1519d134cf5Sskrll static void epcom_common_putc(struct epcom_cons_softc *, int);
1529d134cf5Sskrll static void epcominit(struct epcom_cons_softc *, bus_space_tag_t,
1539d134cf5Sskrll bus_addr_t, bus_space_handle_t, int, tcflag_t);
1549d134cf5Sskrll
1554e771f5dSjoff static struct cnm_state epcom_cnm_state;
1564e771f5dSjoff
1574e771f5dSjoff extern struct cfdriver epcom_cd;
1584e771f5dSjoff
1594e771f5dSjoff dev_type_open(epcomopen);
1604e771f5dSjoff dev_type_close(epcomclose);
1614e771f5dSjoff dev_type_read(epcomread);
1624e771f5dSjoff dev_type_write(epcomwrite);
1634e771f5dSjoff dev_type_ioctl(epcomioctl);
1644e771f5dSjoff dev_type_stop(epcomstop);
1654e771f5dSjoff dev_type_tty(epcomtty);
1664e771f5dSjoff dev_type_poll(epcompoll);
1674e771f5dSjoff
1684e771f5dSjoff const struct cdevsw epcom_cdevsw = {
169a68f9396Sdholland .d_open = epcomopen,
170a68f9396Sdholland .d_close = epcomclose,
171a68f9396Sdholland .d_read = epcomread,
172a68f9396Sdholland .d_write = epcomwrite,
173a68f9396Sdholland .d_ioctl = epcomioctl,
174a68f9396Sdholland .d_stop = epcomstop,
175a68f9396Sdholland .d_tty = epcomtty,
176a68f9396Sdholland .d_poll = epcompoll,
177a68f9396Sdholland .d_mmap = nommap,
178a68f9396Sdholland .d_kqfilter = ttykqfilter,
179f9228f42Sdholland .d_discard = nodiscard,
180a68f9396Sdholland .d_flag = D_TTY
1814e771f5dSjoff };
1824e771f5dSjoff
1834e771f5dSjoff struct consdev epcomcons = {
1844e771f5dSjoff NULL, NULL, epcomcngetc, epcomcnputc, epcomcnpollc, NULL,
1854e771f5dSjoff NULL, NULL, NODEV, CN_NORMAL
1864e771f5dSjoff };
1874e771f5dSjoff
1889d134cf5Sskrll #if defined(KGDB)
1899d134cf5Sskrll #include <sys/kgdb.h>
1909d134cf5Sskrll
1919d134cf5Sskrll static int epcom_kgdb_getc(void *);
1929d134cf5Sskrll static void epcom_kgdb_putc(void *, int);
1939d134cf5Sskrll
1949d134cf5Sskrll /*
1959d134cf5Sskrll * Reuse the console softc structure because
1969d134cf5Sskrll * we'll be reusing the console I/O code.
1979d134cf5Sskrll */
1989d134cf5Sskrll static struct epcom_cons_softc kgdb_sc;
1999d134cf5Sskrll #endif
2009d134cf5Sskrll
2014e771f5dSjoff #ifndef DEFAULT_COMSPEED
2024e771f5dSjoff #define DEFAULT_COMSPEED 115200
2034e771f5dSjoff #endif
2044e771f5dSjoff
205a0a6c85fSchristos #define COMUNIT(x) TTUNIT(x)
206a0a6c85fSchristos #define COMDIALOUT(x) TTDIALOUT(x)
2074e771f5dSjoff
2084e771f5dSjoff #define COM_ISALIVE(sc) ((sc)->enabled != 0 && \
209cbab9cadSchs device_is_active((sc)->sc_dev))
2104e771f5dSjoff
2114e771f5dSjoff void
epcom_attach_subr(struct epcom_softc * sc)2120e9d2fc1Schristos epcom_attach_subr(struct epcom_softc *sc)
2134e771f5dSjoff {
2144e771f5dSjoff struct tty *tp;
2154e771f5dSjoff
2164e771f5dSjoff if (sc->sc_iot == epcomcn_sc.sc_iot
2174e771f5dSjoff && sc->sc_hwbase == epcomcn_sc.sc_hwbase) {
2184e771f5dSjoff sc->sc_lcrlo = EPCOMSPEED2BRD(epcomcn_sc.sc_ospeed) & 0xff;
2194e771f5dSjoff sc->sc_lcrmid = EPCOMSPEED2BRD(epcomcn_sc.sc_ospeed) >> 8;
2204e771f5dSjoff
2214e771f5dSjoff /* Make sure the console is always "hardwired". */
2224e771f5dSjoff delay(10000); /* wait for output to finish */
2234e771f5dSjoff SET(sc->sc_hwflags, COM_HW_CONSOLE);
2244e771f5dSjoff SET(sc->sc_swflags, TIOCFLAG_SOFTCAR);
2254e771f5dSjoff }
2264e771f5dSjoff
2272626d576Srmind tp = tty_alloc();
2284e771f5dSjoff tp->t_oproc = epcomstart;
2294e771f5dSjoff tp->t_param = epcomparam;
2304e771f5dSjoff tp->t_hwiflow = epcomhwiflow;
2314e771f5dSjoff
2324e771f5dSjoff sc->sc_tty = tp;
23338fdb085Sthorpej sc->sc_rbuf = kmem_alloc(EPCOM_RING_SIZE << 1, KM_SLEEP);
2344e771f5dSjoff sc->sc_rbput = sc->sc_rbget = sc->sc_rbuf;
2354e771f5dSjoff sc->sc_rbavail = EPCOM_RING_SIZE;
2364e771f5dSjoff sc->sc_ebuf = sc->sc_rbuf + (EPCOM_RING_SIZE << 1);
2374e771f5dSjoff sc->sc_tbc = 0;
2384e771f5dSjoff
2394e771f5dSjoff sc->sc_lcrlo = EPCOMSPEED2BRD(DEFAULT_COMSPEED) & 0xff;
2404e771f5dSjoff sc->sc_lcrmid = EPCOMSPEED2BRD(DEFAULT_COMSPEED) >> 8;
2414e771f5dSjoff sc->sc_lcrhi = cflag2lcrhi(CS8); /* 8N1 */
2424e771f5dSjoff
2434e771f5dSjoff tty_attach(tp);
2444e771f5dSjoff
2454e771f5dSjoff if (ISSET(sc->sc_hwflags, COM_HW_CONSOLE)) {
2464e771f5dSjoff int maj;
2474e771f5dSjoff
2484e771f5dSjoff /* locate the major number */
2494e771f5dSjoff maj = cdevsw_lookup_major(&epcom_cdevsw);
2504e771f5dSjoff
251cbab9cadSchs cn_tab->cn_dev = makedev(maj, device_unit(sc->sc_dev));
2524e771f5dSjoff
253cbab9cadSchs aprint_normal("%s: console\n", device_xname(sc->sc_dev));
2544e771f5dSjoff }
2554e771f5dSjoff
2569d134cf5Sskrll #ifdef KGDB
2579d134cf5Sskrll /*
2589d134cf5Sskrll * Allow kgdb to "take over" this port. If this is
2599d134cf5Sskrll * the kgdb device, it has exclusive use.
2609d134cf5Sskrll */
2619d134cf5Sskrll if (sc->sc_iot == kgdb_sc.sc_iot &&
2629d134cf5Sskrll sc->sc_hwbase == kgdb_sc.sc_hwbase) {
2639d134cf5Sskrll SET(sc->sc_hwflags, COM_HW_KGDB);
2643aa5a3aeSriastradh device_printf(sc->sc_dev, "kgdb\n");
2659d134cf5Sskrll }
2669d134cf5Sskrll #endif
2679d134cf5Sskrll
2680c0de807Smatt sc->sc_si = softint_establish(SOFTINT_SERIAL, epcomsoft, sc);
2694e771f5dSjoff
2707b0b7dedStls #ifdef RND_COM
271cbab9cadSchs rnd_attach_source(&sc->rnd_source, device_xname(sc->sc_dev),
272ea6af427Stls RND_TYPE_TTY, RND_FLAG_DEFAULT);
2734e771f5dSjoff #endif
2744e771f5dSjoff
2754e771f5dSjoff /* if there are no enable/disable functions, assume the device
2764e771f5dSjoff is always enabled */
2774e771f5dSjoff if (!sc->enable)
2784e771f5dSjoff sc->enabled = 1;
2794e771f5dSjoff
2804e771f5dSjoff /* XXX configure register */
2814e771f5dSjoff /* xxx_config(sc) */
2824e771f5dSjoff
2834e771f5dSjoff SET(sc->sc_hwflags, COM_HW_DEV_OK);
2844e771f5dSjoff }
2854e771f5dSjoff
2864e771f5dSjoff static int
epcomparam(struct tty * tp,struct termios * t)2870e9d2fc1Schristos epcomparam(struct tty *tp, struct termios *t)
2884e771f5dSjoff {
2894e771f5dSjoff struct epcom_softc *sc
2905402ae69Scegger = device_lookup_private(&epcom_cd, COMUNIT(tp->t_dev));
2914e771f5dSjoff int s;
2924e771f5dSjoff
2934e771f5dSjoff if (COM_ISALIVE(sc) == 0)
2944e771f5dSjoff return (EIO);
2954e771f5dSjoff
2964e771f5dSjoff if (t->c_ispeed && t->c_ispeed != t->c_ospeed)
2974e771f5dSjoff return (EINVAL);
2984e771f5dSjoff
2994e771f5dSjoff /*
3004e771f5dSjoff * For the console, always force CLOCAL and !HUPCL, so that the port
3014e771f5dSjoff * is always active.
3024e771f5dSjoff */
3034e771f5dSjoff if (ISSET(sc->sc_swflags, TIOCFLAG_SOFTCAR) ||
3044e771f5dSjoff ISSET(sc->sc_hwflags, COM_HW_CONSOLE)) {
3054e771f5dSjoff SET(t->c_cflag, CLOCAL);
3064e771f5dSjoff CLR(t->c_cflag, HUPCL);
3074e771f5dSjoff }
3084e771f5dSjoff
3094e771f5dSjoff /*
3104e771f5dSjoff * If there were no changes, don't do anything. This avoids dropping
3114e771f5dSjoff * input and improves performance when all we did was frob things like
3124e771f5dSjoff * VMIN and VTIME.
3134e771f5dSjoff */
3144e771f5dSjoff if (tp->t_ospeed == t->c_ospeed &&
3154e771f5dSjoff tp->t_cflag == t->c_cflag)
3164e771f5dSjoff return (0);
3174e771f5dSjoff
3184e771f5dSjoff s = splserial();
3194e771f5dSjoff
3204e771f5dSjoff sc->sc_lcrhi = cflag2lcrhi(t->c_cflag);
3214e771f5dSjoff sc->sc_lcrlo = EPCOMSPEED2BRD(t->c_ospeed) & 0xff;
3224e771f5dSjoff sc->sc_lcrmid = EPCOMSPEED2BRD(t->c_ospeed) >> 8;
3234e771f5dSjoff
3244e771f5dSjoff /* And copy to tty. */
3254e771f5dSjoff tp->t_ispeed = 0;
3264e771f5dSjoff tp->t_ospeed = t->c_ospeed;
3274e771f5dSjoff tp->t_cflag = t->c_cflag;
3284e771f5dSjoff epcom_set(sc);
3294e771f5dSjoff
3304e771f5dSjoff splx(s);
3314e771f5dSjoff
3324e771f5dSjoff /*
3334e771f5dSjoff * Update the tty layer's idea of the carrier bit.
3344e771f5dSjoff * We tell tty the carrier is always on.
3354e771f5dSjoff */
3364e771f5dSjoff (void) (*tp->t_linesw->l_modem)(tp, 1);
3374e771f5dSjoff
3384e771f5dSjoff #ifdef COM_DEBUG
3394e771f5dSjoff if (com_debug)
3404e771f5dSjoff comstatus(sc, "comparam ");
3414e771f5dSjoff #endif
3424e771f5dSjoff
3434e771f5dSjoff if (!ISSET(t->c_cflag, CHWFLOW)) {
3444e771f5dSjoff if (sc->sc_tx_stopped) {
3454e771f5dSjoff sc->sc_tx_stopped = 0;
3464e771f5dSjoff epcomstart(tp);
3474e771f5dSjoff }
3484e771f5dSjoff }
3494e771f5dSjoff
3504e771f5dSjoff return (0);
3514e771f5dSjoff }
3524e771f5dSjoff
3534e771f5dSjoff static int
epcomhwiflow(struct tty * tp,int block)3540e9d2fc1Schristos epcomhwiflow(struct tty *tp, int block)
3554e771f5dSjoff {
3564e771f5dSjoff return (0);
3574e771f5dSjoff }
3584e771f5dSjoff
3594e771f5dSjoff static void
epcom_filltx(struct epcom_softc * sc)3604e771f5dSjoff epcom_filltx(struct epcom_softc *sc)
3614e771f5dSjoff {
3624e771f5dSjoff bus_space_tag_t iot = sc->sc_iot;
3634e771f5dSjoff bus_space_handle_t ioh = sc->sc_ioh;
3644e771f5dSjoff int n;
3654e771f5dSjoff
3664e771f5dSjoff n = 0;
3674e771f5dSjoff while ((bus_space_read_4(iot, ioh, EPCOM_Flag) & Flag_TXFF) == 0) {
3684e771f5dSjoff if (n >= sc->sc_tbc)
3694e771f5dSjoff break;
3704e771f5dSjoff bus_space_write_4(iot, ioh, EPCOM_Data,
3714e771f5dSjoff 0xff & *(sc->sc_tba + n));
3724e771f5dSjoff n++;
3734e771f5dSjoff }
3744e771f5dSjoff sc->sc_tbc -= n;
3754e771f5dSjoff sc->sc_tba += n;
3764e771f5dSjoff }
3774e771f5dSjoff
3784e771f5dSjoff static void
epcomstart(struct tty * tp)3790e9d2fc1Schristos epcomstart(struct tty *tp)
3804e771f5dSjoff {
3814e771f5dSjoff struct epcom_softc *sc
3825402ae69Scegger = device_lookup_private(&epcom_cd, COMUNIT(tp->t_dev));
3834e771f5dSjoff int s;
3844e771f5dSjoff
3854e771f5dSjoff if (COM_ISALIVE(sc) == 0)
3864e771f5dSjoff return;
3874e771f5dSjoff
3884e771f5dSjoff s = spltty();
3894e771f5dSjoff if (ISSET(tp->t_state, TS_BUSY | TS_TIMEOUT | TS_TTSTOP))
3904e771f5dSjoff goto out;
3914e771f5dSjoff if (sc->sc_tx_stopped)
3924e771f5dSjoff goto out;
393dc26833bSad if (!ttypull(tp))
3944e771f5dSjoff goto out;
3954e771f5dSjoff
3964e771f5dSjoff /* Grab the first contiguous region of buffer space. */
3974e771f5dSjoff {
3984e771f5dSjoff u_char *tba;
3994e771f5dSjoff int tbc;
4004e771f5dSjoff
4014e771f5dSjoff tba = tp->t_outq.c_cf;
4024e771f5dSjoff tbc = ndqb(&tp->t_outq, 0);
4034e771f5dSjoff
4044e771f5dSjoff (void)splserial();
4054e771f5dSjoff
4064e771f5dSjoff sc->sc_tba = tba;
4074e771f5dSjoff sc->sc_tbc = tbc;
4084e771f5dSjoff }
4094e771f5dSjoff
4104e771f5dSjoff SET(tp->t_state, TS_BUSY);
4114e771f5dSjoff sc->sc_tx_busy = 1;
4124e771f5dSjoff
4135beb7d30Sjoff /* Output the first chunk of the contiguous buffer. */
4145beb7d30Sjoff epcom_filltx(sc);
4155beb7d30Sjoff
4164e771f5dSjoff if (!ISSET(sc->sc_ctrl, Ctrl_TIE)) {
4174e771f5dSjoff SET(sc->sc_ctrl, Ctrl_TIE);
4184e771f5dSjoff epcom_set(sc);
4194e771f5dSjoff }
4204e771f5dSjoff
4214e771f5dSjoff out:
4224e771f5dSjoff splx(s);
4234e771f5dSjoff return;
4244e771f5dSjoff }
4254e771f5dSjoff
4264e771f5dSjoff static void
epcom_break(struct epcom_softc * sc,int onoff)4274e771f5dSjoff epcom_break(struct epcom_softc *sc, int onoff)
4284e771f5dSjoff {
4294e771f5dSjoff if (onoff)
4304e771f5dSjoff SET(sc->sc_lcrhi, LinCtrlHigh_BRK);
4314e771f5dSjoff else
4324e771f5dSjoff CLR(sc->sc_lcrhi, LinCtrlHigh_BRK);
4334e771f5dSjoff epcom_set(sc);
4344e771f5dSjoff }
4354e771f5dSjoff
4364e771f5dSjoff static void
epcom_shutdown(struct epcom_softc * sc)4374e771f5dSjoff epcom_shutdown(struct epcom_softc *sc)
4384e771f5dSjoff {
4394e771f5dSjoff int s;
4404e771f5dSjoff
4414e771f5dSjoff s = splserial();
4424e771f5dSjoff
4434e771f5dSjoff /* Turn off interrupts. */
4444e771f5dSjoff CLR(sc->sc_ctrl, (Ctrl_TIE|Ctrl_RTIE|Ctrl_RIE));
4454e771f5dSjoff
4464e771f5dSjoff /* Clear any break condition set with TIOCSBRK. */
4474e771f5dSjoff epcom_break(sc, 0);
4484e771f5dSjoff epcom_set(sc);
4494e771f5dSjoff
4504e771f5dSjoff if (sc->disable) {
4514e771f5dSjoff #ifdef DIAGNOSTIC
4524e771f5dSjoff if (!sc->enabled)
4534e771f5dSjoff panic("epcom_shutdown: not enabled?");
4544e771f5dSjoff #endif
4554e771f5dSjoff (*sc->disable)(sc);
4564e771f5dSjoff sc->enabled = 0;
4574e771f5dSjoff }
4584e771f5dSjoff splx(s);
4594e771f5dSjoff }
4604e771f5dSjoff
4614e771f5dSjoff int
epcomopen(dev_t dev,int flag,int mode,struct lwp * l)4620e9d2fc1Schristos epcomopen(dev_t dev, int flag, int mode, struct lwp *l)
4634e771f5dSjoff {
4644e771f5dSjoff struct epcom_softc *sc;
4654e771f5dSjoff struct tty *tp;
4664e771f5dSjoff int s, s2;
4674e771f5dSjoff int error;
4684e771f5dSjoff
4695402ae69Scegger sc = device_lookup_private(&epcom_cd, COMUNIT(dev));
4704e771f5dSjoff if (sc == NULL || !ISSET(sc->sc_hwflags, COM_HW_DEV_OK) ||
4714e771f5dSjoff sc->sc_rbuf == NULL)
4724e771f5dSjoff return (ENXIO);
4734e771f5dSjoff
474cbab9cadSchs if (!device_is_active(sc->sc_dev))
4754e771f5dSjoff return (ENXIO);
4764e771f5dSjoff
4774e771f5dSjoff #ifdef KGDB
4784e771f5dSjoff /*
4794e771f5dSjoff * If this is the kgdb port, no other use is permitted.
4804e771f5dSjoff */
4814e771f5dSjoff if (ISSET(sc->sc_hwflags, COM_HW_KGDB))
4824e771f5dSjoff return (EBUSY);
4834e771f5dSjoff #endif
4844e771f5dSjoff
4854e771f5dSjoff tp = sc->sc_tty;
4864e771f5dSjoff
487e8373398Selad if (kauth_authorize_device_tty(l->l_cred, KAUTH_DEVICE_TTY_OPEN, tp))
4884e771f5dSjoff return (EBUSY);
4894e771f5dSjoff
4904e771f5dSjoff s = spltty();
4914e771f5dSjoff
4924e771f5dSjoff /*
4934e771f5dSjoff * Do the following iff this is a first open.
4944e771f5dSjoff */
4954e771f5dSjoff if (!ISSET(tp->t_state, TS_ISOPEN) && tp->t_wopen == 0) {
4964e771f5dSjoff struct termios t;
4974e771f5dSjoff
4984e771f5dSjoff tp->t_dev = dev;
4994e771f5dSjoff
5004e771f5dSjoff s2 = splserial();
5014e771f5dSjoff
5024e771f5dSjoff if (sc->enable) {
5034e771f5dSjoff if ((*sc->enable)(sc)) {
5044e771f5dSjoff splx(s2);
5054e771f5dSjoff splx(s);
5064e771f5dSjoff printf("%s: device enable failed\n",
507cbab9cadSchs device_xname(sc->sc_dev));
5084e771f5dSjoff return (EIO);
5094e771f5dSjoff }
5104e771f5dSjoff sc->enabled = 1;
5114e771f5dSjoff #if 0
5124e771f5dSjoff /* XXXXXXXXXXXXXXX */
5134e771f5dSjoff com_config(sc);
5144e771f5dSjoff #endif
5154e771f5dSjoff }
5164e771f5dSjoff
5174e771f5dSjoff /* Turn on interrupts. */
5184e771f5dSjoff SET(sc->sc_ctrl, (Ctrl_UARTE|Ctrl_RIE|Ctrl_RTIE));
5194e771f5dSjoff epcom_set(sc);
5204e771f5dSjoff
5214e771f5dSjoff #if 0
5224e771f5dSjoff /* Fetch the current modem control status, needed later. */
5234e771f5dSjoff sc->sc_msr = bus_space_read_1(sc->sc_iot, sc->sc_ioh, com_msr);
5244e771f5dSjoff
5254e771f5dSjoff /* Clear PPS capture state on first open. */
5264e771f5dSjoff sc->sc_ppsmask = 0;
5274e771f5dSjoff sc->ppsparam.mode = 0;
5284e771f5dSjoff #endif
5294e771f5dSjoff
5304e771f5dSjoff splx(s2);
5314e771f5dSjoff
5324e771f5dSjoff /*
5334e771f5dSjoff * Initialize the termios status to the defaults. Add in the
5344e771f5dSjoff * sticky bits from TIOCSFLAGS.
5354e771f5dSjoff */
5364e771f5dSjoff t.c_ispeed = 0;
5374e771f5dSjoff if (ISSET(sc->sc_hwflags, COM_HW_CONSOLE)) {
5384e771f5dSjoff t.c_ospeed = epcomcn_sc.sc_ospeed;
5394e771f5dSjoff t.c_cflag = epcomcn_sc.sc_cflag;
5404e771f5dSjoff } else {
5414e771f5dSjoff t.c_ospeed = TTYDEF_SPEED;
5424e771f5dSjoff t.c_cflag = TTYDEF_CFLAG;
5434e771f5dSjoff }
5444e771f5dSjoff if (ISSET(sc->sc_swflags, TIOCFLAG_CLOCAL))
5454e771f5dSjoff SET(t.c_cflag, CLOCAL);
5464e771f5dSjoff if (ISSET(sc->sc_swflags, TIOCFLAG_CRTSCTS))
5474e771f5dSjoff SET(t.c_cflag, CRTSCTS);
5484e771f5dSjoff if (ISSET(sc->sc_swflags, TIOCFLAG_MDMBUF))
5494e771f5dSjoff SET(t.c_cflag, MDMBUF);
5504e771f5dSjoff /* Make sure epcomparam() will do something. */
5514e771f5dSjoff tp->t_ospeed = 0;
5524e771f5dSjoff (void) epcomparam(tp, &t);
5534e771f5dSjoff tp->t_iflag = TTYDEF_IFLAG;
5544e771f5dSjoff tp->t_oflag = TTYDEF_OFLAG;
5554e771f5dSjoff tp->t_lflag = TTYDEF_LFLAG;
5564e771f5dSjoff ttychars(tp);
5574e771f5dSjoff ttsetwater(tp);
5584e771f5dSjoff
5594e771f5dSjoff s2 = splserial();
5604e771f5dSjoff
5614e771f5dSjoff /* Clear the input ring, and unblock. */
5624e771f5dSjoff sc->sc_rbput = sc->sc_rbget = sc->sc_rbuf;
5634e771f5dSjoff sc->sc_rbavail = EPCOM_RING_SIZE;
5644e771f5dSjoff epcom_iflush(sc);
5654e771f5dSjoff CLR(sc->sc_rx_flags, RX_ANY_BLOCK);
5664e771f5dSjoff
5674e771f5dSjoff #ifdef COM_DEBUG
5684e771f5dSjoff if (epcom_debug)
5694e771f5dSjoff comstatus(sc, "epcomopen ");
5704e771f5dSjoff #endif
5714e771f5dSjoff
5724e771f5dSjoff splx(s2);
5734e771f5dSjoff }
5744e771f5dSjoff
5754e771f5dSjoff splx(s);
5764e771f5dSjoff
5774e771f5dSjoff error = ttyopen(tp, COMDIALOUT(dev), ISSET(flag, O_NONBLOCK));
5784e771f5dSjoff if (error)
5794e771f5dSjoff goto bad;
5804e771f5dSjoff
5814e771f5dSjoff error = (*tp->t_linesw->l_open)(dev, tp);
5824e771f5dSjoff if (error)
5834e771f5dSjoff goto bad;
5844e771f5dSjoff
5854e771f5dSjoff return (0);
5864e771f5dSjoff
5874e771f5dSjoff bad:
5884e771f5dSjoff if (!ISSET(tp->t_state, TS_ISOPEN) && tp->t_wopen == 0) {
5894e771f5dSjoff /*
5904e771f5dSjoff * We failed to open the device, and nobody else had it opened.
5914e771f5dSjoff * Clean up the state as appropriate.
5924e771f5dSjoff */
5934e771f5dSjoff epcom_shutdown(sc);
5944e771f5dSjoff }
5954e771f5dSjoff
5964e771f5dSjoff return (error);
5974e771f5dSjoff }
5984e771f5dSjoff
5994e771f5dSjoff int
epcomclose(dev_t dev,int flag,int mode,struct lwp * l)6000e9d2fc1Schristos epcomclose(dev_t dev, int flag, int mode, struct lwp *l)
6014e771f5dSjoff {
6025402ae69Scegger struct epcom_softc *sc = device_lookup_private(&epcom_cd, COMUNIT(dev));
6034e771f5dSjoff struct tty *tp = sc->sc_tty;
6044e771f5dSjoff
6054e771f5dSjoff /* XXX This is for cons.c. */
6064e771f5dSjoff if (!ISSET(tp->t_state, TS_ISOPEN))
6074e771f5dSjoff return (0);
6084e771f5dSjoff
6094e771f5dSjoff (*tp->t_linesw->l_close)(tp, flag);
6104e771f5dSjoff ttyclose(tp);
6114e771f5dSjoff
6124e771f5dSjoff if (COM_ISALIVE(sc) == 0)
6134e771f5dSjoff return (0);
6144e771f5dSjoff
6154e771f5dSjoff if (!ISSET(tp->t_state, TS_ISOPEN) && tp->t_wopen == 0) {
6164e771f5dSjoff /*
6174e771f5dSjoff * Although we got a last close, the device may still be in
6184e771f5dSjoff * use; e.g. if this was the dialout node, and there are still
6194e771f5dSjoff * processes waiting for carrier on the non-dialout node.
6204e771f5dSjoff */
6214e771f5dSjoff epcom_shutdown(sc);
6224e771f5dSjoff }
6234e771f5dSjoff
6244e771f5dSjoff return (0);
6254e771f5dSjoff }
6264e771f5dSjoff
6274e771f5dSjoff int
epcomread(dev_t dev,struct uio * uio,int flag)6280e9d2fc1Schristos epcomread(dev_t dev, struct uio *uio, int flag)
6294e771f5dSjoff {
6305402ae69Scegger struct epcom_softc *sc = device_lookup_private(&epcom_cd, COMUNIT(dev));
6314e771f5dSjoff struct tty *tp = sc->sc_tty;
6324e771f5dSjoff
6334e771f5dSjoff if (COM_ISALIVE(sc) == 0)
6344e771f5dSjoff return (EIO);
6354e771f5dSjoff
6364e771f5dSjoff return ((*tp->t_linesw->l_read)(tp, uio, flag));
6374e771f5dSjoff }
6384e771f5dSjoff
6394e771f5dSjoff int
epcomwrite(dev_t dev,struct uio * uio,int flag)6400e9d2fc1Schristos epcomwrite(dev_t dev, struct uio *uio, int flag)
6414e771f5dSjoff {
6425402ae69Scegger struct epcom_softc *sc = device_lookup_private(&epcom_cd, COMUNIT(dev));
6434e771f5dSjoff struct tty *tp = sc->sc_tty;
6444e771f5dSjoff
6454e771f5dSjoff if (COM_ISALIVE(sc) == 0)
6464e771f5dSjoff return (EIO);
6474e771f5dSjoff
6484e771f5dSjoff return ((*tp->t_linesw->l_write)(tp, uio, flag));
6494e771f5dSjoff }
6504e771f5dSjoff
6514e771f5dSjoff int
epcompoll(dev_t dev,int events,struct lwp * l)6520e9d2fc1Schristos epcompoll(dev_t dev, int events, struct lwp *l)
6534e771f5dSjoff {
6545402ae69Scegger struct epcom_softc *sc = device_lookup_private(&epcom_cd, COMUNIT(dev));
6554e771f5dSjoff struct tty *tp = sc->sc_tty;
6564e771f5dSjoff
6574e771f5dSjoff if (COM_ISALIVE(sc) == 0)
6584e771f5dSjoff return (EIO);
6594e771f5dSjoff
6600e9d2fc1Schristos return ((*tp->t_linesw->l_poll)(tp, events, l));
6614e771f5dSjoff }
6624e771f5dSjoff
6634e771f5dSjoff struct tty *
epcomtty(dev_t dev)6640e9d2fc1Schristos epcomtty(dev_t dev)
6654e771f5dSjoff {
6665402ae69Scegger struct epcom_softc *sc = device_lookup_private(&epcom_cd, COMUNIT(dev));
6674e771f5dSjoff struct tty *tp = sc->sc_tty;
6684e771f5dSjoff
6694e771f5dSjoff return (tp);
6704e771f5dSjoff }
6714e771f5dSjoff
6724e771f5dSjoff int
epcomioctl(dev_t dev,u_long cmd,void * data,int flag,struct lwp * l)67353524e44Schristos epcomioctl(dev_t dev, u_long cmd, void *data, int flag, struct lwp *l)
6744e771f5dSjoff {
6755402ae69Scegger struct epcom_softc *sc = device_lookup_private(&epcom_cd, COMUNIT(dev));
6764e771f5dSjoff struct tty *tp = sc->sc_tty;
6774e771f5dSjoff int error;
6784e771f5dSjoff int s;
6794e771f5dSjoff
6804e771f5dSjoff if (COM_ISALIVE(sc) == 0)
6814e771f5dSjoff return (EIO);
6824e771f5dSjoff
6830e9d2fc1Schristos error = (*tp->t_linesw->l_ioctl)(tp, cmd, data, flag, l);
6844e771f5dSjoff if (error != EPASSTHROUGH)
6854e771f5dSjoff return (error);
6864e771f5dSjoff
6870e9d2fc1Schristos error = ttioctl(tp, cmd, data, flag, l);
6884e771f5dSjoff if (error != EPASSTHROUGH)
6894e771f5dSjoff return (error);
6904e771f5dSjoff
6914e771f5dSjoff error = 0;
6924e771f5dSjoff
6934e771f5dSjoff s = splserial();
6944e771f5dSjoff
6954e771f5dSjoff switch (cmd) {
6964e771f5dSjoff case TIOCSBRK:
6974e771f5dSjoff epcom_break(sc, 1);
6984e771f5dSjoff break;
6994e771f5dSjoff
7004e771f5dSjoff case TIOCCBRK:
7014e771f5dSjoff epcom_break(sc, 0);
7024e771f5dSjoff break;
7034e771f5dSjoff
7044e771f5dSjoff case TIOCGFLAGS:
7054e771f5dSjoff *(int *)data = sc->sc_swflags;
7064e771f5dSjoff break;
7074e771f5dSjoff
7084e771f5dSjoff case TIOCSFLAGS:
70965792a03Selad error = kauth_authorize_device_tty(l->l_cred,
71065792a03Selad KAUTH_DEVICE_TTY_PRIVSET, tp);
7114e771f5dSjoff if (error)
7124e771f5dSjoff break;
7134e771f5dSjoff sc->sc_swflags = *(int *)data;
7144e771f5dSjoff break;
7154e771f5dSjoff
7164e771f5dSjoff default:
7174e771f5dSjoff error = EPASSTHROUGH;
7184e771f5dSjoff break;
7194e771f5dSjoff }
7204e771f5dSjoff
7214e771f5dSjoff splx(s);
7224e771f5dSjoff
7234e771f5dSjoff return (error);
7244e771f5dSjoff }
7254e771f5dSjoff
7264e771f5dSjoff /*
7274e771f5dSjoff * Stop output on a line.
7284e771f5dSjoff */
7294e771f5dSjoff void
epcomstop(struct tty * tp,int flag)7300e9d2fc1Schristos epcomstop(struct tty *tp, int flag)
7314e771f5dSjoff {
7324e771f5dSjoff struct epcom_softc *sc
7335402ae69Scegger = device_lookup_private(&epcom_cd, COMUNIT(tp->t_dev));
7344e771f5dSjoff int s;
7354e771f5dSjoff
7364e771f5dSjoff s = splserial();
7374e771f5dSjoff if (ISSET(tp->t_state, TS_BUSY)) {
7384e771f5dSjoff /* Stop transmitting at the next chunk. */
7394e771f5dSjoff sc->sc_tbc = 0;
7404e771f5dSjoff if (!ISSET(tp->t_state, TS_TTSTOP))
7414e771f5dSjoff SET(tp->t_state, TS_FLUSH);
7424e771f5dSjoff }
7434e771f5dSjoff splx(s);
7444e771f5dSjoff }
7454e771f5dSjoff
7464e771f5dSjoff static u_int
cflag2lcrhi(tcflag_t cflag)7470e9d2fc1Schristos cflag2lcrhi(tcflag_t cflag)
7484e771f5dSjoff {
7494e771f5dSjoff u_int lcrhi;
7504e771f5dSjoff
7514e771f5dSjoff switch (cflag & CSIZE) {
7524e771f5dSjoff case CS7:
7534e771f5dSjoff lcrhi = 0x40;
7544e771f5dSjoff break;
7554e771f5dSjoff case CS6:
7564e771f5dSjoff lcrhi = 0x20;
7574e771f5dSjoff break;
7584e771f5dSjoff case CS8:
7594e771f5dSjoff default:
7604e771f5dSjoff lcrhi = 0x60;
7614e771f5dSjoff break;
7624e771f5dSjoff }
7634e771f5dSjoff lcrhi |= (cflag & PARENB) ? LinCtrlHigh_PEN : 0;
7644e771f5dSjoff lcrhi |= (cflag & PARODD) ? 0 : LinCtrlHigh_EPS;
7654e771f5dSjoff lcrhi |= (cflag & CSTOPB) ? LinCtrlHigh_STP2 : 0;
7664e771f5dSjoff lcrhi |= LinCtrlHigh_FEN; /* FIFO always enabled */
7674e771f5dSjoff
7684e771f5dSjoff return (lcrhi);
7694e771f5dSjoff }
7704e771f5dSjoff
7714e771f5dSjoff static void
epcom_iflush(struct epcom_softc * sc)7720e9d2fc1Schristos epcom_iflush(struct epcom_softc *sc)
7734e771f5dSjoff {
7744e771f5dSjoff bus_space_tag_t iot = sc->sc_iot;
7754e771f5dSjoff bus_space_handle_t ioh = sc->sc_ioh;
7764e771f5dSjoff #ifdef DIAGNOSTIC
7774e771f5dSjoff int reg;
7784e771f5dSjoff #endif
7794e771f5dSjoff int timo;
7804e771f5dSjoff
7814e771f5dSjoff #ifdef DIAGNOSTIC
7824e771f5dSjoff reg = 0xffff;
7834e771f5dSjoff #endif
7844e771f5dSjoff timo = 50000;
7854e771f5dSjoff /* flush any pending I/O */
7864e771f5dSjoff while ((bus_space_read_4(iot, ioh, EPCOM_Flag) & Flag_RXFE) == 0
7874e771f5dSjoff && --timo)
7884e771f5dSjoff #ifdef DIAGNOSTIC
7894e771f5dSjoff reg =
7904e771f5dSjoff #else
7914e771f5dSjoff (void)
7924e771f5dSjoff #endif
7934e771f5dSjoff bus_space_read_4(iot, ioh, EPCOM_Data);
7944e771f5dSjoff #ifdef DIAGNOSTIC
7954e771f5dSjoff if (!timo)
796cbab9cadSchs printf("%s: com_iflush timeout %02x\n", device_xname(sc->sc_dev),
7974e771f5dSjoff reg);
7984e771f5dSjoff #endif
7994e771f5dSjoff }
8004e771f5dSjoff
8014e771f5dSjoff static void
epcom_set(struct epcom_softc * sc)8024e771f5dSjoff epcom_set(struct epcom_softc *sc)
8034e771f5dSjoff {
8044e771f5dSjoff bus_space_write_4(sc->sc_iot, sc->sc_ioh, EPCOM_LinCtrlLow,
8054e771f5dSjoff sc->sc_lcrlo);
8064e771f5dSjoff bus_space_write_4(sc->sc_iot, sc->sc_ioh, EPCOM_LinCtrlMid,
8074e771f5dSjoff sc->sc_lcrmid);
8084e771f5dSjoff bus_space_write_4(sc->sc_iot, sc->sc_ioh, EPCOM_LinCtrlHigh,
8094e771f5dSjoff sc->sc_lcrhi);
8104e771f5dSjoff bus_space_write_4(sc->sc_iot, sc->sc_ioh, EPCOM_Ctrl,
8114e771f5dSjoff sc->sc_ctrl);
8124e771f5dSjoff }
8134e771f5dSjoff
8144e771f5dSjoff int
epcomcnattach(bus_space_tag_t iot,bus_addr_t iobase,bus_space_handle_t ioh,int ospeed,tcflag_t cflag)8150e9d2fc1Schristos epcomcnattach(bus_space_tag_t iot, bus_addr_t iobase, bus_space_handle_t ioh,
8160e9d2fc1Schristos int ospeed, tcflag_t cflag)
8174e771f5dSjoff {
8184e771f5dSjoff cn_tab = &epcomcons;
8194e771f5dSjoff cn_init_magic(&epcom_cnm_state);
8204e771f5dSjoff cn_set_magic("\047\001");
8214e771f5dSjoff
8229d134cf5Sskrll epcominit(&epcomcn_sc, iot, iobase, ioh, ospeed, cflag);
8234e771f5dSjoff
8244e771f5dSjoff return (0);
8254e771f5dSjoff }
8264e771f5dSjoff
8274e771f5dSjoff void
epcomcnprobe(struct consdev * cp)8280e9d2fc1Schristos epcomcnprobe(struct consdev *cp)
8294e771f5dSjoff {
8304e771f5dSjoff cp->cn_pri = CN_REMOTE;
8314e771f5dSjoff }
8324e771f5dSjoff
8334e771f5dSjoff void
epcomcnpollc(dev_t dev,int on)8340e9d2fc1Schristos epcomcnpollc(dev_t dev, int on)
8354e771f5dSjoff {
8364e771f5dSjoff }
8374e771f5dSjoff
8384e771f5dSjoff void
epcomcnputc(dev_t dev,int c)8390e9d2fc1Schristos epcomcnputc(dev_t dev, int c)
8404e771f5dSjoff {
8419d134cf5Sskrll epcom_common_putc(&epcomcn_sc, c);
8429d134cf5Sskrll }
8439d134cf5Sskrll
8449d134cf5Sskrll static void
epcom_common_putc(struct epcom_cons_softc * sc,int c)8459d134cf5Sskrll epcom_common_putc(struct epcom_cons_softc *sc, int c)
8469d134cf5Sskrll {
8474e771f5dSjoff int s;
8489d134cf5Sskrll bus_space_tag_t iot = sc->sc_iot;
8499d134cf5Sskrll bus_space_handle_t ioh = sc->sc_ioh;
8504e771f5dSjoff
8514e771f5dSjoff s = splserial();
8524e771f5dSjoff
8534e771f5dSjoff while((bus_space_read_4(iot, ioh, EPCOM_Flag) & Flag_TXFF) != 0)
8544e771f5dSjoff ;
8554e771f5dSjoff
8564e771f5dSjoff bus_space_write_4(iot, ioh, EPCOM_Data, c);
8574e771f5dSjoff
8584e771f5dSjoff #ifdef DEBUG
8594e771f5dSjoff if (c == '\r') {
8604e771f5dSjoff while((bus_space_read_4(iot, ioh, EPCOM_Flag) & Flag_TXFE) == 0)
8614e771f5dSjoff ;
8624e771f5dSjoff }
8634e771f5dSjoff #endif
8644e771f5dSjoff
8654e771f5dSjoff splx(s);
8664e771f5dSjoff }
8674e771f5dSjoff
8684e771f5dSjoff int
epcomcngetc(dev_t dev)8690e9d2fc1Schristos epcomcngetc(dev_t dev)
8704e771f5dSjoff {
8719d134cf5Sskrll return epcom_common_getc (&epcomcn_sc, dev);
8729d134cf5Sskrll }
8739d134cf5Sskrll
8749d134cf5Sskrll static int
epcom_common_getc(struct epcom_cons_softc * sc,dev_t dev)8759d134cf5Sskrll epcom_common_getc(struct epcom_cons_softc *sc, dev_t dev)
8769d134cf5Sskrll {
8774e771f5dSjoff int c, sts;
8784e771f5dSjoff int s;
8799d134cf5Sskrll bus_space_tag_t iot = sc->sc_iot;
8809d134cf5Sskrll bus_space_handle_t ioh = sc->sc_ioh;
8814e771f5dSjoff
8824e771f5dSjoff s = splserial();
8834e771f5dSjoff
8844e771f5dSjoff while ((bus_space_read_4(iot, ioh, EPCOM_Flag) & Flag_RXFE) != 0)
885*dbfa10e5Sriastradh continue;
8864e771f5dSjoff
8874e771f5dSjoff c = bus_space_read_4(iot, ioh, EPCOM_Data);
8884e771f5dSjoff sts = bus_space_read_4(iot, ioh, EPCOM_RXSts);
889*dbfa10e5Sriastradh if (ISSET(sts, RXSts_BE))
890*dbfa10e5Sriastradh c = CNC_BREAK;
891*dbfa10e5Sriastradh if (!db_active) {
8929180e146Sskrll int cn_trapped __unused = 0;
8934e771f5dSjoff
8944e771f5dSjoff cn_check_magic(dev, c, epcom_cnm_state);
8954e771f5dSjoff }
8964e771f5dSjoff c &= 0xff;
8974e771f5dSjoff splx(s);
8984e771f5dSjoff
8994e771f5dSjoff return (c);
9004e771f5dSjoff }
9014e771f5dSjoff
9024e771f5dSjoff inline static void
epcom_txsoft(struct epcom_softc * sc,struct tty * tp)9030e9d2fc1Schristos epcom_txsoft(struct epcom_softc *sc, struct tty *tp)
9044e771f5dSjoff {
9054e771f5dSjoff CLR(tp->t_state, TS_BUSY);
9064e771f5dSjoff if (ISSET(tp->t_state, TS_FLUSH))
9074e771f5dSjoff CLR(tp->t_state, TS_FLUSH);
9084e771f5dSjoff else
9094e771f5dSjoff ndflush(&tp->t_outq, (int)(sc->sc_tba - tp->t_outq.c_cf));
9104e771f5dSjoff (*tp->t_linesw->l_start)(tp);
9114e771f5dSjoff }
9124e771f5dSjoff
9134e771f5dSjoff inline static void
epcom_rxsoft(struct epcom_softc * sc,struct tty * tp)9140e9d2fc1Schristos epcom_rxsoft(struct epcom_softc *sc, struct tty *tp)
9154e771f5dSjoff {
91602cdf4d2Sdsl int (*rint)(int, struct tty *) = tp->t_linesw->l_rint;
9174e771f5dSjoff u_char *get, *end;
9184e771f5dSjoff u_int cc, scc;
9194e771f5dSjoff u_char sts;
9204e771f5dSjoff int code;
9214e771f5dSjoff int s;
9224e771f5dSjoff
9234e771f5dSjoff end = sc->sc_ebuf;
9244e771f5dSjoff get = sc->sc_rbget;
9254e771f5dSjoff scc = cc = EPCOM_RING_SIZE - sc->sc_rbavail;
9264e771f5dSjoff #if 0
9274e771f5dSjoff if (cc == EPCOM_RING_SIZE) {
9284e771f5dSjoff sc->sc_floods++;
9294e771f5dSjoff if (sc->sc_errors++ == 0)
9304e771f5dSjoff callout_reset(&sc->sc_diag_callout, 60 * hz,
9314e771f5dSjoff comdiag, sc);
9324e771f5dSjoff }
9334e771f5dSjoff #endif
9344e771f5dSjoff while (cc) {
9354e771f5dSjoff code = get[0];
9364e771f5dSjoff sts = get[1];
9374e771f5dSjoff if (ISSET(sts, RXSts_OE | RXSts_FE | RXSts_PE | RXSts_BE)) {
9384e771f5dSjoff #if 0
9394e771f5dSjoff if (ISSET(lsr, DR_ROR)) {
9404e771f5dSjoff sc->sc_overflows++;
9414e771f5dSjoff if (sc->sc_errors++ == 0)
9424e771f5dSjoff callout_reset(&sc->sc_diag_callout,
9434e771f5dSjoff 60 * hz, comdiag, sc);
9444e771f5dSjoff }
9454e771f5dSjoff #endif
9464e771f5dSjoff if (ISSET(sts, (RXSts_FE|RXSts_BE)))
9474e771f5dSjoff SET(code, TTY_FE);
9484e771f5dSjoff if (ISSET(sts, RXSts_PE))
9494e771f5dSjoff SET(code, TTY_PE);
9504e771f5dSjoff }
9514e771f5dSjoff if ((*rint)(code, tp) == -1) {
9524e771f5dSjoff /*
9534e771f5dSjoff * The line discipline's buffer is out of space.
9544e771f5dSjoff */
9554e771f5dSjoff if (!ISSET(sc->sc_rx_flags, RX_TTY_BLOCKED)) {
9564e771f5dSjoff /*
9574e771f5dSjoff * We're either not using flow control, or the
9584e771f5dSjoff * line discipline didn't tell us to block for
9594e771f5dSjoff * some reason. Either way, we have no way to
9604e771f5dSjoff * know when there's more space available, so
9614e771f5dSjoff * just drop the rest of the data.
9624e771f5dSjoff */
9634e771f5dSjoff get += cc << 1;
9644e771f5dSjoff if (get >= end)
9654e771f5dSjoff get -= EPCOM_RING_SIZE << 1;
9664e771f5dSjoff cc = 0;
9674e771f5dSjoff } else {
9684e771f5dSjoff /*
9694e771f5dSjoff * Don't schedule any more receive processing
9704e771f5dSjoff * until the line discipline tells us there's
9714e771f5dSjoff * space available (through comhwiflow()).
9724e771f5dSjoff * Leave the rest of the data in the input
9734e771f5dSjoff * buffer.
9744e771f5dSjoff */
9754e771f5dSjoff SET(sc->sc_rx_flags, RX_TTY_OVERFLOWED);
9764e771f5dSjoff }
9774e771f5dSjoff break;
9784e771f5dSjoff }
9794e771f5dSjoff get += 2;
9804e771f5dSjoff if (get >= end)
9814e771f5dSjoff get = sc->sc_rbuf;
9824e771f5dSjoff cc--;
9834e771f5dSjoff }
9844e771f5dSjoff
9854e771f5dSjoff if (cc != scc) {
9864e771f5dSjoff sc->sc_rbget = get;
9874e771f5dSjoff s = splserial();
9884e771f5dSjoff
9894e771f5dSjoff cc = sc->sc_rbavail += scc - cc;
9904e771f5dSjoff /* Buffers should be ok again, release possible block. */
9914e771f5dSjoff if (cc >= 1) {
9924e771f5dSjoff if (ISSET(sc->sc_rx_flags, RX_IBUF_OVERFLOWED)) {
9934e771f5dSjoff CLR(sc->sc_rx_flags, RX_IBUF_OVERFLOWED);
9944e771f5dSjoff SET(sc->sc_ctrl, (Ctrl_RIE|Ctrl_RTIE));
9954e771f5dSjoff epcom_set(sc);
9964e771f5dSjoff }
9974e771f5dSjoff if (ISSET(sc->sc_rx_flags, RX_IBUF_BLOCKED)) {
9984e771f5dSjoff CLR(sc->sc_rx_flags, RX_IBUF_BLOCKED);
9994e771f5dSjoff #if 0
10004e771f5dSjoff com_hwiflow(sc);
10014e771f5dSjoff #endif
10024e771f5dSjoff }
10034e771f5dSjoff }
10044e771f5dSjoff splx(s);
10054e771f5dSjoff }
10064e771f5dSjoff }
10074e771f5dSjoff
10084e771f5dSjoff static void
epcomsoft(void * arg)10094e771f5dSjoff epcomsoft(void* arg)
10104e771f5dSjoff {
10114e771f5dSjoff struct epcom_softc *sc = arg;
10124e771f5dSjoff
10134e771f5dSjoff if (COM_ISALIVE(sc) == 0)
10144e771f5dSjoff return;
10154e771f5dSjoff
10164e771f5dSjoff if (sc->sc_rx_ready) {
10174e771f5dSjoff sc->sc_rx_ready = 0;
10184e771f5dSjoff epcom_rxsoft(sc, sc->sc_tty);
10194e771f5dSjoff }
10204e771f5dSjoff if (sc->sc_tx_done) {
10214e771f5dSjoff sc->sc_tx_done = 0;
10224e771f5dSjoff epcom_txsoft(sc, sc->sc_tty);
10234e771f5dSjoff }
10244e771f5dSjoff }
10254e771f5dSjoff
10264e771f5dSjoff int
epcomintr(void * arg)10274e771f5dSjoff epcomintr(void* arg)
10284e771f5dSjoff {
10294e771f5dSjoff struct epcom_softc *sc = arg;
10304e771f5dSjoff bus_space_tag_t iot = sc->sc_iot;
10314e771f5dSjoff bus_space_handle_t ioh = sc->sc_ioh;
10324e771f5dSjoff u_char *put, *end;
10334e771f5dSjoff u_int cc;
10344e771f5dSjoff u_int flagr;
103508a4aba7Sskrll uint32_t c, csts;
10364e771f5dSjoff
10379180e146Sskrll (void) bus_space_read_4(iot, ioh, EPCOM_IntIDIntClr);
10384e771f5dSjoff
10394e771f5dSjoff if (COM_ISALIVE(sc) == 0)
10404e771f5dSjoff panic("intr on disabled epcom");
10414e771f5dSjoff
10424e771f5dSjoff flagr = bus_space_read_4(iot, ioh, EPCOM_Flag);
10434e771f5dSjoff
10444e771f5dSjoff end = sc->sc_ebuf;
10454e771f5dSjoff put = sc->sc_rbput;
10464e771f5dSjoff cc = sc->sc_rbavail;
10474e771f5dSjoff
10484e771f5dSjoff if (!(ISSET(flagr, Flag_RXFE))) {
10494e771f5dSjoff if (!ISSET(sc->sc_rx_flags, RX_IBUF_OVERFLOWED)) {
10504e771f5dSjoff while (cc > 0) {
10514e771f5dSjoff if (ISSET(flagr, Flag_RXFE))
10524e771f5dSjoff break;
10534e771f5dSjoff c = bus_space_read_4(iot, ioh, EPCOM_Data);
10544e771f5dSjoff csts = bus_space_read_4(iot, ioh, EPCOM_RXSts);
10554e771f5dSjoff if (ISSET(csts, RXSts_BE)) {
10564e771f5dSjoff int cn_trapped = 0;
10574e771f5dSjoff
10584e771f5dSjoff cn_check_magic(sc->sc_tty->t_dev,
10594e771f5dSjoff CNC_BREAK, epcom_cnm_state);
10604e771f5dSjoff if (cn_trapped)
10614e771f5dSjoff goto next;
10624e771f5dSjoff #if defined(KGDB) && !defined(DDB)
10634e771f5dSjoff if (ISSET(sc->sc_hwflags, COM_HW_KGDB)){
10644e771f5dSjoff kgdb_connect(1);
10654e771f5dSjoff goto next;
10664e771f5dSjoff }
10674e771f5dSjoff #endif
10684e771f5dSjoff } else {
10694e771f5dSjoff int cn_trapped = 0;
10704e771f5dSjoff
10714e771f5dSjoff cn_check_magic(sc->sc_tty->t_dev,
10724e771f5dSjoff (c & 0xff), epcom_cnm_state);
10734e771f5dSjoff if (cn_trapped)
10744e771f5dSjoff goto next;
10754e771f5dSjoff }
10764e771f5dSjoff
10774e771f5dSjoff
10784e771f5dSjoff put[0] = c & 0xff;
10794e771f5dSjoff put[1] = csts & 0xf;
10804e771f5dSjoff put += 2;
10814e771f5dSjoff if (put >= end)
10824e771f5dSjoff put = sc->sc_rbuf;
10834e771f5dSjoff cc--;
10844e771f5dSjoff next:
10854e771f5dSjoff flagr = bus_space_read_4(iot, ioh, EPCOM_Flag);
10864e771f5dSjoff }
10874e771f5dSjoff
10884e771f5dSjoff /*
10894e771f5dSjoff * Current string of incoming characters ended because
10904e771f5dSjoff * no more data was available or we ran out of space.
10914e771f5dSjoff * Schedule a receive event if any data was received.
10924e771f5dSjoff * If we're out of space, turn off receive interrupts.
10934e771f5dSjoff */
10944e771f5dSjoff sc->sc_rbput = put;
10954e771f5dSjoff sc->sc_rbavail = cc;
10964e771f5dSjoff if (!ISSET(sc->sc_rx_flags, RX_TTY_OVERFLOWED))
10974e771f5dSjoff sc->sc_rx_ready = 1;
10984e771f5dSjoff
10994e771f5dSjoff /*
11004e771f5dSjoff * See if we are in danger of overflowing a buffer. If
11014e771f5dSjoff * so, use hardware flow control to ease the pressure.
11024e771f5dSjoff */
11034e771f5dSjoff
11044e771f5dSjoff /* but epcom cannot. X-( */
11054e771f5dSjoff
11064e771f5dSjoff /*
11074e771f5dSjoff * If we're out of space, disable receive interrupts
11084e771f5dSjoff * until the queue has drained a bit.
11094e771f5dSjoff */
11104e771f5dSjoff if (!cc) {
11114e771f5dSjoff SET(sc->sc_rx_flags, RX_IBUF_OVERFLOWED);
11124e771f5dSjoff CLR(sc->sc_ctrl, (Ctrl_RIE|Ctrl_RTIE));
11134e771f5dSjoff epcom_set(sc);
11144e771f5dSjoff }
11154e771f5dSjoff } else {
11164e771f5dSjoff #ifdef DIAGNOSTIC
11174e771f5dSjoff panic("epcomintr: we shouldn't reach here");
11184e771f5dSjoff #endif
11194e771f5dSjoff CLR(sc->sc_ctrl, (Ctrl_RIE|Ctrl_RTIE));
11204e771f5dSjoff epcom_set(sc);
11214e771f5dSjoff }
11224e771f5dSjoff }
11234e771f5dSjoff
11244e771f5dSjoff /*
11254e771f5dSjoff * Done handling any receive interrupts. See if data can be
11264e771f5dSjoff * transmitted as well. Schedule tx done event if no data left
11274e771f5dSjoff * and tty was marked busy.
11284e771f5dSjoff */
11294e771f5dSjoff
11305beb7d30Sjoff if (!ISSET(flagr, Flag_TXFF) && sc->sc_tbc > 0) {
11314e771f5dSjoff /* Output the next chunk of the contiguous buffer, if any. */
11324e771f5dSjoff epcom_filltx(sc);
11334e771f5dSjoff } else {
11344e771f5dSjoff /* Disable transmit completion interrupts if necessary. */
11354e771f5dSjoff if (ISSET(sc->sc_ctrl, Ctrl_TIE)) {
11364e771f5dSjoff CLR(sc->sc_ctrl, Ctrl_TIE);
11374e771f5dSjoff epcom_set(sc);
11384e771f5dSjoff }
11394e771f5dSjoff if (sc->sc_tx_busy) {
11404e771f5dSjoff sc->sc_tx_busy = 0;
11414e771f5dSjoff sc->sc_tx_done = 1;
11424e771f5dSjoff }
11434e771f5dSjoff }
11444e771f5dSjoff
11454e771f5dSjoff /* Wake up the poller. */
11460c0de807Smatt softint_schedule(sc->sc_si);
11474e771f5dSjoff
11484e771f5dSjoff #if 0 /* XXX: broken */
11497b0b7dedStls #ifdef RND_COM
11504e771f5dSjoff rnd_add_uint32(&sc->rnd_source, intr ^ flagr);
11514e771f5dSjoff #endif
11524e771f5dSjoff #endif
11534e771f5dSjoff return (1);
11544e771f5dSjoff }
11559d134cf5Sskrll
11569d134cf5Sskrll #ifdef KGDB
11579d134cf5Sskrll int
epcom_kgdb_attach(bus_space_tag_t iot,bus_addr_t iobase,int rate,tcflag_t cflag)11589d134cf5Sskrll epcom_kgdb_attach(bus_space_tag_t iot, bus_addr_t iobase, int rate,
11599d134cf5Sskrll tcflag_t cflag)
11609d134cf5Sskrll {
11619d134cf5Sskrll bus_space_handle_t ioh;
11629d134cf5Sskrll
11639d134cf5Sskrll if (iot == epcomcn_sc.sc_iot && iobase == epcomcn_sc.sc_hwbase) {
11649d134cf5Sskrll #if !defined(DDB)
11659d134cf5Sskrll return EBUSY; /* cannot share with console */
11669d134cf5Sskrll #else
11679d134cf5Sskrll /*
11689d134cf5Sskrll * XXX I have no intention of ever testing and code path
11699d134cf5Sskrll * implied by getting here
11709d134cf5Sskrll */
11719d134cf5Sskrll kgdb_sc = epcomcn_sc;
11729d134cf5Sskrll #endif
11739d134cf5Sskrll } else {
11749d134cf5Sskrll bus_space_map(iot, iobase, EP93XX_APB_UART_SIZE, 0, &ioh);
11759d134cf5Sskrll epcominit(&kgdb_sc, iot, iobase, ioh, rate, cflag);
11769d134cf5Sskrll }
11779d134cf5Sskrll
11789d134cf5Sskrll kgdb_attach(epcom_kgdb_getc, epcom_kgdb_putc, &kgdb_sc);
11799d134cf5Sskrll kgdb_dev = 123; /* unneeded, only to satisfy some tests */
11809d134cf5Sskrll
11819d134cf5Sskrll return 0;
11829d134cf5Sskrll }
11839d134cf5Sskrll
11849d134cf5Sskrll static int
epcom_kgdb_getc(void * sc)11859d134cf5Sskrll epcom_kgdb_getc (void *sc)
11869d134cf5Sskrll {
11879d134cf5Sskrll return epcom_common_getc(sc, NODEV);
11889d134cf5Sskrll }
11899d134cf5Sskrll
11909d134cf5Sskrll static void
epcom_kgdb_putc(void * sc,int c)11919d134cf5Sskrll epcom_kgdb_putc (void *sc, int c)
11929d134cf5Sskrll {
11939d134cf5Sskrll epcom_common_putc(sc, c);
11949d134cf5Sskrll }
11959d134cf5Sskrll #endif /* KGDB */
11969d134cf5Sskrll
11979d134cf5Sskrll /*
11989d134cf5Sskrll * Common code used for initialisation of the console or KGDB connection
11999d134cf5Sskrll */
12009d134cf5Sskrll static void
epcominit(struct epcom_cons_softc * sc,bus_space_tag_t iot,bus_addr_t iobase,bus_space_handle_t ioh,int rate,tcflag_t cflag)12019d134cf5Sskrll epcominit(struct epcom_cons_softc *sc, bus_space_tag_t iot,
12029d134cf5Sskrll bus_addr_t iobase, bus_space_handle_t ioh, int rate, tcflag_t cflag)
12039d134cf5Sskrll {
12049d134cf5Sskrll u_int lcrlo, lcrmid, lcrhi, ctrl, pwrcnt;
12059d134cf5Sskrll bus_space_handle_t syscon_ioh;
12069d134cf5Sskrll
12079d134cf5Sskrll sc->sc_iot = iot;
12089d134cf5Sskrll sc->sc_ioh = ioh;
12099d134cf5Sskrll sc->sc_hwbase = iobase;
12109d134cf5Sskrll sc->sc_ospeed = rate;
12119d134cf5Sskrll sc->sc_cflag = cflag;
12129d134cf5Sskrll
12139d134cf5Sskrll lcrhi = cflag2lcrhi(cflag);
12149d134cf5Sskrll lcrlo = EPCOMSPEED2BRD(rate) & 0xff;
12159d134cf5Sskrll lcrmid = EPCOMSPEED2BRD(rate) >> 8;
12169d134cf5Sskrll ctrl = Ctrl_UARTE;
12179d134cf5Sskrll
12189d134cf5Sskrll /* Make sure the UARTs are clocked at the system default rate */
12199d134cf5Sskrll bus_space_map(iot, EP93XX_APB_HWBASE + EP93XX_APB_SYSCON,
12209d134cf5Sskrll EP93XX_APB_SYSCON_SIZE, 0, &syscon_ioh);
12219d134cf5Sskrll pwrcnt = bus_space_read_4(iot, syscon_ioh, EP93XX_SYSCON_PwrCnt);
12229d134cf5Sskrll pwrcnt &= ~(PwrCnt_UARTBAUD);
12239d134cf5Sskrll bus_space_write_4(iot, syscon_ioh, EP93XX_SYSCON_PwrCnt, pwrcnt);
12249d134cf5Sskrll bus_space_unmap(iot, syscon_ioh, EP93XX_APB_SYSCON_SIZE);
12259d134cf5Sskrll
12269d134cf5Sskrll bus_space_write_4(iot, ioh, EPCOM_LinCtrlLow, lcrlo);
12279d134cf5Sskrll bus_space_write_4(iot, ioh, EPCOM_LinCtrlMid, lcrmid);
12289d134cf5Sskrll bus_space_write_4(iot, ioh, EPCOM_LinCtrlHigh, lcrhi);
12299d134cf5Sskrll bus_space_write_4(iot, ioh, EPCOM_Ctrl, ctrl);
12309d134cf5Sskrll }
1231