From: hvozda Date: Mon, 15 Jan 1996 00:05:03 +0000 (+0000) Subject: ISA <-> PCMCIA glue for PCMCIA support. X-Git-Url: http://artulab.com/gitweb/?a=commitdiff_plain;h=1acd3469ba32abf8c24a8be157e5bbe3b2e1df45;p=openbsd ISA <-> PCMCIA glue for PCMCIA support. --- diff --git a/sys/dev/pcmcia/pcmcia.c b/sys/dev/pcmcia/pcmcia.c new file mode 100644 index 00000000000..baae3fecf92 --- /dev/null +++ b/sys/dev/pcmcia/pcmcia.c @@ -0,0 +1,1429 @@ +/* + * 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. + * This product includes software developed by Stefan Grefen. + * 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.c,v 1.1 1996/01/15 00:05:03 hvozda Exp $ + */ + +/* derived from scsiconf.c writte by Julian Elischer et al */ +/* TODO add modload support and loadable lists of devices */ +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#ifdef IBM_WD +#define PCMCIA_DEBUG +#endif +#ifdef PCMCIA_DEBUG +# define PPRINTF(a) printf a +#else +# define PPRINTF(a) +#endif + +static void pcmciadumpcf __P((struct pcmcia_conf *)); +static int pcmcia_strcmp __P((char *, char *, int, char *)); + +void pcmcia_shuthook __P((void *)); + +static struct pcmcia_adapter pcmcia_drivers[4]; +static int pcmcia_cntrl = 0; +static int probed = 0; +static struct device **deldevs = NULL; +static int ndeldevs = 0; + +/* I've decided to re-ifdef these. It makes making a kernel easier + until I either get config(8) modified to deal somehow + or figure out a better to way declare the prototypes and + build up the knowndevs struct. Stefan may have ideas... +*/ + +#ifdef PCMCIA_ED +extern struct pcmciadevs pcmcia_ed_devs[]; +#endif +#ifdef PCMCIA_COM +extern struct pcmciadevs pcmcia_com_devs[]; +#endif +#ifdef PCMCIA_EP +extern struct pcmciadevs pcmcia_ep_devs[]; +#endif + +static struct pcmciadevs *knowndevs[] = { +#ifdef PCMCIA_ED + pcmcia_ed_devs, +#endif +#ifdef PCMCIA_COM + pcmcia_com_devs, +#endif +#ifdef PCMCIA_EP + pcmcia_ep_devs, +#endif + NULL +}; + +#ifdef notyet +static struct pcmciadevs *knowndevs[10] = { NULL }; +#define KNOWNSIZE (sizeof(knowndevs) / sizeof(knowndevs[0])) +#endif + +#define PCMCIA_SERVICE(a,b,c,d,e) ((a)->chip_link->pcmcia_service(b,c,\ + (void *) d,e)) +#define PCMCIA_MAP_IO(a,b,c,d,e) ((a)->chip_link->pcmcia_map_io(b,c,d,e)) +#define PCMCIA_MAP_INTR(a,b,c,d) ((a)->chip_link->pcmcia_map_intr(b,c,d)) +#define PCMCIA_MAP_MEM(a,b,c,d,e,f) ((a)->chip_link->pcmcia_map_mem(b,c,d,e,f)) + +#define PCMCIA_BUS_INIT(a,b,c,d,e,f)((a)->bus_link->bus_init((b),(c),(d),(e)\ + ,(f))) +#define PCMCIA_BUS_SEARCH(a,b,c,d) ((a)->bus_link->bus_search((b),(c),(d))) +#define PCMCIA_BUS_PROBE(a,b,c,d,e) ((a)->bus_link->bus_probe((b),(c),(d),(e))) +#define PCMCIA_BUS_CONFIG(a,b,c,d,e)((a)->bus_link->bus_config((b),(c),(d),(e))) +#define PCMCIA_BUS_UNCONFIG(a,b) ((a)->bus_link->bus_unconfig((b))) + +#define SCRATCH_MEM(a) ((a)->scratch_mem) +#define SCRATCH_SIZE(a) ((a)->scratch_memsiz) +#define SCRATCH_INUSE(a)((a)->scratch_inuse) + +/* + * Declarations + */ +struct pcmciadevs *pcmcia_probedev __P((struct pcmcia_link *)); +struct pcmciadevs *pcmcia_selectdev __P((char *, char *, char *, char *)); +int pcmcia_probe_bus __P((struct pcmcia_link *, int, int, + struct pcmcia_conf *)); +int pcmciabusmatch __P((struct device *, void *, void *)); +void pcmciabusattach __P((struct device *, struct device *, void *)); + +struct cfdriver pcmciabuscd = { + NULL, "pcmcia", pcmciabusmatch, pcmciabusattach, DV_DULL, + sizeof(struct pcmciabus_softc), 1 +}; + +#ifdef notyet +int +pcmcia_add_device(devs) + struct pcmciadevs *devs; +{ + int i; + + if (devs == NULL) + return 0; + + for (i = 0; i < KNOWNSIZE; i++) + if (knowndevs[i] == NULL) + break; + + if (i == KNOWNSIZE) + panic("Too many pcmcia devices"); + + knowndevs[i] = devs; + for (; devs->devname != NULL; devs++) + printf("added %s\n", devs->devname); + return i; +} +#endif + +int +pcmcia_register(adapter_softc, bus_link, chip_link, slot) + void *adapter_softc; + struct pcmciabus_link *bus_link; + struct pcmcia_funcs *chip_link; + int slot; +{ + PPRINTF(("- pcmcia_register\n")); + if (pcmcia_cntrl == 0) + bzero(pcmcia_drivers, sizeof(pcmcia_drivers)); + + if (pcmcia_cntrl < 4) { + pcmcia_drivers[slot].adapter_softc = adapter_softc; + pcmcia_drivers[slot].chip_link = chip_link; + pcmcia_drivers[slot].bus_link = bus_link; + pcmcia_cntrl++; + return 1; + } + return 0; +} + +int +pcmciabusmatch(parent, self, aux) + struct device *parent; + void *self; + void *aux; +{ + struct pcmciabus_softc *sc = (void *)self; + struct cfdata *cf = sc->sc_dev.dv_cfdata; + int i, found = 0; + + PPRINTF(("- pcmciabusmatch\n")); + if (pcmcia_cntrl <= 0) + return 0; + + for (i = 0; i < 4; i++) + if (pcmcia_drivers[i].bus_link) { + if (PCMCIA_BUS_INIT(&pcmcia_drivers[i], parent, cf, + aux, &pcmcia_drivers[i], 0)) + found++; + } + return found != 0; +} + +/* + * The routine called by the adapter boards to get all their + * devices configured in. + */ +void +pcmciabusattach(parent, self, aux) + struct device *parent; + struct device *self; + void *aux; +{ + struct pcmciabus_softc *sc = (struct pcmciabus_softc *) self; + struct cfdata *cf = self->dv_cfdata; + int i, found = 0; + + PPRINTF(("- pcmciabusattach\n")); + for (i = 0; i < 4; i++) + if (pcmcia_drivers[i].bus_link) { + if (PCMCIA_BUS_INIT(&pcmcia_drivers[i], parent, cf, + aux, &pcmcia_drivers[i], 1)) + found++; + } + + printf("\n"); + + pcmcia_probe_bus(NULL, sc->sc_dev.dv_unit, -1, NULL); +} + +/* + * Probe the requested pcmcia bus. It must be already set up. + * -1 requests all set up pcmcia busses. + */ +int +pcmcia_probe_busses(bus, slot) + int bus, slot; +{ + PPRINTF(("- pcmcia_probe_busses\n")); + if (bus == -1) { + for (bus = 0; bus < pcmciabuscd.cd_ndevs; bus++) + if (pcmciabuscd.cd_devs[bus]) + pcmcia_probe_bus(NULL, bus, slot, NULL); + return 0; + } else { + return pcmcia_probe_bus(NULL, bus, slot, NULL); + } +} + +/* + * Probe the requested pcmcia bus. It must be already set up. + */ +int +pcmcia_probe_bus(link, bus, slot, cf) + struct pcmcia_link *link; + int bus, slot; + struct pcmcia_conf *cf; +{ + struct pcmciabus_softc *pcmcia; + int maxslot, minslot, maxlun, minlun; + struct pcmciadevs *bestmatch = NULL; + int spec_probe = (link != NULL); + + PPRINTF(("- pcmcia_probe_bus\n")); + if (bus < 0 || bus >= pcmciabuscd.cd_ndevs) + return ENXIO; + pcmcia = pcmciabuscd.cd_devs[bus]; + if (!pcmcia) + return ENXIO; + + if (slot == -1) { + maxslot = 7; + minslot = 0; + } else { + if (slot < 0 || slot > 7) + return EINVAL; + maxslot = minslot = slot; + } + + for (slot = minslot; slot <= maxslot; slot++) { + if (link = pcmcia->sc_link[slot]) { + if (link->devp) + continue; + } + if (pcmcia_drivers[slot >> 1].adapter_softc == NULL) + continue; + + /* + * If we presently don't have a link block + * then allocate one + */ + if (!link) { + pcmcia->sc_link[slot] = link = + malloc(sizeof(*link), M_TEMP, M_NOWAIT); + if (link == NULL) + return ENOMEM; + bzero(link, sizeof(*link)); + link->opennings = 1; + link->adapter = &pcmcia_drivers[slot >> 1]; + link->slot = slot; + } + bestmatch = pcmcia_probedev(link); + /* + * We already know what the device is. We use a + * special matching routine which insists that the + * cfdata is of the right type rather than putting + * more intelligence in individual match routines for + * each high-level driver. + * We must have the final probe do all of the comparisons, + * or we could get stuck in an infinite loop trying the same + * device repeatedly. We use the `fordriver' field of + * the pcmcia_link for now, rather than inventing a new + * structure just for the config_search(). + */ + if (link->fordriver == NULL) { + if (bestmatch) + link->fordriver = bestmatch->devname; + else { + if (!spec_probe) { + link->device = NULL; + link->devp = NULL; + PCMCIA_SERVICE(link->adapter, + link, PCMCIA_OP_POWER, + 0, 0); + } + } + } + + if (spec_probe) { + if (cf && pcmcia_mapcard(link, -1, cf) != 0) + link->fordriver = NULL; + } + + if (link->fordriver != NULL) { + int i; + struct device **delp = deldevs; + int found = 0; + link->device = bestmatch; + link->flags = (link->flags & + ~(PCMCIA_ATTACH_TYPE)) | + PCMCIA_REATTACH; + for (i = 0; i < ndeldevs; i++, delp++) { + if (*delp && + pcmcia_configure((*delp)->dv_parent, *delp, + link)) { + link->flags = (link->flags & + ~PCMCIA_ATTACH_TYPE) + | PCMCIA_SLOT_INUSE; + found = 1; + *delp = NULL; + break; + } + } + if (!found) { + link->flags = (link->flags & + ~PCMCIA_ATTACH_TYPE) | PCMCIA_ATTACH; + if (PCMCIA_BUS_SEARCH(link->adapter, + &pcmcia->sc_dev, + link, NULL)) { + link->flags = (link->flags & + ~PCMCIA_ATTACH_TYPE) + | PCMCIA_SLOT_INUSE; + } else { + link->flags &= ~(PCMCIA_ATTACH_TYPE | + PCMCIA_SLOT_INUSE); + link->device = NULL; + printf( + "No matching config entry %s.\n", + link->fordriver ? + link->fordriver : "(NULL)"); + if (!spec_probe) + PCMCIA_SERVICE(link->adapter, + link, + PCMCIA_OP_POWER, + 0, 0); + } + } + } + } + return 0; +} + +/* + * given a target ask the device what + * it is, and find the correct driver table + * entry. + */ +struct pcmciadevs * +pcmcia_probedev(link) + struct pcmcia_link *link; +{ + struct pcmcia_adapter *pca = link->adapter; + u_char scratch[CIS_MAXSIZE]; + char manu[MAX_CIS_NAMELEN]; + char model[MAX_CIS_NAMELEN]; + char add_inf1[MAX_CIS_NAMELEN]; + char add_inf2[MAX_CIS_NAMELEN]; + int card_stat; + int err; + int pow = 0; + int slot = link->slot; + + PPRINTF(("- pcmcia_probe_dev\n")); + + printf("%s slot %d:", + ((struct device *) link->adapter->adapter_softc)->dv_xname, + slot & 1); + + if ((err = PCMCIA_SERVICE(pca, link, PCMCIA_OP_STATUS, + &card_stat, 0)) != 0) { + printf("failed to get status %d\n", err); + return NULL; + } + + if ((card_stat & PCMCIA_CARD_PRESENT) == 0) { + printf(" \n"); + return NULL; + } + + if (!(card_stat & PCMCIA_POWER)) { + pow = 1; + if ((err = PCMCIA_SERVICE(pca, link, PCMCIA_OP_POWER, 10000, + PCMCIA_POWER_ON| + PCMCIA_POWER_5V)) != 0) { + printf("failed to turn on power %d\n", err); + return NULL; + } + } + + if (!(link->flags & (PCMCIA_SLOT_INUSE | CARD_IS_MAPPED))) { + if ((err = PCMCIA_SERVICE(pca, link, PCMCIA_OP_RESET, + 500000, 0)) != 0) { + printf("failed to reset %d\n", err); + PCMCIA_SERVICE(pca, link, PCMCIA_OP_POWER, 0, 0); + return NULL; + } + } + + /* + * Ask the device what it is + */ + if ((err = pcmcia_read_cis(link, scratch, 0, sizeof(scratch))) != 0) { + printf("failed to read cis info %d\n", err); + goto bad; + } + + if ((err = pcmcia_get_cisver1(link, scratch, sizeof(scratch), + manu, model, add_inf1, + add_inf2)) != 0) { + printf("failed to get cis info %d\n", err); + goto bad; + } + + printf(" <%s, %s", manu, model); + if (add_inf1[0]) + printf(", %s", add_inf1); + if (add_inf2[0]) + printf(", %s", add_inf2); + printf(">\n"); + + + /* + * Try make as good a match as possible with + * available sub drivers + */ + return pcmcia_selectdev(manu, model, add_inf1, add_inf2); +bad: + if (!pow) + PCMCIA_SERVICE(pca, link, PCMCIA_OP_POWER, 0, 0); + return NULL; +} + +/* + * Try make as good a match as possible with + * available sub drivers + */ +struct pcmciadevs * +pcmcia_selectdev(manu, model, add_inf1, add_inf2) + char *manu, *model, *add_inf1, *add_inf2; +{ + u_int bestmatches = 0; + struct pcmciadevs *bestmatch = (struct pcmciadevs *) 0; + struct pcmciadevs **dlist, *dentry; + + PPRINTF(("- pcmcia_selectdev\n")); + for (dlist = knowndevs; *dlist; dlist++) + for (dentry = *dlist; dentry && + dentry->devname != NULL; dentry++) { + int match = 0; + +#ifdef PCMCIA_DEBUG + dentry->flags |= PC_SHOWME; +#endif + match|=pcmcia_strcmp(dentry->manufacturer, + manu,dentry->flags,"manufacturer")<<6; + match|=pcmcia_strcmp(dentry->model, + model,dentry->flags,"model")<<4; + match|=pcmcia_strcmp(dentry->add_inf1, + add_inf1,dentry->flags,"info1")<<2; + match|=pcmcia_strcmp(dentry->add_inf2, + add_inf2,dentry->flags,"info2"); +/* the following was replaced by the wildcard function called above */ +#if 0 + if (dentry->flags & PC_SHOWME) + printf("manufacturer = `%s'-`%s'\n", + dentry->manufacturer ? + dentry->manufacturer : + "X", + manu); + if (dentry->manufacturer) { + if (strcmp(dentry->manufacturer, manu) == 0) { + match |= 8; + } else { + continue; + } + } + + if (dentry->flags & PC_SHOWME) + printf("model = `%s'-`%s'\n", + dentry->model ? dentry->model : + "X", + model); + if (dentry->model) { + if (strcmp(dentry->model, model) == 0) { + match |= 4; + } else { + continue; + } + } + + + if (dentry->flags & PC_SHOWME) + printf("info1 = `%s'-`%s'\n", + dentry->add_inf1 ? dentry->add_inf1 : + "X", + add_inf1); + if (dentry->add_inf1) { + if (strcmp(dentry->add_inf1, add_inf1) == 0) { + match |= 2; + } else { + continue; + } + } + + if (dentry->flags & PC_SHOWME) + printf("info2 = `%s'-`%s'\n", + dentry->add_inf2 ? dentry->add_inf2 : + "X", + add_inf2); + if (dentry->add_inf2) { + if (strcmp(dentry->add_inf2, add_inf2) == 0) { + match |= 1; + } else { + continue; + } + } +#endif +#ifdef PCMCIA_DEBUG + printf("match == %d [%d]\n",match,bestmatches); +#endif + + if(match > bestmatches) { + bestmatches = match; + bestmatch = dentry; + } + } + + return bestmatch; +} + +int +pcmcia_configure(parent, self, aux) + struct device *parent; + void *self; + void *aux; +{ + struct device *dev = self; + struct pcmcia_link *link = aux; + struct cfdata *cf = dev->dv_cfdata; + struct cfdriver *cd = cf->cf_driver; + char *devname = (char *) link->fordriver; + struct pcmciadevs *pcs = link->device; + struct pcmcia_device *pcd; + struct pcmcia_adapter *pca = link->adapter; + struct pcmcia_conf pc_cf; + u_char scratch[CIS_MAXSIZE]; + int mymap = 0; + + PPRINTF(("- pcmcia_configure\n")); + + if (strcmp(devname, cd->cd_name) || !pca) + return 0; + + if (pcs == NULL) + pcd = NULL; + else + pcd = pcs->dev; + + PPRINTF(("pcmcia_configure: %x\n", pcd)); + if (!(link->flags & CARD_IS_MAPPED)) { + /* read 'suggested' configuration */ + PPRINTF(("pcmcia_configure: calling read cis\n")); + if (pcmcia_read_cis(link, scratch, 0, sizeof(scratch)) != 0) + return 0; + + bzero(&pc_cf, sizeof(pc_cf)); + + PPRINTF(("pcmcia_configure: calling get cf\n")); + if (pcmcia_get_cf(link, scratch, sizeof(scratch), -1, + &pc_cf) != 0) + return 0; +#ifdef PCMCIA_DEBUG + pcmciadumpcf(&pc_cf); +#endif + /* and modify it (device specific) */ + if (pcd && pcd->pcmcia_config) { + PPRINTF(("pcmcia_configure: calling config\n")); + if (pcd->pcmcia_config(link, dev, &pc_cf, cf)) + return 0; + + if ((pc_cf.cfgtype & CFGENTRYMASK) == CFGENTRYID) { + PPRINTF(("pcmcia_configure: calling cf2\n")); + if (pcmcia_get_cf(link, scratch, + sizeof(scratch), -2, + &pc_cf) != 0) + return 0; + + PPRINTF(("pcmcia_configure: calling conf2\n")); + if (pcd->pcmcia_config(link, dev, &pc_cf, cf)) + return 0; + /* give it a try */ + if(pc_cf.cfgid==0) + pc_cf.cfgid=1; + } + } else { + PPRINTF(("pcmcia_configure: calling bus config\n")); + if (PCMCIA_BUS_CONFIG(pca, link, dev, &pc_cf, cf)) + return 0; + } +#ifdef PCMCIA_DEBUG + pcmciadumpcf(&pc_cf); +#endif + + if (pcmcia_mapcard(link, -1, &pc_cf) != 0) + return 0; + + mymap = 1; + } + link->devp = dev; + + PPRINTF(("pcmcia_configure: calling bus probe\n")); + if (!(PCMCIA_BUS_PROBE(pca, parent, dev, cf, link))) { + PPRINTF(("pcmcia_configure: bus probe failed\n")); + goto bad; + } + + if (pcd && pcd->pcmcia_insert && pcd->pcmcia_insert(link, dev, cf)) { + PPRINTF(("pcmcia_configure: pcmcia_insert failed\n")); + goto bad; + } + + link->shuthook = shutdownhook_establish(pcmcia_shuthook, + (void *)link); + return 1; + +bad: + link->devp = NULL; + if (mymap) + pcmcia_unmapcard(link); + printf("pcmcia_configure: configuration error\n"); + return 0; +} + +void +pcmcia_shuthook(arg) +void *arg; +{ + struct pcmcia_link *link = (struct pcmcia_link *)arg; + if (pcmcia_unconfigure(link) == 0) { + /* + * turn off power too. + */ + PCMCIA_SERVICE(link->adapter, link, PCMCIA_OP_RESET, 500000, 0); + PCMCIA_SERVICE(link->adapter, link, PCMCIA_OP_POWER, 0, 0); + } +} + +int +pcmcia_unconfigure(link) + struct pcmcia_link *link; +{ + int status; + int i, err; + struct device **delp; + struct device *dev; + struct pcmcia_adapter *pca = link->adapter; + struct pcmcia_device *pcd; + + PPRINTF(("- pcmcia_unconfigure\n")); + if (link->devp == NULL) + return ENODEV; + + if (link->device) + pcd = link->device->dev; + else + pcd = NULL; + + if (link->flags & CARD_IS_MAPPED) { + if (pcd && pcd->pcmcia_remove) { + if (pcd->pcmcia_remove(link, link->devp)) + return EBUSY; + } + else { + if (PCMCIA_BUS_UNCONFIG(pca, link)) + return EBUSY; + } + if (pcmcia_unmapcard(link) != 0) + return EBUSY; + } + delp = deldevs; + for (i = 0; delp && *delp && i < ndeldevs; i++, delp++) + continue; + if (i >= ndeldevs) { + int sz = ndeldevs ? (ndeldevs * 2) : + (MINALLOCSIZE / sizeof(void *)); + struct device **ndel = malloc(sz * sizeof(void *), + M_DEVBUF, M_NOWAIT); + if (!ndel) { + PPRINTF(("pcmcia_delete: creating dev array")); + return ENOMEM; + } + bzero(ndel, sz * sizeof(void *)); + if (ndeldevs) { + bcopy(deldevs, ndel, ndeldevs * sizeof(void *)); + free(deldevs, M_DEVBUF); + } + ndeldevs = sz - 1; + deldevs = ndel; + delp = deldevs + i; + } + dev = *delp = link->devp; + link->devp = NULL; + printf("device %s in pcmcia slot %d detached\n", dev->dv_xname, + link->slot); + shutdownhook_disestablish(link->shuthook); + link->shuthook = 0; + return 0; +} + +int +pcmcia_mapcard(link, unit, pc_cf) + struct pcmcia_link *link; + struct pcmcia_conf *pc_cf; +{ + struct pcmcia_adapter *pca = link->adapter; + int s, i, err; + PPRINTF(("- pcmcia_mapcard\n")); + + if (pca == NULL) + return ENXIO; + s = splbio(); + + while (SCRATCH_INUSE(pca)) + sleep((caddr_t) & SCRATCH_INUSE(pca), PZERO - 1); + + SCRATCH_INUSE(pca) = 1; + splx(s); + for (i = 0; i < pc_cf->memwin; i++) { + if ((err = PCMCIA_MAP_MEM(pca, link, + (caddr_t) pc_cf->mem[i].start, + pc_cf->mem[i].caddr, + pc_cf->mem[i].len, + (pc_cf->mem[i].flags & + (PCMCIA_MAP_16 | PCMCIA_MAP_ATTR)) | + i | PCMCIA_PHYSICAL_ADDR)) != 0) { + PPRINTF(("pcmcia_mapcard: mapmem %d err%d\n", i, err)); + goto error; + } + } + for (i = 0; i < pc_cf->iowin; i++) { + if ((err = PCMCIA_MAP_IO(pca, link, pc_cf->io[i].start, + pc_cf->io[i].len, + (pc_cf->io[i].flags & (PCMCIA_MAP_16 | + PCMCIA_MAP_8)) | i)) != 0) { + PPRINTF(("pcmcia_mapcard: mapio %d err %d\n", i, err)); + goto error; + } + } + + if ((pc_cf->irq_num & 0xf) > 0) { + if ((err = PCMCIA_MAP_INTR(pca, link, pc_cf->irq_num & 0xf, + 0)) != 0) { + PPRINTF(("pcmcia_mapcard: map_intr %d err %d\n", + pc_cf->irq_num & 0xf, err)); + goto error; + } + } + /* Now we've mapped everything enable it */ + if ((err = PCMCIA_MAP_MEM(pca, link, SCRATCH_MEM(pca), + pc_cf->cfg_off & (~(SCRATCH_SIZE(pca) - 1)), SCRATCH_SIZE(pca), + PCMCIA_MAP_ATTR | PCMCIA_LAST_WIN)) != 0) { + PPRINTF(("pcmcia_mapcard: enable err %d\n", err)); + goto error; + } + + if ((err = PCMCIA_SERVICE(pca, link, PCMCIA_OP_RESET, -500000, + pc_cf->iocard)) != 0) { + PPRINTF(("failed to reset %d\n", err)); + goto error; + } + +#define GETMEM(x) SCRATCH_MEM(pca)[(pc_cf->cfg_off & \ + (SCRATCH_SIZE(pca) - 1)) + x] + if ((pc_cf->cfgtype & DOSRESET)) { + GETMEM(0) = PCMCIA_SRESET; + delay(50000); + } + + + PPRINTF(("CMDR %x\n",((pc_cf->cfgtype & CFGENTRYID) ? + pc_cf->cfgid |CFGENTRYID: + (pc_cf->cfgtype & CFGENTRYMASK)|1)| + (pc_cf->irq_level ? PCMCIA_LVLREQ : 0) + )); + + GETMEM(0) = ((pc_cf->cfgtype & CFGENTRYID) ? + pc_cf->cfgid |CFGENTRYID: + (pc_cf->cfgtype & CFGENTRYMASK)|1)| + (pc_cf->irq_level ? PCMCIA_LVLREQ : 0); + delay(50000); + + if (pc_cf->cfg_regmask & (1 << (PCMCIA_SCR / 2))) + GETMEM(PCMCIA_SCR) = (link->slot & 1) | 0x10; + +#if 0 + DPRINTF(("CCSR %x\n", GETMEM(PCMCIA_CCSR])); + if (GETMEM(PCMCIA_CCSR] & PCMCIA_POWER_DOWN) { + GETMEM(PCMCIA_CCSR] &= ~PCMCIA_POWER_DOWN; + DPRINTF(("CCSR now %x\n", GETMEM(PCMCIA_CCSR])); + } +#endif + + if ((err = PCMCIA_SERVICE(pca, link, PCMCIA_OP_WAIT, + 500000, 0)) != 0) + PPRINTF(("failed to initialize %d\n", err)); +error: + PCMCIA_MAP_MEM(pca, link, 0, 0, 0, PCMCIA_LAST_WIN | PCMCIA_UNMAP); + if (err != 0) { + for (i = 0; i < pc_cf->memwin; i++) { + PCMCIA_MAP_MEM(pca, link, + (caddr_t) pc_cf->mem[i].start, + pc_cf->mem[i].caddr, + pc_cf->mem[i].len, + (pc_cf->mem[i].flags & (PCMCIA_MAP_16 | + PCMCIA_MAP_ATTR)) | i | + PCMCIA_PHYSICAL_ADDR | PCMCIA_UNMAP); + } + for (i = 0; i < pc_cf->iowin; i++) { + PCMCIA_MAP_IO(pca, link, pc_cf->io[i].start, + pc_cf->io[i].len, + (pc_cf->io[i].flags & (PCMCIA_MAP_16 | + PCMCIA_MAP_8)) | i | PCMCIA_UNMAP); + } + PCMCIA_MAP_INTR(pca, link, pc_cf->irq_num, PCMCIA_UNMAP); + link->flags &= ~CARD_IS_MAPPED; + link->iowin = 0; + link->memwin = 0; + link->intr = 0; + } else { + link->flags |= CARD_IS_MAPPED; + link->iowin = pc_cf->iowin; + link->memwin = pc_cf->memwin; + link->intr = pc_cf->irq_num; + } + s = splbio(); + SCRATCH_INUSE(pca) = 0; + wakeup((caddr_t) & SCRATCH_INUSE(pca)); + splx(s); + return err; +} + +int +pcmcia_unmapcard(link) + struct pcmcia_link *link; +{ + int i; + struct pcmcia_adapter *pca = link->adapter; + PPRINTF(("- pcmcia_unmapcard\n")); + if (!pca) + return ENODEV; + + for (i = 0; i < link->memwin; i++) + PCMCIA_MAP_MEM(pca, link, 0, 0, 0, (i | PCMCIA_UNMAP)); + + for (i = 0; i < link->iowin; i++) + PCMCIA_MAP_IO(pca, link, 0, 0, (i | PCMCIA_UNMAP)); + + PCMCIA_MAP_INTR(pca, link, link->intr, PCMCIA_UNMAP); + PCMCIA_SERVICE(pca, link, PCMCIA_OP_RESET, 0, 0); + link->flags &= ~(CARD_IS_MAPPED | PCMCIA_SLOT_INUSE); + link->iowin = 0; + link->memwin = 0; + link->intr = 0; + return 0; +} + + +static int +pcmcia_mapcard_and_configure(link, unit, pc_cf) + struct pcmcia_link *link; + struct pcmcia_conf *pc_cf; + int unit; +{ + int err; + int mymap = 0; + + PPRINTF(("- pcmcia_mapcard_and_configure\n")); + if (pc_cf->driver_name[0][0]) { +#if 0 + if ((err = pcmcia_mapcard(link, unit, pc_cf)) != 0) { + return err; + } + mymap=1; +#endif + link->fordriver = pc_cf->driver_name[0]; + } else { + link->fordriver = NULL; + pc_cf = NULL; + } + pcmcia_probe_bus(link, 0, link->slot, pc_cf); + if ((link->flags & PCMCIA_SLOT_INUSE) == 0) { + if (mymap) + pcmcia_unmapcard(link); + return ENODEV; + } + return 0; +} + + +int +pcmcia_read_cis(link, scratch, offs, len) + struct pcmcia_link *link; + u_char *scratch; + int offs, len; +{ + struct pcmcia_adapter *pca = link->adapter; + int s; + int err = 0; + int j = 0; + u_char *p = SCRATCH_MEM(pca); + int size = SCRATCH_SIZE(pca); + volatile int *inuse = &SCRATCH_INUSE(pca); + + PPRINTF(("- pcmcia_read_cis\n")); + if (pca == NULL) + return ENXIO; + + s = splbio(); + while (*inuse) + sleep((caddr_t) inuse, PZERO - 1); + *inuse = 1; + splx(s); + + while (len > 0) { + int pgoff = offs / size; + int toff = offs - (pgoff * size); + int tlen = min(len + toff, size / 2) - toff; + int i; + + if ((err = PCMCIA_MAP_MEM(pca, link, p, pgoff, size, + PCMCIA_MAP_ATTR | + PCMCIA_LAST_WIN)) != 0) + goto error; + + for (i = 0; i < tlen; j++, i++) + scratch[j] = p[toff + i * 2]; + + PCMCIA_MAP_MEM(pca, link, p, 0, size, + PCMCIA_LAST_WIN | PCMCIA_UNMAP); + len -= tlen; + } +error: + s = splbio(); + *inuse = 0; + wakeup((caddr_t) inuse); + splx(s); + + return err; +} + +/* here we start our pseudodev for controlling the slots */ +#define PCMCIABUS_UNIT(a) (minor(a)) +#define PCMCIABUS_SLOT(a) (a&0x7) +#define PCMCIABUS_CHIPIID(a) (a&0x3) +#define PCMCIABUS_CHIP 0x10 +#define PCMCIABUS_BUS 0x20 +#define PCMCIABUS_DEVTYPE(a) ((a)&(PCMCIABUS_CHIP|PCMCIABUS_BUS)) +static int busopen = 0; +static int chipopen[4] = {0, 0, 0, 0}; + +int +pcmciabusopen(dev, flag, mode, p) + dev_t dev; + int flag, mode; + struct proc *p; +{ + int unit = PCMCIABUS_UNIT(dev); + int chipid, slot; + struct pcmcia_link *link; + struct pcmciabus_softc *pcmcia; + + PPRINTF(("- pcmciabusopen\n")); + if (pcmcia_cntrl == 0) + return ENXIO; + switch (PCMCIABUS_DEVTYPE(unit)) { + case PCMCIABUS_BUS: + if (unit != PCMCIABUS_BUS) + return ENXIO; + if (busopen) + return EBUSY; + busopen = 1; + break; + + case PCMCIABUS_CHIP: + chipid = PCMCIABUS_CHIPIID(unit); + if (chipid > 3) + return ENXIO; + if (pcmcia_drivers[chipid].adapter_softc == NULL) + return ENXIO; + + if (chipopen[chipid]) + return EBUSY; + + chipopen[chipid] = 1; + break; + + case 0: + slot = PCMCIABUS_SLOT(unit); + chipid = slot >> 1; + + if (chipid > 7) + return ENXIO; + + if (pcmcia_drivers[chipid].adapter_softc == NULL) + return ENXIO; + pcmcia = pcmciabuscd.cd_devs[0]; + link = pcmcia->sc_link[slot]; + + if (link->flags & PCMCIA_SLOT_OPEN) + return EBUSY; + + link->flags |= PCMCIA_SLOT_OPEN; + break; + + default: + return ENXIO; + + } + return 0; +} + + +int +pcmciabusclose(dev) +{ + int unit = PCMCIABUS_UNIT(dev); + int chipid, slot; + struct pcmcia_link *link; + struct pcmciabus_softc *pcmcia; + int s; + + PPRINTF(("- pcmciabusclose\n")); + if (pcmcia_cntrl == 0) + return ENXIO; + switch (PCMCIABUS_DEVTYPE(unit)) { + case PCMCIABUS_BUS: + busopen = 0; + break; + + case PCMCIABUS_CHIP: + chipid = PCMCIABUS_CHIPIID(unit); + chipopen[chipid] = 0; + break; + + case 0: + slot = PCMCIABUS_SLOT(unit); + pcmcia = pcmciabuscd.cd_devs[0]; + link = pcmcia->sc_link[slot]; + + s = splclock(); + link->flags &= ~(PCMCIA_SLOT_OPEN|PCMCIA_SLOT_EVENT); + splx(s); + break; + + default: + return ENXIO; + } + return 0; +} + +int +pcmciachip_ioctl(chipid, cmd, data) + int chipid, cmd; + caddr_t data; +{ + int err = 0; + struct pcmcia_adapter *pca = &pcmcia_drivers[chipid]; + struct pcmcia_link link; + struct pcmcia_regs *pi = (void *) data; + + PPRINTF(("- pcmciachip_ioctl\n")); + if (pca->chip_link == NULL || pca->adapter_softc == NULL) + return ENXIO; + + switch (cmd) { + case PCMCIAIO_READ_REGS: + pi->chip = chipid; + link.adapter = pca; + link.slot = chipid << 1; + return PCMCIA_SERVICE(pca, &link, PCMCIA_OP_GETREGS, + pi->chip_data, 0); + } + return ENOTTY; +} + +int +pcmciaslot_ioctl(link, slotid, cmd, data) + struct pcmcia_link *link; + int slotid, cmd; + caddr_t data; +{ + int err = 0; + struct pcmcia_adapter *pca = &pcmcia_drivers[slotid >> 1]; + + PPRINTF(("- pcmciaslot_ioctl\n")); + if (link == NULL || pca->chip_link == NULL || + pca->adapter_softc == NULL) + return ENXIO; + + switch (cmd) { + case PCMCIAIO_GET_STATUS: + { + struct pcmcia_status *pi = (void *) data; + pi->slot = slotid; + pi->status = 0; + err = PCMCIA_SERVICE(pca, link, PCMCIA_OP_STATUS, + &pi->status, 0); + if (!err) { + pi->status |= ((link->flags & CARD_IS_MAPPED) ? + PCMCIA_CARD_IS_MAPPED : 0) | + ((link->flags & PCMCIA_SLOT_INUSE) ? + PCMCIA_CARD_INUSE : 0); + } + return err; + } + + case PCMCIAIO_GET_INFO: + { + struct pcmcia_info *pi = (void *) data; + int status; + + if ((err = PCMCIA_SERVICE(pca, link, PCMCIA_OP_STATUS, + &status, 0)) != 0) + return err; + if ((status & PCMCIA_CARD_PRESENT) == 0) + return ENODEV; + pi->slot = slotid; + return pcmcia_read_cis(link, pi->cis_data, 0, + CIS_MAXSIZE); + } + + case PCMCIAIO_CONFIGURE: + { + struct pcmcia_conf *pc_cf = (void *) data; + return pcmcia_mapcard_and_configure(link, -1, pc_cf); + } + + case PCMCIAIO_UNCONFIGURE: + return pcmcia_unconfigure(link); + + case PCMCIAIO_UNMAP: + return pcmcia_unmapcard(link); + + case PCMCIAIO_SET_POWER: + { + int pi = *(int *) data; + pi &= 0x3; + switch (pi) { + case PCMCIASIO_POWER_OFF: + return PCMCIA_SERVICE(pca, link, + PCMCIA_OP_POWER, 0, 0); + + case PCMCIASIO_POWER_5V: + case PCMCIASIO_POWER_3V: + case PCMCIASIO_POWER_AUTO: + err = PCMCIA_SERVICE(pca, link, + PCMCIA_OP_POWER, + 10000, pi); + if (err) + return err; + + err = PCMCIA_SERVICE(pca, link, + PCMCIA_OP_RESET, + 500000, 0); + if (err) { + PPRINTF(("failed to reset %d\n", err)); + PCMCIA_SERVICE(pca, link, + PCMCIA_OP_POWER, 0, 0); + return err; + } + return 0; + + default: + return EINVAL; + } + } + + case PCMCIAIO_READ_COR: + { + struct pcmcia_info *pi = (void *)data; + struct pcmcia_conf pc_cf; + int status,s; + + err = PCMCIA_SERVICE(pca, link, PCMCIA_OP_STATUS, + &status, 0); + if (err) + return err; + if ((status & PCMCIA_CARD_PRESENT) == 0) + return ENODEV; + + if (status = pcmcia_read_cis(link, pi->cis_data, 0, + CIS_MAXSIZE)) + return status; + + bzero(&pc_cf, sizeof(pc_cf)); + if (pcmcia_get_cf(link, pi->cis_data, + sizeof(pi->cis_data), -1, + &pc_cf) != 0 ) + return EIO; + + s=splbio(); + + while(SCRATCH_INUSE(pca)) + sleep((caddr_t)&SCRATCH_INUSE(pca), PZERO - 1); + + SCRATCH_INUSE(pca) = 1; + splx(s); + if ((err = PCMCIA_MAP_MEM(pca, link, SCRATCH_MEM(pca), + pc_cf.cfg_off & + ~(SCRATCH_SIZE(pca)-1), + SCRATCH_SIZE(pca), + PCMCIA_MAP_ATTR| + PCMCIA_LAST_WIN)) == 0) { + int m, i; + u_char *d = pi->cis_data,*p; + p = SCRATCH_MEM(pca)+ + (pc_cf.cfg_off & (SCRATCH_SIZE(pca)-1)); + for (i = 0, m = 1; i < 32; i++, m <<= 1) { + if (pc_cf.cfg_regmask & m) { + *d++ = i; + *d++ = p[i*2]; + } + } + *d++ = 0xff; + *d++ = 0xff; + PCMCIA_MAP_MEM(pca, link, SCRATCH_MEM(pca), + 0,SCRATCH_SIZE(pca), + PCMCIA_LAST_WIN|PCMCIA_UNMAP); + } + s = splbio(); + SCRATCH_INUSE(pca)=0; + wakeup((caddr_t)&SCRATCH_INUSE(pca)); + splx(s); + return err; + } + default: + return ENOTTY; + } + return ENOTTY; +} + +int +pcmciabusioctl(dev, cmd, data, flag, p) + dev_t dev; + int cmd; + caddr_t data; + int flag; + struct proc *p; +{ + int unit = PCMCIABUS_UNIT(dev); + struct pcmciabus_softc *pcmcia; + struct pcmcia_link *link; + + PPRINTF(("- pcmciabus_ioctl\n")); + pcmcia = pcmciabuscd.cd_devs[0]; + if (pcmcia_cntrl == 0 || pcmcia == NULL) + return ENXIO; + switch (PCMCIABUS_DEVTYPE(unit)) { +#if 0 + case PCMCIABUS_BUS: + return pcmciabus_ioctl(0, cmd, data); +#endif + case PCMCIABUS_CHIP: + return pcmciachip_ioctl(PCMCIABUS_CHIPIID(unit), cmd, data); + case 0: + link = pcmcia->sc_link[PCMCIABUS_SLOT(unit)]; + return pcmciaslot_ioctl(link, PCMCIABUS_SLOT(unit), cmd, data); + default: + return ENXIO; + } +} + +int +pcmciabusselect(device, rw, p) + dev_t device; + int rw; + struct proc *p; +{ + int s; + int unit = PCMCIABUS_UNIT(device); + struct pcmciabus_softc *pcmcia; + struct pcmcia_link *link; + + PPRINTF(("- pcmciabus_ioctl\n")); + pcmcia = pcmciabuscd.cd_devs[0]; + + switch (PCMCIABUS_DEVTYPE(unit)) { + case 0: + link = pcmcia->sc_link[PCMCIABUS_SLOT(unit)]; + break; + case PCMCIABUS_BUS: + case PCMCIABUS_CHIP: + default: + return 0; + } + + s = splclock(); /* XXX something higher than all devices that can plug in.... */ + switch (rw) { + case FREAD: + case FWRITE: + break; + case 0: + if (link->flags & PCMCIA_SLOT_EVENT) { + link->flags &= ~PCMCIA_SLOT_EVENT; + splx(s); + return 1; + } + selrecord(p, &link->pcmcialink_sel); + break; + } + splx(s); + return 0; +} + +int +pcmciabusmmap() +{ + return ENXIO; +} + +/* pcmcia template string match. A '*' matches any number of characters. + A NULL template matches all strings. + return-value + 0 nomatch + 1 wildcard match + 2 excact match + */ +static int +pcmcia_strcmp(templ,val,flags,msg) + char *templ; + char *val; + int flags; + char *msg; +{ + char *ltempl=NULL,*lval=NULL; + + if (flags & PC_SHOWME) + printf("%s = `%s'-`%s'\n", msg, templ ? templ : "X", val); + + if(templ==NULL) + return 1; + while(*val) { + while(*templ=='*') { + ltempl=++templ; + lval=val; + } + if(*templ==*val) { + templ++; + val++; + } else { + if(ltempl==NULL) + return 0; + val=++lval; + templ=ltempl; + } + } + if(*templ!=0 && *templ!='*') + return 0; + return ltempl?1:2; +} + +#ifdef PCMCIA_DEBUG +static void +pcmciadumpcf(cf) + struct pcmcia_conf * cf; +{ + int i; + static char *ios[] = { + "auto", "8bit", "16bit", "illegal" + }; + printf("Driver name %s\n", cf->driver_name[0]); + printf("CFG offset %x\n", cf->cfg_off); + printf("IRQ type %s%s\n", cf->irq_level ? "Level " : "", + cf->irq_pulse ? "Pulse" : ""); + printf("IRQ num %x\n", cf->irq_num); + printf("CFG type %x %x\n", cf->cfgtype,cf->cfgid); + printf("Cardtype %s\n", cf->iocard ? "IO" : "MEM"); + for (i = 0; i < cf->iowin; i++) { + printf("iowin %x-%x %s\n", cf->io[i].start, + cf->io[i].start + cf->io[i].len - 1, + ios[(cf->io[i].flags & + (PCMCIA_MAP_8 | PCMCIA_MAP_16)) >> 8]); + } + for (i = 0; i < cf->memwin; i++) { + printf("memwin (%x)%x-%x %x\n", + cf->mem[i].caddr, + cf->mem[i].start, + cf->mem[i].start + cf->mem[i].len - 1, + cf->mem[i].flags); + } +} +#endif diff --git a/sys/dev/pcmcia/pcmcia.h b/sys/dev/pcmcia/pcmcia.h new file mode 100644 index 00000000000..67df7217ff5 --- /dev/null +++ b/sys/dev/pcmcia/pcmcia.h @@ -0,0 +1,132 @@ +#ifndef __PCMCIA_H__ +#define __PCMCIA_H__ + +#define MAX_CIS_NAMELEN /*32*/64 /* version info string len */ + +/* + * Configuration Registers + * + * These are the registers required by Release 2.0 of the standard + * (Section 4.15) + */ + +/* Offsets for register ordering */ +#define PCMCIA_COR 0x00 /* Configuration and Option Register */ +#define PCMCIA_CCSR 0x02 /* Card Configuration and Status Register */ +#define PCMCIA_PIR 0x04 /* Pin Replacement Register */ +#define PCMCIA_SCR 0x06 /* Socket and Copy Register */ + +/* Now register bits, ordered by reg # */ + +/* For Configuration and Option Register (PCMCIA_COR) */ +/*#define PCMCIA_MEMIO 0x01 /* Use I/O Space */ +/*#define PCMCIA_CNFG 0x0e /* I/O decoding configuration */ +#define PCMCIA_CNFGMASK 0x3f /* Use template */ +#define PCMCIA_LVLREQ 0x40 /* Generate level mode interrupts */ +#define PCMCIA_SRESET 0x80 /* Reset Card */ + +/* For Card Configuration and Status Register (PCMCIA_CCSR) */ +#define PCMCIA_INTR 0x02 /* Interrupt Pending */ +#define PCMCIA_POWER_DOWN 0x04 +#define PCMCIA_AUDIO_ENA 0x08 +#define PCMCIA_IOIS8 0x20 +#define PCMCIA_SIGCHG_ENA 0x40 +#define PCMCIA_CHANGED 0x80 + +/* Pin Replacement Register (PCMCIA_PIR) */ +#define PCMCIA_WP_STATUS 0x01 +#define PCMCIA_READY_STATUS 0x02 +#define PCMCIA_BVD2_STATUS 0x04 +#define PCMCIA_BVD1_STATUS 0x08 +#define PCMCIA_WP_EVENT 0x10 +#define PCMCIA_READY_EVENT 0x20 +#define PCMCIA_BVD2_EVENT 0x40 +#define PCMCIA_BVD1_EVENT 0x80 + + +/* For Socket and Copy Register (PCMCIA_SCR) */ +#define PCMCIA_SOCKNUM 0x0f /* Which socket I'm sitting in */ +#define PCMCIA_COPNUM 0x70 /* Which instance I am. */ + +/* + * CIS Tuple defines + */ +#define CIS_MAXSIZE 512 + +/* Define tuple types */ +#define CIS_NULL 0x00 /* null tuple */ +#define CIS_DEVICE 0x01 /* Device descriptor, common mem */ +#define CIS_DEVICE_A 0x17 /* Device descriptor, attribute mem */ +#define CIS_DEVICE_TYPE 0xf0 /* type mask */ +#define CIS_DEVICE_TYPE_SHIFT 4 /* type offset */ +#define CIS_DEVICE_WPS 0x08 /* WPS mask */ +#define CIS_DEVICE_SPEED 0x07 /* speed mask */ +#define CIS_DEVICE_ADDRS 0xf8 /* # addr units */ +#define CIS_DEVICE_ADDRS_SHIFT 3 /* # addr units offset */ +#define CIS_DEVICE_SIZE 0x07 +#define CIS_CSUM 0x10 /* Checksum field */ +#define CIS_NOLINK 0x14 /* No Link */ +#define CIS_VER1 0x15 /* Level 1 Version/Product info */ +#define CIS_CFG_INFO 0x1a /* Configuration info map */ +#define TPCC_RASZ 0x03 /* size of regaddr */ +#define TPCC_RASZ_SHIFT 0 +#define TPCC_RMSZ 0x3c /* size of regmask */ +#define TPCC_RMSZ_SHIFT 2 +#define TPCC_LAST 0x3f /* last con entry idx */ +#define TPCC_LAST_SHIFT 0 +#define CIS_CFG_ENT 0x1b /* Configuration info entry */ +#define TPCE_INDX_ENTRY 0x3f /* config entry # */ +#define TPCE_INDX_DEF 0x40 /* default bit */ +#define TPCE_INDX_INT 0x80 /* interface bit */ +#define TPCE_IF_TYPE 0x0f /* interface type */ +#define TPCE_IF_BVD 0x10 /* BVD active bit */ +#define TPCE_IF_WP 0x20 /* WP active bit */ +#define TPCE_IF_RDYBSY 0x40 /* RdyBsy active bit */ +#define TPCE_IF_MWAIT 0x80 /* Wait Sig req. bit */ +#define TPCE_FS_PWR 0x03 /* Power */ +#define TPCE_FS_PWR_VCC 0x01 /* Vcc struct */ +#define TPCE_FS_PWR_VPP 0x02 /* Vpp struct */ +#define TPCE_FS_TD 0x04 /* Timing */ +#define TPCE_FS_TD_WAIT 0x03 /* wait scale */ +#define TPCE_FS_TD_RDY 0x1c /* rdy/bsy scale */ +#define TPCE_FS_TD_RDY_SHIFT 2 +#define TPCE_FS_TD_RSV 0xe0 /* reserved scale */ +#define TPCE_FS_TD_RSV_SHIFT 5 +#define TPCE_FS_IO 0x08 /* I/O Space */ +#define TPCE_FS_IO_LINES 0x1f /* IO addr lines */ +#define TPCE_FS_IO_BUS8 0x20 /* bus 8 bit */ +#define TPCE_FS_IO_BUS16 0x40 /* bus 16 bit */ +#define TPCE_FS_IO_RANGE 0x80 /* range bit */ +#define TPCE_FS_IO_LEN 0xc0 /* block len size */ +#define TPCE_FS_IO_LEN_SHIFT 6 +#define TPCE_FS_IO_SIZE 0x30 /* block size size */ +#define TPCE_FS_IO_SIZE_SHIFT 4 +#define TPCE_FS_IO_NUM 0x0f /* # of blocks */ +#define TPCE_FS_IRQ 0x10 /* IRQ */ +#define TPCE_FS_IRQ_SHARE 0x80 /* int sharing */ +#define TPCE_FS_IRQ_PULSE 0x40 /* pulse request */ +#define TPCE_FS_IRQ_LEVEL 0x20 /* level-trig int */ +#define TPCE_FS_IRQ_MASK 0x10 /* irq mask bit */ +#define TPCE_FS_IRQ_IRQN 0x0f /* irqn mask */ +#define TPCE_FS_IRQ_VEND 0x08 /* vendor sig */ +#define TPCE_FS_IRQ_BERR 0x04 /* bus error */ +#define TPCE_FS_IRQ_IOCK 0x02 /* io check */ +#define TPCE_FS_IRQ_NMI 0x01 /* nmi */ +#define TPCE_FS_MEM 0x60 /* Mem Space */ +#define TPCE_FS_MEM_SHIFT 5 +#define TPCE_FS_MEM_HOST 0x80 +#define TPCE_FS_MEM_ADDR 0x60 +#define TPCE_FS_MEM_ADDR_SHIFT 5 +#define TPCE_FS_MEM_LEN 0x18 +#define TPCE_FS_MEM_LEN_SHIFT 3 +#define TPCE_FS_MEM_WINS 0x07 +#define TPCE_FS_MISC 0x80 /* Misc */ +#define CIS_MFG 0x20 /* Manufacturer's ID */ +#define CIS_FUNC 0x21 /* Function ID */ +#define CIS_FUNE 0x22 /* Function Extension */ +#define CIS_DRIVER 0x77 /* Driver ID */ +#define CIS_END 0xff /* Last Entry */ + +extern int pcmcia_configure __P((struct device *, void *, void *)); + +#endif /* __PCMCIA_H__ */ diff --git a/sys/dev/pcmcia/pcmcia_conf.c b/sys/dev/pcmcia/pcmcia_conf.c new file mode 100644 index 00000000000..ab0e5b0d741 --- /dev/null +++ b/sys/dev/pcmcia/pcmcia_conf.c @@ -0,0 +1,477 @@ +#include +#include +#include +#include +#include + +#include +#include +#include + +#ifdef CFG_DEBUG +static +void dump(addr, len) + u_char *addr; + int len; +{ + int i; + + for (i = 0; i < len; i++) { + printf("%02x ", addr[i]); + if (i != 0 && (i & 0xf) == 0) + printf("\n"); + } + if (i != 0 && (i & 0xf) == 0) + printf("\n"); +} +#endif + +int +pcmcia_get_cf(pc_link, data, dlen, idx, pc_cf) + struct pcmcia_link *pc_link; + u_char *data; + int dlen, idx; + struct pcmcia_conf *pc_cf; +{ + u_char code, len, *tbuf, *endp; + int done; + + endp = data + dlen; + + done = 0; + while (!done && data < endp) { + code = *data++; + if (code == CIS_NULL) { + continue; + } + len = *data++; + + tbuf = data; + data += len; + switch (code) { + case CIS_END: + done = 1; + break; + case CIS_CFG_INFO: + read_cfg_info(tbuf, len, pc_cf); + break; + case CIS_CFG_ENT: + if ((idx & CFGENTRYMASK) != CFGENTRYID || + pc_cf->cfgid == 0) + parse_cfent(tbuf, len, idx, pc_cf); + break; + default: + break; + } + } + return 0; +} + + +int +read_cfg_info(tbuf, len, pc_cf) + u_char *tbuf; + int len; + struct pcmcia_conf *pc_cf; +{ + int rasz, rmsz; + + rasz = (tbuf[0] & TPCC_RASZ) >> TPCC_RASZ_SHIFT; + rmsz = (tbuf[0] & TPCC_RMSZ) >> TPCC_RMSZ_SHIFT; + +#ifdef CFG_DEBUG + printf("read_cfg_info\n"); + dump(tbuf, len); +#endif + + pc_cf->cfg_off = 0; + switch (rasz) { + case 3: + pc_cf->cfg_off |= (tbuf[5] << 24); + case 2: + pc_cf->cfg_off |= (tbuf[4] << 16); + case 1: + pc_cf->cfg_off |= (tbuf[3] << 8); + case 0: + pc_cf->cfg_off |= tbuf[2]; + } + + tbuf += rasz + 3; + pc_cf->cfg_regmask = 0; + switch (rmsz & 3) { + case 3: + pc_cf->cfg_regmask |= (tbuf[3] << 24); + case 2: + pc_cf->cfg_regmask |= (tbuf[2] << 16); + case 1: + pc_cf->cfg_regmask |= (tbuf[1] << 8); + case 0: + pc_cf->cfg_regmask |= tbuf[0]; + } +} + +int +parse_cfent(tbuf, len, slotid, pc_cf) + u_char *tbuf; + int len; + int slotid; + struct pcmcia_conf *pc_cf; +{ + int i, idx, defp, iop, io_16, ios, ftrs, intface, k; + int host_addr_p, addr_size, len_size; + +#ifdef CFG_DEBUG + printf("parse_cfent\n"); + dump(tbuf, len); +#endif + + i = 0; + intface = (tbuf[i] & TPCE_INDX_INT); + idx = (tbuf[i] & TPCE_INDX_ENTRY); + defp = (tbuf[i] & TPCE_INDX_DEF); + + if ((idx == slotid) || (defp && slotid!=-2 && + (slotid & CFGENTRYMASK) == CFGENTRYMASK)) { + int j; + if (intface) { + i++; + pc_cf->iocard = (tbuf[i] & TPCE_IF_TYPE) == 1; + } + i++; + ftrs = tbuf[i++]; + for (j = 0; j < (ftrs & TPCE_FS_PWR); j++) { + int pwr_desc = tbuf[i++]; + /* for each struct, skip all parameter defns */ + for (k = 0; k < 8; pwr_desc >>= 1, k++) { + if (pwr_desc & 0x01) { + /* skip bytes until non-ext found */ + while (tbuf[i++] & 0x80) + continue; + } + } + } + /* TODO read timing info */ + if (ftrs & TPCE_FS_TD) { +#define BONE(a,b) (j & a) != 7 << b ? 1 : 0 + int j = tbuf[i++]; + i += ((j & TPCE_FS_TD_WAIT) != 3 ? 1 : 0); + i += BONE(TPCE_FS_TD_RDY,TPCE_FS_TD_RDY_SHIFT); + i += BONE(TPCE_FS_TD_RSV,TPCE_FS_TD_RSV_SHIFT); +#undef BONE + } + if (ftrs & TPCE_FS_IO) { + int io_addrs[16], io_lens[16]; + int io_16, io_block_len, io_block_size, io_lines; + int io_range; + + iop = 1; + io_lines = tbuf[i] & TPCE_FS_IO_LINES; + io_16 = tbuf[i] & TPCE_FS_IO_BUS16; + io_range = tbuf[i] &TPCE_FS_IO_RANGE; + i++; + if (io_range) { + int iptr, ilen, elen; + + io_block_len = (tbuf[i] & TPCE_FS_IO_LEN) >> + TPCE_FS_IO_LEN_SHIFT; + io_block_size = (tbuf[i] & TPCE_FS_IO_SIZE) >> + TPCE_FS_IO_SIZE_SHIFT; + ios = (tbuf[i] & TPCE_FS_IO_NUM) + 1; + i++; + if ((ftrs & TPCE_FS_IRQ) != 0) { + iptr=(ios * elen) + i; +#define IRQTYPE (TPCE_FS_IRQ_PULSE|TPCE_FS_IRQ_LEVEL) +#define IRQMASK TPCE_FS_IRQ_MASK + if ((tbuf[iptr] & IRQTYPE) == 0) + if ((tbuf[iptr-elen] & + IRQTYPE) != 0) + iptr -= elen; + if ((tbuf[iptr] & IRQMASK) != 0) + ilen = 2; + else + ilen=1; + } + else + ilen=0; + + if ((i + (ios * elen) + ilen) > len) { + printf( +"Warning: CIS range info doesn't fit in entry! Reducing # of ranges by 1\n"); + ios--; + } + + for (j = 0; j < ios; j++) { + io_addrs[j] = io_lens[j] = 0; + switch (io_block_size) { + case 3: + io_addrs[j] |= tbuf[i+3] << 24; + io_addrs[j] |= tbuf[i+2] << 16; + case 2: + io_addrs[j] |= tbuf[i+1] << 8; + case 1: + io_addrs[j] |= tbuf[i]; + break; + } + pc_cf->io[j].start = io_addrs[j]; + i += io_block_size + + (io_block_size == 3 ? 1 : 0); + switch (io_block_len) { + case 3: + io_lens[j] |= tbuf[i+3] << 24; + io_lens[j] |= tbuf[i+2] << 16; + case 2: + io_lens[j] |= tbuf[i+1] << 8; + case 1: + io_lens[j] |= tbuf[i]; + break; + } + io_lens[j]++; + if(io_lens[j] & 1) { + printf( +"Odd IO window length!! (Assuming incorrect CIS entry %d)\n", io_lens[j]); + io_lens[j]--; + } + + pc_cf->io[j].len = io_lens[j]; + pc_cf->io[j].flags = io_16 ? + PCMCIA_MAP_16 : PCMCIA_MAP_8; + i += io_block_len + + (io_block_len == 3 ? 1 : 0); + } + pc_cf->iowin = ios; + } + else { + pc_cf->iowin = 1; + pc_cf->io[0].len = 1 << io_lines; + pc_cf->io[0].start= 0 ; + pc_cf->io[0].flags = io_16 ? + PCMCIA_MAP_16 : PCMCIA_MAP_8; + } + } + if (ftrs & TPCE_FS_IRQ) { + int irq_mask, irqp, irq; + pc_cf->irq_level = (tbuf[i] & TPCE_FS_IRQ_LEVEL) != 0; + pc_cf->irq_pulse = (tbuf[i] & TPCE_FS_IRQ_PULSE) != 0; + pc_cf->irq_share = (tbuf[i] & TPCE_FS_IRQ_SHARE) != 0; + if (tbuf[i] & TPCE_FS_IRQ_MASK) { + pc_cf->irq_mask = (tbuf[i+2] << 8) + tbuf[i+1]; + if (pc_cf->irq_mask & (1 << 2)) + pc_cf->irq_mask |= 1 << 9; + i += 2; + } else { + pc_cf->irq_num = tbuf[i] & TPCE_FS_IRQ_IRQN; + pc_cf->irq_mask = -1; + } + + i++; + } + if (ftrs & TPCE_FS_MEM) { + int memp, mems, mem_lens[16], mem_caddrs[16], + mem_haddrs[16]; + memp = 1; + switch ((ftrs & TPCE_FS_MEM) >> TPCE_FS_MEM_SHIFT) { + case 1: + mems = 1; + mem_lens[0] = (tbuf[i+1] << 8) + tbuf[i]; + mem_lens[0] <<= 8; + + break; + case 2: + mems = 1; + mem_lens[0] = (tbuf[i+1] << 8) + tbuf[i]; + mem_caddrs[0] = mem_haddrs[0] = + (tbuf[i+3] << 8) + tbuf[i+2]; + + mem_lens[0] <<= 8; + mem_caddrs[0] <<= 8; + + break; + case 3: + host_addr_p = tbuf[i] & TPCE_FS_MEM_HOST; + addr_size = (tbuf[i] & TPCE_FS_MEM_ADDR) >> + TPCE_FS_MEM_ADDR_SHIFT; + len_size = (tbuf[i] & TPCE_FS_MEM_LEN) >> + TPCE_FS_MEM_LEN_SHIFT; + mems = (tbuf[i] & TPCE_FS_MEM_WINS) + 1; + i++; + for (j = 0; j < mems; j++) { + mem_lens[j] = 0; + mem_caddrs[j] = 0; + mem_haddrs[j] = 0; + switch (len_size) { + case 3: + mem_lens[j] |= + (tbuf[i+2] << 16); + case 2: + mem_lens[j] |= + (tbuf[i+1] << 8); + case 1: + mem_lens[j] |= tbuf[i]; + } + i += len_size; + switch (addr_size) { + case 3: + mem_caddrs[j] |= + (tbuf[i+2] << 16); + case 2: + mem_caddrs[j] |= + (tbuf[i+1] << 8); + case 1: + mem_caddrs[j] |= tbuf[i]; + } + i += addr_size; + if (host_addr_p) { + switch (addr_size) { + case 3: + mem_haddrs[j] |= + (tbuf[i+2] << 16); + case 2: + mem_haddrs[j] |= + (tbuf[i+1] << 8); + case 1: + mem_haddrs[j] |= + tbuf[i]; + } + i += addr_size; + } + mem_lens[j] <<= 8; + mem_caddrs[j] <<= 8; + mem_haddrs[j] <<= 8; + + } + } + for (j = 0; j < mems; j++) { + pc_cf->mem[j].len = mem_lens[j]; + pc_cf->mem[j].caddr = mem_caddrs[j]; + pc_cf->mem[j].start = mem_haddrs[j]; + pc_cf->mem[j].flags = 0; + } + pc_cf->memwin = mems; + } else + pc_cf->memwin = 0; + return; + } + + + if (slotid == -2 ) { + /* find matching slotid */ + struct pcmcia_conf tmp_cf; + /* get defaults */ + parse_cfent(tbuf, len, -1, &tmp_cf); + /* change to selected */ + parse_cfent(tbuf, len, idx, &tmp_cf); +#ifdef CFG_DEBUG + printf("slotid %d %d iowin %d %d memwin %d %d wins %x %x %d %d\n", + pc_cf->iocard , tmp_cf.iocard, + pc_cf->iowin , tmp_cf.iowin, + pc_cf->memwin , tmp_cf.memwin, + pc_cf->io[0].start , tmp_cf.io[0].start, + pc_cf->io[0].len , tmp_cf.io[0].len + ); +#endif + + if((pc_cf->iocard == tmp_cf.iocard) && /* same type */ + (pc_cf->iowin == tmp_cf.iowin) && + (pc_cf->memwin == tmp_cf.memwin)) { + int i; + for (i = 0; i < tmp_cf.iowin; i++) + if (pc_cf->io[i].len != tmp_cf.io[i].len || + pc_cf->io[i].start != tmp_cf.io[i].start) + return; + + for (i = 0; i < tmp_cf.memwin; i++) + if (pc_cf->mem[i].len!=tmp_cf.mem[i].len || + pc_cf->mem[i].start!=tmp_cf.mem[i].start) + return; + + /* *pc_cf = tmp_cf;/**/ + pc_cf->cfgid = idx; + } + return; + } +} + +void +pcmcia_getstr(buf, pptr, end) + char *buf; + u_char **pptr; + u_char *end; +{ + u_char *ptr = *pptr; + char *eb = buf + MAX_CIS_NAMELEN - 1; + + while (buf < eb && ptr < end) + switch (*ptr) { + case 0x00: + ptr++; + /*FALLTHROUGH*/ + case 0xff: + *pptr = ptr; + *buf = '\0'; + return; + + default: + *buf++ = *ptr++; + break; + } + printf("Warning: Maximum CIS string length exceeded\n"); + *buf = '\0'; + + /* Keep going until we find the end */ + while (ptr < end) + switch (*ptr) { + case 0x00: + ptr++; + /*FALLTHROUGH*/ + case 0xff: + *pptr = ptr; + return; + + default: + ptr++; + break; + } + + *pptr = ptr; +} + + +int +pcmcia_get_cisver1(pc_link, data, len, manu, model, add_inf1, add_inf2) + struct pcmcia_link *pc_link; + u_char *data; + int len; + char *manu, *model, *add_inf1, *add_inf2; +{ + u_char *p, *end; + + p = data; + end = data + len; + while ((*p != (u_char) 0xff) && (p < end)) { + int clen = *(p + 1); + int maj, min; + if (*p == CIS_VER1) { + u_char *pp = p + 2; + maj = *pp++; + min = *pp++; + if (maj != 4 || min != 1) { + printf("wrong version id %d.%d for card in slot %d\n", + maj, min, pc_link->slot); + return ENODEV; + } + pcmcia_getstr(manu, &pp, end); + pcmcia_getstr(model, &pp, end); + pcmcia_getstr(add_inf1, &pp, end); + pcmcia_getstr(add_inf2, &pp, end); + if (*pp != (u_char) 0xff) { + printf("WARNING: broken id for card in slot %d\n", pc_link->slot); + printf("manu %s model %s add_inf1 %s add_inf2 %s\n", manu, model, add_inf1, add_inf2); + return 0; + } + return 0; + } + p += clen + 2; + } + printf("%x %x\n", p, end); + return ENODEV; +} diff --git a/sys/dev/pcmcia/pcmcia_ioctl.h b/sys/dev/pcmcia/pcmcia_ioctl.h new file mode 100644 index 00000000000..535e12241b7 --- /dev/null +++ b/sys/dev/pcmcia/pcmcia_ioctl.h @@ -0,0 +1,59 @@ +/* + * Copyright (c) 1993, 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 dipclaimer. + * 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 Stefan Grefen. + * 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. + */ +struct pcmcia_info { + int slot; + u_char cis_data[CIS_MAXSIZE]; +}; + +struct pcmcia_status { + int slot; + int status; +}; + +struct pcmcia_regs { + int chip; + int chiptype; +#define PCMCIA_CHIP_UNKNOWN 0 +#define PCMCIA_PCIC 1 + u_char chip_data[CIS_MAXSIZE]; +}; + +#define PCMCIAIO_GET_STATUS _IOR('s', 128, struct pcmcia_status) +#define PCMCIAIO_GET_INFO _IOR('s', 129, struct pcmcia_info) +#define PCMCIAIO_SET_POWER _IOW('s', 139, int) +#define PCMCIASIO_POWER_5V 0x3 +#define PCMCIASIO_POWER_3V 0x5 +#define PCMCIASIO_POWER_AUTO 0x7 +#define PCMCIASIO_POWER_OFF 0x0 +#define PCMCIAIO_CONFIGURE _IOW('s', 140, struct pcmcia_conf) +#define PCMCIAIO_UNMAP _IOW('s', 141, int) +#define PCMCIAIO_UNCONFIGURE _IOW('s', 142, int) +#define PCMCIAIO_READ_COR _IOR('s', 143, struct pcmcia_info) +#define PCMCIAIO_READ_REGS _IOWR('s', 160, struct pcmcia_regs) + diff --git a/sys/dev/pcmcia/pcmciabus.h b/sys/dev/pcmcia/pcmciabus.h new file mode 100644 index 00000000000..4dceb987690 --- /dev/null +++ b/sys/dev/pcmcia/pcmciabus.h @@ -0,0 +1,262 @@ +/* + * Copyright (c) 1993, 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 dipclaimer. + * 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: pcmciabus.h,v 1.1 1996/01/15 00:05:13 hvozda Exp $ + */ + /* derived from scsicconf.[ch] writenn by Julian Elischer et al */ + +#ifndef _PCMCIA_PCMCIABUS_H_ +#define _PCMCIA_PCMCIABUS_H_ 1 + +#include +#include +#include + +/* + * The following documentation tries to describe the relationship between the + * various structures defined in this file: + * + * each adapter type has a pcmcia_adapter struct. This describes the adapter and + * identifies routines that can be called to use the adapter. + * each device type has a pcmcia_device struct. This describes the device and + * identifies routines that can be called to use the device. + * each existing device position (pcmciabus + port) + * can be described by a pcmcia_link struct. + * Only port positions that actually have devices, have a pcmcia_link + * structure assigned. so in effect each device has pcmcia_link struct. + * The pcmcia_link structure contains information identifying both the + * device driver and the adapter driver for that port on that pcmcia bus, + * and can be said to 'link' the two. + * each individual pcmcia bus has an array that points to all the pcmcia_link + * structs associated with that pcmcia bus. Slots with no device have + * a NULL pointer. + * each individual device also knows the address of it's own pcmcia_link + * structure. + * + * ------------- + * + * The key to all this is the pcmcia_link structure which associates all the + * other structures with each other in the correct configuration. The + * pcmcia_link is the connecting information that allows each part of the + * pcmcia system to find the associated other parts. + */ + + +struct pcmcia_link; +struct pcmcia_conf; +struct pcmcia_adapter; + +/* + * These entrypoints are called by the high-end drivers to get services from + * whatever low-end drivers they are attached to each adapter type has one of + * these statically allocated. + */ +struct pcmcia_funcs { +/* 4 map io range */ + int (*pcmcia_map_io) __P((struct pcmcia_link *, u_int, u_int, int)); +/* 8 map memory window */ + int (*pcmcia_map_mem) __P((struct pcmcia_link *, caddr_t, + u_int, u_int, int)); +/*12 map interrupt */ + int (*pcmcia_map_intr) __P((struct pcmcia_link *, int, int)); +/*26 power on/off etc */ + int (*pcmcia_service) __P((struct pcmcia_link *, int, void *, int)); +}; + +struct pcmciabus_link { /* Link back to the bus we are on */ + /* Bus specific configure */ + int (*bus_config) __P((struct pcmcia_link *, struct device *, + struct pcmcia_conf *, struct cfdata *)); + /* Bus specific unconfigure */ + int (*bus_unconfig) __P((struct pcmcia_link *)); + /* Bus specific probe */ + int (*bus_probe) __P((struct device *, void *, + void *, struct pcmcia_link *)); + /* Bus specific search */ + int (*bus_search) __P((struct device *, void *, cfprint_t)); + /* initialize scratch */ + int (*bus_init) __P((struct device *, struct cfdata *, + void *, struct pcmcia_adapter *, int)); +}; +struct pcmcia_adapter { + struct pcmcia_funcs *chip_link; + struct pcmciabus_link *bus_link; + void * adapter_softc; + caddr_t scratch_mem; /* pointer to scratch window */ + int scratch_memsiz; /* size of scratch window */ + int scratch_inuse; /* window in use */ +}; + +#define PCMCIA_MAP_ATTR 0x0100 /* for memory only */ +#define PCMCIA_MAP_8 0x0100 /* for io only */ +#define PCMCIA_MAP_16 0x0200 +#define PCMCIA_UNMAP 0x0400 +#define PCMCIA_PHYSICAL_ADDR 0x0800 +#define PCMCIA_UNMAP_ALL 0x0c00 +#define PCMCIA_FIXED_WIN 0x1000 +#define PCMCIA_LAST_WIN 0x0010 +#define PCMCIA_FIRST_WIN 0x0020 +#define PCMCIA_ANY_WIN 0x0030 + +#define PCMCIA_OP_RESET 0x0000 +#define PCMCIA_OP_POWER 0x0001 +#define PCMCIA_OP_STATUS 0x0002 +#define PCMCIA_OP_GETREGS 0x0003 +#define PCMCIA_OP_WAIT 0x0004 + +#define PCMCIA_POWER_ON 0x0001 +#define PCMCIA_POWER_5V 0x0002 +#define PCMCIA_POWER_3V 0x0004 +#define PCMCIA_POWER_AUTO 0x0008 + +#define PCMCIA_CARD_PRESENT 0x0001 +#define PCMCIA_BATTERY 0x0002 +#define PCMCIA_WRITE_PROT 0x0004 +#define PCMCIA_READY 0x0008 +#define PCMCIA_POWER 0x0010 +#define PCMCIA_POWER_PP 0x0020 +#define PCMCIA_CARD_IS_MAPPED 0x1000 +#define PCMCIA_CARD_INUSE 0x2000 + + +/* + * This structure describes the connection between an adapter driver and + * a device driver, and is used by each to call services provided by + * the other, and to allow generic pcmcia glue code to call these services + * as well. + */ +struct pcmcia_link { + char pcmciabus; /* the Nth pcmciabus */ + char slot; /* slot of this dev */ + char flags; +#define CARD_IS_MAPPED 0x01 +#define PCMCIA_ATTACH 0x02 +#define PCMCIA_REATTACH 0x04 +#define PCMCIA_SLOT_INUSE 0x08 +#define PCMCIA_ATTACH_TYPE (PCMCIA_ATTACH|PCMCIA_REATTACH) +#define PCMCIA_SLOT_EVENT 0x80 +#define PCMCIA_SLOT_OPEN 0x40 + char opennings; + + char iowin; + char memwin; + char intr; + char dummy; + struct pcmcia_adapter *adapter; /* adapter entry points etc. */ + struct pcmciadevs *device; /* device entry points etc. */ + void *devp; /* pointer to configured device */ + void *fordriver; /* for private use by the driver */ + void *shuthook; /* shutdown hook handle */ + struct selinfo pcmcialink_sel; /* for select users */ +}; + +/* + * One of these is allocated and filled in for each pcmcia bus. + * it holds pointers to allow the pcmcia bus to get to the driver + * it also has a template entry which is the prototype struct + * supplied by the adapter driver, this is used to initialise + * the others, before they have the rest of the fields filled in + */ +struct pcmciabus_softc { + struct device sc_dev; + struct pcmcia_link *sc_link[8]; +}; + +struct pcmcia_conf { + int irq_share:1; + int irq_level:1; /* 1 level */ + int irq_pulse:1; /* 1 pulse */ + int irq_vend:1; + int irq_iock:1; + int irq_berr:1; + int irq_nmi:1; + int iocard:1; + u_char iowin; + u_char memwin; + u_char irq_num; + u_char cfgtype; +#define CFGENTRYID 0x20 +#define CFGENTRYMASK (CFGENTRYID|(CFGENTRYID-1)) +#define DOSRESET 0x40 + int cfg_regmask; + int irq_mask; + int cfg_off; + struct iowin { + int start; + int len; + int flags; + }io[4]; + struct memwin { + int start; + int caddr; + int len; + int flags; + }mem[4]; + char driver_name[8][4]; /* up to four different functions on a card */ + int unitid; + int cfgid; +}; + +struct pcmcia_device { + char *name; + int (*pcmcia_config) __P((struct pcmcia_link *, struct device *, + struct pcmcia_conf *, struct cfdata *)); + int (*pcmcia_probe) __P((struct device *, void *, + void *, struct pcmcia_link *)); + int (*pcmcia_insert) __P((struct pcmcia_link *, struct device *, + struct cfdata *)); + int (*pcmcia_remove) __P((struct pcmcia_link *, struct device *)); +}; + +struct pcmciadevs { + char *devname; + int flags; /* 1 show my comparisons during boot(debug) */ +#define PC_SHOWME 0x01 + char *manufacturer; + char *model; + char *add_inf1; + char *add_inf2; + void *param; + struct pcmcia_device *dev; +}; + +#ifdef _KERNEL +extern int pcmcia_add_device __P((struct pcmciadevs *)); +extern int pcmcia_get_cf __P((struct pcmcia_link *, u_char *, int, int, + struct pcmcia_conf *)); +extern int pcmcia_targmatch __P((struct device *, struct cfdata *, void *)); +#endif + +/* in pcmcia_conf.c, available for user space too: */ +extern int pcmcia_get_cisver1 __P((struct pcmcia_link *, u_char *, int, + char *, char *, char *, char *)); +int parse_cfent __P((u_char *, int, int, struct pcmcia_conf *)); +int read_cfg_info __P((u_char *, int, struct pcmcia_conf *)); +void pcmcia_getstr __P((char *buf, u_char **, u_char *)); + +#endif /* _PCMCIA_PCMCIABUS_H_ */