diff --git a/sys/dev/pccard/card_if.m b/sys/dev/pccard/card_if.m index 256ec7e3ee64..51b3bcba93a0 100644 --- a/sys/dev/pccard/card_if.m +++ b/sys/dev/pccard/card_if.m @@ -1,56 +1,52 @@ # # Copyright (c) 1999 M. Warner Losh. # 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. # # THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND # ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE # ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE # FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL # DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS # OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) # HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT # LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY # OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF # SUCH DAMAGE. # # $FreeBSD$ # INTERFACE card; # # Companion interface for pccard. We need to set attributes for memory # and i/o port mappings (as well as other types of attributes) that have # a well defined meaning inside the pccard/cardbus system. The bus # methods are inadequate for this because this must be done at the time the # resources are set for the device, which predates their activation. Also, # the driver activating the resources doesn't necessarily know or need to know # these attributes. # -# XXX A nagging doubt in the back of my mind suggests that these sorts of -# XXX things might be able to be done with ivars. This nagging doubt doesn't -# XXX offer a good way to actually do this, but remains nonetheless. -# METHOD int set_resource_attribute { device_t dev; device_t child; - int *rid; + int rid; u_int flags; }; METHOD int get_resource_attribute { device_t dev; device_t child; int rid; u_int *flags; }; diff --git a/sys/dev/pccard/pccard.c b/sys/dev/pccard/pccard.c index fd351260bafd..ee95cd0b102e 100644 --- a/sys/dev/pccard/pccard.c +++ b/sys/dev/pccard/pccard.c @@ -1,764 +1,765 @@ /* $NetBSD: pcmcia.c,v 1.13 1998/12/24 04:51:59 marc Exp $ */ /* $FreeBSD$ */ /* * Copyright (c) 1997 Marc Horowitz. 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 Marc Horowitz. * 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. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "power_if.h" #define PCCARDDEBUG #ifdef PCCARDDEBUG int pccard_debug = 1; #define DPRINTF(arg) if (pccard_debug) printf arg #define DEVPRINTF(arg) if (pccard_debug) device_printf arg int pccardintr_debug = 0; /* this is done this way to avoid doing lots of conditionals at interrupt level. */ #define PCCARD_CARD_INTR (pccardintr_debug?pccard_card_intrdebug:pccard_card_intr) #else #define DPRINTF(arg) #define DEVPRINTF(arg) #define PCCARD_CARD_INTR (pccard_card_intr) #endif #ifdef PCCARDVERBOSE int pccard_verbose = 1; #else int pccard_verbose = 0; #endif int pccard_print(void *, const char *); int pccard_card_intr(void *); #ifdef PCCARDDEBUG int pccard_card_intrdebug(void *); #endif int pccard_ccr_read(pf, ccr) struct pccard_function *pf; int ccr; { return (bus_space_read_1(pf->pf_ccrt, pf->pf_ccrh, pf->pf_ccr_offset + ccr)); } void pccard_ccr_write(pf, ccr, val) struct pccard_function *pf; int ccr; int val; { if ((pf->ccr_mask) & (1 << (ccr / 2))) { bus_space_write_1(pf->pf_ccrt, pf->pf_ccrh, pf->pf_ccr_offset + ccr, val); } } int pccard_card_attach(device_t dev) { struct pccard_softc *sc = (struct pccard_softc *) device_get_softc(dev); struct pccard_function *pf; int attached; DEVPRINTF((dev, "pccard_card_attach\n")); /* * this is here so that when socket_enable calls gettype, trt happens */ STAILQ_INIT(&sc->card.pf_head); DEVPRINTF((dev, "chip_socket_enable\n")); POWER_ENABLE_SOCKET(device_get_parent(dev), dev); DEVPRINTF((dev, "read_cis\n")); pccard_read_cis(sc); DEVPRINTF((dev, "chip_socket_disable\n")); POWER_DISABLE_SOCKET(device_get_parent(dev), dev); DEVPRINTF((dev, "check_cis_quirks\n")); pccard_check_cis_quirks(dev); /* * bail now if the card has no functions, or if there was an error in * the cis. */ if (sc->card.error) return (1); if (STAILQ_EMPTY(&sc->card.pf_head)) return (1); if (pccard_verbose) pccard_print_cis(dev); attached = 0; DEVPRINTF((dev, "functions scanning\n")); STAILQ_FOREACH(pf, &sc->card.pf_head, pf_list) { if (STAILQ_EMPTY(&pf->cfe_head)) continue; #ifdef DIAGNOSTIC if (pf->child != NULL) { device_printf(sc->dev, "%s still attached to function %d!\n", device_get_name(pf->child), pf->number); panic("pccard_card_attach"); } #endif pf->sc = sc; pf->child = NULL; pf->cfe = NULL; pf->ih_fct = NULL; pf->ih_arg = NULL; } STAILQ_FOREACH(pf, &sc->card.pf_head, pf_list) { if (STAILQ_EMPTY(&pf->cfe_head)) continue; #if XXX if (attach_child()) { attached++; DEVPRINTF((sc->dev, "function %d CCR at %d " "offset %lx: %x %x %x %x, %x %x %x %x, %x\n", pf->number, pf->pf_ccr_window, pf->pf_ccr_offset, pccard_ccr_read(pf, 0x00), pccard_ccr_read(pf, 0x02), pccard_ccr_read(pf, 0x04), pccard_ccr_read(pf, 0x06), pccard_ccr_read(pf, 0x0A), pccard_ccr_read(pf, 0x0C), pccard_ccr_read(pf, 0x0E), pccard_ccr_read(pf, 0x10), pccard_ccr_read(pf, 0x12))); } #endif } return (attached ? 0 : 1); } void pccard_card_detach(device_t dev, int flags) { struct pccard_softc *sc = (struct pccard_softc *) device_get_softc(dev); struct pccard_function *pf; #if XXX int error; #endif /* * We are running on either the PCCARD socket's event thread * or in user context detaching a device by user request. */ STAILQ_FOREACH(pf, &sc->card.pf_head, pf_list) { if (STAILQ_FIRST(&pf->cfe_head) == NULL) continue; if (pf->child == NULL) continue; DEVPRINTF((sc->dev, "detaching %s (function %d)\n", device_get_name(pf->child), pf->number)); #if XXX if ((error = config_detach(pf->child, flags)) != 0) { device_printf(sc->dev, "error %d detaching %s (function %d)\n", error, device_get_name(pf->child), pf->number); } else pf->child = NULL; #endif } } void pccard_card_deactivate(device_t dev) { struct pccard_softc *sc = (struct pccard_softc *) device_get_softc(dev); struct pccard_function *pf; /* * We're in the chip's card removal interrupt handler. * Deactivate the child driver. The PCCARD socket's * event thread will run later to finish the detach. */ STAILQ_FOREACH(pf, &sc->card.pf_head, pf_list) { if (STAILQ_FIRST(&pf->cfe_head) == NULL) continue; if (pf->child == NULL) continue; DEVPRINTF((sc->dev, "deactivating %s (function %d)\n", device_get_name(pf->child), pf->number)); #if XXX config_deactivate(pf->child); #endif } } int pccard_card_gettype(device_t dev) { struct pccard_softc *sc = (struct pccard_softc *) device_get_softc(dev); struct pccard_function *pf; /* * set the iftype to memory if this card has no functions (not yet * probed), or only one function, and that is not initialized yet or * that is memory. */ pf = STAILQ_FIRST(&sc->card.pf_head); if (pf == NULL || (STAILQ_NEXT(pf, pf_list) == NULL && (pf->cfe == NULL || pf->cfe->iftype == PCCARD_IFTYPE_MEMORY))) return (PCCARD_IFTYPE_MEMORY); else return (PCCARD_IFTYPE_IO); } /* * Initialize a PCCARD function. May be called as long as the function is * disabled. */ void pccard_function_init(struct pccard_function *pf, struct pccard_config_entry *cfe) { if (pf->pf_flags & PFF_ENABLED) panic("pccard_function_init: function is enabled"); /* Remember which configuration entry we are using. */ pf->cfe = cfe; } /* Enable a PCCARD function */ int pccard_function_enable(struct pccard_function *pf) { struct pccard_function *tmp; int reg; device_t dev = pf->sc->dev; if (pf->cfe == NULL) panic("pccard_function_enable: function not initialized"); /* * Increase the reference count on the socket, enabling power, if * necessary. */ if (pf->sc->sc_enabled_count++ == 0) POWER_ENABLE_SOCKET(device_get_parent(dev), dev); DEVPRINTF((dev, "++enabled_count = %d\n", pf->sc->sc_enabled_count)); if (pf->pf_flags & PFF_ENABLED) { /* * Don't do anything if we're already enabled. */ return (0); } /* * it's possible for different functions' CCRs to be in the same * underlying page. Check for that. */ STAILQ_FOREACH(tmp, &pf->sc->card.pf_head, pf_list) { if ((tmp->pf_flags & PFF_ENABLED) && (pf->ccr_base >= (tmp->ccr_base - tmp->pf_ccr_offset)) && ((pf->ccr_base + PCCARD_CCR_SIZE) <= (tmp->ccr_base - tmp->pf_ccr_offset + tmp->pf_ccr_realsize))) { pf->pf_ccrt = tmp->pf_ccrt; pf->pf_ccrh = tmp->pf_ccrh; pf->pf_ccr_realsize = tmp->pf_ccr_realsize; /* * pf->pf_ccr_offset = (tmp->pf_ccr_offset - * tmp->ccr_base) + pf->ccr_base; */ pf->pf_ccr_offset = (tmp->pf_ccr_offset + pf->ccr_base) - tmp->ccr_base; pf->pf_ccr_window = tmp->pf_ccr_window; break; } } if (tmp == NULL) { pf->ccr_rid = 0; pf->ccr_res = bus_alloc_resource(dev, SYS_RES_MEMORY, &pf->ccr_rid, pf->ccr_base, pf->ccr_base + PCCARD_CCR_SIZE, - PCCARD_CCR_SIZE, RF_ACTIVE | RF_PCCARD_ATTR); + PCCARD_CCR_SIZE, RF_ACTIVE); + /* XXX SET MEM_ATTR */ if (!pf->ccr_res) goto bad; pf->pf_ccrt = rman_get_bustag(pf->ccr_res); pf->pf_ccrh = rman_get_bushandle(pf->ccr_res); pf->pf_ccr_offset = rman_get_start(pf->ccr_res); pf->pf_ccr_realsize = 1; } reg = (pf->cfe->number & PCCARD_CCR_OPTION_CFINDEX); reg |= PCCARD_CCR_OPTION_LEVIREQ; if (pccard_mfc(pf->sc)) { reg |= (PCCARD_CCR_OPTION_FUNC_ENABLE | PCCARD_CCR_OPTION_ADDR_DECODE); if (pf->ih_fct) reg |= PCCARD_CCR_OPTION_IREQ_ENABLE; } pccard_ccr_write(pf, PCCARD_CCR_OPTION, reg); reg = 0; if ((pf->cfe->flags & PCCARD_CFE_IO16) == 0) reg |= PCCARD_CCR_STATUS_IOIS8; if (pf->cfe->flags & PCCARD_CFE_AUDIO) reg |= PCCARD_CCR_STATUS_AUDIO; pccard_ccr_write(pf, PCCARD_CCR_STATUS, reg); pccard_ccr_write(pf, PCCARD_CCR_SOCKETCOPY, 0); if (pccard_mfc(pf->sc)) { long tmp, iosize; tmp = pf->pf_mfc_iomax - pf->pf_mfc_iobase; /* round up to nearest (2^n)-1 */ for (iosize = 1; iosize < tmp; iosize <<= 1) ; iosize--; pccard_ccr_write(pf, PCCARD_CCR_IOBASE0, pf->pf_mfc_iobase & 0xff); pccard_ccr_write(pf, PCCARD_CCR_IOBASE1, (pf->pf_mfc_iobase >> 8) & 0xff); pccard_ccr_write(pf, PCCARD_CCR_IOBASE2, 0); pccard_ccr_write(pf, PCCARD_CCR_IOBASE3, 0); pccard_ccr_write(pf, PCCARD_CCR_IOSIZE, iosize); } #ifdef PCCARDDEBUG if (pccard_debug) { STAILQ_FOREACH(tmp, &pf->sc->card.pf_head, pf_list) { device_printf(tmp->sc->dev, "function %d CCR at %d offset %x: " "%x %x %x %x, %x %x %x %x, %x\n", tmp->number, tmp->pf_ccr_window, tmp->pf_ccr_offset, pccard_ccr_read(tmp, 0x00), pccard_ccr_read(tmp, 0x02), pccard_ccr_read(tmp, 0x04), pccard_ccr_read(tmp, 0x06), pccard_ccr_read(tmp, 0x0A), pccard_ccr_read(tmp, 0x0C), pccard_ccr_read(tmp, 0x0E), pccard_ccr_read(tmp, 0x10), pccard_ccr_read(tmp, 0x12)); } } #endif pf->pf_flags |= PFF_ENABLED; return (0); bad: /* * Decrement the reference count, and power down the socket, if * necessary. */ if (--pf->sc->sc_enabled_count == 0) POWER_DISABLE_SOCKET(device_get_parent(dev), dev); DEVPRINTF((dev, "--enabled_count = %d\n", pf->sc->sc_enabled_count)); return (1); } /* Disable PCCARD function. */ void pccard_function_disable(struct pccard_function *pf) { struct pccard_function *tmp; device_t dev = pf->sc->dev; if (pf->cfe == NULL) panic("pccard_function_enable: function not initialized"); if ((pf->pf_flags & PFF_ENABLED) == 0) { /* * Don't do anything if we're already disabled. */ return; } /* * it's possible for different functions' CCRs to be in the same * underlying page. Check for that. Note we mark us as disabled * first to avoid matching ourself. */ pf->pf_flags &= ~PFF_ENABLED; STAILQ_FOREACH(tmp, &pf->sc->card.pf_head, pf_list) { if ((tmp->pf_flags & PFF_ENABLED) && (pf->ccr_base >= (tmp->ccr_base - tmp->pf_ccr_offset)) && ((pf->ccr_base + PCCARD_CCR_SIZE) <= (tmp->ccr_base - tmp->pf_ccr_offset + tmp->pf_ccr_realsize))) break; } /* Not used by anyone else; unmap the CCR. */ if (tmp == NULL) { bus_release_resource(dev, SYS_RES_MEMORY, pf->ccr_rid, pf->ccr_res); pf->ccr_res = NULL; } /* * Decrement the reference count, and power down the socket, if * necessary. */ if (--pf->sc->sc_enabled_count == 0) POWER_DISABLE_SOCKET(device_get_parent(dev), dev); DEVPRINTF((dev, "--enabled_count = %d\n", pf->sc->sc_enabled_count)); } #if 0 /* XXX These functions are needed, but not like this XXX */ int pccard_io_map(struct pccard_function *pf, int width, bus_addr_t offset, bus_size_t size, struct pccard_io_handle *pcihp, int *windowp) { int reg; if (pccard_chip_io_map(pf->sc->pct, pf->sc->pch, width, offset, size, pcihp, windowp)) return (1); /* * XXX in the multifunction multi-iospace-per-function case, this * needs to cooperate with io_alloc to make sure that the spaces * don't overlap, and that the ccr's are set correctly */ if (pccard_mfc(pf->sc)) { long tmp, iosize; if (pf->pf_mfc_iomax == 0) { pf->pf_mfc_iobase = pcihp->addr + offset; pf->pf_mfc_iomax = pf->pf_mfc_iobase + size; } else { /* this makes the assumption that nothing overlaps */ if (pf->pf_mfc_iobase > pcihp->addr + offset) pf->pf_mfc_iobase = pcihp->addr + offset; if (pf->pf_mfc_iomax < pcihp->addr + offset + size) pf->pf_mfc_iomax = pcihp->addr + offset + size; } tmp = pf->pf_mfc_iomax - pf->pf_mfc_iobase; /* round up to nearest (2^n)-1 */ for (iosize = 1; iosize >= tmp; iosize <<= 1) ; iosize--; pccard_ccr_write(pf, PCCARD_CCR_IOBASE0, pf->pf_mfc_iobase & 0xff); pccard_ccr_write(pf, PCCARD_CCR_IOBASE1, (pf->pf_mfc_iobase >> 8) & 0xff); pccard_ccr_write(pf, PCCARD_CCR_IOBASE2, 0); pccard_ccr_write(pf, PCCARD_CCR_IOBASE3, 0); pccard_ccr_write(pf, PCCARD_CCR_IOSIZE, iosize); reg = pccard_ccr_read(pf, PCCARD_CCR_OPTION); reg |= PCCARD_CCR_OPTION_ADDR_DECODE; pccard_ccr_write(pf, PCCARD_CCR_OPTION, reg); } return (0); } void pccard_io_unmap(struct pccard_function *pf, int window) { pccard_chip_io_unmap(pf->sc->pct, pf->sc->pch, window); /* XXX Anything for multi-function cards? */ } #endif /* I don't think FreeBSD needs the next two functions at all */ /* XXX */ int pccard_card_intr(void *arg) { struct pccard_softc *sc = arg; struct pccard_function *pf; int reg, ret, ret2; ret = 0; STAILQ_FOREACH(pf, &sc->card.pf_head, pf_list) { if (pf->ih_fct != NULL && (pf->ccr_mask & (1 << (PCCARD_CCR_STATUS / 2)))) { reg = pccard_ccr_read(pf, PCCARD_CCR_STATUS); if (reg & PCCARD_CCR_STATUS_INTR) { ret2 = (*pf->ih_fct)(pf->ih_arg); if (ret2 != 0 && ret == 0) ret = ret2; reg = pccard_ccr_read(pf, PCCARD_CCR_STATUS); pccard_ccr_write(pf, PCCARD_CCR_STATUS, reg & ~PCCARD_CCR_STATUS_INTR); } } } return (ret); } #ifdef PCCARDDEBUG int pccard_card_intrdebug(arg) void *arg; { struct pccard_softc *sc = arg; struct pccard_function *pf; int reg, ret, ret2; ret = 0; STAILQ_FOREACH(pf, &sc->card.pf_head, pf_list) { device_printf(sc->dev, "intr flags=%x fct=%d cor=%02x csr=%02x pin=%02x", pf->pf_flags, pf->number, pccard_ccr_read(pf, PCCARD_CCR_OPTION), pccard_ccr_read(pf, PCCARD_CCR_STATUS), pccard_ccr_read(pf, PCCARD_CCR_PIN)); if (pf->ih_fct != NULL && (pf->ccr_mask & (1 << (PCCARD_CCR_STATUS / 2)))) { reg = pccard_ccr_read(pf, PCCARD_CCR_STATUS); if (reg & PCCARD_CCR_STATUS_INTR) { ret2 = (*pf->ih_fct)(pf->ih_arg); if (ret2 != 0 && ret == 0) ret = ret2; reg = pccard_ccr_read(pf, PCCARD_CCR_STATUS); printf("; csr %02x->%02x", reg, reg & ~PCCARD_CCR_STATUS_INTR); pccard_ccr_write(pf, PCCARD_CCR_STATUS, reg & ~PCCARD_CCR_STATUS_INTR); } } printf("\n"); } return (ret); } #endif #define PCCARD_NPORT 2 #define PCCARD_NMEM 5 #define PCCARD_NIRQ 1 #define PCCARD_NDRQ 0 static int pccard_add_children(device_t dev, int busno) { return 0; } static int pccard_probe(device_t dev) { device_set_desc(dev, "PC Card bus -- newconfig version"); return pccard_add_children(dev, device_get_unit(dev)); } static void pccard_print_resources(struct resource_list *rl, const char *name, int type, int count, const char *format) { struct resource_list_entry *rle; int printed; int i; printed = 0; for (i = 0; i < count; i++) { rle = resource_list_find(rl, type, i); if (rle) { if (printed == 0) printf(" %s ", name); else if (printed > 0) printf(","); printed++; printf(format, rle->start); if (rle->count > 1) { printf("-"); printf(format, rle->start + rle->count - 1); } } else if (i > 3) { /* check the first few regardless */ break; } } } static int pccard_print_child(device_t dev, device_t child) { struct pccard_ivar *devi = (struct pccard_ivar *) device_get_ivars(child); struct resource_list *rl = &devi->resources; int retval = 0; retval += bus_print_child_header(dev, child); retval += printf(" at"); if (devi) { pccard_print_resources(rl, "port", SYS_RES_IOPORT, PCCARD_NPORT, "%#lx"); pccard_print_resources(rl, "iomem", SYS_RES_MEMORY, PCCARD_NMEM, "%#lx"); pccard_print_resources(rl, "irq", SYS_RES_IRQ, PCCARD_NIRQ, "%ld"); pccard_print_resources(rl, "drq", SYS_RES_DRQ, PCCARD_NDRQ, "%ld"); retval += printf(" slot %d", devi->slotnum); } retval += bus_print_child_footer(dev, child); return (retval); } static int pccard_set_resource(device_t dev, device_t child, int type, int rid, u_long start, u_long count) { struct pccard_ivar *devi = (struct pccard_ivar *) device_get_ivars(child); struct resource_list *rl = &devi->resources; if (type != SYS_RES_IOPORT && type != SYS_RES_MEMORY && type != SYS_RES_IRQ && type != SYS_RES_DRQ) return EINVAL; if (rid < 0) return EINVAL; if (type == SYS_RES_IOPORT && rid >= PCCARD_NPORT) return EINVAL; if (type == SYS_RES_MEMORY && rid >= PCCARD_NMEM) return EINVAL; if (type == SYS_RES_IRQ && rid >= PCCARD_NIRQ) return EINVAL; if (type == SYS_RES_DRQ && rid >= PCCARD_NDRQ) return EINVAL; resource_list_add(rl, type, rid, start, start + count - 1, count); return 0; } static int pccard_get_resource(device_t dev, device_t child, int type, int rid, u_long *startp, u_long *countp) { struct pccard_ivar *devi = (struct pccard_ivar *) device_get_ivars(child); struct resource_list *rl = &devi->resources; struct resource_list_entry *rle; rle = resource_list_find(rl, type, rid); if (!rle) return ENOENT; if (startp) *startp = rle->start; if (countp) *countp = rle->count; return 0; } static void pccard_delete_resource(device_t dev, device_t child, int type, int rid) { struct pccard_ivar *devi = (struct pccard_ivar *) device_get_ivars(child); struct resource_list *rl = &devi->resources; resource_list_delete(rl, type, rid); } static device_method_t pccard_methods[] = { /* Device interface */ DEVMETHOD(device_probe, pccard_probe), DEVMETHOD(device_attach, bus_generic_attach), DEVMETHOD(device_shutdown, bus_generic_shutdown), DEVMETHOD(device_suspend, bus_generic_suspend), DEVMETHOD(device_resume, bus_generic_resume), /* Bus interface */ DEVMETHOD(bus_print_child, pccard_print_child), DEVMETHOD(bus_driver_added, bus_generic_driver_added), DEVMETHOD(bus_alloc_resource, bus_generic_alloc_resource), DEVMETHOD(bus_release_resource, bus_generic_release_resource), DEVMETHOD(bus_activate_resource, bus_generic_activate_resource), DEVMETHOD(bus_deactivate_resource, bus_generic_deactivate_resource), DEVMETHOD(bus_setup_intr, bus_generic_setup_intr), DEVMETHOD(bus_teardown_intr, bus_generic_teardown_intr), DEVMETHOD(bus_set_resource, pccard_set_resource), DEVMETHOD(bus_get_resource, pccard_get_resource), DEVMETHOD(bus_delete_resource, pccard_delete_resource), { 0, 0 } }; static driver_t pccard_driver = { "pccard", pccard_methods, 1, /* no softc */ }; devclass_t pccard_devclass; DRIVER_MODULE(pccard, pcic, pccard_driver, pccard_devclass, 0, 0); DRIVER_MODULE(pccard, pc98pcic, pccard_driver, pccard_devclass, 0, 0); DRIVER_MODULE(pccard, pccbb, pccard_driver, pccard_devclass, 0, 0); DRIVER_MODULE(pccard, tcic, pccard_driver, pccard_devclass, 0, 0); diff --git a/sys/dev/pccard/pccard_cis.c b/sys/dev/pccard/pccard_cis.c index 5d61269ee448..779763d16497 100644 --- a/sys/dev/pccard/pccard_cis.c +++ b/sys/dev/pccard/pccard_cis.c @@ -1,1205 +1,1205 @@ /* $NetBSD: pcmcia_cis.c,v 1.10 1998/12/29 09:03:15 marc Exp $ */ /* $FreeBSD$ */ /* * Copyright (c) 1997 Marc Horowitz. 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 Marc Horowitz. * 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. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef PCCARDCISDEBUG int pccardcis_debug = 0; #define DPRINTF(arg) if (pccardcis_debug) printf arg #define DEVPRINTF(arg) if (pccardcis_debug) device_printf arg #else #define DPRINTF(arg) #define DEVPRINTF(arg) #endif #define PCCARD_CIS_SIZE 1024 struct cis_state { int count; int gotmfc; struct pccard_config_entry temp_cfe; struct pccard_config_entry *default_cfe; struct pccard_card *card; struct pccard_function *pf; }; int pccard_parse_cis_tuple(struct pccard_tuple *, void *); void pccard_read_cis(struct pccard_softc *sc) { struct cis_state state; state.count = 0; state.gotmfc = 0; state.card = &sc->card; state.card->error = 0; state.card->cis1_major = -1; state.card->cis1_minor = -1; state.card->cis1_info[0] = NULL; state.card->cis1_info[1] = NULL; state.card->cis1_info[2] = NULL; state.card->cis1_info[3] = NULL; state.card->manufacturer = PCCARD_VENDOR_INVALID; state.card->product = PCCARD_PRODUCT_INVALID; STAILQ_INIT(&state.card->pf_head); state.pf = NULL; if (pccard_scan_cis((struct device *)sc, pccard_parse_cis_tuple, &state) == -1) state.card->error++; } int pccard_scan_cis(device_t dev, int (*fct)(struct pccard_tuple *, void *), void *arg) { struct resource *res; int rid; struct pccard_tuple tuple; int longlink_present; int longlink_common; u_long longlink_addr; int mfc_count; int mfc_index; struct { int common; u_long addr; } mfc[256 / 5]; int ret; ret = 0; /* allocate some memory */ rid = 0; res = bus_alloc_resource(dev, SYS_RES_MEMORY, &rid, 0, ~0, - PCCARD_CIS_SIZE, RF_ACTIVE | RF_PCCARD_ATTR); + PCCARD_CIS_SIZE, RF_ACTIVE /* | RF_PCCARD_ATTR */); if (res == NULL) { #ifdef DIAGNOSTIC device_printf(dev, "can't alloc memory to read attributes\n"); #endif return -1; } tuple.memt = rman_get_bustag(res); tuple.memh = rman_get_bushandle(res); DPRINTF(("cis mem map %x\n", (unsigned int) tuple.memh)); tuple.mult = 2; longlink_present = 1; longlink_common = 1; longlink_addr = 0; mfc_count = 0; mfc_index = 0; DEVPRINTF((dev, "CIS tuple chain:\n")); while (1) { while (1) { /* get the tuple code */ tuple.code = pccard_cis_read_1(&tuple, tuple.ptr); /* two special-case tuples */ if (tuple.code == PCCARD_CISTPL_NULL) { DPRINTF(("CISTPL_NONE\n 00\n")); tuple.ptr++; continue; } else if (tuple.code == PCCARD_CISTPL_END) { DPRINTF(("CISTPL_END\n ff\n")); /* Call the function for the END tuple, since the CIS semantics depend on it */ if ((*fct) (&tuple, arg)) { ret = 1; goto done; } tuple.ptr++; break; } /* now all the normal tuples */ tuple.length = pccard_cis_read_1(&tuple, tuple.ptr + 1); switch (tuple.code) { case PCCARD_CISTPL_LONGLINK_A: case PCCARD_CISTPL_LONGLINK_C: if (tuple.length < 4) { DPRINTF(("CISTPL_LONGLINK_%s too " "short %d\n", longlink_common ? "C" : "A", tuple.length)); break; } longlink_present = 1; longlink_common = (tuple.code == PCCARD_CISTPL_LONGLINK_C) ? 1 : 0; longlink_addr = pccard_tuple_read_4(&tuple, 0); DPRINTF(("CISTPL_LONGLINK_%s %lx\n", longlink_common ? "C" : "A", longlink_addr)); break; case PCCARD_CISTPL_NO_LINK: longlink_present = 0; DPRINTF(("CISTPL_NO_LINK\n")); break; case PCCARD_CISTPL_CHECKSUM: if (tuple.length < 5) { DPRINTF(("CISTPL_CHECKSUM too " "short %d\n", tuple.length)); break; } { int16_t offset; u_long addr, length; u_int cksum, sum; int i; *((u_int16_t *) & offset) = pccard_tuple_read_2(&tuple, 0); length = pccard_tuple_read_2(&tuple, 2); cksum = pccard_tuple_read_1(&tuple, 4); addr = tuple.ptr + offset; DPRINTF(("CISTPL_CHECKSUM addr=%lx " "len=%lx cksum=%x", addr, length, cksum)); /* * XXX do more work to deal with * distant regions */ if ((addr >= PCCARD_CIS_SIZE) || ((addr + length) < 0) || ((addr + length) >= PCCARD_CIS_SIZE)) { DPRINTF((" skipped, " "too distant\n")); break; } sum = 0; for (i = 0; i < length; i++) sum += bus_space_read_1(tuple.memt, tuple.memh, addr + tuple.mult * i); if (cksum != (sum & 0xff)) { DPRINTF((" failed sum=%x\n", sum)); device_printf(dev, "CIS checksum failed\n"); #if 0 /* * XXX Some working cards have * XXX bad checksums!! */ ret = -1; #endif } else { DPRINTF((" ok\n")); } } break; case PCCARD_CISTPL_LONGLINK_MFC: if (tuple.length < 1) { DPRINTF(("CISTPL_LONGLINK_MFC too " "short %d\n", tuple.length)); break; } /* * this is kind of ad hoc, as I don't have * any real documentation */ { int i; mfc_count = pccard_tuple_read_1(&tuple, 0); DPRINTF(("CISTPL_LONGLINK_MFC %d", mfc_count)); for (i = 0; i < mfc_count; i++) { mfc[i].common = (pccard_tuple_read_1(&tuple, 1 + 5 * i) == PCCARD_MFC_MEM_COMMON) ? 1 : 0; mfc[i].addr = pccard_tuple_read_4(&tuple, 1 + 5 * i + 1); DPRINTF((" %s:%lx", mfc[i].common ? "common" : "attr", mfc[i].addr)); } DPRINTF(("\n")); } /* * for LONGLINK_MFC, fall through to the * function. This tuple has structural and * semantic content. */ default: { if ((*fct) (&tuple, arg)) { ret = 1; goto done; } } break; } /* switch */ #ifdef PCCARDCISDEBUG /* print the tuple */ { int i; DPRINTF((" %02x %02x", tuple.code, tuple.length)); for (i = 0; i < tuple.length; i++) { DPRINTF((" %02x", pccard_tuple_read_1(&tuple, i))); if ((i % 16) == 13) DPRINTF(("\n")); } if ((i % 16) != 14) DPRINTF(("\n")); } #endif /* skip to the next tuple */ tuple.ptr += 2 + tuple.length; } #ifdef XXX /* I'm not up to this tonight, need to implement new API */ /* to deal with moving windows and such. At least that's */ /* what it appears at this instant */ /* * the chain is done. Clean up and move onto the next one, * if any. The loop is here in the case that there is an MFC * card with no longlink (which defaults to existing, == 0). * In general, this means that if one pointer fails, it will * try the next one, instead of just bailing. */ while (1) { pccard_chip_mem_unmap(pct, pch, window); if (longlink_present) { /* * if the longlink is to attribute memory, * then it is unindexed. That is, if the * link value is 0x100, then the actual * memory address is 0x200. This means that * we need to multiply by 2 before calling * mem_map, and then divide the resulting ptr * by 2 after. */ if (!longlink_common) longlink_addr *= 2; pccard_chip_mem_map(pct, pch, longlink_common ? PCCARD_MEM_COMMON : PCCARD_MEM_ATTR, longlink_addr, PCCARD_CIS_SIZE, &pcmh, &tuple.ptr, &window); if (!longlink_common) tuple.ptr /= 2; DPRINTF(("cis mem map %x\n", (unsigned int) tuple.memh)); tuple.mult = longlink_common ? 1 : 2; longlink_present = 0; longlink_common = 1; longlink_addr = 0; } else if (mfc_count && (mfc_index < mfc_count)) { if (!mfc[mfc_index].common) mfc[mfc_index].addr *= 2; pccard_chip_mem_map(pct, pch, mfc[mfc_index].common ? PCCARD_MEM_COMMON : PCCARD_MEM_ATTR, mfc[mfc_index].addr, PCCARD_CIS_SIZE, &pcmh, &tuple.ptr, &window); if (!mfc[mfc_index].common) tuple.ptr /= 2; DPRINTF(("cis mem map %x\n", (unsigned int) tuple.memh)); /* set parse state, and point at the next one */ tuple.mult = mfc[mfc_index].common ? 1 : 2; mfc_index++; } else { goto done; } /* make sure that the link is valid */ tuple.code = pccard_cis_read_1(&tuple, tuple.ptr); if (tuple.code != PCCARD_CISTPL_LINKTARGET) { DPRINTF(("CISTPL_LINKTARGET expected, " "code %02x observed\n", tuple.code)); continue; } tuple.length = pccard_cis_read_1(&tuple, tuple.ptr + 1); if (tuple.length < 3) { DPRINTF(("CISTPL_LINKTARGET too short %d\n", tuple.length)); continue; } if ((pccard_tuple_read_1(&tuple, 0) != 'C') || (pccard_tuple_read_1(&tuple, 1) != 'I') || (pccard_tuple_read_1(&tuple, 2) != 'S')) { DPRINTF(("CISTPL_LINKTARGET magic " "%02x%02x%02x incorrect\n", pccard_tuple_read_1(&tuple, 0), pccard_tuple_read_1(&tuple, 1), pccard_tuple_read_1(&tuple, 2))); continue; } tuple.ptr += 2 + tuple.length; break; } #endif /* XXX */ } done: bus_release_resource(dev, SYS_RES_MEMORY, rid, res); return (ret); } /* XXX this is incredibly verbose. Not sure what trt is */ void pccard_print_cis(device_t dev) { struct pccard_softc *sc = (struct pccard_softc *) device_get_softc(dev); struct pccard_card *card = &sc->card; struct pccard_function *pf; struct pccard_config_entry *cfe; int i; device_printf(dev, "CIS version "); if (card->cis1_major == 4) { if (card->cis1_minor == 0) printf("PCCARD 1.0\n"); else if (card->cis1_minor == 1) printf("PCCARD 2.0 or 2.1\n"); } else if (card->cis1_major >= 5) printf("PC Card Standard %d.%d\n", card->cis1_major, card->cis1_minor); else printf("unknown (major=%d, minor=%d)\n", card->cis1_major, card->cis1_minor); device_printf(dev, "CIS info: "); for (i = 0; i < 4; i++) { if (card->cis1_info[i] == NULL) break; if (i) printf(", "); printf("%s", card->cis1_info[i]); } printf("\n"); device_printf(dev, "Manufacturer code 0x%x, product 0x%x\n", card->manufacturer, card->product); STAILQ_FOREACH(pf, &card->pf_head, pf_list) { device_printf(dev, "function %d: ", pf->number); switch (pf->function) { case PCCARD_FUNCTION_UNSPEC: printf("unspecified"); break; case PCCARD_FUNCTION_MULTIFUNCTION: printf("multi-function"); break; case PCCARD_FUNCTION_MEMORY: printf("memory"); break; case PCCARD_FUNCTION_SERIAL: printf("serial port"); break; case PCCARD_FUNCTION_PARALLEL: printf("parallel port"); break; case PCCARD_FUNCTION_DISK: printf("fixed disk"); break; case PCCARD_FUNCTION_VIDEO: printf("video adapter"); break; case PCCARD_FUNCTION_NETWORK: printf("network adapter"); break; case PCCARD_FUNCTION_AIMS: printf("auto incrementing mass storage"); break; case PCCARD_FUNCTION_SCSI: printf("SCSI bridge"); break; case PCCARD_FUNCTION_SECURITY: printf("Security services"); break; case PCCARD_FUNCTION_INSTRUMENT: printf("Instrument"); break; default: printf("unknown (%d)", pf->function); break; } printf(", ccr addr %lx mask %lx\n", pf->ccr_base, pf->ccr_mask); STAILQ_FOREACH(cfe, &pf->cfe_head, cfe_list) { device_printf(dev, "function %d, config table entry " "%d: ", pf->number, cfe->number); switch (cfe->iftype) { case PCCARD_IFTYPE_MEMORY: printf("memory card"); break; case PCCARD_IFTYPE_IO: printf("I/O card"); break; default: printf("card type unknown"); break; } printf("; irq mask %x", cfe->irqmask); if (cfe->num_iospace) { printf("; iomask %lx, iospace", cfe->iomask); for (i = 0; i < cfe->num_iospace; i++) { printf(" %lx", cfe->iospace[i].start); if (cfe->iospace[i].length) printf("-%lx", cfe->iospace[i].start + cfe->iospace[i].length - 1); } } if (cfe->num_memspace) { printf("; memspace"); for (i = 0; i < cfe->num_memspace; i++) { printf(" %lx", cfe->memspace[i].cardaddr); if (cfe->memspace[i].length) printf("-%lx", cfe->memspace[i].cardaddr + cfe->memspace[i].length - 1); if (cfe->memspace[i].hostaddr) printf("@%lx", cfe->memspace[i].hostaddr); } } if (cfe->maxtwins) printf("; maxtwins %d", cfe->maxtwins); printf(";"); if (cfe->flags & PCCARD_CFE_MWAIT_REQUIRED) printf(" mwait_required"); if (cfe->flags & PCCARD_CFE_RDYBSY_ACTIVE) printf(" rdybsy_active"); if (cfe->flags & PCCARD_CFE_WP_ACTIVE) printf(" wp_active"); if (cfe->flags & PCCARD_CFE_BVD_ACTIVE) printf(" bvd_active"); if (cfe->flags & PCCARD_CFE_IO8) printf(" io8"); if (cfe->flags & PCCARD_CFE_IO16) printf(" io16"); if (cfe->flags & PCCARD_CFE_IRQSHARE) printf(" irqshare"); if (cfe->flags & PCCARD_CFE_IRQPULSE) printf(" irqpulse"); if (cfe->flags & PCCARD_CFE_IRQLEVEL) printf(" irqlevel"); if (cfe->flags & PCCARD_CFE_POWERDOWN) printf(" powerdown"); if (cfe->flags & PCCARD_CFE_READONLY) printf(" readonly"); if (cfe->flags & PCCARD_CFE_AUDIO) printf(" audio"); printf("\n"); } } if (card->error) device_printf(dev, "%d errors found while parsing CIS\n", card->error); } int pccard_parse_cis_tuple(struct pccard_tuple *tuple, void *arg) { /* most of these are educated guesses */ static struct pccard_config_entry init_cfe = { -1, PCCARD_CFE_RDYBSY_ACTIVE | PCCARD_CFE_WP_ACTIVE | PCCARD_CFE_BVD_ACTIVE, PCCARD_IFTYPE_MEMORY, }; struct cis_state *state = arg; switch (tuple->code) { case PCCARD_CISTPL_END: /* if we've seen a LONGLINK_MFC, and this is the first * END after it, reset the function list. * * XXX This might also be the right place to start a * new function, but that assumes that a function * definition never crosses any longlink, and I'm not * sure about that. This is probably safe for MFC * cards, but what we have now isn't broken, so I'd * rather not change it. */ if (state->gotmfc == 1) { struct pccard_function *pf, *pfnext; for (pf = STAILQ_FIRST(&state->card->pf_head); pf != NULL; pf = pfnext) { pfnext = STAILQ_NEXT(pf, pf_list); free(pf, M_DEVBUF); } STAILQ_INIT(&state->card->pf_head); state->count = 0; state->gotmfc = 2; state->pf = NULL; } break; case PCCARD_CISTPL_LONGLINK_MFC: /* * this tuple's structure was dealt with in scan_cis. here, * record the fact that the MFC tuple was seen, so that * functions declared before the MFC link can be cleaned * up. */ state->gotmfc = 1; break; #ifdef PCCARDCISDEBUG case PCCARD_CISTPL_DEVICE: case PCCARD_CISTPL_DEVICE_A: { u_int reg, dtype, dspeed; reg = pccard_tuple_read_1(tuple, 0); dtype = reg & PCCARD_DTYPE_MASK; dspeed = reg & PCCARD_DSPEED_MASK; DPRINTF(("CISTPL_DEVICE%s type=", (tuple->code == PCCARD_CISTPL_DEVICE) ? "" : "_A")); switch (dtype) { case PCCARD_DTYPE_NULL: DPRINTF(("null")); break; case PCCARD_DTYPE_ROM: DPRINTF(("rom")); break; case PCCARD_DTYPE_OTPROM: DPRINTF(("otprom")); break; case PCCARD_DTYPE_EPROM: DPRINTF(("eprom")); break; case PCCARD_DTYPE_EEPROM: DPRINTF(("eeprom")); break; case PCCARD_DTYPE_FLASH: DPRINTF(("flash")); break; case PCCARD_DTYPE_SRAM: DPRINTF(("sram")); break; case PCCARD_DTYPE_DRAM: DPRINTF(("dram")); break; case PCCARD_DTYPE_FUNCSPEC: DPRINTF(("funcspec")); break; case PCCARD_DTYPE_EXTEND: DPRINTF(("extend")); break; default: DPRINTF(("reserved")); break; } DPRINTF((" speed=")); switch (dspeed) { case PCCARD_DSPEED_NULL: DPRINTF(("null")); break; case PCCARD_DSPEED_250NS: DPRINTF(("250ns")); break; case PCCARD_DSPEED_200NS: DPRINTF(("200ns")); break; case PCCARD_DSPEED_150NS: DPRINTF(("150ns")); break; case PCCARD_DSPEED_100NS: DPRINTF(("100ns")); break; case PCCARD_DSPEED_EXT: DPRINTF(("ext")); break; default: DPRINTF(("reserved")); break; } } DPRINTF(("\n")); break; #endif case PCCARD_CISTPL_VERS_1: if (tuple->length < 6) { DPRINTF(("CISTPL_VERS_1 too short %d\n", tuple->length)); break; } { int start, i, ch, count; state->card->cis1_major = pccard_tuple_read_1(tuple, 0); state->card->cis1_minor = pccard_tuple_read_1(tuple, 1); for (count = 0, start = 0, i = 0; (count < 4) && ((i + 4) < 256); i++) { ch = pccard_tuple_read_1(tuple, 2 + i); if (ch == 0xff) break; state->card->cis1_info_buf[i] = ch; if (ch == 0) { state->card->cis1_info[count] = state->card->cis1_info_buf + start; start = i + 1; count++; } } DPRINTF(("CISTPL_VERS_1\n")); } break; case PCCARD_CISTPL_MANFID: if (tuple->length < 4) { DPRINTF(("CISTPL_MANFID too short %d\n", tuple->length)); break; } state->card->manufacturer = pccard_tuple_read_2(tuple, 0); state->card->product = pccard_tuple_read_2(tuple, 2); DPRINTF(("CISTPL_MANFID\n")); break; case PCCARD_CISTPL_FUNCID: if (tuple->length < 1) { DPRINTF(("CISTPL_FUNCID too short %d\n", tuple->length)); break; } if ((state->pf == NULL) || (state->gotmfc == 2)) { state->pf = malloc(sizeof(*state->pf), M_DEVBUF, M_NOWAIT); bzero(state->pf, sizeof(*state->pf)); state->pf->number = state->count++; state->pf->last_config_index = -1; STAILQ_INIT(&state->pf->cfe_head); STAILQ_INSERT_TAIL(&state->card->pf_head, state->pf, pf_list); } state->pf->function = pccard_tuple_read_1(tuple, 0); DPRINTF(("CISTPL_FUNCID\n")); break; case PCCARD_CISTPL_CONFIG: if (tuple->length < 3) { DPRINTF(("CISTPL_CONFIG too short %d\n", tuple->length)); break; } { u_int reg, rasz, rmsz, rfsz; int i; reg = pccard_tuple_read_1(tuple, 0); rasz = 1 + ((reg & PCCARD_TPCC_RASZ_MASK) >> PCCARD_TPCC_RASZ_SHIFT); rmsz = 1 + ((reg & PCCARD_TPCC_RMSZ_MASK) >> PCCARD_TPCC_RMSZ_SHIFT); rfsz = ((reg & PCCARD_TPCC_RFSZ_MASK) >> PCCARD_TPCC_RFSZ_SHIFT); if (tuple->length < (rasz + rmsz + rfsz)) { DPRINTF(("CISTPL_CONFIG (%d,%d,%d) too " "short %d\n", rasz, rmsz, rfsz, tuple->length)); break; } if (state->pf == NULL) { state->pf = malloc(sizeof(*state->pf), M_DEVBUF, M_NOWAIT); bzero(state->pf, sizeof(*state->pf)); state->pf->number = state->count++; state->pf->last_config_index = -1; STAILQ_INIT(&state->pf->cfe_head); STAILQ_INSERT_TAIL(&state->card->pf_head, state->pf, pf_list); state->pf->function = PCCARD_FUNCTION_UNSPEC; } state->pf->last_config_index = pccard_tuple_read_1(tuple, 1); state->pf->ccr_base = 0; for (i = 0; i < rasz; i++) state->pf->ccr_base |= ((pccard_tuple_read_1(tuple, 2 + i)) << (i * 8)); state->pf->ccr_mask = 0; for (i = 0; i < rmsz; i++) state->pf->ccr_mask |= ((pccard_tuple_read_1(tuple, 2 + rasz + i)) << (i * 8)); /* skip the reserved area and subtuples */ /* reset the default cfe for each cfe list */ state->temp_cfe = init_cfe; state->default_cfe = &state->temp_cfe; } DPRINTF(("CISTPL_CONFIG\n")); break; case PCCARD_CISTPL_CFTABLE_ENTRY: { int idx, i, j; u_int reg, reg2; u_int intface, def, num; u_int power, timing, iospace, irq, memspace, misc; struct pccard_config_entry *cfe; idx = 0; reg = pccard_tuple_read_1(tuple, idx); idx++; intface = reg & PCCARD_TPCE_INDX_INTFACE; def = reg & PCCARD_TPCE_INDX_DEFAULT; num = reg & PCCARD_TPCE_INDX_NUM_MASK; /* * this is a little messy. Some cards have only a * cfentry with the default bit set. So, as we go * through the list, we add new indexes to the queue, * and keep a pointer to the last one with the * default bit set. if we see a record with the same * index, as the default, we stash the default and * replace the queue entry. otherwise, we just add * new entries to the queue, pointing the default ptr * at them if the default bit is set. if we get to * the end with the default pointer pointing at a * record which hasn't had a matching index, that's * ok; it just becomes a cfentry like any other. */ /* * if the index in the cis differs from the default * cis, create new entry in the queue and start it * with the current default */ if (num != state->default_cfe->number) { cfe = (struct pccard_config_entry *) malloc(sizeof(*cfe), M_DEVBUF, M_NOWAIT); *cfe = *state->default_cfe; STAILQ_INSERT_TAIL(&state->pf->cfe_head, cfe, cfe_list); cfe->number = num; /* * if the default bit is set in the cis, then * point the new default at whatever is being * filled in */ if (def) state->default_cfe = cfe; } else { /* * the cis index matches the default index, * fill in the default cfentry. It is * assumed that the cfdefault index is in the * queue. For it to be otherwise, the cis * index would have to be -1 (initial * condition) which is not possible, or there * would have to be a preceding cis entry * which had the same cis index and had the * default bit unset. Neither condition * should happen. If it does, this cfentry * is lost (written into temp space), which * is an acceptable failure mode. */ cfe = state->default_cfe; /* * if the cis entry does not have the default * bit set, copy the default out of the way * first. */ if (!def) { state->temp_cfe = *state->default_cfe; state->default_cfe = &state->temp_cfe; } } if (intface) { reg = pccard_tuple_read_1(tuple, idx); idx++; if (reg & PCCARD_TPCE_IF_MWAIT) cfe->flags |= PCCARD_CFE_MWAIT_REQUIRED; if (reg & PCCARD_TPCE_IF_RDYBSY) cfe->flags |= PCCARD_CFE_RDYBSY_ACTIVE; if (reg & PCCARD_TPCE_IF_WP) cfe->flags |= PCCARD_CFE_WP_ACTIVE; if (reg & PCCARD_TPCE_IF_BVD) cfe->flags |= PCCARD_CFE_BVD_ACTIVE; cfe->iftype = reg & PCCARD_TPCE_IF_IFTYPE; } reg = pccard_tuple_read_1(tuple, idx); idx++; power = reg & PCCARD_TPCE_FS_POWER_MASK; timing = reg & PCCARD_TPCE_FS_TIMING; iospace = reg & PCCARD_TPCE_FS_IOSPACE; irq = reg & PCCARD_TPCE_FS_IRQ; memspace = reg & PCCARD_TPCE_FS_MEMSPACE_MASK; misc = reg & PCCARD_TPCE_FS_MISC; if (power) { /* skip over power, don't save */ /* for each parameter selection byte */ for (i = 0; i < power; i++) { reg = pccard_tuple_read_1(tuple, idx); idx++; /* for each bit */ for (j = 0; j < 7; j++) { /* if the bit is set */ if ((reg >> j) & 0x01) { /* skip over bytes */ do { reg2 = pccard_tuple_read_1(tuple, idx); idx++; /* * until * non-extensi * on byte */ } while (reg2 & 0x80); } } } } if (timing) { /* skip over timing, don't save */ reg = pccard_tuple_read_1(tuple, idx); idx++; if ((reg & PCCARD_TPCE_TD_RESERVED_MASK) != PCCARD_TPCE_TD_RESERVED_MASK) idx++; if ((reg & PCCARD_TPCE_TD_RDYBSY_MASK) != PCCARD_TPCE_TD_RDYBSY_MASK) idx++; if ((reg & PCCARD_TPCE_TD_WAIT_MASK) != PCCARD_TPCE_TD_WAIT_MASK) idx++; } if (iospace) { if (tuple->length <= idx) { DPRINTF(("ran out of space before TCPE_IO\n")); goto abort_cfe; } reg = pccard_tuple_read_1(tuple, idx); idx++; if (reg & PCCARD_TPCE_IO_BUSWIDTH_8BIT) cfe->flags |= PCCARD_CFE_IO8; if (reg & PCCARD_TPCE_IO_BUSWIDTH_16BIT) cfe->flags |= PCCARD_CFE_IO16; cfe->iomask = reg & PCCARD_TPCE_IO_IOADDRLINES_MASK; if (reg & PCCARD_TPCE_IO_HASRANGE) { reg = pccard_tuple_read_1(tuple, idx); idx++; cfe->num_iospace = 1 + (reg & PCCARD_TPCE_IO_RANGE_COUNT); if (cfe->num_iospace > (sizeof(cfe->iospace) / sizeof(cfe->iospace[0]))) { DPRINTF(("too many io " "spaces %d", cfe->num_iospace)); state->card->error++; break; } for (i = 0; i < cfe->num_iospace; i++) { switch (reg & PCCARD_TPCE_IO_RANGE_ADDRSIZE_MASK) { case PCCARD_TPCE_IO_RANGE_ADDRSIZE_ONE: cfe->iospace[i].start = pccard_tuple_read_1(tuple, idx); idx++; break; case PCCARD_TPCE_IO_RANGE_ADDRSIZE_TWO: cfe->iospace[i].start = pccard_tuple_read_2(tuple, idx); idx += 2; break; case PCCARD_TPCE_IO_RANGE_ADDRSIZE_FOUR: cfe->iospace[i].start = pccard_tuple_read_4(tuple, idx); idx += 4; break; } switch (reg & PCCARD_TPCE_IO_RANGE_LENGTHSIZE_MASK) { case PCCARD_TPCE_IO_RANGE_LENGTHSIZE_ONE: cfe->iospace[i].length = pccard_tuple_read_1(tuple, idx); idx++; break; case PCCARD_TPCE_IO_RANGE_LENGTHSIZE_TWO: cfe->iospace[i].length = pccard_tuple_read_2(tuple, idx); idx += 2; break; case PCCARD_TPCE_IO_RANGE_LENGTHSIZE_FOUR: cfe->iospace[i].length = pccard_tuple_read_4(tuple, idx); idx += 4; break; } cfe->iospace[i].length++; } } else { cfe->num_iospace = 1; cfe->iospace[0].start = 0; cfe->iospace[0].length = (1 << cfe->iomask); } } if (irq) { if (tuple->length <= idx) { DPRINTF(("ran out of space before TCPE_IR\n")); goto abort_cfe; } reg = pccard_tuple_read_1(tuple, idx); idx++; if (reg & PCCARD_TPCE_IR_SHARE) cfe->flags |= PCCARD_CFE_IRQSHARE; if (reg & PCCARD_TPCE_IR_PULSE) cfe->flags |= PCCARD_CFE_IRQPULSE; if (reg & PCCARD_TPCE_IR_LEVEL) cfe->flags |= PCCARD_CFE_IRQLEVEL; if (reg & PCCARD_TPCE_IR_HASMASK) { /* * it's legal to ignore the * special-interrupt bits, so I will */ cfe->irqmask = pccard_tuple_read_2(tuple, idx); idx += 2; } else { cfe->irqmask = (1 << (reg & PCCARD_TPCE_IR_IRQ)); } } if (memspace) { if (tuple->length <= idx) { DPRINTF(("ran out of space before TCPE_MS\n")); goto abort_cfe; } if (memspace == PCCARD_TPCE_FS_MEMSPACE_NONE) { cfe->num_memspace = 0; } else if (memspace == PCCARD_TPCE_FS_MEMSPACE_LENGTH) { cfe->num_memspace = 1; cfe->memspace[0].length = 256 * pccard_tuple_read_2(tuple, idx); idx += 2; cfe->memspace[0].cardaddr = 0; cfe->memspace[0].hostaddr = 0; } else if (memspace == PCCARD_TPCE_FS_MEMSPACE_LENGTHADDR) { cfe->num_memspace = 1; cfe->memspace[0].length = 256 * pccard_tuple_read_2(tuple, idx); idx += 2; cfe->memspace[0].cardaddr = 256 * pccard_tuple_read_2(tuple, idx); idx += 2; cfe->memspace[0].hostaddr = cfe->memspace[0].cardaddr; } else { int lengthsize; int cardaddrsize; int hostaddrsize; reg = pccard_tuple_read_1(tuple, idx); idx++; cfe->num_memspace = (reg & PCCARD_TPCE_MS_COUNT) + 1; if (cfe->num_memspace > (sizeof(cfe->memspace) / sizeof(cfe->memspace[0]))) { DPRINTF(("too many mem " "spaces %d", cfe->num_memspace)); state->card->error++; break; } lengthsize = ((reg & PCCARD_TPCE_MS_LENGTH_SIZE_MASK) >> PCCARD_TPCE_MS_LENGTH_SIZE_SHIFT); cardaddrsize = ((reg & PCCARD_TPCE_MS_CARDADDR_SIZE_MASK) >> PCCARD_TPCE_MS_CARDADDR_SIZE_SHIFT); hostaddrsize = (reg & PCCARD_TPCE_MS_HOSTADDR) ? cardaddrsize : 0; if (lengthsize == 0) { DPRINTF(("cfe memspace " "lengthsize == 0")); state->card->error++; } for (i = 0; i < cfe->num_memspace; i++) { if (lengthsize) { cfe->memspace[i].length = 256 * pccard_tuple_read_n(tuple, lengthsize, idx); idx += lengthsize; } else { cfe->memspace[i].length = 0; } if (cfe->memspace[i].length == 0) { DPRINTF(("cfe->memspace[%d].length == 0", i)); state->card->error++; } if (cardaddrsize) { cfe->memspace[i].cardaddr = 256 * pccard_tuple_read_n(tuple, cardaddrsize, idx); idx += cardaddrsize; } else { cfe->memspace[i].cardaddr = 0; } if (hostaddrsize) { cfe->memspace[i].hostaddr = 256 * pccard_tuple_read_n(tuple, hostaddrsize, idx); idx += hostaddrsize; } else { cfe->memspace[i].hostaddr = 0; } } } } if (misc) { if (tuple->length <= idx) { DPRINTF(("ran out of space before TCPE_MI\n")); goto abort_cfe; } reg = pccard_tuple_read_1(tuple, idx); idx++; if (reg & PCCARD_TPCE_MI_PWRDOWN) cfe->flags = PCCARD_CFE_POWERDOWN; if (reg & PCCARD_TPCE_MI_READONLY) cfe->flags = PCCARD_CFE_READONLY; if (reg & PCCARD_TPCE_MI_AUDIO) cfe->flags = PCCARD_CFE_AUDIO; cfe->maxtwins = reg & PCCARD_TPCE_MI_MAXTWINS; while (reg & PCCARD_TPCE_MI_EXT) { reg = pccard_tuple_read_1(tuple, idx); idx++; } } /* skip all the subtuples */ } abort_cfe: DPRINTF(("CISTPL_CFTABLE_ENTRY\n")); break; default: DPRINTF(("unhandled CISTPL %x\n", tuple->code)); break; } return (0); } diff --git a/sys/dev/pccard/pccardvar.h b/sys/dev/pccard/pccardvar.h index ed26e3f83f5e..dbf2825607cd 100644 --- a/sys/dev/pccard/pccardvar.h +++ b/sys/dev/pccard/pccardvar.h @@ -1,284 +1,295 @@ /* $NetBSD: pcmciavar.h,v 1.9 1998/12/29 09:00:28 marc Exp $ */ /* $FreeBSD$ */ /* * Copyright (c) 1997 Marc Horowitz. 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 Marc Horowitz. * 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. */ #include #include #include #include extern int pccard_verbose; /* * Contains information about mapped/allocated i/o spaces. */ struct pccard_io_handle { bus_space_tag_t iot; /* bus space tag (from chipset) */ bus_space_handle_t ioh; /* mapped space handle */ bus_addr_t addr; /* resulting address in bus space */ bus_size_t size; /* size of i/o space */ int flags; /* misc. information */ int width; }; #define PCCARD_IO_ALLOCATED 0x01 /* i/o space was allocated */ /* * Contains information about allocated memory space. */ struct pccard_mem_handle { bus_space_tag_t memt; /* bus space tag (from chipset) */ bus_space_handle_t memh; /* mapped space handle */ bus_addr_t addr; /* resulting address in bus space */ bus_size_t size; /* size of mem space */ pccard_mem_handle_t mhandle; /* opaque memory handle */ bus_size_t realsize; /* how much we really allocated */ long offset; int kind; }; /* pccard itself */ #define PCCARD_CFE_MWAIT_REQUIRED 0x0001 #define PCCARD_CFE_RDYBSY_ACTIVE 0x0002 #define PCCARD_CFE_WP_ACTIVE 0x0004 #define PCCARD_CFE_BVD_ACTIVE 0x0008 #define PCCARD_CFE_IO8 0x0010 #define PCCARD_CFE_IO16 0x0020 #define PCCARD_CFE_IRQSHARE 0x0040 #define PCCARD_CFE_IRQPULSE 0x0080 #define PCCARD_CFE_IRQLEVEL 0x0100 #define PCCARD_CFE_POWERDOWN 0x0200 #define PCCARD_CFE_READONLY 0x0400 #define PCCARD_CFE_AUDIO 0x0800 struct pccard_config_entry { int number; u_int32_t flags; int iftype; int num_iospace; /* * The card will only decode this mask in any case, so we can * do dynamic allocation with this in mind, in case the suggestions * below are no good. */ u_long iomask; struct { u_long length; u_long start; } iospace[4]; /* XXX this could be as high as 16 */ u_int16_t irqmask; int num_memspace; struct { u_long length; u_long cardaddr; u_long hostaddr; } memspace[2]; /* XXX this could be as high as 8 */ int maxtwins; STAILQ_ENTRY(pccard_config_entry) cfe_list; }; struct pccard_function { /* read off the card */ int number; int function; int last_config_index; u_long ccr_base; u_long ccr_mask; struct resource *ccr_res; int ccr_rid; STAILQ_HEAD(, pccard_config_entry) cfe_head; STAILQ_ENTRY(pccard_function) pf_list; /* run-time state */ struct pccard_softc *sc; struct device *child; struct pccard_config_entry *cfe; struct pccard_mem_handle pf_pcmh; #define pf_ccrt pf_pcmh.memt #define pf_ccrh pf_pcmh.memh #define pf_ccr_mhandle pf_pcmh.mhandle #define pf_ccr_realsize pf_pcmh.realsize bus_addr_t pf_ccr_offset; int pf_ccr_window; long pf_mfc_iobase; long pf_mfc_iomax; int (*ih_fct)(void *); void *ih_arg; int ih_ipl; int pf_flags; }; /* pf_flags */ #define PFF_ENABLED 0x0001 /* function is enabled */ struct pccard_card { int cis1_major; int cis1_minor; /* XXX waste of space? */ char cis1_info_buf[256]; char *cis1_info[4]; /* * Use int32_t for manufacturer and product so that they can * hold the id value found in card CIS and special value that * indicates no id was found. */ int32_t manufacturer; #define PCCARD_VENDOR_INVALID -1 int32_t product; #define PCCARD_PRODUCT_INVALID -1 u_int16_t error; #define PCCARD_CIS_INVALID { NULL, NULL, NULL, NULL } STAILQ_HEAD(, pccard_function) pf_head; }; /* More later? */ struct pccard_ivar { struct resource_list resources; int slotnum; }; struct pccard_softc { device_t dev; /* this stuff is for the socket */ /* this stuff is for the card */ struct pccard_card card; void *ih; int sc_enabled_count; /* num functions enabled */ }; void pccardbus_if_setup(struct pccard_softc*); struct pccard_cis_quirk { int32_t manufacturer; int32_t product; char *cis1_info[4]; struct pccard_function *pf; struct pccard_config_entry *cfe; }; struct pccard_tuple { unsigned int code; unsigned int length; u_long mult; bus_addr_t ptr; bus_space_tag_t memt; bus_space_handle_t memh; }; void pccard_read_cis(struct pccard_softc *); void pccard_check_cis_quirks(device_t); void pccard_print_cis(device_t); int pccard_scan_cis(struct device * dev, int (*) (struct pccard_tuple *, void *), void *); #define pccard_cis_read_1(tuple, idx0) \ (bus_space_read_1((tuple)->memt, (tuple)->memh, (tuple)->mult*(idx0))) #define pccard_tuple_read_1(tuple, idx1) \ (pccard_cis_read_1((tuple), ((tuple)->ptr+(2+(idx1))))) #define pccard_tuple_read_2(tuple, idx2) \ (pccard_tuple_read_1((tuple), (idx2)) | \ (pccard_tuple_read_1((tuple), (idx2)+1)<<8)) #define pccard_tuple_read_3(tuple, idx3) \ (pccard_tuple_read_1((tuple), (idx3)) | \ (pccard_tuple_read_1((tuple), (idx3)+1)<<8) | \ (pccard_tuple_read_1((tuple), (idx3)+2)<<16)) #define pccard_tuple_read_4(tuple, idx4) \ (pccard_tuple_read_1((tuple), (idx4)) | \ (pccard_tuple_read_1((tuple), (idx4)+1)<<8) | \ (pccard_tuple_read_1((tuple), (idx4)+2)<<16) | \ (pccard_tuple_read_1((tuple), (idx4)+3)<<24)) #define pccard_tuple_read_n(tuple, n, idxn) \ (((n)==1)?pccard_tuple_read_1((tuple), (idxn)) : \ (((n)==2)?pccard_tuple_read_2((tuple), (idxn)) : \ (((n)==3)?pccard_tuple_read_3((tuple), (idxn)) : \ /* n == 4 */ pccard_tuple_read_4((tuple), (idxn))))) #define PCCARD_SPACE_MEMORY 1 #define PCCARD_SPACE_IO 2 int pccard_ccr_read(struct pccard_function *, int); void pccard_ccr_write(struct pccard_function *, int, int); #define pccard_mfc(sc) (STAILQ_FIRST(&(sc)->card.pf_head) && \ STAILQ_NEXT(STAILQ_FIRST(&(sc)->card.pf_head),pf_list)) void pccard_function_init(struct pccard_function *, struct pccard_config_entry *); int pccard_function_enable(struct pccard_function *); void pccard_function_disable(struct pccard_function *); #define pccard_io_alloc(pf, start, size, align, pciop) \ (pccard_chip_io_alloc((pf)->sc->pct, pf->sc->pch, (start), \ (size), (align), (pciop))) #define pccard_io_free(pf, pciohp) \ (pccard_chip_io_free((pf)->sc->pct, (pf)->sc->pch, (pciohp))) int pccard_io_map(struct pccard_function *, int, bus_addr_t, bus_size_t, struct pccard_io_handle *, int *); void pccard_io_unmap(struct pccard_function *, int); #define pccard_mem_alloc(pf, size, pcmhp) \ (pccard_chip_mem_alloc((pf)->sc->pct, (pf)->sc->pch, (size), (pcmhp))) #define pccard_mem_free(pf, pcmhp) \ (pccard_chip_mem_free((pf)->sc->pct, (pf)->sc->pch, (pcmhp))) #define pccard_mem_map(pf, kind, card_addr, size, pcmhp, offsetp, windowp) \ (pccard_chip_mem_map((pf)->sc->pct, (pf)->sc->pch, (kind), \ (card_addr), (size), (pcmhp), (offsetp), (windowp))) #define pccard_mem_unmap(pf, window) \ (pccard_chip_mem_unmap((pf)->sc->pct, (pf)->sc->pch, (window))) /* ivar interface */ enum { PCCARD_IVAR_ETHADDR, /* read ethernet address from CIS tupple */ }; /* read ethernet address from CIS tupple */ __inline static int pccard_get_ether(device_t dev, u_char *enaddr) { return BUS_READ_IVAR(device_get_parent(dev), dev, - PCCARD_IVAR_ETHADDR, (uintptr_t *)enaddr); + PCCARD_IVAR_ETHADDR, (uintptr_t *)enaddr); +} + +enum { + PCCARD_A_MEM_ATTR +}; + +/* Set the */ + +static __inline__ void +pccard_set_attribute(device_t dev, struct resource *r, int rid, int flags) +{ }