diff options
Diffstat (limited to 'sys/dev/pcmcia/com_pcmcia.c')
-rw-r--r-- | sys/dev/pcmcia/com_pcmcia.c | 367 |
1 files changed, 367 insertions, 0 deletions
diff --git a/sys/dev/pcmcia/com_pcmcia.c b/sys/dev/pcmcia/com_pcmcia.c new file mode 100644 index 00000000000..acefe3ab9b3 --- /dev/null +++ b/sys/dev/pcmcia/com_pcmcia.c @@ -0,0 +1,367 @@ +/* $OpenBSD: com_pcmcia.c,v 1.1 1996/11/30 13:39:24 niklas Exp $ */ +/* $NetBSD: com.c,v 1.82.4.1 1996/06/02 09:08:00 mrg Exp $ */ + +/*- + * Copyright (c) 1993, 1994, 1995, 1996 + * Charles M. Hannum. All rights reserved. + * 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. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. 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. + */ + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/device.h> +#include <sys/tty.h> + +#include <machine/bus.h> +#include <machine/intr.h> + +#include <dev/isa/isavar.h> + +#include <dev/ic/comreg.h> +#include <dev/ic/comvar.h> +#include <dev/ic/ns16550reg.h> + +#include <dev/pcmcia/pcmciavar.h> + +/* Macros to clear/set/test flags. */ +#define SET(t, f) (t) |= (f) +#define CLR(t, f) (t) &= ~(f) +#define ISSET(t, f) ((t) & (f)) + +int com_pcmcia_match __P((struct device *, void *, void *)); +void com_pcmcia_attach __P((struct device *, struct device *, void *)); +int com_pcmcia_detach __P((struct device *)); + +int com_pcmcia_mod __P((struct pcmcia_link *pc_link, struct device *self, + struct pcmcia_conf *pc_cf, struct cfdata *cf)); +int com_pcmcia_isa_attach __P((struct device *, void *, void *, + struct pcmcia_link *)); +int com_pcmcia_remove __P((struct pcmcia_link *, struct device *)); +int com_pcmcia_probe __P((struct device *, void *, void *)); + +struct cfattach com_pcmcia_ca = { + sizeof(struct com_softc), com_pcmcia_match, com_pcmcia_attach, + com_pcmcia_detach +}; + +/* additional setup needed for pcmcia devices */ +/* modify config entry */ +int +com_pcmcia_mod(pc_link, self, pc_cf, cf) + struct pcmcia_link *pc_link; + struct device *self; + struct pcmcia_conf *pc_cf; + struct cfdata *cf; +{ + int err; + + if (!(err = PCMCIA_BUS_CONFIG(pc_link->adapter, pc_link, self, + pc_cf, cf))) { + pc_cf->memwin = 0; + if (pc_cf->cfgtype == 0) + pc_cf->cfgtype = CFGENTRYID; /* determine from ioaddr */ + } + return err; +} + +static struct pcmcia_com { + struct pcmcia_device pcd; +} pcmcia_com = { + {"PCMCIA Modem card", com_pcmcia_mod, com_pcmcia_isa_attach, + NULL, com_pcmcia_remove} +}; + + +struct pcmciadevs pcmcia_com_devs[] = { + { "com", 0, + NULL, "*MODEM*", NULL, NULL, + NULL, (void *)&pcmcia_com + }, + { "com", 0, + NULL, NULL, "*MODEM*", NULL, + NULL, (void *)&pcmcia_com + }, + { "com", 0, + NULL, NULL, NULL, "*MODEM*", + NULL, (void *)&pcmcia_com + }, + {NULL} +}; +#define ncom_pcmcia_devs sizeof(pcmcia_com_devs)/sizeof(pcmcia_com_devs[0]) + +int +com_pcmcia_match(parent, match, aux) + struct device *parent; + void *match, *aux; +{ + return pcmcia_slave_match(parent, match, aux, pcmcia_com_devs, + ncom_pcmcia_devs); +} + +int +com_pcmcia_isa_attach(parent, match, aux, pc_link) + struct device *parent; + void *match; + void *aux; + struct pcmcia_link *pc_link; +{ + struct isa_attach_args *ia = aux; + struct com_softc *sc = match; + int rval; + + if ((rval = com_pcmcia_probe(parent, sc->sc_dev.dv_cfdata, ia))) { + if (ISSET(pc_link->flags, PCMCIA_REATTACH)) { +#ifdef COM_DEBUG + printf("comreattach, hwflags=%x\n", sc->sc_hwflags); +#endif + sc->sc_hwflags = COM_HW_REATTACH | (sc->sc_hwflags & + (COM_HW_ABSENT_PENDING|COM_HW_CONSOLE)); + } else + sc->sc_hwflags = 0; + sc->sc_ic = ia->ia_ic; + } + return rval; +} + + +/* + * Called by config_detach attempts, shortly after com_pcmcia_remove + * was called. + */ +int +com_pcmcia_detach(self) + struct device *self; +{ + struct com_softc *sc = (void *)self; + + if (ISSET(sc->sc_hwflags, COM_HW_ABSENT_PENDING)) { + /* don't let it really be detached, it is still open */ + return EBUSY; + } + return 0; /* OK! */ +} + +/* + * called by pcmcia framework to accept/reject remove attempts. + * If we return 0, then the detach will proceed. + */ +int +com_pcmcia_remove(pc_link, self) + struct pcmcia_link *pc_link; + struct device *self; +{ + struct com_softc *sc = (void *)self; + struct tty *tp; + int s; + + if (!sc->sc_tty) + goto ok; + tp = sc->sc_tty; + + /* not in use ? if so, return "OK" */ + if (!ISSET(tp->t_state, TS_ISOPEN) && + !ISSET(tp->t_state, TS_WOPEN)) { + ttyfree(sc->sc_tty); + sc->sc_tty = NULL; + ok: + isa_intr_disestablish(sc->sc_ic, sc->sc_ih); + sc->sc_ih = NULL; + SET(sc->sc_hwflags, COM_HW_ABSENT); + return 0; /* OK! */ + } + /* + * Not easily removed. Put device into a dead state, clean state + * as best we can. notify all waiters. + */ + SET(sc->sc_hwflags, COM_HW_ABSENT|COM_HW_ABSENT_PENDING); +#ifdef COM_DEBUG + printf("pending detach flags %x\n", sc->sc_hwflags); +#endif + + s = spltty(); + com_absent_notify(sc); + splx(s); + + return 0; +} + +#if 0 +void +com_pcmcia_attach(parent, self, aux) + struct device *parent, *self; + void *aux; +{ + struct pcmcia_attach_args *paa = aux; + + printf("com_pcmcia_attach %p %p %p\n", parent, self, aux); + delay(2000000); + if (!pcmcia_configure(parent, self, paa->paa_link)) { + struct com_softc *sc = (void *)self; + sc->sc_hwflags |= COM_HW_ABSENT; + printf(": not attached\n"); + } +} +#endif + +int +com_pcmcia_probe(parent, match, aux) + struct device *parent; + void *match, *aux; +{ + bus_space_tag_t iot; + bus_space_handle_t ioh; + int iobase, needioh; + int rv = 1; + struct isa_attach_args *ia = aux; + + iot = ia->ia_iot; + iobase = ia->ia_iobase; + needioh = 1; + + /* if it's in use as console, it's there. */ + if (iobase == comconsaddr && !comconsattached) + goto out; + + if (needioh && bus_space_map(iot, iobase, COM_NPORTS, 0, &ioh)) { + rv = 0; + goto out; + } + rv = comprobe1(iot, ioh, iobase); + if (needioh) + bus_space_unmap(iot, ioh, COM_NPORTS); + +out: + ia->ia_iosize = COM_NPORTS; + ia->ia_msize = 0; + return (rv); +} + +void +com_pcmcia_attach(parent, self, aux) + struct device *parent, *self; + void *aux; +{ + struct com_softc *sc = (void *)self; + int iobase, irq; + bus_space_tag_t iot; + bus_space_handle_t ioh; + struct isa_attach_args *ia = aux; + + if (ISSET(sc->sc_hwflags, COM_HW_REATTACH)) { + int s; + s = spltty(); + com_absent_notify(sc); + splx(s); + } else + sc->sc_hwflags = 0; + sc->sc_swflags = 0; + + /* + * We're living on an isa. + */ + iobase = ia->ia_iobase; + iot = ia->ia_iot; + if (iobase != comconsaddr) { + if (bus_space_map(iot, iobase, COM_NPORTS, 0, &ioh)) + panic("comattach: io mapping failed"); + } else + ioh = comconsioh; + irq = ia->ia_irq; + + sc->sc_iot = iot; + sc->sc_ioh = ioh; + sc->sc_iobase = iobase; + + if (iobase == comconsaddr) { + comconsattached = 1; + + /* + * Need to reset baud rate, etc. of next print so reset + * comconsinit. Also make sure console is always "hardwired". + */ + delay(1000); /* wait for output to finish */ + comconsinit = 0; + SET(sc->sc_hwflags, COM_HW_CONSOLE); + SET(sc->sc_swflags, COM_SW_SOFTCAR); + } + + /* look for a NS 16550AF UART with FIFOs */ + bus_space_write_1(iot, ioh, com_fifo, + FIFO_ENABLE | FIFO_RCV_RST | FIFO_XMT_RST | FIFO_TRIGGER_14); + delay(100); + if (ISSET(bus_space_read_1(iot, ioh, com_iir), IIR_FIFO_MASK) == + IIR_FIFO_MASK) + if (ISSET(bus_space_read_1(iot, ioh, com_fifo), + FIFO_TRIGGER_14) == FIFO_TRIGGER_14) { + SET(sc->sc_hwflags, COM_HW_FIFO); + printf(": ns16550a, working fifo\n"); + } else + printf(": ns16550, broken fifo\n"); + else + printf(": ns8250 or ns16450, no fifo\n"); + bus_space_write_1(iot, ioh, com_fifo, 0); + + /* disable interrupts */ + bus_space_write_1(iot, ioh, com_ier, 0); + bus_space_write_1(iot, ioh, com_mcr, 0); + + if (irq != IRQUNK) { + struct isa_attach_args *ia = aux; + + sc->sc_ih = isa_intr_establish(ia->ia_ic, irq, + IST_EDGE, IPL_TTY, comintr, sc, sc->sc_dev.dv_xname); + } + +#ifdef KGDB + if (kgdb_dev == makedev(commajor, unit)) { + if (ISSET(sc->sc_hwflags, COM_HW_CONSOLE)) + kgdb_dev = -1; /* can't debug over console port */ + else { + cominit(iot, ioh, kgdb_rate); + if (kgdb_debug_init) { + /* + * Print prefix of device name, + * let kgdb_connect print the rest. + */ + printf("%s: ", sc->sc_dev.dv_xname); + kgdb_connect(1); + } else + printf("%s: kgdb enabled\n", + sc->sc_dev.dv_xname); + } + } +#endif + + /* XXX maybe move up some? */ + if (ISSET(sc->sc_hwflags, COM_HW_CONSOLE)) + printf("%s: console\n", sc->sc_dev.dv_xname); +} |