diff --git a/sys/arch/arm/ep93xx/epcom.c b/sys/arch/arm/ep93xx/epcom.c new file mode 100644 index 000000000000..6910938de9af --- /dev/null +++ b/sys/arch/arm/ep93xx/epcom.c @@ -0,0 +1,1227 @@ +/* $NetBSD: epcom.c,v 1.1 2004/12/22 19:10:25 joff Exp $ */ +/* + * Copyright (c) 1998, 1999, 2001, 2002, 2004 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Jesse Off + * + * This code is derived from software contributed to The NetBSD Foundation + * by Ichiro FUKUHARA and Naoto Shimazaki. + * + * This code is derived from software contributed to The NetBSD Foundation + * by IWAMOTO Toshihiro. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Charles M. Hannum. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the NetBSD + * Foundation, Inc. and its contributors. + * 4. Neither the name of The NetBSD Foundation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +/* + * Copyright (c) 1991 The Regents of the University of California. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)com.c 7.5 (Berkeley) 5/16/91 + */ + +/* + * TODO: hardware flow control + */ + +#include +__KERNEL_RCSID(0, "$NetBSD: epcom.c,v 1.1 2004/12/22 19:10:25 joff Exp $"); + +#include "opt_ddb.h" +#include "opt_kgdb.h" +#include "epcom.h" + +#include "rnd.h" +#if NRND > 0 && defined(RND_COM) +#include +#endif + +/* + * Override cnmagic(9) macro before including . + * We need to know if cn_check_magic triggered debugger, so set a flag. + * Callers of cn_check_magic must declare int cn_trapped = 0; + * XXX: this is *ugly*! + */ +#define cn_trap() \ + do { \ + console_debugger(); \ + cn_trapped = 1; \ + } while (/* CONSTCOND */ 0) + + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include +#include + +#include + +static int epcomparam(struct tty *, struct termios *); +static void epcomstart(struct tty *); +static int epcomhwiflow(struct tty *, int); + +static u_int cflag2lcrhi(tcflag_t); +static void epcom_iflush(struct epcom_softc *); +static void epcom_set(struct epcom_softc *); + +int epcomcngetc(dev_t); +void epcomcnputc(dev_t, int); +void epcomcnpollc(dev_t, int); + +static void epcomsoft(void* arg); +inline static void epcom_txsoft(struct epcom_softc *, struct tty *); +inline static void epcom_rxsoft(struct epcom_softc *, struct tty *); + +void epcomcnprobe(struct consdev *); +void epcomcninit(struct consdev *); + +static struct epcom_cons_softc { + bus_space_tag_t sc_iot; + bus_space_handle_t sc_ioh; + bus_addr_t sc_hwbase; + int sc_ospeed; + tcflag_t sc_cflag; + int sc_attached; +} epcomcn_sc; + +static struct cnm_state epcom_cnm_state; + +extern struct cfdriver epcom_cd; + +dev_type_open(epcomopen); +dev_type_close(epcomclose); +dev_type_read(epcomread); +dev_type_write(epcomwrite); +dev_type_ioctl(epcomioctl); +dev_type_stop(epcomstop); +dev_type_tty(epcomtty); +dev_type_poll(epcompoll); + +const struct cdevsw epcom_cdevsw = { + epcomopen, epcomclose, epcomread, epcomwrite, epcomioctl, + epcomstop, epcomtty, epcompoll, nommap, ttykqfilter, D_TTY +}; + +struct consdev epcomcons = { + NULL, NULL, epcomcngetc, epcomcnputc, epcomcnpollc, NULL, + NULL, NULL, NODEV, CN_NORMAL +}; + +#ifndef DEFAULT_COMSPEED +#define DEFAULT_COMSPEED 115200 +#endif + +#define COMUNIT_MASK 0x7ffff +#define COMDIALOUT_MASK 0x80000 + +#define COMUNIT(x) (minor(x) & COMUNIT_MASK) +#define COMDIALOUT(x) (minor(x) & COMDIALOUT_MASK) + +#define COM_ISALIVE(sc) ((sc)->enabled != 0 && \ + ISSET((sc)->sc_dev.dv_flags, DVF_ACTIVE)) + +#define SET(t, f) (t) |= (f) +#define CLR(t, f) (t) &= ~(f) +#define ISSET(t, f) ((t) & (f)) + +void +epcom_attach_subr(sc) + struct epcom_softc *sc; +{ + struct tty *tp; + + if (sc->sc_iot == epcomcn_sc.sc_iot + && sc->sc_hwbase == epcomcn_sc.sc_hwbase) { + epcomcn_sc.sc_attached = 1; + sc->sc_lcrlo = EPCOMSPEED2BRD(epcomcn_sc.sc_ospeed) & 0xff; + sc->sc_lcrmid = EPCOMSPEED2BRD(epcomcn_sc.sc_ospeed) >> 8; + + /* Make sure the console is always "hardwired". */ + delay(10000); /* wait for output to finish */ + SET(sc->sc_hwflags, COM_HW_CONSOLE); + SET(sc->sc_swflags, TIOCFLAG_SOFTCAR); + } + + tp = ttymalloc(); + tp->t_oproc = epcomstart; + tp->t_param = epcomparam; + tp->t_hwiflow = epcomhwiflow; + + sc->sc_tty = tp; + sc->sc_rbuf = malloc(EPCOM_RING_SIZE << 1, M_DEVBUF, M_NOWAIT); + sc->sc_rbput = sc->sc_rbget = sc->sc_rbuf; + sc->sc_rbavail = EPCOM_RING_SIZE; + if (sc->sc_rbuf == NULL) { + printf("%s: unable to allocate ring buffer\n", + sc->sc_dev.dv_xname); + return; + } + sc->sc_ebuf = sc->sc_rbuf + (EPCOM_RING_SIZE << 1); + sc->sc_tbc = 0; + + sc->sc_lcrlo = EPCOMSPEED2BRD(DEFAULT_COMSPEED) & 0xff; + sc->sc_lcrmid = EPCOMSPEED2BRD(DEFAULT_COMSPEED) >> 8; + sc->sc_lcrhi = cflag2lcrhi(CS8); /* 8N1 */ + + tty_attach(tp); + + if (ISSET(sc->sc_hwflags, COM_HW_CONSOLE)) { + int maj; + + /* locate the major number */ + maj = cdevsw_lookup_major(&epcom_cdevsw); + + cn_tab->cn_dev = makedev(maj, sc->sc_dev.dv_unit); + + aprint_normal("%s: console\n", sc->sc_dev.dv_xname); + } + + sc->sc_si = softintr_establish(IPL_SOFTSERIAL, epcomsoft, sc); + +#if NRND > 0 && defined(RND_COM) + rnd_attach_source(&sc->rnd_source, sc->sc_dev.dv_xname, + RND_TYPE_TTY, 0); +#endif + + /* if there are no enable/disable functions, assume the device + is always enabled */ + if (!sc->enable) + sc->enabled = 1; + + /* XXX configure register */ + /* xxx_config(sc) */ + + SET(sc->sc_hwflags, COM_HW_DEV_OK); +} + +static int +epcomparam(tp, t) + struct tty *tp; + struct termios *t; +{ + struct epcom_softc *sc + = device_lookup(&epcom_cd, COMUNIT(tp->t_dev)); + int s; + + if (COM_ISALIVE(sc) == 0) + return (EIO); + + if (t->c_ispeed && t->c_ispeed != t->c_ospeed) + return (EINVAL); + + /* + * For the console, always force CLOCAL and !HUPCL, so that the port + * is always active. + */ + if (ISSET(sc->sc_swflags, TIOCFLAG_SOFTCAR) || + ISSET(sc->sc_hwflags, COM_HW_CONSOLE)) { + SET(t->c_cflag, CLOCAL); + CLR(t->c_cflag, HUPCL); + } + + /* + * If there were no changes, don't do anything. This avoids dropping + * input and improves performance when all we did was frob things like + * VMIN and VTIME. + */ + if (tp->t_ospeed == t->c_ospeed && + tp->t_cflag == t->c_cflag) + return (0); + + s = splserial(); + + sc->sc_lcrhi = cflag2lcrhi(t->c_cflag); + sc->sc_lcrlo = EPCOMSPEED2BRD(t->c_ospeed) & 0xff; + sc->sc_lcrmid = EPCOMSPEED2BRD(t->c_ospeed) >> 8; + + /* And copy to tty. */ + tp->t_ispeed = 0; + tp->t_ospeed = t->c_ospeed; + tp->t_cflag = t->c_cflag; + + if (!sc->sc_heldchange) { + if (sc->sc_tx_busy) { + sc->sc_heldtbc = sc->sc_tbc; + sc->sc_tbc = 0; + sc->sc_heldchange = 1; + } else + epcom_set(sc); + } + + splx(s); + + /* + * Update the tty layer's idea of the carrier bit. + * We tell tty the carrier is always on. + */ + (void) (*tp->t_linesw->l_modem)(tp, 1); + +#ifdef COM_DEBUG + if (com_debug) + comstatus(sc, "comparam "); +#endif + + if (!ISSET(t->c_cflag, CHWFLOW)) { + if (sc->sc_tx_stopped) { + sc->sc_tx_stopped = 0; + epcomstart(tp); + } + } + + return (0); +} + +static int +epcomhwiflow(tp, block) + struct tty *tp; + int block; +{ + return (0); +} + +static void +epcom_filltx(struct epcom_softc *sc) +{ + bus_space_tag_t iot = sc->sc_iot; + bus_space_handle_t ioh = sc->sc_ioh; + int n; + + n = 0; + while ((bus_space_read_4(iot, ioh, EPCOM_Flag) & Flag_TXFF) == 0) { + if (n >= sc->sc_tbc) + break; + bus_space_write_4(iot, ioh, EPCOM_Data, + 0xff & *(sc->sc_tba + n)); + n++; + } + sc->sc_tbc -= n; + sc->sc_tba += n; +} + +static void +epcomstart(tp) + struct tty *tp; +{ + struct epcom_softc *sc + = device_lookup(&epcom_cd, COMUNIT(tp->t_dev)); + int s; + + if (COM_ISALIVE(sc) == 0) + return; + + s = spltty(); + if (ISSET(tp->t_state, TS_BUSY | TS_TIMEOUT | TS_TTSTOP)) + goto out; + if (sc->sc_tx_stopped) + goto out; + + if (tp->t_outq.c_cc <= tp->t_lowat) { + if (ISSET(tp->t_state, TS_ASLEEP)) { + CLR(tp->t_state, TS_ASLEEP); + wakeup(&tp->t_outq); + } + selwakeup(&tp->t_wsel); + if (tp->t_outq.c_cc == 0) + goto out; + } + + /* Grab the first contiguous region of buffer space. */ + { + u_char *tba; + int tbc; + + tba = tp->t_outq.c_cf; + tbc = ndqb(&tp->t_outq, 0); + + (void)splserial(); + + sc->sc_tba = tba; + sc->sc_tbc = tbc; + } + + SET(tp->t_state, TS_BUSY); + sc->sc_tx_busy = 1; + + if (!ISSET(sc->sc_ctrl, Ctrl_TIE)) { + SET(sc->sc_ctrl, Ctrl_TIE); + epcom_set(sc); + } + + /* Output the first chunk of the contiguous buffer. */ + epcom_filltx(sc); + +out: + splx(s); + return; +} + +static void +epcom_break(struct epcom_softc *sc, int onoff) +{ + if (onoff) + SET(sc->sc_lcrhi, LinCtrlHigh_BRK); + else + CLR(sc->sc_lcrhi, LinCtrlHigh_BRK); + if (!sc->sc_heldchange) { + if (sc->sc_tx_busy) { + sc->sc_heldtbc = sc->sc_tbc; + sc->sc_tbc = 0; + sc->sc_heldchange = 1; + } else + epcom_set(sc); + } +} + +static void +epcom_shutdown(struct epcom_softc *sc) +{ + int s; + + s = splserial(); + + /* Turn off interrupts. */ + CLR(sc->sc_ctrl, (Ctrl_TIE|Ctrl_RTIE|Ctrl_RIE)); + + /* Clear any break condition set with TIOCSBRK. */ + epcom_break(sc, 0); + epcom_set(sc); + + if (sc->disable) { +#ifdef DIAGNOSTIC + if (!sc->enabled) + panic("epcom_shutdown: not enabled?"); +#endif + (*sc->disable)(sc); + sc->enabled = 0; + } + splx(s); +} + +int +epcomopen(dev, flag, mode, p) + dev_t dev; + int flag, mode; + struct proc *p; +{ + struct epcom_softc *sc; + struct tty *tp; + int s, s2; + int error; + + sc = device_lookup(&epcom_cd, COMUNIT(dev)); + if (sc == NULL || !ISSET(sc->sc_hwflags, COM_HW_DEV_OK) || + sc->sc_rbuf == NULL) + return (ENXIO); + + if (ISSET(sc->sc_dev.dv_flags, DVF_ACTIVE) == 0) + return (ENXIO); + +#ifdef KGDB + /* + * If this is the kgdb port, no other use is permitted. + */ + if (ISSET(sc->sc_hwflags, COM_HW_KGDB)) + return (EBUSY); +#endif + + tp = sc->sc_tty; + + if (ISSET(tp->t_state, TS_ISOPEN) && + ISSET(tp->t_state, TS_XCLUDE) && + p->p_ucred->cr_uid != 0) + return (EBUSY); + + s = spltty(); + + /* + * Do the following iff this is a first open. + */ + if (!ISSET(tp->t_state, TS_ISOPEN) && tp->t_wopen == 0) { + struct termios t; + + tp->t_dev = dev; + + s2 = splserial(); + + if (sc->enable) { + if ((*sc->enable)(sc)) { + splx(s2); + splx(s); + printf("%s: device enable failed\n", + sc->sc_dev.dv_xname); + return (EIO); + } + sc->enabled = 1; +#if 0 +/* XXXXXXXXXXXXXXX */ + com_config(sc); +#endif + } + + /* Turn on interrupts. */ + SET(sc->sc_ctrl, (Ctrl_UARTE|Ctrl_RIE|Ctrl_RTIE)); + epcom_set(sc); + +#if 0 + /* Fetch the current modem control status, needed later. */ + sc->sc_msr = bus_space_read_1(sc->sc_iot, sc->sc_ioh, com_msr); + + /* Clear PPS capture state on first open. */ + sc->sc_ppsmask = 0; + sc->ppsparam.mode = 0; +#endif + + splx(s2); + + /* + * Initialize the termios status to the defaults. Add in the + * sticky bits from TIOCSFLAGS. + */ + t.c_ispeed = 0; + if (ISSET(sc->sc_hwflags, COM_HW_CONSOLE)) { + t.c_ospeed = epcomcn_sc.sc_ospeed; + t.c_cflag = epcomcn_sc.sc_cflag; + } else { + t.c_ospeed = TTYDEF_SPEED; + t.c_cflag = TTYDEF_CFLAG; + } + if (ISSET(sc->sc_swflags, TIOCFLAG_CLOCAL)) + SET(t.c_cflag, CLOCAL); + if (ISSET(sc->sc_swflags, TIOCFLAG_CRTSCTS)) + SET(t.c_cflag, CRTSCTS); + if (ISSET(sc->sc_swflags, TIOCFLAG_MDMBUF)) + SET(t.c_cflag, MDMBUF); + /* Make sure epcomparam() will do something. */ + tp->t_ospeed = 0; + (void) epcomparam(tp, &t); + tp->t_iflag = TTYDEF_IFLAG; + tp->t_oflag = TTYDEF_OFLAG; + tp->t_lflag = TTYDEF_LFLAG; + ttychars(tp); + ttsetwater(tp); + + s2 = splserial(); + + /* Clear the input ring, and unblock. */ + sc->sc_rbput = sc->sc_rbget = sc->sc_rbuf; + sc->sc_rbavail = EPCOM_RING_SIZE; + epcom_iflush(sc); + CLR(sc->sc_rx_flags, RX_ANY_BLOCK); + +#ifdef COM_DEBUG + if (epcom_debug) + comstatus(sc, "epcomopen "); +#endif + + splx(s2); + } + + splx(s); + + error = ttyopen(tp, COMDIALOUT(dev), ISSET(flag, O_NONBLOCK)); + if (error) + goto bad; + + error = (*tp->t_linesw->l_open)(dev, tp); + if (error) + goto bad; + + return (0); + +bad: + if (!ISSET(tp->t_state, TS_ISOPEN) && tp->t_wopen == 0) { + /* + * We failed to open the device, and nobody else had it opened. + * Clean up the state as appropriate. + */ + epcom_shutdown(sc); + } + + return (error); +} + +int +epcomclose(dev, flag, mode, p) + dev_t dev; + int flag, mode; + struct proc *p; +{ + struct epcom_softc *sc = device_lookup(&epcom_cd, COMUNIT(dev)); + struct tty *tp = sc->sc_tty; + + /* XXX This is for cons.c. */ + if (!ISSET(tp->t_state, TS_ISOPEN)) + return (0); + + (*tp->t_linesw->l_close)(tp, flag); + ttyclose(tp); + + if (COM_ISALIVE(sc) == 0) + return (0); + + if (!ISSET(tp->t_state, TS_ISOPEN) && tp->t_wopen == 0) { + /* + * Although we got a last close, the device may still be in + * use; e.g. if this was the dialout node, and there are still + * processes waiting for carrier on the non-dialout node. + */ + epcom_shutdown(sc); + } + + return (0); +} + +int +epcomread(dev, uio, flag) + dev_t dev; + struct uio *uio; + int flag; +{ + struct epcom_softc *sc = device_lookup(&epcom_cd, COMUNIT(dev)); + struct tty *tp = sc->sc_tty; + + if (COM_ISALIVE(sc) == 0) + return (EIO); + + return ((*tp->t_linesw->l_read)(tp, uio, flag)); +} + +int +epcomwrite(dev, uio, flag) + dev_t dev; + struct uio *uio; + int flag; +{ + struct epcom_softc *sc = device_lookup(&epcom_cd, COMUNIT(dev)); + struct tty *tp = sc->sc_tty; + + if (COM_ISALIVE(sc) == 0) + return (EIO); + + return ((*tp->t_linesw->l_write)(tp, uio, flag)); +} + +int +epcompoll(dev, events, p) + dev_t dev; + int events; + struct proc *p; +{ + struct epcom_softc *sc = device_lookup(&epcom_cd, COMUNIT(dev)); + struct tty *tp = sc->sc_tty; + + if (COM_ISALIVE(sc) == 0) + return (EIO); + + return ((*tp->t_linesw->l_poll)(tp, events, p)); +} + +struct tty * +epcomtty(dev) + dev_t dev; +{ + struct epcom_softc *sc = device_lookup(&epcom_cd, COMUNIT(dev)); + struct tty *tp = sc->sc_tty; + + return (tp); +} + +int +epcomioctl(dev_t dev, u_long cmd, caddr_t data, int flag, struct proc *p) +{ + struct epcom_softc *sc = device_lookup(&epcom_cd, COMUNIT(dev)); + struct tty *tp = sc->sc_tty; + int error; + int s; + + if (COM_ISALIVE(sc) == 0) + return (EIO); + + error = (*tp->t_linesw->l_ioctl)(tp, cmd, data, flag, p); + if (error != EPASSTHROUGH) + return (error); + + error = ttioctl(tp, cmd, data, flag, p); + if (error != EPASSTHROUGH) + return (error); + + error = 0; + + s = splserial(); + + switch (cmd) { + case TIOCSBRK: + epcom_break(sc, 1); + break; + + case TIOCCBRK: + epcom_break(sc, 0); + break; + + case TIOCGFLAGS: + *(int *)data = sc->sc_swflags; + break; + + case TIOCSFLAGS: + error = suser(p->p_ucred, &p->p_acflag); + if (error) + break; + sc->sc_swflags = *(int *)data; + break; + + default: + error = EPASSTHROUGH; + break; + } + + splx(s); + + return (error); +} + +/* + * Stop output on a line. + */ +void +epcomstop(tp, flag) + struct tty *tp; + int flag; +{ + struct epcom_softc *sc + = device_lookup(&epcom_cd, COMUNIT(tp->t_dev)); + int s; + + s = splserial(); + if (ISSET(tp->t_state, TS_BUSY)) { + /* Stop transmitting at the next chunk. */ + sc->sc_tbc = 0; + sc->sc_heldtbc = 0; + if (!ISSET(tp->t_state, TS_TTSTOP)) + SET(tp->t_state, TS_FLUSH); + } + splx(s); +} + +static u_int +cflag2lcrhi(cflag) + tcflag_t cflag; +{ + u_int lcrhi; + + switch (cflag & CSIZE) { + case CS7: + lcrhi = 0x40; + break; + case CS6: + lcrhi = 0x20; + break; + case CS8: + default: + lcrhi = 0x60; + break; + } + lcrhi |= (cflag & PARENB) ? LinCtrlHigh_PEN : 0; + lcrhi |= (cflag & PARODD) ? 0 : LinCtrlHigh_EPS; + lcrhi |= (cflag & CSTOPB) ? LinCtrlHigh_STP2 : 0; + lcrhi |= LinCtrlHigh_FEN; /* FIFO always enabled */ + + return (lcrhi); +} + +static void +epcom_iflush(sc) + struct epcom_softc *sc; +{ + bus_space_tag_t iot = sc->sc_iot; + bus_space_handle_t ioh = sc->sc_ioh; +#ifdef DIAGNOSTIC + int reg; +#endif + int timo; + +#ifdef DIAGNOSTIC + reg = 0xffff; +#endif + timo = 50000; + /* flush any pending I/O */ + while ((bus_space_read_4(iot, ioh, EPCOM_Flag) & Flag_RXFE) == 0 + && --timo) +#ifdef DIAGNOSTIC + reg = +#else + (void) +#endif + bus_space_read_4(iot, ioh, EPCOM_Data); +#ifdef DIAGNOSTIC + if (!timo) + printf("%s: com_iflush timeout %02x\n", sc->sc_dev.dv_xname, + reg); +#endif +} + +static void +epcom_set(struct epcom_softc *sc) +{ + bus_space_write_4(sc->sc_iot, sc->sc_ioh, EPCOM_LinCtrlLow, + sc->sc_lcrlo); + bus_space_write_4(sc->sc_iot, sc->sc_ioh, EPCOM_LinCtrlMid, + sc->sc_lcrmid); + bus_space_write_4(sc->sc_iot, sc->sc_ioh, EPCOM_LinCtrlHigh, + sc->sc_lcrhi); + bus_space_write_4(sc->sc_iot, sc->sc_ioh, EPCOM_Ctrl, + sc->sc_ctrl); +} + +int +epcomcnattach(iot, iobase, ioh, ospeed, cflag) + bus_space_tag_t iot; + bus_addr_t iobase; + bus_space_handle_t ioh; + int ospeed; + tcflag_t cflag; +{ + u_int lcrlo, lcrmid, lcrhi, ctrl, pwrcnt; + bus_space_handle_t syscon_ioh; + + cn_tab = &epcomcons; + cn_init_magic(&epcom_cnm_state); + cn_set_magic("\047\001"); + + epcomcn_sc.sc_iot = iot; + epcomcn_sc.sc_ioh = ioh; + epcomcn_sc.sc_hwbase = iobase; + epcomcn_sc.sc_ospeed = ospeed; + epcomcn_sc.sc_cflag = cflag; + + lcrhi = cflag2lcrhi(cflag); + lcrlo = EPCOMSPEED2BRD(ospeed) & 0xff; + lcrmid = EPCOMSPEED2BRD(ospeed) >> 8; + ctrl = Ctrl_UARTE; + + bus_space_map(iot, EP93XX_APB_HWBASE + EP93XX_APB_SYSCON, + EP93XX_APB_SYSCON_SIZE, 0, &syscon_ioh); + pwrcnt = bus_space_read_4(iot, syscon_ioh, EP93XX_SYSCON_PwrCnt); + pwrcnt &= ~(PwrCnt_UARTBAUD); + bus_space_write_4(iot, syscon_ioh, EP93XX_SYSCON_PwrCnt, pwrcnt); + bus_space_unmap(iot, syscon_ioh, EP93XX_APB_SYSCON_SIZE); + + bus_space_write_4(iot, ioh, EPCOM_LinCtrlLow, lcrlo); + bus_space_write_4(iot, ioh, EPCOM_LinCtrlMid, lcrmid); + bus_space_write_4(iot, ioh, EPCOM_LinCtrlHigh, lcrhi); + bus_space_write_4(iot, ioh, EPCOM_Ctrl, ctrl); + + return (0); +} + +void +epcomcnprobe(cp) + struct consdev *cp; +{ + cp->cn_pri = CN_REMOTE; +} + +void +epcomcnpollc(dev, on) + dev_t dev; + int on; +{ +} + +void +epcomcnputc(dev, c) + dev_t dev; + int c; +{ + int s; + bus_space_tag_t iot = epcomcn_sc.sc_iot; + bus_space_handle_t ioh = epcomcn_sc.sc_ioh; + + s = splserial(); + + while((bus_space_read_4(iot, ioh, EPCOM_Flag) & Flag_TXFF) != 0) + ; + + bus_space_write_4(iot, ioh, EPCOM_Data, c); + +#ifdef DEBUG + if (c == '\r') { + while((bus_space_read_4(iot, ioh, EPCOM_Flag) & Flag_TXFE) == 0) + ; + } +#endif + + splx(s); +} + +int +epcomcngetc(dev) + dev_t dev; +{ + int c, sts; + int s; + bus_space_tag_t iot = epcomcn_sc.sc_iot; + bus_space_handle_t ioh = epcomcn_sc.sc_ioh; + + s = splserial(); + + while((bus_space_read_4(iot, ioh, EPCOM_Flag) & Flag_RXFE) != 0) + ; + + c = bus_space_read_4(iot, ioh, EPCOM_Data); + sts = bus_space_read_4(iot, ioh, EPCOM_RXSts); + if (ISSET(sts, RXSts_BE)) c = CNC_BREAK; +#ifdef DDB + extern int db_active; + if (!db_active) +#endif + { + int cn_trapped = 0; /* unused */ + + cn_check_magic(dev, c, epcom_cnm_state); + } + c &= 0xff; + splx(s); + + return (c); +} + +inline static void +epcom_txsoft(sc, tp) + struct epcom_softc *sc; + struct tty *tp; +{ + CLR(tp->t_state, TS_BUSY); + if (ISSET(tp->t_state, TS_FLUSH)) + CLR(tp->t_state, TS_FLUSH); + else + ndflush(&tp->t_outq, (int)(sc->sc_tba - tp->t_outq.c_cf)); + (*tp->t_linesw->l_start)(tp); +} + +inline static void +epcom_rxsoft(sc, tp) + struct epcom_softc *sc; + struct tty *tp; +{ + int (*rint) __P((int c, struct tty *tp)) = tp->t_linesw->l_rint; + u_char *get, *end; + u_int cc, scc; + u_char sts; + int code; + int s; + + end = sc->sc_ebuf; + get = sc->sc_rbget; + scc = cc = EPCOM_RING_SIZE - sc->sc_rbavail; +#if 0 + if (cc == EPCOM_RING_SIZE) { + sc->sc_floods++; + if (sc->sc_errors++ == 0) + callout_reset(&sc->sc_diag_callout, 60 * hz, + comdiag, sc); + } +#endif + while (cc) { + code = get[0]; + sts = get[1]; + if (ISSET(sts, RXSts_OE | RXSts_FE | RXSts_PE | RXSts_BE)) { +#if 0 + if (ISSET(lsr, DR_ROR)) { + sc->sc_overflows++; + if (sc->sc_errors++ == 0) + callout_reset(&sc->sc_diag_callout, + 60 * hz, comdiag, sc); + } +#endif + if (ISSET(sts, (RXSts_FE|RXSts_BE))) + SET(code, TTY_FE); + if (ISSET(sts, RXSts_PE)) + SET(code, TTY_PE); + } + if ((*rint)(code, tp) == -1) { + /* + * The line discipline's buffer is out of space. + */ + if (!ISSET(sc->sc_rx_flags, RX_TTY_BLOCKED)) { + /* + * We're either not using flow control, or the + * line discipline didn't tell us to block for + * some reason. Either way, we have no way to + * know when there's more space available, so + * just drop the rest of the data. + */ + get += cc << 1; + if (get >= end) + get -= EPCOM_RING_SIZE << 1; + cc = 0; + } else { + /* + * Don't schedule any more receive processing + * until the line discipline tells us there's + * space available (through comhwiflow()). + * Leave the rest of the data in the input + * buffer. + */ + SET(sc->sc_rx_flags, RX_TTY_OVERFLOWED); + } + break; + } + get += 2; + if (get >= end) + get = sc->sc_rbuf; + cc--; + } + + if (cc != scc) { + sc->sc_rbget = get; + s = splserial(); + + cc = sc->sc_rbavail += scc - cc; + /* Buffers should be ok again, release possible block. */ + if (cc >= 1) { + if (ISSET(sc->sc_rx_flags, RX_IBUF_OVERFLOWED)) { + CLR(sc->sc_rx_flags, RX_IBUF_OVERFLOWED); + SET(sc->sc_ctrl, (Ctrl_RIE|Ctrl_RTIE)); + epcom_set(sc); + } + if (ISSET(sc->sc_rx_flags, RX_IBUF_BLOCKED)) { + CLR(sc->sc_rx_flags, RX_IBUF_BLOCKED); +#if 0 + com_hwiflow(sc); +#endif + } + } + splx(s); + } +} + +static void +epcomsoft(void* arg) +{ + struct epcom_softc *sc = arg; + + if (COM_ISALIVE(sc) == 0) + return; + + if (sc->sc_rx_ready) { + sc->sc_rx_ready = 0; + epcom_rxsoft(sc, sc->sc_tty); + } + if (sc->sc_tx_done) { + sc->sc_tx_done = 0; + epcom_txsoft(sc, sc->sc_tty); + } +} + +int +epcomintr(void* arg) +{ + struct epcom_softc *sc = arg; + bus_space_tag_t iot = sc->sc_iot; + bus_space_handle_t ioh = sc->sc_ioh; + u_char *put, *end; + u_int cc; + u_int flagr; + u_int intr; + u_int32_t c, csts; + + intr = bus_space_read_4(iot, ioh, EPCOM_IntIDIntClr); + + if (COM_ISALIVE(sc) == 0) + panic("intr on disabled epcom"); + + flagr = bus_space_read_4(iot, ioh, EPCOM_Flag); + + end = sc->sc_ebuf; + put = sc->sc_rbput; + cc = sc->sc_rbavail; + + if (!(ISSET(flagr, Flag_RXFE))) { + if (!ISSET(sc->sc_rx_flags, RX_IBUF_OVERFLOWED)) { + while (cc > 0) { + if (ISSET(flagr, Flag_RXFE)) + break; + c = bus_space_read_4(iot, ioh, EPCOM_Data); + csts = bus_space_read_4(iot, ioh, EPCOM_RXSts); + if (ISSET(csts, RXSts_BE)) { + int cn_trapped = 0; + + cn_check_magic(sc->sc_tty->t_dev, + CNC_BREAK, epcom_cnm_state); + if (cn_trapped) + goto next; +#if defined(KGDB) && !defined(DDB) + if (ISSET(sc->sc_hwflags, COM_HW_KGDB)){ + kgdb_connect(1); + goto next; + } +#endif + } else { + int cn_trapped = 0; + + cn_check_magic(sc->sc_tty->t_dev, + (c & 0xff), epcom_cnm_state); + if (cn_trapped) + goto next; + } + + + put[0] = c & 0xff; + put[1] = csts & 0xf; + put += 2; + if (put >= end) + put = sc->sc_rbuf; + cc--; + next: + flagr = bus_space_read_4(iot, ioh, EPCOM_Flag); + } + + /* + * Current string of incoming characters ended because + * no more data was available or we ran out of space. + * Schedule a receive event if any data was received. + * If we're out of space, turn off receive interrupts. + */ + sc->sc_rbput = put; + sc->sc_rbavail = cc; + if (!ISSET(sc->sc_rx_flags, RX_TTY_OVERFLOWED)) + sc->sc_rx_ready = 1; + + /* + * See if we are in danger of overflowing a buffer. If + * so, use hardware flow control to ease the pressure. + */ + + /* but epcom cannot. X-( */ + + /* + * If we're out of space, disable receive interrupts + * until the queue has drained a bit. + */ + if (!cc) { + SET(sc->sc_rx_flags, RX_IBUF_OVERFLOWED); + CLR(sc->sc_ctrl, (Ctrl_RIE|Ctrl_RTIE)); + epcom_set(sc); + } + } else { +#ifdef DIAGNOSTIC + panic("epcomintr: we shouldn't reach here"); +#endif + CLR(sc->sc_ctrl, (Ctrl_RIE|Ctrl_RTIE)); + epcom_set(sc); + } + } + + /* + * Done handling any receive interrupts. See if data can be + * transmitted as well. Schedule tx done event if no data left + * and tty was marked busy. + */ + if (ISSET(flagr, Flag_TXFE)) { + /* + * If we've delayed a parameter change, do it now, and restart + * output. + */ + if (sc->sc_heldchange) { + epcom_set(sc); + sc->sc_heldchange = 0; + sc->sc_tbc = sc->sc_heldtbc; + sc->sc_heldtbc = 0; + } + + /* Output the next chunk of the contiguous buffer, if any. */ + if (sc->sc_tbc > 0) { + epcom_filltx(sc); + } else { + /* Disable transmit completion interrupts if necessary. */ + if (ISSET(sc->sc_ctrl, Ctrl_TIE)) { + CLR(sc->sc_ctrl, Ctrl_TIE); + epcom_set(sc); + } + if (sc->sc_tx_busy) { + sc->sc_tx_busy = 0; + sc->sc_tx_done = 1; + } + } + } + + /* Wake up the poller. */ + softintr_schedule(sc->sc_si); + +#if 0 /* XXX: broken */ +#if NRND > 0 && defined(RND_COM) + rnd_add_uint32(&sc->rnd_source, intr ^ flagr); +#endif +#endif + return (1); +} diff --git a/sys/arch/arm/ep93xx/epcomreg.h b/sys/arch/arm/ep93xx/epcomreg.h new file mode 100644 index 000000000000..1189b8347e7d --- /dev/null +++ b/sys/arch/arm/ep93xx/epcomreg.h @@ -0,0 +1,109 @@ +/* $NetBSD: epcomreg.h,v 1.1 2004/12/22 19:10:25 joff Exp $ */ + +/* + * Copyright (c) 2004 Jesse Off + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by Ichiro FUKUHARA. + * 4. Neither the name of the author nor the names of any co-contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY ICHIRO FUKUHARA AND CONTRIBUTORS ``AS IS'' + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL ICHIRO FUKUHARA OR THE VOICES IN HIS + * HEAD BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, + * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef _EPCOMREG_H_ +#define _EPCOMREG_H_ + +#define EPCOM_FREQ 7372800 +#define EPCOMSPEED2BRD(b) ((EPCOM_FREQ / (16 * (b))) - 1) + + +/* UART Data Register */ +#define EPCOM_Data 0x00000000UL + +/* UART Receive Status/Error Clear Register */ +#define EPCOM_RXSts 0x00000004UL +#define RXSts_FE 0x01 +#define RXSts_PE 0x02 +#define RXSts_BE 0x04 +#define RXSts_OE 0x08 + +/* UART Line Control Register High */ +#define EPCOM_LinCtrlHigh 0x00000008UL +#define LinCtrlHigh_BRK 0x01 +#define LinCtrlHigh_PEN 0x02 +#define LinCtrlHigh_EPS 0x04 +#define LinCtrlHigh_STP2 0x08 +#define LinCtrlHigh_FEN 0x10 +#define LinCtrlHigh_WLEN 0x60 + +/* UART Line Control Register Middle */ +#define EPCOM_LinCtrlMid 0x0000000cUL + +/* UART Line Control Register Low */ +#define EPCOM_LinCtrlLow 0x00000010UL + +/* UART control register */ +#define EPCOM_Ctrl 0x00000014UL +#define Ctrl_UARTE 0x01 /* UART Enable */ +#define Ctrl_MSIE 0x08 /* Modem Status Interrupt Enable */ +#define Ctrl_RIE 0x10 /* Receive Interrupt Enable */ +#define Ctrl_TIE 0x20 /* Transmit Interrupt Enable */ +#define Ctrl_RTIE 0x40 /* Receive Timeout Enable */ +#define Ctrl_LBE 0x80 /* Loopback Enable */ + +/* UART Flag register */ +#define EPCOM_Flag 0x00000018UL +#define Flag_CTS 0x01 /* Clear To Send status */ +#define Flag_DSR 0x02 /* Data Set Ready status */ +#define Flag_DCD 0x04 /* Data Carrier Detect status */ +#define Flag_BUSY 0x08 /* UART Busy */ +#define Flag_RXFE 0x10 /* Receive FIFO Empty */ +#define Flag_TXFF 0x20 /* Transmit FIFO Full */ +#define Flag_RXFF 0x40 /* Receive FIFO Full */ +#define Flag_TXFE 0x80 /* Transmit FIFO Empty */ + +/* UART Interrupt Identification and Interrupt Clear Register */ +#define EPCOM_IntIDIntClr 0x0000001cUL +#define IntIDIntClr_MIS 0x01 /* Modem Interrupt Status */ +#define IntIDIntClr_RIS 0x01 /* Receive Interrupt Status */ +#define IntIDIntClr_TIS 0x01 /* Transmit Interrupt Status */ +#define IntIDIntClr_RTIS 0x01 /* Receive Timeout Interrupt Status */ + +/* UART Modem Control Register */ +#define EPCOM_ModemCtrl 0x00000100UL +#define ModemCtrl_DTR 0x01 /* DTR output signal */ +#define ModemCtrl_RTS 0x02 /* RTS output signal */ + +/* UART Modem Status Register */ +#define EPCOM_ModemSts 0x00000104UL +#define ModemSts_DCTS 0x01 /* Delta CTS */ +#define ModemSts_DDSR 0x02 /* Delta DSR */ +#define ModemSts_TERI 0x04 /* Trailing Edge Ring Indicator */ +#define ModemSts_DDCD 0x08 /* Delta DCD */ +#define ModemSts_CTS 0x10 /* Inverse CTSn input pin */ +#define ModemSts_DSR 0x20 /* Inverse of the DSRn pin */ +#define ModemSts_RI 0x40 /* Inverse of RI input pin */ +#define ModemSts_DCD 0x80 /* Inverse of DCDn input pin */ + +#endif /* _EPCOMREG_H_ */ diff --git a/sys/arch/arm/ep93xx/epcomvar.h b/sys/arch/arm/ep93xx/epcomvar.h new file mode 100644 index 000000000000..62ceb79cd537 --- /dev/null +++ b/sys/arch/arm/ep93xx/epcomvar.h @@ -0,0 +1,103 @@ +/* $NetBSD: epcomvar.h,v 1.1 2004/12/22 19:10:25 joff Exp $ */ +/*- + * Copyright (c) 2004 Jesse Off + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the NetBSD + * Foundation, Inc. and its contributors. + * 4. Neither the name of The NetBSD Foundation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + */ + +#ifndef _EPCOMVAR_H_ +#define _EPCOMVAR_H_ + +/* Hardware flag masks */ +#define COM_HW_NOIEN 0x01 +#define COM_HW_DEV_OK 0x20 +#define COM_HW_CONSOLE 0x40 +#define COM_HW_KGDB 0x80 + +#define RX_TTY_BLOCKED 0x01 +#define RX_TTY_OVERFLOWED 0x02 +#define RX_IBUF_BLOCKED 0x04 +#define RX_IBUF_OVERFLOWED 0x08 +#define RX_ANY_BLOCK 0x0f + +#define EPCOM_RING_SIZE 2048 + +struct epcom_softc { + struct device sc_dev; + bus_addr_t sc_hwbase; + bus_space_tag_t sc_iot; + bus_space_handle_t sc_ioh; + + void *sc_si; + + struct tty *sc_tty; + + u_char *sc_rbuf, *sc_ebuf; + + u_char *sc_tba; + u_int sc_tbc, sc_heldtbc; + + u_char *volatile sc_rbget, + *volatile sc_rbput; + volatile u_int sc_rbavail; + + /* status flags */ + int sc_hwflags, sc_swflags; + + volatile u_int sc_rx_flags, + sc_tx_busy, + sc_tx_done, + sc_tx_stopped, + sc_st_check, + sc_rx_ready; + volatile int sc_heldchange; + + /* control registers */ + u_int sc_lcrlo; + u_int sc_lcrmid; + u_int sc_lcrhi; + u_int sc_ctrl; + + /* power management hooks */ + int (*enable)(struct epcom_softc *); + int (*disable)(struct epcom_softc *); + + int enabled; +#if NRND > 0 && defined(RND_COM) + rndsource_element_t rnd_source; +#endif +}; + +void epcom_attach_subr(struct epcom_softc *); + +int epcomintr(void* arg); +int epcomcnattach(bus_space_tag_t, bus_addr_t, bus_space_handle_t, + int, tcflag_t); + +#endif /* _EPCOMVAR_H_ */