From: hvozda Date: Tue, 16 Jan 1996 20:12:58 +0000 (+0000) Subject: Last of the glue and devices for for PCMCIA support from Stefan Grefen X-Git-Url: http://artulab.com/gitweb/?a=commitdiff_plain;h=480aae508922c66d4b6c6c9a0951456890305dc7;p=openbsd Last of the glue and devices for for PCMCIA support from Stefan Grefen with modifications by John Kohl --- diff --git a/sys/dev/ic/com.c b/sys/dev/ic/com.c index ad63516f0ff..c0003debb79 100644 --- a/sys/dev/ic/com.c +++ b/sys/dev/ic/com.c @@ -134,6 +134,50 @@ extern int kgdb_debug_init; #define CLR(t, f) (t) &= ~(f) #define ISSET(t, f) ((t) & (f)) +#include "pcmciabus.h" +#if NPCMCIABUS >0 +/* additional setup needed for pcmcia devices */ +#include +/* modify config entry */ +static int +commod(pc_link,self,pc_cf,cf) + struct pcmcia_link *pc_link; + struct device *self; + struct pcmcia_conf *pc_cf; + struct cfdata *cf; +{ + int err; + struct pcmciadevs *dev=pc_link->device; + struct ed_softc *sc = (void *)self; + if(!(err=pc_link->adapter->bus_link->bus_config(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",commod,NULL,NULL,NULL +}; +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} +}; +#endif + int comspeed(speed) long speed; @@ -163,12 +207,21 @@ int comprobe1(iobase) int iobase; { + int tmp; + int i,k; /* force access to id reg */ outb(iobase + com_lcr, 0); outb(iobase + com_iir, 0); - if (inb(iobase + com_iir) & 0x38) - return 0; + for(i=0;i<32;i++) { + k=inb(iobase + com_iir); + if (k & 0x38) { + inb(iobase + com_data ); /* cleanup */ + } else + break; + } + if(i>=32) + return 0; return 1; } diff --git a/sys/dev/isa/com.c b/sys/dev/isa/com.c index ad63516f0ff..c0003debb79 100644 --- a/sys/dev/isa/com.c +++ b/sys/dev/isa/com.c @@ -134,6 +134,50 @@ extern int kgdb_debug_init; #define CLR(t, f) (t) &= ~(f) #define ISSET(t, f) ((t) & (f)) +#include "pcmciabus.h" +#if NPCMCIABUS >0 +/* additional setup needed for pcmcia devices */ +#include +/* modify config entry */ +static int +commod(pc_link,self,pc_cf,cf) + struct pcmcia_link *pc_link; + struct device *self; + struct pcmcia_conf *pc_cf; + struct cfdata *cf; +{ + int err; + struct pcmciadevs *dev=pc_link->device; + struct ed_softc *sc = (void *)self; + if(!(err=pc_link->adapter->bus_link->bus_config(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",commod,NULL,NULL,NULL +}; +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} +}; +#endif + int comspeed(speed) long speed; @@ -163,12 +207,21 @@ int comprobe1(iobase) int iobase; { + int tmp; + int i,k; /* force access to id reg */ outb(iobase + com_lcr, 0); outb(iobase + com_iir, 0); - if (inb(iobase + com_iir) & 0x38) - return 0; + for(i=0;i<32;i++) { + k=inb(iobase + com_iir); + if (k & 0x38) { + inb(iobase + com_data ); /* cleanup */ + } else + break; + } + if(i>=32) + return 0; return 1; } diff --git a/sys/dev/isa/files.isa b/sys/dev/isa/files.isa index 99ac2a0ed60..63981d69dea 100644 --- a/sys/dev/isa/files.isa +++ b/sys/dev/isa/files.isa @@ -21,6 +21,12 @@ file dev/isa/isa.c isa needs-flag define isadma file dev/isa/isadma.c isadma needs-flag +#pcmcia +#config problems +#device pcic at isa +#file dev/isa/pcmcia_pcic.c pcic pcmciabus +file dev/isa/pcmcia_isa.c pcmciabus + # # 8250/16[45]50-based multi-port serial boards # @@ -44,7 +50,7 @@ file dev/isa/rtfps.c rtfps # # 8250/16[45]50-based "com" ports -device com at isa, commulti: tty +device com at isa, commulti, pcmciabus: tty file dev/isa/com.c com needs-flag # Cyclades Cyclom multiport serial cards @@ -120,7 +126,7 @@ file dev/isa/elink.c elink # National Semiconductor DS8390/WD83C690-based boards # (WD/SMC 80x3 family, SMC Ultra [8216], 3Com 3C503, NE[12]000, and clones) -device ed at isa: ether, ifnet +device ed at isa, pcmciabus: ether, ifnet file dev/isa/if_ed.c ed # 3Com 3C505 @@ -132,7 +138,7 @@ device el at isa: ether, ifnet file dev/isa/if_el.c el # 3Com 3C5x9, 3c59x (EtherLink III) family -device ep at isa, pci: ether, ifnet, elink +device ep at isa, pci, pcmciabus: ether, ifnet, elink file dev/isa/if_ep.c ep # Fujitsu MB8696[05]-based boards diff --git a/sys/dev/isa/if_ed.c b/sys/dev/isa/if_ed.c index a3617cd6685..06c0142a9ca 100644 --- a/sys/dev/isa/if_ed.c +++ b/sys/dev/isa/if_ed.c @@ -17,6 +17,7 @@ * similar clones. */ +#include "pcmciabus.h" #include "bpfilter.h" #include @@ -68,10 +69,15 @@ struct ed_softc { void *sc_ih; struct arpcom sc_arpcom; /* ethernet common */ + void *sc_sh; /* shutdown hook */ char *type_str; /* pointer to type string */ u_char vendor; /* interface vendor */ u_char type; /* interface type code */ + u_short spec_flags; +#define ED_REATTACH 0x0001 /* Reattach */ +#define ED_NOTPRESENT 0x0002 /* card not present; do not allow + reconfiguration */ int asic_addr; /* ASIC I/O bus address */ int nic_addr; /* NIC (DS8390) I/O bus address */ @@ -143,6 +149,148 @@ struct cfdriver edcd = { #define NIC_PUT(sc, off, val) outb(sc->nic_addr + off, val) #define NIC_GET(sc, off) inb(sc->nic_addr + off) +#if NPCMCIABUS > 0 + +#include +static int ed_probe_pcmcia_ne __P((struct device *, void *, + void *, struct pcmcia_link *)); + +static int edmod __P((struct pcmcia_link *, struct device *, + struct pcmcia_conf *, struct cfdata *cf)); + +static int ed_remove __P((struct pcmcia_link *, struct device *)); + +/* additional setup needed for pcmcia devices */ +static int +ed_probe_pcmcia_ne(parent, match, aux, pc_link) + struct device *parent; + void *match; + void *aux; + struct pcmcia_link *pc_link; +{ + struct ed_softc *sc = match; + struct cfdata *cf = sc->sc_dev.dv_cfdata; + struct isa_attach_args *ia = aux; + struct pcmciadevs *dev=pc_link->device; + int err; + extern int ifqmaxlen; + u_char enaddr[ETHER_ADDR_LEN]; + + if ((int)dev->param >= 0) + err = pcmcia_read_cis(pc_link, enaddr, + (int) dev->param, ETHER_ADDR_LEN); + else + err = 0; + if (err) + printf("Cannot read cis info %d\n", err); + + if (ed_probe_Novell(sc, cf, ia)) { + delay(100); + if ((int)dev->param >= 0) { + err = pcmcia_read_cis(pc_link, sc->sc_arpcom.ac_enaddr, + (int) dev->param, ETHER_ADDR_LEN); + if (err) { + printf("Cannot read cis info %d\n", err); + return 0; + } + if(bcmp(enaddr, sc->sc_arpcom.ac_enaddr, ETHER_ADDR_LEN)) { + printf("ENADDR MISMATCH %s ", + ether_sprintf(sc->sc_arpcom.ac_enaddr)); + printf("- %s\n", ether_sprintf(enaddr)); + bcopy(enaddr,sc->sc_arpcom.ac_enaddr, + ETHER_ADDR_LEN); + } + } + /* clear ED_NOTPRESENT, set ED_REATTACH if needed */ + sc->spec_flags=pc_link->flags&PCMCIA_REATTACH?ED_REATTACH:0; + sc->type_str = dev->model; + sc->sc_arpcom.ac_if.if_snd.ifq_maxlen=ifqmaxlen; + return 1; + } + return 0; +} + +/* modify config entry */ +static int +edmod(pc_link, self, pc_cf, cf) + struct pcmcia_link *pc_link; + struct device *self; + struct pcmcia_conf *pc_cf; + struct cfdata *cf; +{ + int err; + struct pcmciadevs *dev=pc_link->device; + struct ed_softc *sc = (void *)self; + int svec_card = strcmp(dev->manufacturer, "SVEC") == 0; + int de650_0 = (pc_cf->memwin != 0) && !svec_card; + err = pc_link->adapter->bus_link->bus_config(pc_link, self, pc_cf, cf); + if (err) + return err; + + if (svec_card) { + pc_cf->memwin = 0; + } + if (de650_0) { + pc_cf->io[0].flags = + (pc_cf->io[0].flags&~PCMCIA_MAP_16)|PCMCIA_MAP_8; + pc_cf->memwin = 0; + pc_cf->cfgtype = DOSRESET|1; + } + else { + /* still wrong in CIS; fix it here */ + pc_cf->io[0].flags = PCMCIA_MAP_8|PCMCIA_MAP_16; + pc_cf->cfgtype = 1; + } + + return err; +} + + +static int +ed_remove(pc_link,self) + struct pcmcia_link *pc_link; + struct device *self; +{ + struct ed_softc *sc = (void *)self; + struct ifnet *ifp = &sc->sc_arpcom.ac_if; + if_down(ifp); + edstop(sc); + shutdownhook_disestablish(sc->sc_sh); + ifp->if_flags &= ~(IFF_RUNNING|IFF_UP); + sc->spec_flags |= ED_NOTPRESENT; + return pc_link->adapter->bus_link->bus_unconfig(pc_link); +} + +static struct pcmcia_dlink { + struct pcmcia_device pcd; +} pcmcia_dlink= { + "PCMCIA Novell compatible", edmod, ed_probe_pcmcia_ne, NULL, ed_remove +}; + +struct pcmciadevs pcmcia_ed_devs[]={ + { "ed", 0, "D-Link", "DE-650", "Ver 01.00", NULL, (void *) -1, + (void *)&pcmcia_dlink }, + { "ed", 0, "D-Link", "DE-650", "", NULL, (void *) 0x40, + (void *)&pcmcia_dlink }, + { "ed", 0, "LINKSYS", "E-CARD", "Ver 01.00", NULL, (void *)-1, + (void *)&pcmcia_dlink }, + { "ed", 0, "IBM Corp.", "Ethernet", "0933495", NULL, (void *) 0xff0, + (void *)&pcmcia_dlink }, + { "ed", 0, "Socket Communications Inc", + "Socket EA PCMCIA LAN Adapter Revision D", "Ethernet ID 000000000000", + NULL, (void *) -1, + (void *)&pcmcia_dlink }, + /* probably not right for ethernet address--card does not seem to + have it anywhere. */ + { "ed", 0, "SVEC", "FD605 PCMCIA EtherNet Card", "V1-1", NULL, + (void *)0xb4, (void *)&pcmcia_dlink }, + { "ed", 0, "PMX ", "PE-200", "ETHERNET", "R01", (void *) 0x110, + (void *)&pcmcia_dlink }, /* 0x110 is a guess */ + { NULL } +}; +#endif + + /* * Determine if the device is present. */ @@ -1078,7 +1226,10 @@ edattach(parent, self, aux) } /* Attach the interface. */ - if_attach(ifp); + if ((sc->spec_flags & ED_REATTACH) == 0) { + if_attach(ifp); + ether_ifattach(ifp); + } ether_ifattach(ifp); /* Print additional info when attached. */ @@ -1106,11 +1257,14 @@ edattach(parent, self, aux) printf("\n"); #if NBPFILTER > 0 - bpfattach(&ifp->if_bpf, ifp, DLT_EN10MB, sizeof(struct ether_header)); + if ((sc->spec_flags & ED_REATTACH) == 0) + bpfattach(&ifp->if_bpf, ifp, DLT_EN10MB, + sizeof(struct ether_header)); #endif sc->sc_ih = isa_intr_establish(ia->ia_irq, IST_EDGE, IPL_NET, edintr, sc); + sc->sc_sh = shutdownhook_establish((void (*)(void *))edstop, sc); } /* @@ -1784,6 +1938,12 @@ edioctl(ifp, cmd, data) int s, error = 0; s = splnet(); + if ((sc->spec_flags & ED_NOTPRESENT) != 0) { + if_down(ifp); + printf("%s: device offline\n", sc->sc_dev.dv_xname); + splx(s); + return ENXIO; /* may be ignored, oh well. */ + } switch (cmd) { diff --git a/sys/dev/isa/if_ep.c b/sys/dev/isa/if_ep.c index 50c9a636677..4ee4dcc4135 100644 --- a/sys/dev/isa/if_ep.c +++ b/sys/dev/isa/if_ep.c @@ -30,6 +30,7 @@ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ +#include "pcmciabus.h" #include "bpfilter.h" #include @@ -167,6 +168,142 @@ epaddcard(iobase, irq, bustype) nepcards++; } +#if NPCMCIABUS > 0 +#include +static int ep_probe_pcmcia __P((struct device *, void *, + void *, struct pcmcia_link *)); +static int epmod __P((struct pcmcia_link *, struct device *, + struct pcmcia_conf *, struct cfdata * cf)); +static int ep_remove __P((struct pcmcia_link *, struct device *)); + +/* additional setup needed for pcmcia devices */ +static int +ep_probe_pcmcia(parent, match, aux, pc_link) + struct device *parent; + void *match; + void *aux; + struct pcmcia_link *pc_link; +{ + struct ep_softc *sc = (void *) match; + struct cfdata *cf = sc->sc_dev.dv_cfdata; + struct isa_attach_args *ia = aux; + struct pcmciadevs *dev = pc_link->device; + struct ifnet *ifp = &sc->sc_arpcom.ac_if; + int i; + extern int ifqmaxlen; + short addr[3]; + + outw(ia->ia_iobase + EP_COMMAND, WINDOW_SELECT | 0); + outw(ia->ia_iobase + EP_W0_CONFIG_CTRL, ENABLE_DRQ_IRQ); + outw(ia->ia_iobase + EP_W0_RESOURCE_CFG, 0x3f00); + + /* + * ok til here. Now try to figure out which link we have. + * try coax first... + */ + sc->bustype = EP_BUS_PCMCIA; +#ifdef EP_COAX_DEFAULT + outw(ia->ia_iobase + EP_W0_ADDRESS_CFG, 0xC000); +#else + /* COAX as default is reportet to be a problem */ + outw(ia->ia_iobase + EP_W0_ADDRESS_CFG, 0x0000); +#endif + ifp->if_snd.ifq_maxlen = ifqmaxlen; + + epaddcard(ia->ia_iobase, ia->ia_irq, 0); + + for (i = 0; i < nepcards; i++) { + if (epcards[i].available == 0) + continue; + if (ia->ia_iobase != IOBASEUNK && + ia->ia_iobase != epcards[i].iobase) + continue; + if (ia->ia_irq != IRQUNK && + ia->ia_irq != epcards[i].irq) + continue; + goto good; + } + return 0; + +good: + + epcards[i].available = 0; + ia->ia_iobase = epcards[i].iobase; + ia->ia_irq = epcards[i].irq; + ia->ia_iosize = 0x10; + ia->ia_msize = 0; + + return 1; +} + + +/* modify config entry */ +static int +epmod(pc_link, self, pc_cf, cf) + struct pcmcia_link *pc_link; + struct device *self; + struct pcmcia_conf *pc_cf; + struct cfdata *cf; +{ + int err; + struct pcmciadevs *dev = pc_link->device; + struct ep_softc *sc = (void *) self; + + if ((err = pc_link->adapter->bus_link->bus_config(pc_link, self, + pc_cf, cf)) != 0) { + printf("bus_config failed %d\n", err); + return err; + } + + if (pc_cf->io[0].len > 0x10) + pc_cf->io[0].len = 0x10; +#if 0 + pc_cf->cfgtype = DOSRESET; +#endif + pc_cf->cfgtype = 1; + + return 0; +} + +static int +ep_remove(pc_link, self) + struct pcmcia_link *pc_link; + struct device *self; +{ + struct ep_softc *sc = (void *) self; + struct ifnet *ifp = &sc->sc_arpcom.ac_if; + if_down(ifp); + epstop(sc); + ifp->if_flags &= ~(IFF_RUNNING | IFF_UP); + return pc_link->adapter->bus_link->bus_unconfig(pc_link); +} + +static struct pcmcia_3com { + struct pcmcia_device pcd; +} pcmcia_3com = { + "PCMCIA 3COM 3C589", epmod, ep_probe_pcmcia, NULL, ep_remove +}; + +struct pcmciadevs pcmcia_ep_devs[] = { + { + "ep", 0, "3Com Corporation", "3C589", +#if 0 + "TP/BNC LAN Card Ver. 1a", "000001", +#else + NULL, NULL, +#endif + (void *) -1, (void *) &pcmcia_3com + }, +#if 0 + { + "ep", 0, "3Com Corporation", "3C589", "TP/BNC LAN Card Ver. 2a", "000002", + (void *) -1, (void *) &pcmcia_3com + }, +#endif + { NULL } +}; +#endif + /* * 3c579 cards on the EISA bus are probed by their slot number. 3c509 * cards on the ISA bus are probed in ethernet address order. The probe @@ -339,8 +476,9 @@ epconfig(sc, conn) */ for (i = 0; i < 3; i++) { u_short x; - if (epbusyeeprom(sc)) + if (epbusyeeprom(sc)) { return; + } outw(BASE + EP_W0_EEPROM_COMMAND, READ_EEPROM | i); if (epbusyeeprom(sc)) return; diff --git a/sys/dev/isa/pcmcia_isa.c b/sys/dev/isa/pcmcia_isa.c new file mode 100644 index 00000000000..d2fd46ee66f --- /dev/null +++ b/sys/dev/isa/pcmcia_isa.c @@ -0,0 +1,287 @@ +/* + * Copyright (c) 1994 Stefan Grefen. 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 Charles Hannum. + * 4. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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. + * + * $Id: pcmcia_isa.c,v 1.1 1996/01/16 20:13:01 hvozda Exp $ + */ + +/* TODO add modload support and loadable lists of devices */ +/* How to do cards with more than one function (modem/ethernet ..) */ +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include +#include /* XXX USES ISA HOLE DIRECTLY */ + +#ifdef IBM_WD +#define PCMCIA_ISA_DEBUG +#endif + +static int pcmcia_isa_init __P((struct device *, struct cfdata *, + void *, struct pcmcia_adapter *, int)); +static int pcmcia_isa_search __P((struct device *, void *, cfprint_t)); +static int pcmcia_isa_probe __P((struct device *, void *, + void *, struct pcmcia_link *)); +static int pcmcia_isa_config __P((struct pcmcia_link *, struct device *, + struct pcmcia_conf *, struct cfdata *)); +static int pcmcia_isa_unconfig __P((struct pcmcia_link *)); + +struct pcmciabus_link pcmcia_isa_link = { + pcmcia_isa_config, + pcmcia_isa_unconfig, + pcmcia_isa_probe, + pcmcia_isa_search, + pcmcia_isa_init +}; + +/* copy out the addr and length from machine specific attach struct */ +static int +pcmcia_isa_init(parent, cf, aux, pca, flag) + struct device *parent; + struct cfdata *cf; + void *aux; + struct pcmcia_adapter *pca; + int flag; +{ + struct isa_attach_args *ia = aux; + +#ifdef PCMCIA_ISA_DEBUG + if (parent != NULL) + printf("PARENT %s\n", parent->dv_xname); +#endif + if (flag) { /* attach */ + pca->scratch_mem = (caddr_t) ISA_HOLE_VADDR(ia->ia_maddr); + pca->scratch_memsiz = ia->ia_msize; + } + ia->ia_iosize = 0; + return 1; +} + +/* probe and attach a device, the has to be configured already */ +static int +pcmcia_isa_probe(parent, match, aux, pc_link) + struct device *parent; + void *match; + void *aux; + struct pcmcia_link *pc_link; +{ + struct device *dev = match; + struct cfdata *cf = aux; + struct isa_attach_args ia; + struct pcmciadevs *pcs = pc_link->device; + int (*probe) () = (pcs != NULL) ? pcs->dev->pcmcia_probe : NULL; + + ia.ia_iobase = cf->cf_loc[0]; + ia.ia_iosize = 0x666; + ia.ia_maddr = cf->cf_loc[2]; + ia.ia_msize = cf->cf_loc[3]; + ia.ia_irq = cf->cf_loc[4]; + ia.ia_drq = cf->cf_loc[5]; + if (probe == NULL) + probe = cf->cf_driver->cd_match; + +#ifdef PCMCIA_ISA_DEBUG + printf("pcmcia probe %x %x %x\n", ia.ia_iobase, ia.ia_irq, probe); + printf("parentname = %s\n", parent->dv_xname); + printf("devname = %s\n", dev->dv_xname); + printf("driver name = %s\n", cf->cf_driver->cd_name); +#endif + if ((*probe) (parent, dev, &ia, pc_link) > 0) { + extern isaprint(); + config_attach(parent, dev, &ia, isaprint); +#ifdef PCMCIA_ISA_DEBUG + printf("biomask %x netmask %x ttymask %x\n", + (u_short) imask[IPL_BIO], (u_short) imask[IPL_NET], + (u_short) imask[IPL_TTY]); +#endif + return 1; + } + else + free(dev, M_DEVBUF); + return 0; +} + +/* + * Modify a pcmcia_conf struct to match the config entry. Pc_cf was filled + * with config data from the card and may be modified before and after the + * call to pcmcia_isa_config. Unless the FIXED_WIN flag is set we assume + * contiguous windows and shift according to the offset for the first not + * fixed window + */ +static int +pcmcia_isa_config(pc_link, self, pc_cf, cf) + struct pcmcia_link *pc_link; + struct device *self; + struct pcmcia_conf *pc_cf; + struct cfdata *cf; +{ + struct isa_attach_args ia; + + ia.ia_iobase = cf->cf_loc[0]; + ia.ia_iosize = 0x666; + ia.ia_maddr = cf->cf_loc[2]; + ia.ia_msize = cf->cf_loc[3]; + ia.ia_irq = cf->cf_loc[4]; + ia.ia_drq = cf->cf_loc[5]; +#ifdef PCMCIA_ISA_DEBUG + printf("pcmcia_isa_config iobase=%x maddr=%x msize=%x irq=%x drq=%x\n", + ia.ia_iobase, ISA_HOLE_VADDR(ia.ia_maddr), ia.ia_msize, + ia.ia_irq, ia.ia_drq); +#endif + + if (ia.ia_irq != IRQUNK) { + int irq = 1 << ia.ia_irq; + /* + * This is tricky irq 9 must match irq 2 in a device mask and + * configured irq 9 must match irq 2 + */ +#ifdef PCMCIA_ISA_DEBUG + printf("pcmcia_isa_config irq=%x num=%x mask=%x and=%x\n", irq, + 1 << pc_cf->irq_num, pc_cf->irq_mask, + irq & pc_cf->irq_mask); +#endif + if (irq != (1 << pc_cf->irq_num) && + !(irq == (1 << 9) && pc_cf->irq_num == 2)) { + if (irq == (1 << 9) || irq == (1 << 2)) + irq = (1 << 9) | (1 << 2); + if ((irq & pc_cf->irq_mask) == 0) { + printf("irq %d mask %x\n", ia.ia_irq, + pc_cf->irq_mask); + return ENODEV; + } + /* 2 is 9 is 2 ... */ + irq&=~(1 << 2); + cf->cf_loc[4] = pc_cf->irq_num = ffs(irq) - 1; +#ifdef PCMCIA_ISA_DEBUG + printf("pcmcia_isa_config modify num=%x\n", + pc_cf->irq_num); +#endif + } + } + if (ia.ia_iobase != IOBASEUNK) { + int i; + int offs = 0; + if (pc_cf->iowin == 0) + return 0; + for (i = 0; i < pc_cf->iowin; i++) { + if (pc_cf->io[i].flags & PCMCIA_FIXED_WIN) + continue; + if (offs == 0) { + if (pc_cf->io[i].start != ia.ia_iobase) { + offs = ia.ia_iobase - + pc_cf->io[i].start; + } else + break; + } + pc_cf->io[i].start += offs; + } + } else + pc_cf->iowin = 0; + if (ia.ia_maddr != MADDRUNK && ia.ia_msize) { + int i; + unsigned long offs = 0; + int mlen = ia.ia_msize; + int maddr = (int) ISA_HOLE_VADDR(ia.ia_maddr); + + if (pc_cf->memwin == 0) + return ENODEV; + +#ifdef PCMCIA_ISA_DEBUG + printf("Doing ia=%x ma=%x ms=%d\n", ia.ia_maddr, + maddr, ia.ia_msize); +#endif + for (i = 0; i < pc_cf->memwin && mlen > 0; i++) { +#ifdef PCMCIA_ISA_DEBUG + printf("Doing i=%d st=%x len=%d, flags=%x offs=%d mlen=%d\n", + i, pc_cf->mem[i].start, pc_cf->mem[i].len, + pc_cf->mem[i].flags, offs, mlen); +#endif + if (pc_cf->mem[i].flags & PCMCIA_FIXED_WIN) + continue; + if (offs == 0) { + if (pc_cf->mem[i].start != maddr) { + offs = maddr - pc_cf->mem[i].start; + } else + break; + } + mlen -= pc_cf->mem[i].len; + if (mlen < 0) + pc_cf->mem[i].len += mlen; + pc_cf->mem[i].start += offs; + } + } else + pc_cf->memwin = 0; + return 0; +} + + +static int +pcmcia_isa_unconfig(pc_link) + struct pcmcia_link *pc_link; +{ + if (pc_link && pc_link->intr > 0) { + /* THIS IS A GUESS ... TODO check all possible drivers */ + struct softc { + struct device sc_dev; + void *sc_ih; + } *sc = pc_link->devp; + if (sc) + isa_intr_disestablish(sc->sc_ih); + } + return 0; +} + +/* Searches for for configured devices on the pcmciabus */ +static int +pcmcia_isa_search(parent, aux, print) + struct device *parent; + void *aux; + cfprint_t print; +{ + static char *msgs[3] = {"", " not configured\n", " unsupported\n"}; + +#ifdef PCMCIA_ISA_DEBUG + printf("pcmcia_isa_search\n"); +#endif + if (config_search(pcmcia_configure, parent, aux) != NULL) + return 1; + + if (print) { + int i; + i = (*print) (aux, parent->dv_xname); + printf(msgs[i]); + } + return 0; +} diff --git a/sys/dev/isa/pcmcia_pcic.c b/sys/dev/isa/pcmcia_pcic.c new file mode 100644 index 00000000000..30e4d84707d --- /dev/null +++ b/sys/dev/isa/pcmcia_pcic.c @@ -0,0 +1,733 @@ +/* + * Device Driver for Intel 82365 based pcmcia slots + * + * Copyright (c) 1994 Stefan Grefen. This software may be used, modified, + * copied, distributed, and sold, in both source and binary form provided that + * the above copyright and these terms are retained. Under no circumstances is + * the author responsible for the proper functioning of this software, nor does + * the author assume any responsibility for damages incurred with its use. + * + */ + + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include + +#include +#ifdef IBM_WD +#define PCIC_DEBUG 0xf +#endif +#if PCIC_DEBUG +#define PCDMEM 0x01 +#define PCDIO 0x02 +#define PCDINTR 0x04 +#define PCDSERV 0x08 +#define PCDRW 0x10 +int pcic_debug = PCIC_DEBUG; +#define DEBUG(a) (pcic_debug & (a)) +#else +#define DEBUG(a) (0) +#endif + + +/* + * pcic_softc: per line info and status + */ +#define MAX_IOSECTION 2 +#define MAX_MEMSECTION 5 +struct slot { + int status; +#define SLOT_EMPTY 0x01 +#define SLOT_PROBING 0x02 +#define SLOT_INUSE 0x04 + u_short io_addr[MAX_IOSECTION], io_len[MAX_IOSECTION]; + u_short io_used[MAX_IOSECTION]; + caddr_t mem_caddr[MAX_MEMSECTION]; + u_int mem_haddr[MAX_MEMSECTION]; + u_int mem_len[MAX_MEMSECTION], mem_used[MAX_MEMSECTION]; + u_short region_flag; + u_short ioctl_flag; + u_short irq; + u_short pow; + u_short irqt; + u_short reg_off;/* 2 chips share an address */ + void (*handler)(struct slot *, void *); + void * handle_arg; + struct pcmcia_link *link; + struct pcic_softc *chip; +}; +struct pcic_softc { + struct device sc_dev; + void *sc_ih; + + int sc_polltimo; + int sc_pcic_irq; + u_short pcic_base; /* base port for each board */ + u_char slot_id; + u_char chip_inf; + struct slot slot[2]; +} pcic_softc[4]; + +static int pcic_map_io __P((struct pcmcia_link *, u_int, u_int, int)); +static int pcic_map_mem __P((struct pcmcia_link *, caddr_t, + u_int, u_int, int)); +static int pcic_map_intr __P((struct pcmcia_link *, int, int)); +static int pcic_service __P((struct pcmcia_link *, int, void *, int)); + +static struct pcmcia_funcs pcic_funcs = { + pcic_map_io, + pcic_map_mem, + pcic_map_intr, + pcic_service +}; + +int pcicprobe __P((struct device *, void *, void *)); +void pcicattach __P((struct device *, struct device *, void *)); + +extern struct pcmciabus_link pcmcia_isa_link; + +struct cfdriver pciccd = { + NULL, "pcic", pcicprobe, pcicattach, DV_DULL, sizeof(struct pcic_softc) +}; + + +static u_char pcic_rd __P((struct slot *, int)); +static void pcic_wr __P((struct slot *, int, int)); + + +static __inline u_char +pcic_rd(slot, reg) + struct slot *slot; + int reg; +{ + u_char res; + if (DEBUG(PCDRW)) + printf("pcic_rd(%x [%x %x]) = ", reg, slot->reg_off, + slot->chip->pcic_base); + outb(slot->chip->pcic_base, slot->reg_off + reg); + delay(1); + res = inb(slot->chip->pcic_base + 1); + if (DEBUG(PCDRW)) + printf("%x\n", res); + return res; +} + +static __inline void +pcic_wr(slot, reg, val) + struct slot *slot; + int reg, val; +{ + outb(slot->chip->pcic_base, slot->reg_off + reg); + delay(1); + outb(slot->chip->pcic_base + 1, val); + if (DEBUG(PCDRW)) { + int res; + delay(1); + outb(slot->chip->pcic_base, slot->reg_off + reg); + delay(1); + res = inb(slot->chip->pcic_base + 1); + printf("pcic_wr(%x %x) = %x\n", reg, val, res); + } +} + +static __inline int +pcic_wait(slot, i) + struct slot *slot; + int i; +{ + while (i-- && ((pcic_rd(slot, PCIC_STATUS) & PCIC_READY) == 0)) + delay(500); + return i; +} + +int +pcicprobe(parent, self, aux) + struct device *parent; + void *self; + void *aux; +{ + struct pcic_softc *pcic = (void *) self; + struct isa_attach_args *ia = aux; + struct cfdata *cf = pcic->sc_dev.dv_cfdata; + u_int chip_inf = 0; + int i; + + pcic->pcic_base = ia->ia_iobase; + pcic->slot_id = 0; /* XXX */ + bzero(pcic->slot, sizeof(pcic->slot)); + pcic->slot[0].chip = pcic; + pcic->slot[0].reg_off = (pcic->slot_id & 1) * 0x80; + pcic->slot[1].chip = pcic; + pcic->slot[1].reg_off = ((pcic->slot_id & 1) * 0x80) + 0x40; + chip_inf = pcic_rd(&pcic->slot[0], PCIC_ID_REV); + switch (chip_inf) { + case PCIC_INTEL0: + pcic->chip_inf = PCMICA_CHIP_82365_0; + goto ok; + case PCIC_INTEL1: + pcic->chip_inf = PCMICA_CHIP_82365_1; + goto ok; + case PCIC_IBM1: + pcic->chip_inf = PCMICA_CHIP_IBM_1; + goto ok; + case PCIC_IBM2: + pcic->chip_inf = PCMICA_CHIP_IBM_2; +ok: + ia->ia_msize = 0; + ia->ia_iosize = 2; + pcmcia_register(pcic, &pcmcia_isa_link, &pcic_funcs, + pcic->slot_id); + return 1; + default: + printf("found ID %x at pcic position\n", chip_inf & 0xff); + break; + } + /* reset mappings .... */ + pcic_wr(&pcic->slot[0], PCIC_POWER, pcic->slot[0].pow=PCIC_DISRST); + pcic_wr(&pcic->slot[1], PCIC_POWER, pcic->slot[1].pow=PCIC_DISRST); + + delay(1000); + + for (i = PCIC_INT_GEN; i < 0x40; i++) { + pcic_wr(&pcic->slot[0], i, 0); + pcic_wr(&pcic->slot[1], i, 0); + } + delay(10000); + return 0; +} + +int +pcic_intr __P((void *)); + + +void +pcicattach(parent, self, aux) + struct device *parent, *self; + void *aux; +{ + struct pcic_softc *pcic = (void *) self; + struct isa_attach_args *ia = aux; + struct slot *slot; + int i; + static char *pcic_names[] = { + "Intel 82365sl Rev. 0", + "Intel 82365sl Rev. 1", + "IBM 82365sl clone Rev. 1", + "IBM 82365sl clone Rev. 2"}; + printf(": %s slots %d-%d (%x %x)\n", pcic_names[pcic->chip_inf - + PCMICA_CHIP_82365_0], pcic->slot_id * 2, pcic->slot_id * 2 + 1, + &pcic->slot[0], &pcic->slot[1]); + /* enable interrupts on events */ + if (ia->ia_irq != IRQUNK) + pcic->sc_pcic_irq = ia->ia_irq; + else + pcic->sc_pcic_irq = 0; + + for (i = 0; i < 2; i++) { + slot = &pcic->slot[i]; + slot->irq = pcic->sc_pcic_irq | PCIC_INTR_ENA; + pcic_wr(slot, PCIC_STAT_INT, + (pcic->sc_pcic_irq << 4) |PCIC_CDTCH | PCIC_STCH); + pcic_wr(&pcic->slot[i], PCIC_INT_GEN, slot->irq); + (void) pcic_rd(&pcic->slot[i], PCIC_STAT_CHG); + } + if (ia->ia_irq == IRQUNK) { + pcic->sc_polltimo = hz/2; + timeout((void (*)(void *))pcic_intr, pcic, pcic->sc_polltimo); + } else { + pcic->sc_ih = isa_intr_establish(ia->ia_irq, IST_EDGE, + IPL_NET, pcic_intr, pcic); + pcic->sc_polltimo = 0; + } +} + +#ifdef DDB +int pcic_intr_test(slot) +struct slot *slot; +{ + printf("CSC interrupt state: %x\n", pcic_rd(slot, PCIC_STAT_INT)); + printf("General interrupt state: %x\n", pcic_rd(slot, PCIC_INT_GEN)); +} + +int pcic_intr_set(slot) +struct slot *slot; +{ + pcic_wr(slot, PCIC_INT_GEN, pcic_rd(slot, PCIC_INT_GEN)|PCIC_INTR_ENA); + pcic_intr_test(slot); +} +#endif + +int +pcic_intr(arg) +void *arg; +{ + struct pcic_softc *pcic = arg; + u_char statchg, intgen; + register int i; + + if (pcic->sc_polltimo == 0) + printf("%s: interrupt:", pcic->sc_dev.dv_xname); + for (i = 0; i < 2; i++) { + struct pcmcia_link *link = pcic->slot[i].link; + statchg = pcic_rd(&pcic->slot[i], PCIC_STAT_CHG); + if (statchg == 0) + continue; + intgen = pcic_rd(&pcic->slot[i], PCIC_INT_GEN); + if (intgen & PCIC_IOCARD) { + printf("%s: slot %d iocard status %s%s\n", + pcic->sc_dev.dv_xname, i, + statchg & PCIC_STCH ? "statchange " : "", + statchg & PCIC_CDTCH ? "cardchange" : ""); + } else { + printf("%s: slot %d memcard status %x\n", + pcic->sc_dev.dv_xname, i, statchg); + } + if ((statchg & PCIC_CDTCH) && + (link->flags & PCMCIA_SLOT_OPEN) == 0) { +#if 0 + if (pcic->slot[i].status & SLOT_INUSE) { + pcmcia_unconfigure(link); + } else { + if (link) { + link->fordriver = NULL; + pcmcia_probe_bus(link, 0, link->slot, + NULL); + } + } +#endif + } + if (link && (link->flags & PCMCIA_SLOT_OPEN)) { + link->flags |= PCMCIA_SLOT_EVENT; + selwakeup(&link->pcmcialink_sel); + } + if (pcic->slot[i].handler == NULL) + continue; + (*pcic->slot[i].handler)(&pcic->slot[i], + pcic->slot[i].handle_arg); + } + if (pcic->sc_polltimo) + timeout((void (*)(void *))pcic_intr, pcic, pcic->sc_polltimo); + return 1; +} + +static int +pcic_map_io(link, start, len, flags) + struct pcmcia_link *link; + u_int start, len; + int flags; +{ + struct pcic_softc *sc = link->adapter->adapter_softc; + struct slot *slot = &sc->slot[link->slot & 1]; + + len--; + if (DEBUG(PCDIO)) { + printf("pcic_map_io %x %x %x\n", start, len, flags); + } + if (!(flags & PCMCIA_UNMAP)) { + u_int stop; + int window; + int winid; + int ioflags; + + if (flags & PCMCIA_LAST_WIN) { + window = MAX_IOSECTION - 1; + } else if (flags & PCMCIA_FIRST_WIN) { + window = 0; + } else if (flags & PCMCIA_ANY_WIN) { + for (window = 0; window < MAX_IOSECTION; window++) { + if (slot->io_used[window] == 0) + break; + if (window >= MAX_IOSECTION) + return EBUSY; + } + } else { + window = flags & 0xf; + if (window >= MAX_IOSECTION) + return EINVAL; + } + slot->status |= SLOT_INUSE; + slot->io_used[window] = 1; + winid = window * 0x4 + 0x08; + stop = start + len; + + pcic_wr(slot, winid | PCIC_START | PCIC_ADDR_LOW, + (u_long) start & 0xff); + pcic_wr(slot, winid | PCIC_START | PCIC_ADDR_HIGH, + ((u_long) start >> 8) & 0xff); + + pcic_wr(slot, winid | PCIC_END | PCIC_ADDR_LOW, + stop & 0xff); + pcic_wr(slot, winid | PCIC_END | PCIC_ADDR_HIGH, + (stop >> 8) & 0xff); + flags &= (PCMCIA_MAP_8 | PCMCIA_MAP_16); + switch (flags) { + case PCMCIA_MAP_8: + ioflags = PCIC_IO0_0WS; + break; + case PCMCIA_MAP_16: + ioflags = PCIC_IO0_16BIT; + break; + default: + ioflags = PCIC_IO0_CS16; + break; + } + + if (window == 1) { + ioflags <<= 4; + slot->ioctl_flag &= ~(3 << 4); + } + else { + slot->ioctl_flag &= ~3; + } + + delay(1000); + + pcic_wr(slot, PCIC_IOCTL, slot->ioctl_flag |= ioflags); + pcic_wr(slot, PCIC_ADDRWINE, + slot->region_flag |= (0x40 << window)); + slot->io_addr[window] = start; + slot->io_len[window] = len; + delay(1000); + return 0; + } else { + u_int stop; + int window; + int winid; + int ioflags; + if (flags & PCMCIA_LAST_WIN) { + window = MAX_IOSECTION - 1; + } else if (flags & PCMCIA_FIRST_WIN) { + window = 0; + } else if (flags & PCMCIA_ANY_WIN) { + for (window = 0; window < MAX_IOSECTION; window++) { + if (slot->io_addr[window] == start) + if (len == -1 || + slot->io_len[window] == len) + break; + } + if (window >= MAX_IOSECTION) + return EINVAL; + } else { + window = flags & 0xf; + if (window >= MAX_IOSECTION) + return EINVAL; + } + slot->status &= ~SLOT_INUSE; + winid = window * 0x4 + 0x08; + + pcic_wr(slot, PCIC_ADDRWINE, + slot->region_flag &= ~(0x40 << window)); + delay(1000); + pcic_wr(slot, PCIC_IOCTL, + slot->ioctl_flag &= ~(0xf << window)); + pcic_wr(slot, winid | PCIC_START | PCIC_ADDR_LOW, 0); + pcic_wr(slot, winid | PCIC_START | PCIC_ADDR_HIGH, 0); + pcic_wr(slot, winid | PCIC_END | PCIC_ADDR_LOW, 0); + pcic_wr(slot, winid | PCIC_END | PCIC_ADDR_HIGH, 0); + + slot->io_addr[window] = start; + slot->io_len[window] = len; + } + +} +static int +pcic_map_mem(link, haddr, start, len, flags) + struct pcmcia_link *link; + caddr_t haddr; + u_int start, len; + int flags; +{ + struct pcic_softc *sc = link->adapter->adapter_softc; + struct slot *slot = &sc->slot[link->slot & 1]; + vm_offset_t physaddr; + + if (flags & PCMCIA_PHYSICAL_ADDR) + physaddr = (vm_offset_t) haddr; + else + physaddr = pmap_extract(pmap_kernel(), (vm_offset_t) haddr); + if (DEBUG(PCDMEM)) + printf("pcic_map_mem %x %x %x %x %x\n", haddr, physaddr, + start, len, flags); + + (u_long) physaddr >>= 12; + start >>= 12; + len = (len - 1) >> 12; + + if (!(flags & PCMCIA_UNMAP)) { + u_int offs; + u_int stop; + int window; + int winid; + if (flags & PCMCIA_LAST_WIN) { + window = MAX_MEMSECTION - 1; + } else if (flags & PCMCIA_FIRST_WIN) { + window = 0; + } else if (flags & PCMCIA_ANY_WIN) { + for (window = 0; window < MAX_MEMSECTION; window++) { + if (slot->mem_used[window] == 0) + break; + if (window >= MAX_MEMSECTION) + return EBUSY; + } + } else { + window = flags & 0xf; + if (window >= MAX_MEMSECTION) + return EINVAL; + } + slot->mem_used[window] = 1; + + offs = (start - (u_long) physaddr) & 0x3fff; + if (DEBUG(PCDMEM)) + printf("mapmem 2:%x %x %x\n", offs, physaddr + offs, + start); + + stop = (u_long) physaddr + len; + + winid = window * 0x8 + 0x10; + + + pcic_wr(slot, winid | PCIC_START | PCIC_ADDR_LOW, + (u_long) physaddr & 0xff); + pcic_wr(slot, winid | PCIC_START | PCIC_ADDR_HIGH, + (((u_long) physaddr >> 8) & 0x3f) | + /* PCIC_ZEROWS|/* */ + ((flags & PCMCIA_MAP_16) ? PCIC_DATA16 : 0)); + + pcic_wr(slot, winid | PCIC_END | PCIC_ADDR_LOW, + stop & 0xff); + pcic_wr(slot, winid | PCIC_END | PCIC_ADDR_HIGH, + PCIC_MW1 | ((stop >> 8) & 0x3f)); + + + pcic_wr(slot, winid | PCIC_MOFF | PCIC_ADDR_LOW, + offs & 0xff); + pcic_wr(slot, winid | PCIC_MOFF | PCIC_ADDR_HIGH, + ((offs >> 8) & 0x3f) | + ((flags & PCMCIA_MAP_ATTR) ? PCIC_REG : 0)); + delay(1000); + + pcic_wr(slot, PCIC_ADDRWINE, + slot->region_flag |= ((1 << window) | PCIC_MEMCS16)); + slot->mem_caddr[window] = (caddr_t) physaddr; + slot->mem_haddr[window] = start; + slot->mem_len[window] = len; + delay(1000); + return 0; + } else { + u_int offs; + u_int stop; + int window; + int winid; + + if (flags & PCMCIA_LAST_WIN) { + window = MAX_MEMSECTION - 1; + } else if (flags & PCMCIA_FIRST_WIN) { + window = 0; + } else if (flags & PCMCIA_ANY_WIN) { + for (window = 0; window < MAX_MEMSECTION; window++) { + if ((slot->mem_caddr[window] == + (caddr_t) physaddr) && + ((start == -1) || + (slot->mem_haddr[window] == start)) && + ((len == -1) || + (slot->mem_len[window] == len))) + break; + } + if (window >= MAX_MEMSECTION) + return EINVAL; + } else { + window = flags & 0xf; + if (window >= MAX_MEMSECTION) + return EINVAL; + } + winid = window * 0x8 + 0x10; + + slot->region_flag &= (~(1 << window)); + pcic_wr(slot, PCIC_ADDRWINE, slot->region_flag); + delay(1000); + pcic_wr(slot, winid | PCIC_START | PCIC_ADDR_LOW, 0); + pcic_wr(slot, winid | PCIC_START | PCIC_ADDR_HIGH, 0); + pcic_wr(slot, winid | PCIC_END | PCIC_ADDR_LOW, 0); + pcic_wr(slot, winid | PCIC_END | PCIC_ADDR_HIGH, 0); + pcic_wr(slot, winid | PCIC_MOFF | PCIC_ADDR_LOW, 0); + pcic_wr(slot, winid | PCIC_MOFF | PCIC_ADDR_HIGH, 0); + slot->mem_caddr[window] = 0; + slot->mem_haddr[window] = 0; + slot->mem_len[window] = 0; + slot->mem_used[window] = 0; + return 0; + } +} +static int +pcic_map_intr(link, irq, flags) + struct pcmcia_link *link; + int irq, flags; +{ + struct pcic_softc *sc = link->adapter->adapter_softc; + struct slot *slot = &sc->slot[link->slot & 1]; + + if (DEBUG(PCDINTR)) + printf("pcic_map_intr %x %x\n", irq, flags); + + if (flags & PCMCIA_UNMAP) { + slot->irq &= ~PCIC_INT_MASK; + slot->irq |= sc->sc_pcic_irq | PCIC_INTR_ENA; + pcic_wr(slot, PCIC_INT_GEN, slot->irq); + } + else { + if (irq < 2 || irq > 15 || irq == 6 || irq == 8 || irq == 13) + return EINVAL; + if(irq==2) + irq=9; + slot->irq = (slot->irq & PCIC_INT_FLAGMASK) | + irq | PCIC_INTR_ENA; + pcic_wr(slot, PCIC_INT_GEN, slot->irq); + } + return 0; +} + + +static int +pcic_service(link, opcode, arg, flags) + struct pcmcia_link *link; + int opcode; + void *arg; + int flags; +{ + struct pcic_softc *sc = link->adapter->adapter_softc; + struct slot *slot = &sc->slot[link->slot & 1]; + + slot->link = link; /* save it for later :) */ + switch (opcode) { + case PCMCIA_OP_STATUS:{ + u_char cp; + int *iarg = arg; + + if (DEBUG(PCDSERV)) + printf("pcic_service(status)\n"); + cp = pcic_rd(slot, PCIC_STATUS); + if (DEBUG(PCDSERV)) + printf("status for slot %d %b\n", + link->slot, cp, PCIC_STATUSBITS); + *iarg = 0; +#define DO_STATUS(cp, val, map) ((cp & val) == val ? map : 0) + *iarg |= DO_STATUS(cp, PCIC_CD, PCMCIA_CARD_PRESENT); + *iarg |= DO_STATUS(cp, PCIC_BVD, PCMCIA_BATTERY); + *iarg |= DO_STATUS(cp, PCIC_MWP, PCMCIA_WRITE_PROT); + *iarg |= DO_STATUS(cp, PCIC_READY, PCMCIA_READY); + *iarg |= DO_STATUS(cp, PCIC_POW, PCMCIA_POWER); + *iarg |= DO_STATUS(cp, PCIC_VPPV, PCMCIA_POWER_PP); + return 0; + + } + case PCMCIA_OP_WAIT:{ + int iarg = (int) arg; + int i = iarg * 4; + + if (DEBUG(PCDSERV)) + printf("pcic_service(wait)\n"); + i = pcic_wait(slot, i); + if (DEBUG(PCDSERV)) + printf("op99 %b %d\n", + pcic_rd(slot, PCIC_STATUS), + PCIC_STATUSBITS, i); + if (i <= 0) + return EIO; + else + return 0; + } + case PCMCIA_OP_RESET:{ + int force = ((int) arg) < 0; + int iarg = abs((int) arg); + int i = iarg * 4; + + if (DEBUG(PCDSERV)) + printf("pcic_service(reset)\n"); + slot->irq |= flags ? PCIC_IOCARD : 0; + pcic_wr(slot, PCIC_POWER, slot->pow &= ~PCIC_DISRST); + slot->irq &= ~PCIC_CARDRESET; + pcic_wr(slot, PCIC_INT_GEN, slot->irq); + if (iarg == 0) + return 0; + delay(iarg); + pcic_wr(slot, PCIC_POWER, slot->pow |= PCIC_DISRST); + slot->irq |= PCIC_CARDRESET; + pcic_wr(slot, PCIC_INT_GEN, slot->irq); + delay(iarg); + i = pcic_wait(slot, i); + if (DEBUG(PCDSERV)) + printf("opreset %d %b %d\n", force, + pcic_rd(slot, PCIC_STATUS), + PCIC_STATUSBITS, i); + if (i <= 0) + return EIO; + else + return 0; + } + case PCMCIA_OP_POWER:{ + int iarg = (int) arg; + if (DEBUG(PCDSERV)) + printf("pcic_service(power): "); + if (flags & PCMCIA_POWER_ON) { + int nv = (PCIC_DISRST|PCIC_OUTENA); + pcic_wr(slot, PCIC_INT_GEN, slot->irq = 0); + if(flags & PCMCIA_POWER_3V) + nv |= PCIC_VCC3V; + if(flags & PCMCIA_POWER_5V) + nv |= PCIC_VCC5V; + if(flags & PCMCIA_POWER_AUTO) + nv |= PCIC_APSENA| + PCIC_VCC5V|PCIC_VCC3V; + slot->pow &= ~(PCIC_APSENA|PCIC_VCC5V| + PCIC_VCC3V|PCIC_VPP12V| + PCIC_VPP5V); + slot->pow |= nv; + pcic_wr(slot, PCIC_POWER, slot->pow); +#if 0 + delay(iarg); + slot->pow |= PCIC_OUTENA; + pcic_wr(slot, PCIC_POWER, slot->pow); +#endif + delay(iarg); + if (DEBUG(PCDSERV)) + printf("on\n"); + } else { + slot->pow &= ~(PCIC_APSENA|PCIC_VCC5V| + PCIC_VCC3V); + slot->pow &= ~(PCIC_DISRST|PCIC_OUTENA); + pcic_wr(slot,PCIC_POWER, slot->pow); + if (DEBUG(PCDSERV)) + printf("off\n"); + } + return 0; + } + case PCMCIA_OP_GETREGS:{ + struct pcic_regs *pi = arg; + int i; + if (DEBUG(PCDSERV)) + printf("pcic_service(getregs)\n"); + pi->chip_vers = sc->chip_inf; + for (i = 0; i < pi->cnt; i++) + pi->reg[i].val = + pcic_rd(slot, pi->reg[i].addr); + return 0; + } + default: + if (DEBUG(PCDSERV)) + printf("pcic_service(%x)\n", opcode); + return EINVAL; + } +}