Index: head/sys/dev/cardbus/cardbus.c =================================================================== --- head/sys/dev/cardbus/cardbus.c (revision 331270) +++ head/sys/dev/cardbus/cardbus.c (revision 331271) @@ -1,369 +1,371 @@ /*- * SPDX-License-Identifier: BSD-2-Clause-FreeBSD * * Copyright (c) 2003-2008 M. Warner Losh. All Rights Reserved. * Copyright (c) 2000,2001 Jonathan Chen. 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. */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "power_if.h" #include "pcib_if.h" /* sysctl vars */ static SYSCTL_NODE(_hw, OID_AUTO, cardbus, CTLFLAG_RD, 0, "CardBus parameters"); int cardbus_debug = 0; SYSCTL_INT(_hw_cardbus, OID_AUTO, debug, CTLFLAG_RWTUN, &cardbus_debug, 0, "CardBus debug"); int cardbus_cis_debug = 0; SYSCTL_INT(_hw_cardbus, OID_AUTO, cis_debug, CTLFLAG_RWTUN, &cardbus_cis_debug, 0, "CardBus CIS debug"); #define DPRINTF(a) if (cardbus_debug) printf a #define DEVPRINTF(x) if (cardbus_debug) device_printf x static int cardbus_attach(device_t cbdev); static int cardbus_attach_card(device_t cbdev); static int cardbus_detach(device_t cbdev); static int cardbus_detach_card(device_t cbdev); static void cardbus_device_setup_regs(pcicfgregs *cfg); static void cardbus_driver_added(device_t cbdev, driver_t *driver); static int cardbus_probe(device_t cbdev); static int cardbus_read_ivar(device_t cbdev, device_t child, int which, uintptr_t *result); /************************************************************************/ /* Probe/Attach */ /************************************************************************/ static int cardbus_probe(device_t cbdev) { device_set_desc(cbdev, "CardBus bus"); return (0); } static int cardbus_attach(device_t cbdev) { struct cardbus_softc *sc; #ifdef PCI_RES_BUS int rid; #endif sc = device_get_softc(cbdev); sc->sc_dev = cbdev; #ifdef PCI_RES_BUS rid = 0; sc->sc_bus = bus_alloc_resource(cbdev, PCI_RES_BUS, &rid, pcib_get_bus(cbdev), pcib_get_bus(cbdev), 1, 0); if (sc->sc_bus == NULL) { device_printf(cbdev, "failed to allocate bus number\n"); return (ENXIO); } #else device_printf(cbdev, "Your bus numbers may be AFU\n"); #endif return (0); } static int cardbus_detach(device_t cbdev) { #ifdef PCI_RES_BUS struct cardbus_softc *sc; #endif cardbus_detach_card(cbdev); #ifdef PCI_RES_BUS sc = device_get_softc(cbdev); device_printf(cbdev, "Freeing up the allocatd bus\n"); (void)bus_release_resource(cbdev, PCI_RES_BUS, 0, sc->sc_bus); #endif return (0); } static int cardbus_suspend(device_t self) { cardbus_detach_card(self); return (0); } static int cardbus_resume(device_t self) { return (0); } /************************************************************************/ /* Attach/Detach card */ /************************************************************************/ static void cardbus_device_setup_regs(pcicfgregs *cfg) { device_t dev = cfg->dev; int i; /* * Some cards power up with garbage in their BARs. This * code clears all that junk out. */ for (i = 0; i < PCIR_MAX_BAR_0; i++) pci_write_config(dev, PCIR_BAR(i), 0, 4); cfg->intline = pci_get_irq(device_get_parent(device_get_parent(dev))); pci_write_config(dev, PCIR_INTLINE, cfg->intline, 1); pci_write_config(dev, PCIR_CACHELNSZ, 0x08, 1); pci_write_config(dev, PCIR_LATTIMER, 0xa8, 1); pci_write_config(dev, PCIR_MINGNT, 0x14, 1); pci_write_config(dev, PCIR_MAXLAT, 0x14, 1); } static struct pci_devinfo * cardbus_alloc_devinfo(device_t dev) { struct cardbus_devinfo *dinfo; dinfo = malloc(sizeof(*dinfo), M_DEVBUF, M_WAITOK | M_ZERO); return (&dinfo->pci); } static int cardbus_attach_card(device_t cbdev) { device_t brdev = device_get_parent(cbdev); device_t child; int bus, domain, slot, func; int cardattached = 0; int cardbusfunchigh = 0; struct cardbus_softc *sc; sc = device_get_softc(cbdev); cardbus_detach_card(cbdev); /* detach existing cards */ POWER_DISABLE_SOCKET(brdev, cbdev); /* Turn the socket off first */ POWER_ENABLE_SOCKET(brdev, cbdev); domain = pcib_get_domain(cbdev); bus = pcib_get_bus(cbdev); slot = 0; + mtx_lock(&Giant); /* For each function, set it up and try to attach a driver to it */ for (func = 0; func <= cardbusfunchigh; func++) { struct cardbus_devinfo *dinfo; dinfo = (struct cardbus_devinfo *) pci_read_device(brdev, cbdev, domain, bus, slot, func); if (dinfo == NULL) continue; if (dinfo->pci.cfg.mfdev) cardbusfunchigh = PCI_FUNCMAX; child = device_add_child(cbdev, NULL, -1); if (child == NULL) { DEVPRINTF((cbdev, "Cannot add child!\n")); pci_freecfg((struct pci_devinfo *)dinfo); continue; } dinfo->pci.cfg.dev = child; resource_list_init(&dinfo->pci.resources); device_set_ivars(child, dinfo); cardbus_device_create(sc, dinfo, cbdev, child); if (cardbus_do_cis(cbdev, child) != 0) DEVPRINTF((cbdev, "Warning: Bogus CIS ignored\n")); pci_cfg_save(dinfo->pci.cfg.dev, &dinfo->pci, 0); pci_cfg_restore(dinfo->pci.cfg.dev, &dinfo->pci); cardbus_device_setup_regs(&dinfo->pci.cfg); pci_add_resources(cbdev, child, 1, dinfo->mprefetchable); pci_print_verbose(&dinfo->pci); if (device_probe_and_attach(child) == 0) cardattached++; else pci_cfg_save(dinfo->pci.cfg.dev, &dinfo->pci, 1); } + mtx_unlock(&Giant); if (cardattached > 0) return (0); /* POWER_DISABLE_SOCKET(brdev, cbdev); */ return (ENOENT); } static void cardbus_child_deleted(device_t cbdev, device_t child) { struct cardbus_devinfo *dinfo = device_get_ivars(child); if (dinfo->pci.cfg.dev != child) device_printf(cbdev, "devinfo dev mismatch\n"); cardbus_device_destroy(dinfo); pci_child_deleted(cbdev, child); } static int cardbus_detach_card(device_t cbdev) { int err = 0; err = bus_generic_detach(cbdev); if (err) return (err); err = device_delete_children(cbdev); if (err) return (err); POWER_DISABLE_SOCKET(device_get_parent(cbdev), cbdev); return (err); } static void cardbus_driver_added(device_t cbdev, driver_t *driver) { int numdevs; device_t *devlist; device_t dev; int i; struct cardbus_devinfo *dinfo; DEVICE_IDENTIFY(driver, cbdev); if (device_get_children(cbdev, &devlist, &numdevs) != 0) return; /* * If there are no drivers attached, but there are children, * then power the card up. */ for (i = 0; i < numdevs; i++) { dev = devlist[i]; if (device_get_state(dev) != DS_NOTPRESENT) break; } if (i > 0 && i == numdevs) POWER_ENABLE_SOCKET(device_get_parent(cbdev), cbdev); for (i = 0; i < numdevs; i++) { dev = devlist[i]; if (device_get_state(dev) != DS_NOTPRESENT) continue; dinfo = device_get_ivars(dev); pci_print_verbose(&dinfo->pci); if (bootverbose) printf("pci%d:%d:%d:%d: reprobing on driver added\n", dinfo->pci.cfg.domain, dinfo->pci.cfg.bus, dinfo->pci.cfg.slot, dinfo->pci.cfg.func); pci_cfg_restore(dinfo->pci.cfg.dev, &dinfo->pci); if (device_probe_and_attach(dev) != 0) pci_cfg_save(dev, &dinfo->pci, 1); } free(devlist, M_TEMP); } /************************************************************************/ /* Other Bus Methods */ /************************************************************************/ static int cardbus_read_ivar(device_t cbdev, device_t child, int which, uintptr_t *result) { struct cardbus_devinfo *dinfo; pcicfgregs *cfg; dinfo = device_get_ivars(child); cfg = &dinfo->pci.cfg; switch (which) { case PCI_IVAR_ETHADDR: /* * The generic accessor doesn't deal with failure, so * we set the return value, then return an error. */ if (dinfo->fepresent & (1 << PCCARD_TPLFE_TYPE_LAN_NID)) { *((uint8_t **) result) = dinfo->funce.lan.nid; break; } *((uint8_t **) result) = NULL; return (EINVAL); default: return (pci_read_ivar(cbdev, child, which, result)); } return 0; } static device_method_t cardbus_methods[] = { /* Device interface */ DEVMETHOD(device_probe, cardbus_probe), DEVMETHOD(device_attach, cardbus_attach), DEVMETHOD(device_detach, cardbus_detach), DEVMETHOD(device_suspend, cardbus_suspend), DEVMETHOD(device_resume, cardbus_resume), /* Bus interface */ DEVMETHOD(bus_child_deleted, cardbus_child_deleted), DEVMETHOD(bus_get_dma_tag, bus_generic_get_dma_tag), DEVMETHOD(bus_read_ivar, cardbus_read_ivar), DEVMETHOD(bus_driver_added, cardbus_driver_added), DEVMETHOD(bus_rescan, bus_null_rescan), /* Card Interface */ DEVMETHOD(card_attach_card, cardbus_attach_card), DEVMETHOD(card_detach_card, cardbus_detach_card), /* PCI interface */ DEVMETHOD(pci_alloc_devinfo, cardbus_alloc_devinfo), {0,0} }; DEFINE_CLASS_1(cardbus, cardbus_driver, cardbus_methods, sizeof(struct cardbus_softc), pci_driver); static devclass_t cardbus_devclass; DRIVER_MODULE(cardbus, cbb, cardbus_driver, cardbus_devclass, 0, 0); MODULE_VERSION(cardbus, 1); Index: head/sys/dev/pccard/pccard.c =================================================================== --- head/sys/dev/pccard/pccard.c (revision 331270) +++ head/sys/dev/pccard/pccard.c (revision 331271) @@ -1,1475 +1,1477 @@ /* $NetBSD: pcmcia.c,v 1.23 2000/07/28 19:17:02 drochner Exp $ */ /*- * SPDX-License-Identifier: BSD-4-Clause * * 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 __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "power_if.h" #include "card_if.h" #define PCCARDDEBUG /* sysctl vars */ static SYSCTL_NODE(_hw, OID_AUTO, pccard, CTLFLAG_RD, 0, "PCCARD parameters"); int pccard_debug = 0; SYSCTL_INT(_hw_pccard, OID_AUTO, debug, CTLFLAG_RWTUN, &pccard_debug, 0, "pccard debug"); int pccard_cis_debug = 0; SYSCTL_INT(_hw_pccard, OID_AUTO, cis_debug, CTLFLAG_RWTUN, &pccard_cis_debug, 0, "pccard CIS debug"); #ifdef PCCARDDEBUG #define DPRINTF(arg) if (pccard_debug) printf arg #define DEVPRINTF(arg) if (pccard_debug) device_printf arg #define PRVERBOSE(arg) printf arg #define DEVPRVERBOSE(arg) device_printf arg #else #define DPRINTF(arg) #define DEVPRINTF(arg) #define PRVERBOSE(arg) if (bootverbose) printf arg #define DEVPRVERBOSE(arg) if (bootverbose) device_printf arg #endif static int pccard_ccr_read(struct pccard_function *pf, int ccr); static void pccard_ccr_write(struct pccard_function *pf, int ccr, int val); static int pccard_attach_card(device_t dev); static int pccard_detach_card(device_t dev); static void pccard_function_init(struct pccard_function *pf, int entry); static void pccard_function_free(struct pccard_function *pf); static int pccard_function_enable(struct pccard_function *pf); static void pccard_function_disable(struct pccard_function *pf); static int pccard_probe(device_t dev); static int pccard_attach(device_t dev); static int pccard_detach(device_t dev); static void pccard_print_resources(struct resource_list *rl, const char *name, int type, int count, const char *format); static int pccard_print_child(device_t dev, device_t child); static int pccard_set_resource(device_t dev, device_t child, int type, int rid, rman_res_t start, rman_res_t count); static int pccard_get_resource(device_t dev, device_t child, int type, int rid, rman_res_t *startp, rman_res_t *countp); static void pccard_delete_resource(device_t dev, device_t child, int type, int rid); static int pccard_set_res_flags(device_t dev, device_t child, int type, int rid, u_long flags); static int pccard_set_memory_offset(device_t dev, device_t child, int rid, uint32_t offset, uint32_t *deltap); static int pccard_probe_and_attach_child(device_t dev, device_t child, struct pccard_function *pf); static void pccard_probe_nomatch(device_t cbdev, device_t child); static int pccard_read_ivar(device_t bus, device_t child, int which, uintptr_t *result); static void pccard_driver_added(device_t dev, driver_t *driver); static struct resource *pccard_alloc_resource(device_t dev, device_t child, int type, int *rid, rman_res_t start, rman_res_t end, rman_res_t count, u_int flags); static int pccard_release_resource(device_t dev, device_t child, int type, int rid, struct resource *r); static void pccard_child_detached(device_t parent, device_t dev); static int pccard_filter(void *arg); static void pccard_intr(void *arg); static int pccard_setup_intr(device_t dev, device_t child, struct resource *irq, int flags, driver_filter_t *filt, driver_intr_t *intr, void *arg, void **cookiep); static int pccard_teardown_intr(device_t dev, device_t child, struct resource *r, void *cookie); static const struct pccard_product * pccard_do_product_lookup(device_t bus, device_t dev, const struct pccard_product *tab, size_t ent_size, pccard_product_match_fn matchfn); static int pccard_ccr_read(struct pccard_function *pf, int ccr) { return (bus_space_read_1(pf->pf_ccrt, pf->pf_ccrh, pf->pf_ccr_offset + ccr)); } static void pccard_ccr_write(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); } } static int pccard_set_default_descr(device_t dev) { const char *vendorstr, *prodstr; uint32_t vendor, prod; char *str; if (pccard_get_vendor_str(dev, &vendorstr)) return (0); if (pccard_get_product_str(dev, &prodstr)) return (0); if (vendorstr != NULL && prodstr != NULL) { str = malloc(strlen(vendorstr) + strlen(prodstr) + 2, M_DEVBUF, M_WAITOK); sprintf(str, "%s %s", vendorstr, prodstr); device_set_desc_copy(dev, str); free(str, M_DEVBUF); } else { if (pccard_get_vendor(dev, &vendor)) return (0); if (pccard_get_product(dev, &prod)) return (0); str = malloc(100, M_DEVBUF, M_WAITOK); snprintf(str, 100, "vendor=%#x product=%#x", vendor, prod); device_set_desc_copy(dev, str); free(str, M_DEVBUF); } return (0); } static int pccard_attach_card(device_t dev) { struct pccard_softc *sc = PCCARD_SOFTC(dev); struct pccard_function *pf; struct pccard_ivar *ivar; device_t child; int i; if (!STAILQ_EMPTY(&sc->card.pf_head)) { if (bootverbose || pccard_debug) device_printf(dev, "Card already inserted.\n"); } 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, "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) { device_printf(dev, "CARD ERROR!\n"); return (1); } if (STAILQ_EMPTY(&sc->card.pf_head)) { device_printf(dev, "Card has no functions!\n"); return (1); } if (bootverbose || pccard_debug) pccard_print_cis(dev); DEVPRINTF((dev, "functions scanning\n")); i = -1; STAILQ_FOREACH(pf, &sc->card.pf_head, pf_list) { i++; if (STAILQ_EMPTY(&pf->cfe_head)) { device_printf(dev, "Function %d has no config entries.!\n", i); continue; } pf->sc = sc; pf->cfe = NULL; pf->dev = NULL; } DEVPRINTF((dev, "Card has %d functions. pccard_mfc is %d\n", i + 1, pccard_mfc(sc))); + mtx_lock(&Giant); STAILQ_FOREACH(pf, &sc->card.pf_head, pf_list) { if (STAILQ_EMPTY(&pf->cfe_head)) continue; ivar = malloc(sizeof(struct pccard_ivar), M_DEVBUF, M_WAITOK | M_ZERO); resource_list_init(&ivar->resources); child = device_add_child(dev, NULL, -1); device_set_ivars(child, ivar); ivar->pf = pf; pf->dev = child; pccard_probe_and_attach_child(dev, child, pf); } + mtx_unlock(&Giant); return (0); } static int pccard_probe_and_attach_child(device_t dev, device_t child, struct pccard_function *pf) { struct pccard_softc *sc = PCCARD_SOFTC(dev); int error; /* * In NetBSD, the drivers are responsible for activating each * function of a card and selecting the config to use. In * FreeBSD, all that's done automatically in the typical lazy * way we do device resoruce allocation (except we pick the * cfe up front). This is the biggest depature from the * inherited NetBSD model, apart from the FreeBSD resource code. * * This seems to work well in practice for most cards. * However, there are two cases that are problematic. If a * driver wishes to pick and chose which config entry to use, * then this method falls down. These are usually older * cards. In addition, there are some cards that have * multiple hardware units on the cards, but presents only one * CIS chain. These cards are combination cards, but only one * of these units can be on at a time. * * To overcome this limitation, while preserving the basic * model, the probe routine can select a cfe and try to * activate it. If that succeeds, then we'll keep track of * and let that information persist until we attach the card. * Probe routines that do this MUST return 0, and cannot * participate in the bidding process for a device. This * seems harsh until you realize that if a probe routine knows * enough to override the cfe we pick, then chances are very * very good that it is the only driver that could hope to * cope with the card. Bidding is for generic drivers, and * while some of them may also match, none of them will do * configuration override. */ error = device_probe(child); if (error != 0) goto out; pccard_function_init(pf, -1); if (sc->sc_enabled_count == 0) POWER_ENABLE_SOCKET(device_get_parent(dev), dev); if (pccard_function_enable(pf) == 0 && pccard_set_default_descr(child) == 0 && device_attach(child) == 0) { DEVPRINTF((sc->dev, "function %d CCR at %d offset %#x " "mask %#x: %#x %#x %#x %#x, %#x %#x %#x %#x, %#x\n", pf->number, pf->pf_ccr_window, pf->pf_ccr_offset, pf->ccr_mask, 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))); return (0); } error = ENXIO; out:; /* * Probe may fail AND also try to select a cfe, if so, free * it. This is how we do cfe override. Or the attach fails. * Either way, we have to clean up. */ if (pf->cfe != NULL) pccard_function_disable(pf); pf->cfe = NULL; pccard_function_free(pf); return error; } static int pccard_detach_card(device_t dev) { struct pccard_softc *sc = PCCARD_SOFTC(dev); struct pccard_function *pf; struct pccard_config_entry *cfe; struct pccard_ivar *devi; int state; /* * 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 (pf->dev == NULL) continue; state = device_get_state(pf->dev); if (state == DS_ATTACHED || state == DS_BUSY) device_detach(pf->dev); if (pf->cfe != NULL) pccard_function_disable(pf); pccard_function_free(pf); devi = PCCARD_IVAR(pf->dev); device_delete_child(dev, pf->dev); free(devi, M_DEVBUF); } if (sc->sc_enabled_count == 0) POWER_DISABLE_SOCKET(device_get_parent(dev), dev); while (NULL != (pf = STAILQ_FIRST(&sc->card.pf_head))) { while (NULL != (cfe = STAILQ_FIRST(&pf->cfe_head))) { STAILQ_REMOVE_HEAD(&pf->cfe_head, cfe_list); free(cfe, M_DEVBUF); } STAILQ_REMOVE_HEAD(&sc->card.pf_head, pf_list); free(pf, M_DEVBUF); } STAILQ_INIT(&sc->card.pf_head); return (0); } static const struct pccard_product * pccard_do_product_lookup(device_t bus, device_t dev, const struct pccard_product *tab, size_t ent_size, pccard_product_match_fn matchfn) { const struct pccard_product *ent; int matches; uint32_t vendor; uint32_t prod; const char *vendorstr; const char *prodstr; const char *cis3str; const char *cis4str; #ifdef DIAGNOSTIC if (sizeof *ent > ent_size) panic("pccard_product_lookup: bogus ent_size %jd", (intmax_t) ent_size); #endif if (pccard_get_vendor(dev, &vendor)) return (NULL); if (pccard_get_product(dev, &prod)) return (NULL); if (pccard_get_vendor_str(dev, &vendorstr)) return (NULL); if (pccard_get_product_str(dev, &prodstr)) return (NULL); if (pccard_get_cis3_str(dev, &cis3str)) return (NULL); if (pccard_get_cis4_str(dev, &cis4str)) return (NULL); for (ent = tab; ent->pp_vendor != 0; ent = (const struct pccard_product *) ((const char *) ent + ent_size)) { matches = 1; if (ent->pp_vendor == PCCARD_VENDOR_ANY && ent->pp_product == PCCARD_PRODUCT_ANY && ent->pp_cis[0] == NULL && ent->pp_cis[1] == NULL) { if (ent->pp_name) device_printf(dev, "Total wildcard entry ignored for %s\n", ent->pp_name); continue; } if (matches && ent->pp_vendor != PCCARD_VENDOR_ANY && vendor != ent->pp_vendor) matches = 0; if (matches && ent->pp_product != PCCARD_PRODUCT_ANY && prod != ent->pp_product) matches = 0; if (matches && ent->pp_cis[0] && (vendorstr == NULL || strcmp(ent->pp_cis[0], vendorstr) != 0)) matches = 0; if (matches && ent->pp_cis[1] && (prodstr == NULL || strcmp(ent->pp_cis[1], prodstr) != 0)) matches = 0; if (matches && ent->pp_cis[2] && (cis3str == NULL || strcmp(ent->pp_cis[2], cis3str) != 0)) matches = 0; if (matches && ent->pp_cis[3] && (cis4str == NULL || strcmp(ent->pp_cis[3], cis4str) != 0)) matches = 0; if (matchfn != NULL) matches = (*matchfn)(dev, ent, matches); if (matches) return (ent); } return (NULL); } /** * @brief pccard_select_cfe * * Select a cfe entry to use. Should be called from the pccard's probe * routine after it knows for sure that it wants this card. * * XXX I think we need to make this symbol be static, ala the kobj stuff * we do for everything else. This is a quick hack. */ int pccard_select_cfe(device_t dev, int entry) { struct pccard_ivar *devi = PCCARD_IVAR(dev); struct pccard_function *pf = devi->pf; pccard_function_init(pf, entry); return (pf->cfe ? 0 : ENOMEM); } /* * Initialize a PCCARD function. May be called as long as the function is * disabled. * * Note: pccard_function_init should not keep resources allocated. It should * only set them up ala isa pnp, set the values in the rl lists, and return. * Any resource held after pccard_function_init is called is a bug. However, * the bus routines to get the resources also assume that pccard_function_init * does this, so they need to be fixed too. */ static void pccard_function_init(struct pccard_function *pf, int entry) { struct pccard_config_entry *cfe; struct pccard_ivar *devi = PCCARD_IVAR(pf->dev); struct resource_list *rl = &devi->resources; struct resource_list_entry *rle; struct resource *r = NULL; struct pccard_ce_iospace *ios; struct pccard_ce_memspace *mems; device_t bus; rman_res_t start, end, len; int i, rid, spaces; if (pf->pf_flags & PFF_ENABLED) { printf("pccard_function_init: function is enabled"); return; } /* * Driver probe routine requested a specific entry already * that succeeded. */ if (pf->cfe != NULL) return; /* * walk the list of configuration entries until we find one that * we can allocate all the resources to. */ bus = device_get_parent(pf->dev); STAILQ_FOREACH(cfe, &pf->cfe_head, cfe_list) { if (cfe->iftype != PCCARD_IFTYPE_IO) continue; if (entry != -1 && cfe->number != entry) continue; spaces = 0; for (i = 0; i < cfe->num_iospace; i++) { ios = cfe->iospace + i; start = ios->start; if (start) end = start + ios->length - 1; else end = ~0; DEVPRINTF((bus, "I/O rid %d start %#jx end %#jx\n", i, start, end)); rid = i; len = ios->length; r = bus_alloc_resource(bus, SYS_RES_IOPORT, &rid, start, end, len, rman_make_alignment_flags(len)); if (r == NULL) { DEVPRINTF((bus, "I/O rid %d failed\n", i)); goto not_this_one; } rle = resource_list_add(rl, SYS_RES_IOPORT, rid, rman_get_start(r), rman_get_end(r), len); if (rle == NULL) panic("Cannot add resource rid %d IOPORT", rid); rle->res = r; spaces++; } for (i = 0; i < cfe->num_memspace; i++) { mems = cfe->memspace + i; start = mems->cardaddr + mems->hostaddr; if (start) end = start + mems->length - 1; else end = ~0; DEVPRINTF((bus, "Memory rid %d start %#jx end %#jx\ncardaddr %#jx hostaddr %#jx length %#jx\n", i, start, end, mems->cardaddr, mems->hostaddr, mems->length)); rid = i; len = mems->length; r = bus_alloc_resource(bus, SYS_RES_MEMORY, &rid, start, end, len, rman_make_alignment_flags(len)); if (r == NULL) { DEVPRINTF((bus, "Memory rid %d failed\n", i)); // goto not_this_one; continue; } rle = resource_list_add(rl, SYS_RES_MEMORY, rid, rman_get_start(r), rman_get_end(r), len); if (rle == NULL) panic("Cannot add resource rid %d MEM", rid); rle->res = r; spaces++; } if (spaces == 0) { DEVPRINTF((bus, "Neither memory nor I/O mapped\n")); goto not_this_one; } if (cfe->irqmask) { rid = 0; r = bus_alloc_resource_any(bus, SYS_RES_IRQ, &rid, RF_SHAREABLE); if (r == NULL) { DEVPRINTF((bus, "IRQ rid %d failed\n", rid)); goto not_this_one; } rle = resource_list_add(rl, SYS_RES_IRQ, rid, rman_get_start(r), rman_get_end(r), 1); if (rle == NULL) panic("Cannot add resource rid %d IRQ", rid); rle->res = r; } /* If we get to here, we've allocated all we need */ pf->cfe = cfe; break; not_this_one:; DEVPRVERBOSE((bus, "Allocation failed for cfe %d\n", cfe->number)); resource_list_purge(rl); } } /* * Free resources allocated by pccard_function_init(), May be called as long * as the function is disabled. * * NOTE: This function should be unnecessary. pccard_function_init should * never keep resources initialized. */ static void pccard_function_free(struct pccard_function *pf) { struct pccard_ivar *devi = PCCARD_IVAR(pf->dev); struct resource_list_entry *rle; if (pf->pf_flags & PFF_ENABLED) { printf("pccard_function_free: function is enabled"); return; } STAILQ_FOREACH(rle, &devi->resources, link) { if (rle->res) { if (rman_get_device(rle->res) != pf->sc->dev) device_printf(pf->sc->dev, "function_free: Resource still owned by " "child, oops. " "(type=%d, rid=%d, addr=%#jx)\n", rle->type, rle->rid, rman_get_start(rle->res)); BUS_RELEASE_RESOURCE(device_get_parent(pf->sc->dev), pf->sc->dev, rle->type, rle->rid, rle->res); rle->res = NULL; } } resource_list_free(&devi->resources); } static void pccard_mfc_adjust_iobase(struct pccard_function *pf, rman_res_t addr, rman_res_t offset, rman_res_t size) { bus_size_t iosize, tmp; if (addr != 0) { if (pf->pf_mfc_iomax == 0) { pf->pf_mfc_iobase = addr + offset; pf->pf_mfc_iomax = pf->pf_mfc_iobase + size; } else { /* this makes the assumption that nothing overlaps */ if (pf->pf_mfc_iobase > addr + offset) pf->pf_mfc_iobase = addr + offset; if (pf->pf_mfc_iomax < addr + offset + size) pf->pf_mfc_iomax = 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--; DEVPRINTF((pf->dev, "MFC: I/O base %#jx IOSIZE %#jx\n", (uintmax_t)pf->pf_mfc_iobase, (uintmax_t)(iosize + 1))); 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); } /* Enable a PCCARD function */ static int pccard_function_enable(struct pccard_function *pf) { struct pccard_function *tmp; int reg; device_t dev = pf->sc->dev; if (pf->cfe == NULL) { DEVPRVERBOSE((dev, "No config entry could be allocated.\n")); return (ENOMEM); } if (pf->pf_flags & PFF_ENABLED) return (0); pf->sc->sc_enabled_count++; /* * 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_anywhere(dev, SYS_RES_MEMORY, &pf->ccr_rid, PCCARD_MEM_PAGE_SIZE, RF_ACTIVE); if (!pf->ccr_res) goto bad; DEVPRINTF((dev, "ccr_res == %#jx-%#jx, base=%#x\n", rman_get_start(pf->ccr_res), rman_get_end(pf->ccr_res), pf->ccr_base)); CARD_SET_RES_FLAGS(device_get_parent(dev), dev, SYS_RES_MEMORY, pf->ccr_rid, PCCARD_A_MEM_ATTR); CARD_SET_MEMORY_OFFSET(device_get_parent(dev), dev, pf->ccr_rid, pf->ccr_base, &pf->pf_ccr_offset); pf->pf_ccrt = rman_get_bustag(pf->ccr_res); pf->pf_ccrh = rman_get_bushandle(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); /* PCCARD_CCR_OPTION_IRQ_ENABLE set elsewhere as needed */ } 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)) pccard_mfc_adjust_iobase(pf, 0, 0, 0); #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. */ pf->sc->sc_enabled_count--; DEVPRINTF((dev, "bad --enabled_count = %d\n", pf->sc->sc_enabled_count)); return (1); } /* Disable PCCARD function. */ static 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_disable: function not initialized"); if ((pf->pf_flags & PFF_ENABLED) == 0) return; if (pf->intr_handler != NULL) { struct pccard_ivar *devi = PCCARD_IVAR(pf->dev); struct resource_list_entry *rle = resource_list_find(&devi->resources, SYS_RES_IRQ, 0); if (rle == NULL) panic("Can't disable an interrupt with no IRQ res\n"); BUS_TEARDOWN_INTR(dev, pf->dev, rle->res, pf->intr_handler_cookie); } /* * 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. */ pf->sc->sc_enabled_count--; } #define PCCARD_NPORT 2 #define PCCARD_NMEM 5 #define PCCARD_NIRQ 1 #define PCCARD_NDRQ 0 static int pccard_probe(device_t dev) { device_set_desc(dev, "16-bit PCCard bus"); return (0); } static int pccard_attach(device_t dev) { struct pccard_softc *sc = PCCARD_SOFTC(dev); int err; sc->dev = dev; sc->sc_enabled_count = 0; if ((err = pccard_device_create(sc)) != 0) return (err); STAILQ_INIT(&sc->card.pf_head); return (bus_generic_attach(dev)); } static int pccard_detach(device_t dev) { pccard_detach_card(dev); pccard_device_destroy(device_get_softc(dev)); return (0); } static int pccard_suspend(device_t self) { pccard_detach_card(self); return (0); } static int pccard_resume(device_t self) { return (0); } 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 != NULL) { 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 = PCCARD_IVAR(child); struct resource_list *rl = &devi->resources; int retval = 0; retval += bus_print_child_header(dev, child); retval += printf(" at"); if (devi != NULL) { 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(" function %d config %d", devi->pf->number, devi->pf->cfe->number); } retval += bus_print_child_footer(dev, child); return (retval); } static int pccard_set_resource(device_t dev, device_t child, int type, int rid, rman_res_t start, rman_res_t count) { struct pccard_ivar *devi = PCCARD_IVAR(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); if (NULL != resource_list_alloc(rl, device_get_parent(dev), dev, type, &rid, start, start + count - 1, count, 0)) return 0; else return ENOMEM; } static int pccard_get_resource(device_t dev, device_t child, int type, int rid, rman_res_t *startp, rman_res_t *countp) { struct pccard_ivar *devi = PCCARD_IVAR(child); struct resource_list *rl = &devi->resources; struct resource_list_entry *rle; rle = resource_list_find(rl, type, rid); if (rle == NULL) return (ENOENT); if (startp != NULL) *startp = rle->start; if (countp != NULL) *countp = rle->count; return (0); } static void pccard_delete_resource(device_t dev, device_t child, int type, int rid) { struct pccard_ivar *devi = PCCARD_IVAR(child); struct resource_list *rl = &devi->resources; resource_list_delete(rl, type, rid); } static int pccard_set_res_flags(device_t dev, device_t child, int type, int rid, u_long flags) { return (CARD_SET_RES_FLAGS(device_get_parent(dev), child, type, rid, flags)); } static int pccard_set_memory_offset(device_t dev, device_t child, int rid, uint32_t offset, uint32_t *deltap) { return (CARD_SET_MEMORY_OFFSET(device_get_parent(dev), child, rid, offset, deltap)); } static void pccard_probe_nomatch(device_t bus, device_t child) { struct pccard_ivar *devi = PCCARD_IVAR(child); struct pccard_function *pf = devi->pf; struct pccard_softc *sc = PCCARD_SOFTC(bus); int i; device_printf(bus, ""); printf(" (manufacturer=0x%04x, product=0x%04x, function_type=%d) " "at function %d\n", sc->card.manufacturer, sc->card.product, pf->function, pf->number); device_printf(bus, " CIS info: "); for (i = 0; sc->card.cis1_info[i] != NULL && i < 4; i++) printf("%s%s", i > 0 ? ", " : "", sc->card.cis1_info[i]); printf("\n"); return; } static int pccard_child_location_str(device_t bus, device_t child, char *buf, size_t buflen) { struct pccard_ivar *devi = PCCARD_IVAR(child); struct pccard_function *pf = devi->pf; snprintf(buf, buflen, "function=%d", pf->number); return (0); } static int pccard_child_pnpinfo_str(device_t bus, device_t child, char *buf, size_t buflen) { struct pccard_ivar *devi = PCCARD_IVAR(child); struct pccard_function *pf = devi->pf; struct pccard_softc *sc = PCCARD_SOFTC(bus); char cis0[128], cis1[128]; devctl_safe_quote(cis0, sc->card.cis1_info[0], sizeof(cis0)); devctl_safe_quote(cis1, sc->card.cis1_info[1], sizeof(cis1)); snprintf(buf, buflen, "manufacturer=0x%04x product=0x%04x " "cisvendor=\"%s\" cisproduct=\"%s\" function_type=%d", sc->card.manufacturer, sc->card.product, cis0, cis1, pf->function); return (0); } static int pccard_read_ivar(device_t bus, device_t child, int which, uintptr_t *result) { struct pccard_ivar *devi = PCCARD_IVAR(child); struct pccard_function *pf = devi->pf; struct pccard_softc *sc = PCCARD_SOFTC(bus); if (!pf) panic("No pccard function pointer"); switch (which) { default: return (EINVAL); case PCCARD_IVAR_FUNCE_DISK: *(uint16_t *)result = pf->pf_funce_disk_interface | (pf->pf_funce_disk_power << 8); break; case PCCARD_IVAR_ETHADDR: bcopy(pf->pf_funce_lan_nid, result, ETHER_ADDR_LEN); break; case PCCARD_IVAR_VENDOR: *(uint32_t *)result = sc->card.manufacturer; break; case PCCARD_IVAR_PRODUCT: *(uint32_t *)result = sc->card.product; break; case PCCARD_IVAR_PRODEXT: *(uint16_t *)result = sc->card.prodext; break; case PCCARD_IVAR_FUNCTION: *(uint32_t *)result = pf->function; break; case PCCARD_IVAR_FUNCTION_NUMBER: *(uint32_t *)result = pf->number; break; case PCCARD_IVAR_VENDOR_STR: *(const char **)result = sc->card.cis1_info[0]; break; case PCCARD_IVAR_PRODUCT_STR: *(const char **)result = sc->card.cis1_info[1]; break; case PCCARD_IVAR_CIS3_STR: *(const char **)result = sc->card.cis1_info[2]; break; case PCCARD_IVAR_CIS4_STR: *(const char **)result = sc->card.cis1_info[3]; break; } return (0); } static void pccard_driver_added(device_t dev, driver_t *driver) { struct pccard_softc *sc = PCCARD_SOFTC(dev); struct pccard_function *pf; device_t child; STAILQ_FOREACH(pf, &sc->card.pf_head, pf_list) { if (STAILQ_EMPTY(&pf->cfe_head)) continue; child = pf->dev; if (device_get_state(child) != DS_NOTPRESENT) continue; pccard_probe_and_attach_child(dev, child, pf); } return; } static struct resource * pccard_alloc_resource(device_t dev, device_t child, int type, int *rid, rman_res_t start, rman_res_t end, rman_res_t count, u_int flags) { struct pccard_ivar *dinfo; struct resource_list_entry *rle = NULL; int passthrough = (device_get_parent(child) != dev); int isdefault = (RMAN_IS_DEFAULT_RANGE(start, end) && count == 1); struct resource *r = NULL; /* XXX I'm no longer sure this is right */ if (passthrough) { return (BUS_ALLOC_RESOURCE(device_get_parent(dev), child, type, rid, start, end, count, flags)); } dinfo = device_get_ivars(child); rle = resource_list_find(&dinfo->resources, type, *rid); if (rle == NULL && isdefault) return (NULL); /* no resource of that type/rid */ if (rle == NULL || rle->res == NULL) { /* XXX Need to adjust flags */ r = bus_alloc_resource(dev, type, rid, start, end, count, flags); if (r == NULL) goto bad; resource_list_add(&dinfo->resources, type, *rid, rman_get_start(r), rman_get_end(r), count); rle = resource_list_find(&dinfo->resources, type, *rid); if (!rle) goto bad; rle->res = r; } /* * If dev doesn't own the device, then we can't give this device * out. */ if (rman_get_device(rle->res) != dev) return (NULL); rman_set_device(rle->res, child); if (flags & RF_ACTIVE) BUS_ACTIVATE_RESOURCE(dev, child, type, *rid, rle->res); return (rle->res); bad:; device_printf(dev, "WARNING: Resource not reserved by pccard\n"); return (NULL); } static int pccard_release_resource(device_t dev, device_t child, int type, int rid, struct resource *r) { struct pccard_ivar *dinfo; int passthrough = (device_get_parent(child) != dev); struct resource_list_entry *rle = NULL; if (passthrough) return BUS_RELEASE_RESOURCE(device_get_parent(dev), child, type, rid, r); dinfo = device_get_ivars(child); rle = resource_list_find(&dinfo->resources, type, rid); if (!rle) { device_printf(dev, "Allocated resource not found, " "%d %#x %#jx %#jx\n", type, rid, rman_get_start(r), rman_get_size(r)); return ENOENT; } if (!rle->res) { device_printf(dev, "Allocated resource not recorded\n"); return ENOENT; } /* * Deactivate the resource (since it is being released), and * assign it to the bus. */ BUS_DEACTIVATE_RESOURCE(dev, child, type, rid, rle->res); rman_set_device(rle->res, dev); return (0); } static void pccard_child_detached(device_t parent, device_t dev) { struct pccard_ivar *ivar = PCCARD_IVAR(dev); struct pccard_function *pf = ivar->pf; pccard_function_disable(pf); } static int pccard_filter(void *arg) { struct pccard_function *pf = (struct pccard_function*) arg; int reg; int doisr = 1; /* * MFC cards know if they interrupted, so we have to ack the * interrupt and call the ISR. Non-MFC cards don't have these * bits, so they always get called. Many non-MFC cards have * this bit set always upon read, but some do not. * * We always ack the interrupt, even if there's no ISR * for the card. This is done on the theory that acking * the interrupt will pacify the card enough to keep an * interrupt storm from happening. Of course this won't * help in the non-MFC case. * * This has no impact for MPSAFEness of the client drivers. * We register this with whatever flags the intr_handler * was registered with. All these functions are MPSAFE. */ if (pccard_mfc(pf->sc)) { reg = pccard_ccr_read(pf, PCCARD_CCR_STATUS); if (reg & PCCARD_CCR_STATUS_INTR) pccard_ccr_write(pf, PCCARD_CCR_STATUS, reg & ~PCCARD_CCR_STATUS_INTR); else doisr = 0; } if (doisr) { if (pf->intr_filter != NULL) return (pf->intr_filter(pf->intr_handler_arg)); return (FILTER_SCHEDULE_THREAD); } return (FILTER_STRAY); } static void pccard_intr(void *arg) { struct pccard_function *pf = (struct pccard_function*) arg; pf->intr_handler(pf->intr_handler_arg); } static int pccard_setup_intr(device_t dev, device_t child, struct resource *irq, int flags, driver_filter_t *filt, driver_intr_t *intr, void *arg, void **cookiep) { struct pccard_softc *sc = PCCARD_SOFTC(dev); struct pccard_ivar *ivar = PCCARD_IVAR(child); struct pccard_function *pf = ivar->pf; int err; if (pf->intr_filter != NULL || pf->intr_handler != NULL) panic("Only one interrupt handler per function allowed"); err = bus_generic_setup_intr(dev, child, irq, flags, pccard_filter, intr ? pccard_intr : NULL, pf, cookiep); if (err != 0) return (err); pf->intr_filter = filt; pf->intr_handler = intr; pf->intr_handler_arg = arg; pf->intr_handler_cookie = *cookiep; if (pccard_mfc(sc)) { pccard_ccr_write(pf, PCCARD_CCR_OPTION, pccard_ccr_read(pf, PCCARD_CCR_OPTION) | PCCARD_CCR_OPTION_IREQ_ENABLE); } return (0); } static int pccard_teardown_intr(device_t dev, device_t child, struct resource *r, void *cookie) { struct pccard_softc *sc = PCCARD_SOFTC(dev); struct pccard_ivar *ivar = PCCARD_IVAR(child); struct pccard_function *pf = ivar->pf; int ret; if (pccard_mfc(sc)) { pccard_ccr_write(pf, PCCARD_CCR_OPTION, pccard_ccr_read(pf, PCCARD_CCR_OPTION) & ~PCCARD_CCR_OPTION_IREQ_ENABLE); } ret = bus_generic_teardown_intr(dev, child, r, cookie); if (ret == 0) { pf->intr_handler = NULL; pf->intr_handler_arg = NULL; pf->intr_handler_cookie = NULL; } return (ret); } static int pccard_activate_resource(device_t brdev, device_t child, int type, int rid, struct resource *r) { struct pccard_ivar *ivar = PCCARD_IVAR(child); struct pccard_function *pf = ivar->pf; switch(type) { case SYS_RES_IOPORT: /* * We need to adjust IOBASE[01] and IOSIZE if we're an MFC * card. */ if (pccard_mfc(pf->sc)) pccard_mfc_adjust_iobase(pf, rman_get_start(r), 0, rman_get_size(r)); break; default: break; } return (bus_generic_activate_resource(brdev, child, type, rid, r)); } static int pccard_deactivate_resource(device_t brdev, device_t child, int type, int rid, struct resource *r) { /* XXX undo pccard_activate_resource? XXX */ return (bus_generic_deactivate_resource(brdev, child, type, rid, r)); } static int pccard_attr_read_impl(device_t brdev, device_t child, uint32_t offset, uint8_t *val) { struct pccard_ivar *devi = PCCARD_IVAR(child); struct pccard_function *pf = devi->pf; /* * Optimization. Most of the time, devices want to access * the same page of the attribute memory that the CCR is in. * We take advantage of this fact here. */ if (offset / PCCARD_MEM_PAGE_SIZE == pf->ccr_base / PCCARD_MEM_PAGE_SIZE) *val = bus_space_read_1(pf->pf_ccrt, pf->pf_ccrh, offset % PCCARD_MEM_PAGE_SIZE); else { CARD_SET_MEMORY_OFFSET(brdev, child, pf->ccr_rid, offset, &offset); *val = bus_space_read_1(pf->pf_ccrt, pf->pf_ccrh, offset); CARD_SET_MEMORY_OFFSET(brdev, child, pf->ccr_rid, pf->ccr_base, &offset); } return 0; } static int pccard_attr_write_impl(device_t brdev, device_t child, uint32_t offset, uint8_t val) { struct pccard_ivar *devi = PCCARD_IVAR(child); struct pccard_function *pf = devi->pf; /* * Optimization. Most of the time, devices want to access * the same page of the attribute memory that the CCR is in. * We take advantage of this fact here. */ if (offset / PCCARD_MEM_PAGE_SIZE == pf->ccr_base / PCCARD_MEM_PAGE_SIZE) bus_space_write_1(pf->pf_ccrt, pf->pf_ccrh, offset % PCCARD_MEM_PAGE_SIZE, val); else { CARD_SET_MEMORY_OFFSET(brdev, child, pf->ccr_rid, offset, &offset); bus_space_write_1(pf->pf_ccrt, pf->pf_ccrh, offset, val); CARD_SET_MEMORY_OFFSET(brdev, child, pf->ccr_rid, pf->ccr_base, &offset); } return 0; } static int pccard_ccr_read_impl(device_t brdev, device_t child, uint32_t offset, uint8_t *val) { struct pccard_ivar *devi = PCCARD_IVAR(child); *val = pccard_ccr_read(devi->pf, offset); DEVPRINTF((child, "ccr_read of %#x (%#x) is %#x\n", offset, devi->pf->pf_ccr_offset, *val)); return 0; } static int pccard_ccr_write_impl(device_t brdev, device_t child, uint32_t offset, uint8_t val) { struct pccard_ivar *devi = PCCARD_IVAR(child); struct pccard_function *pf = devi->pf; /* * Can't use pccard_ccr_write since client drivers may access * registers not contained in the 'mask' if they are non-standard. */ DEVPRINTF((child, "ccr_write of %#x to %#x (%#x)\n", val, offset, devi->pf->pf_ccr_offset)); bus_space_write_1(pf->pf_ccrt, pf->pf_ccrh, pf->pf_ccr_offset + offset, val); return 0; } static device_method_t pccard_methods[] = { /* Device interface */ DEVMETHOD(device_probe, pccard_probe), DEVMETHOD(device_attach, pccard_attach), DEVMETHOD(device_detach, pccard_detach), DEVMETHOD(device_shutdown, bus_generic_shutdown), DEVMETHOD(device_suspend, pccard_suspend), DEVMETHOD(device_resume, pccard_resume), /* Bus interface */ DEVMETHOD(bus_print_child, pccard_print_child), DEVMETHOD(bus_driver_added, pccard_driver_added), DEVMETHOD(bus_child_detached, pccard_child_detached), DEVMETHOD(bus_alloc_resource, pccard_alloc_resource), DEVMETHOD(bus_release_resource, pccard_release_resource), DEVMETHOD(bus_activate_resource, pccard_activate_resource), DEVMETHOD(bus_deactivate_resource, pccard_deactivate_resource), DEVMETHOD(bus_setup_intr, pccard_setup_intr), DEVMETHOD(bus_teardown_intr, pccard_teardown_intr), DEVMETHOD(bus_set_resource, pccard_set_resource), DEVMETHOD(bus_get_resource, pccard_get_resource), DEVMETHOD(bus_delete_resource, pccard_delete_resource), DEVMETHOD(bus_probe_nomatch, pccard_probe_nomatch), DEVMETHOD(bus_read_ivar, pccard_read_ivar), DEVMETHOD(bus_child_pnpinfo_str, pccard_child_pnpinfo_str), DEVMETHOD(bus_child_location_str, pccard_child_location_str), /* Card Interface */ DEVMETHOD(card_set_res_flags, pccard_set_res_flags), DEVMETHOD(card_set_memory_offset, pccard_set_memory_offset), DEVMETHOD(card_attach_card, pccard_attach_card), DEVMETHOD(card_detach_card, pccard_detach_card), DEVMETHOD(card_do_product_lookup, pccard_do_product_lookup), DEVMETHOD(card_cis_scan, pccard_scan_cis), DEVMETHOD(card_attr_read, pccard_attr_read_impl), DEVMETHOD(card_attr_write, pccard_attr_write_impl), DEVMETHOD(card_ccr_read, pccard_ccr_read_impl), DEVMETHOD(card_ccr_write, pccard_ccr_write_impl), { 0, 0 } }; static driver_t pccard_driver = { "pccard", pccard_methods, sizeof(struct pccard_softc) }; devclass_t pccard_devclass; /* Maybe we need to have a slot device? */ DRIVER_MODULE(pccard, pcic, pccard_driver, pccard_devclass, 0, 0); DRIVER_MODULE(pccard, cbb, pccard_driver, pccard_devclass, 0, 0); MODULE_VERSION(pccard, 1); Index: head/sys/dev/pccbb/pccbb.c =================================================================== --- head/sys/dev/pccbb/pccbb.c (revision 331270) +++ head/sys/dev/pccbb/pccbb.c (revision 331271) @@ -1,1606 +1,1597 @@ /*- * SPDX-License-Identifier: BSD-2-Clause-FreeBSD * * Copyright (c) 2002-2004 M. Warner Losh. * Copyright (c) 2000-2001 Jonathan Chen. * 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. * */ /*- * Copyright (c) 1998, 1999 and 2000 * HAYAKAWA Koichi. 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 HAYAKAWA Koichi. * 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. */ /* * Driver for PCI to CardBus Bridge chips * and PCI to PCMCIA Bridge chips * and ISA to PCMCIA host adapters * and C Bus to PCMCIA host adapters * * References: * TI Datasheets: * http://www-s.ti.com/cgi-bin/sc/generic2.cgi?family=PCI+CARDBUS+CONTROLLERS * * Written by Jonathan Chen * The author would like to acknowledge: * * HAYAKAWA Koichi: Author of the NetBSD code for the same thing * * Warner Losh: Newbus/newcard guru and author of the pccard side of things * * YAMAMOTO Shigeru: Author of another FreeBSD cardbus driver * * David Cross: Author of the initial ugly hack for a specific cardbus card */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "power_if.h" #include "card_if.h" #include "pcib_if.h" #define DPRINTF(x) do { if (cbb_debug) printf x; } while (0) #define DEVPRINTF(x) do { if (cbb_debug) device_printf x; } while (0) #define PCI_MASK_CONFIG(DEV,REG,MASK,SIZE) \ pci_write_config(DEV, REG, pci_read_config(DEV, REG, SIZE) MASK, SIZE) #define PCI_MASK2_CONFIG(DEV,REG,MASK1,MASK2,SIZE) \ pci_write_config(DEV, REG, ( \ pci_read_config(DEV, REG, SIZE) MASK1) MASK2, SIZE) #define CBB_CARD_PRESENT(s) ((s & CBB_STATE_CD) == 0) #define CBB_START_MEM 0x88000000 #define CBB_START_32_IO 0x1000 #define CBB_START_16_IO 0x100 devclass_t cbb_devclass; /* sysctl vars */ static SYSCTL_NODE(_hw, OID_AUTO, cbb, CTLFLAG_RD, 0, "CBB parameters"); /* There's no way to say TUNEABLE_LONG to get the right types */ u_long cbb_start_mem = CBB_START_MEM; SYSCTL_ULONG(_hw_cbb, OID_AUTO, start_memory, CTLFLAG_RWTUN, &cbb_start_mem, CBB_START_MEM, "Starting address for memory allocations"); u_long cbb_start_16_io = CBB_START_16_IO; SYSCTL_ULONG(_hw_cbb, OID_AUTO, start_16_io, CTLFLAG_RWTUN, &cbb_start_16_io, CBB_START_16_IO, "Starting ioport for 16-bit cards"); u_long cbb_start_32_io = CBB_START_32_IO; SYSCTL_ULONG(_hw_cbb, OID_AUTO, start_32_io, CTLFLAG_RWTUN, &cbb_start_32_io, CBB_START_32_IO, "Starting ioport for 32-bit cards"); int cbb_debug = 0; SYSCTL_INT(_hw_cbb, OID_AUTO, debug, CTLFLAG_RWTUN, &cbb_debug, 0, "Verbose cardbus bridge debugging"); static void cbb_insert(struct cbb_softc *sc); static void cbb_removal(struct cbb_softc *sc); static uint32_t cbb_detect_voltage(device_t brdev); static int cbb_cardbus_reset_power(device_t brdev, device_t child, int on); static int cbb_cardbus_io_open(device_t brdev, int win, uint32_t start, uint32_t end); static int cbb_cardbus_mem_open(device_t brdev, int win, uint32_t start, uint32_t end); static void cbb_cardbus_auto_open(struct cbb_softc *sc, int type); static int cbb_cardbus_activate_resource(device_t brdev, device_t child, int type, int rid, struct resource *res); static int cbb_cardbus_deactivate_resource(device_t brdev, device_t child, int type, int rid, struct resource *res); static struct resource *cbb_cardbus_alloc_resource(device_t brdev, device_t child, int type, int *rid, rman_res_t start, rman_res_t end, rman_res_t count, u_int flags); static int cbb_cardbus_release_resource(device_t brdev, device_t child, int type, int rid, struct resource *res); static int cbb_cardbus_power_enable_socket(device_t brdev, device_t child); static int cbb_cardbus_power_disable_socket(device_t brdev, device_t child); static int cbb_func_filt(void *arg); static void cbb_func_intr(void *arg); static void cbb_remove_res(struct cbb_softc *sc, struct resource *res) { struct cbb_reslist *rle; SLIST_FOREACH(rle, &sc->rl, link) { if (rle->res == res) { SLIST_REMOVE(&sc->rl, rle, cbb_reslist, link); free(rle, M_DEVBUF); return; } } } static struct resource * cbb_find_res(struct cbb_softc *sc, int type, int rid) { struct cbb_reslist *rle; SLIST_FOREACH(rle, &sc->rl, link) if (SYS_RES_MEMORY == rle->type && rid == rle->rid) return (rle->res); return (NULL); } static void cbb_insert_res(struct cbb_softc *sc, struct resource *res, int type, int rid) { struct cbb_reslist *rle; /* * Need to record allocated resource so we can iterate through * it later. */ rle = malloc(sizeof(struct cbb_reslist), M_DEVBUF, M_NOWAIT); if (rle == NULL) panic("cbb_cardbus_alloc_resource: can't record entry!"); rle->res = res; rle->type = type; rle->rid = rid; SLIST_INSERT_HEAD(&sc->rl, rle, link); } static void cbb_destroy_res(struct cbb_softc *sc) { struct cbb_reslist *rle; while ((rle = SLIST_FIRST(&sc->rl)) != NULL) { device_printf(sc->dev, "Danger Will Robinson: Resource " "left allocated! This is a bug... " "(rid=%x, type=%d, addr=%jx)\n", rle->rid, rle->type, rman_get_start(rle->res)); SLIST_REMOVE_HEAD(&sc->rl, link); free(rle, M_DEVBUF); } } /* * Disable function interrupts by telling the bridge to generate IRQ1 * interrupts. These interrupts aren't really generated by the chip, since * IRQ1 is reserved. Some chipsets assert INTA# inappropriately during * initialization, so this helps to work around the problem. * * XXX We can't do this workaround for all chipsets, because this * XXX causes interference with the keyboard because somechipsets will * XXX actually signal IRQ1 over their serial interrupt connections to * XXX the south bridge. Disable it it for now. */ void cbb_disable_func_intr(struct cbb_softc *sc) { #if 0 uint8_t reg; reg = (exca_getb(&sc->exca[0], EXCA_INTR) & ~EXCA_INTR_IRQ_MASK) | EXCA_INTR_IRQ_RESERVED1; exca_putb(&sc->exca[0], EXCA_INTR, reg); #endif } /* * Enable function interrupts. We turn on function interrupts when the card * requests an interrupt. The PCMCIA standard says that we should set * the lower 4 bits to 0 to route via PCI. Note: we call this for both * CardBus and R2 (PC Card) cases, but it should have no effect on CardBus * cards. */ static void cbb_enable_func_intr(struct cbb_softc *sc) { uint8_t reg; reg = (exca_getb(&sc->exca[0], EXCA_INTR) & ~EXCA_INTR_IRQ_MASK) | EXCA_INTR_IRQ_NONE; exca_putb(&sc->exca[0], EXCA_INTR, reg); } int cbb_detach(device_t brdev) { struct cbb_softc *sc = device_get_softc(brdev); device_t *devlist; int tmp, tries, error, numdevs; /* * Before we delete the children (which we have to do because * attach doesn't check for children busses correctly), we have * to detach the children. Even if we didn't need to delete the * children, we have to detach them. */ error = bus_generic_detach(brdev); if (error != 0) return (error); /* * Since the attach routine doesn't search for children before it * attaches them to this device, we must delete them here in order * for the kldload/unload case to work. If we failed to do that, then * we'd get duplicate devices when cbb.ko was reloaded. */ tries = 10; do { error = device_get_children(brdev, &devlist, &numdevs); if (error == 0) break; /* * Try hard to cope with low memory. */ if (error == ENOMEM) { pause("cbbnomem", 1); continue; } } while (tries-- > 0); for (tmp = 0; tmp < numdevs; tmp++) device_delete_child(brdev, devlist[tmp]); free(devlist, M_TEMP); /* Turn off the interrupts */ cbb_set(sc, CBB_SOCKET_MASK, 0); /* reset 16-bit pcmcia bus */ exca_clrb(&sc->exca[0], EXCA_INTR, EXCA_INTR_RESET); /* turn off power */ cbb_power(brdev, CARD_OFF); /* Ack the interrupt */ cbb_set(sc, CBB_SOCKET_EVENT, 0xffffffff); /* * Wait for the thread to die. kproc_exit will do a wakeup * on the event thread's struct proc * so that we know it is * safe to proceed. IF the thread is running, set the please * die flag and wait for it to comply. Since the wakeup on * the event thread happens only in kproc_exit, we don't * need to loop here. */ bus_teardown_intr(brdev, sc->irq_res, sc->intrhand); mtx_lock(&sc->mtx); sc->flags |= CBB_KTHREAD_DONE; while (sc->flags & CBB_KTHREAD_RUNNING) { DEVPRINTF((sc->dev, "Waiting for thread to die\n")); wakeup(&sc->intrhand); msleep(sc->event_thread, &sc->mtx, PWAIT, "cbbun", 0); } mtx_unlock(&sc->mtx); bus_release_resource(brdev, SYS_RES_IRQ, 0, sc->irq_res); bus_release_resource(brdev, SYS_RES_MEMORY, CBBR_SOCKBASE, sc->base_res); mtx_destroy(&sc->mtx); return (0); } int cbb_setup_intr(device_t dev, device_t child, struct resource *irq, int flags, driver_filter_t *filt, driver_intr_t *intr, void *arg, void **cookiep) { struct cbb_intrhand *ih; struct cbb_softc *sc = device_get_softc(dev); int err; if (filt == NULL && intr == NULL) return (EINVAL); ih = malloc(sizeof(struct cbb_intrhand), M_DEVBUF, M_NOWAIT); if (ih == NULL) return (ENOMEM); *cookiep = ih; ih->filt = filt; ih->intr = intr; ih->arg = arg; ih->sc = sc; /* * XXX need to turn on ISA interrupts, if we ever support them, but * XXX for now that's all we need to do. */ err = BUS_SETUP_INTR(device_get_parent(dev), child, irq, flags, filt ? cbb_func_filt : NULL, intr ? cbb_func_intr : NULL, ih, &ih->cookie); if (err != 0) { free(ih, M_DEVBUF); return (err); } cbb_enable_func_intr(sc); sc->cardok = 1; return 0; } int cbb_teardown_intr(device_t dev, device_t child, struct resource *irq, void *cookie) { struct cbb_intrhand *ih; int err; /* XXX Need to do different things for ISA interrupts. */ ih = (struct cbb_intrhand *) cookie; err = BUS_TEARDOWN_INTR(device_get_parent(dev), child, irq, ih->cookie); if (err != 0) return (err); free(ih, M_DEVBUF); return (0); } void cbb_driver_added(device_t brdev, driver_t *driver) { struct cbb_softc *sc = device_get_softc(brdev); device_t *devlist; device_t dev; int tmp; int numdevs; int wake = 0; DEVICE_IDENTIFY(driver, brdev); tmp = device_get_children(brdev, &devlist, &numdevs); if (tmp != 0) { device_printf(brdev, "Cannot get children list, no reprobe\n"); return; } for (tmp = 0; tmp < numdevs; tmp++) { dev = devlist[tmp]; if (device_get_state(dev) == DS_NOTPRESENT && device_probe_and_attach(dev) == 0) wake++; } free(devlist, M_TEMP); if (wake > 0) wakeup(&sc->intrhand); } void cbb_child_detached(device_t brdev, device_t child) { struct cbb_softc *sc = device_get_softc(brdev); /* I'm not sure we even need this */ if (child != sc->cbdev && child != sc->exca[0].pccarddev) device_printf(brdev, "Unknown child detached: %s\n", device_get_nameunit(child)); } /************************************************************************/ /* Kthreads */ /************************************************************************/ void cbb_event_thread(void *arg) { struct cbb_softc *sc = arg; uint32_t status; int err; int not_a_card = 0; /* * We need to act as a power sequencer on startup. Delay 2s/channel * to ensure the other channels have had a chance to come up. We likely * should add a lock that's shared on a per-slot basis so that only * one power event can happen per slot at a time. */ pause("cbbstart", hz * device_get_unit(sc->dev) * 2); mtx_lock(&sc->mtx); sc->flags |= CBB_KTHREAD_RUNNING; while ((sc->flags & CBB_KTHREAD_DONE) == 0) { mtx_unlock(&sc->mtx); - /* - * We take out Giant here because we need it deep, - * down in the bowels of the vm system for mapping the - * memory we need to read the CIS. In addition, since - * we are adding/deleting devices from the dev tree, - * and that code isn't MP safe, we have to hold Giant. - */ - mtx_lock(&Giant); status = cbb_get(sc, CBB_SOCKET_STATE); DPRINTF(("Status is 0x%x\n", status)); if (!CBB_CARD_PRESENT(status)) { not_a_card = 0; /* We know card type */ cbb_removal(sc); } else if (status & CBB_STATE_NOT_A_CARD) { /* * Up to 10 times, try to rescan the card when we see * NOT_A_CARD. 10 is somehwat arbitrary. When this * pathology hits, there's a ~40% chance each try will * fail. 10 tries takes about 5s and results in a * 99.99% certainty of the results. */ if (not_a_card++ < 10) { DEVPRINTF((sc->dev, "Not a card bit set, rescanning\n")); cbb_setb(sc, CBB_SOCKET_FORCE, CBB_FORCE_CV_TEST); } else { device_printf(sc->dev, "Can't determine card type\n"); } } else { not_a_card = 0; /* We know card type */ cbb_insert(sc); } - mtx_unlock(&Giant); /* * First time through we need to tell mountroot that we're * done. */ if (sc->sc_root_token) { root_mount_rel(sc->sc_root_token); sc->sc_root_token = NULL; } /* * Wait until it has been 250ms since the last time we * get an interrupt. We handle the rest of the interrupt * at the top of the loop. Although we clear the bit in the * ISR, we signal sc->cv from the detach path after we've * set the CBB_KTHREAD_DONE bit, so we can't do a simple * 250ms sleep here. * * In our ISR, we turn off the card changed interrupt. Turn * them back on here before we wait for them to happen. We * turn them on/off so that we can tolerate a large latency * between the time we signal cbb_event_thread and it gets * a chance to run. */ mtx_lock(&sc->mtx); cbb_setb(sc, CBB_SOCKET_MASK, CBB_SOCKET_MASK_CD | CBB_SOCKET_MASK_CSTS); msleep(&sc->intrhand, &sc->mtx, 0, "-", 0); err = 0; while (err != EWOULDBLOCK && (sc->flags & CBB_KTHREAD_DONE) == 0) err = msleep(&sc->intrhand, &sc->mtx, 0, "-", hz / 5); } DEVPRINTF((sc->dev, "Thread terminating\n")); sc->flags &= ~CBB_KTHREAD_RUNNING; mtx_unlock(&sc->mtx); kproc_exit(0); } /************************************************************************/ /* Insert/removal */ /************************************************************************/ static void cbb_insert(struct cbb_softc *sc) { uint32_t sockevent, sockstate; sockevent = cbb_get(sc, CBB_SOCKET_EVENT); sockstate = cbb_get(sc, CBB_SOCKET_STATE); DEVPRINTF((sc->dev, "card inserted: event=0x%08x, state=%08x\n", sockevent, sockstate)); if (sockstate & CBB_STATE_R2_CARD) { if (device_is_attached(sc->exca[0].pccarddev)) { sc->flags |= CBB_16BIT_CARD; exca_insert(&sc->exca[0]); } else { device_printf(sc->dev, "16-bit card inserted, but no pccard bus.\n"); } } else if (sockstate & CBB_STATE_CB_CARD) { if (device_is_attached(sc->cbdev)) { sc->flags &= ~CBB_16BIT_CARD; CARD_ATTACH_CARD(sc->cbdev); } else { device_printf(sc->dev, "CardBus card inserted, but no cardbus bus.\n"); } } else { /* * We should power the card down, and try again a couple of * times if this happens. XXX */ device_printf(sc->dev, "Unsupported card type detected\n"); } } static void cbb_removal(struct cbb_softc *sc) { sc->cardok = 0; if (sc->flags & CBB_16BIT_CARD) { exca_removal(&sc->exca[0]); } else { if (device_is_attached(sc->cbdev)) CARD_DETACH_CARD(sc->cbdev); } cbb_destroy_res(sc); } /************************************************************************/ /* Interrupt Handler */ /************************************************************************/ static int cbb_func_filt(void *arg) { struct cbb_intrhand *ih = (struct cbb_intrhand *)arg; struct cbb_softc *sc = ih->sc; /* * Make sure that the card is really there. */ if (!sc->cardok) return (FILTER_STRAY); if (!CBB_CARD_PRESENT(cbb_get(sc, CBB_SOCKET_STATE))) { sc->cardok = 0; return (FILTER_HANDLED); } /* * nb: don't have to check for giant or not, since that's done in the * ISR dispatch and one can't hold Giant in a filter anyway... */ return ((*ih->filt)(ih->arg)); } static void cbb_func_intr(void *arg) { struct cbb_intrhand *ih = (struct cbb_intrhand *)arg; struct cbb_softc *sc = ih->sc; /* * While this check may seem redundant, it helps close a race * condition. If the card is ejected after the filter runs, but * before this ISR can be scheduled, then we need to do the same * filtering to prevent the card's ISR from being called. One could * argue that the card's ISR should be able to cope, but experience * has shown they can't always. This mitigates the problem by making * the race quite a bit smaller. Properly written client ISRs should * cope with the card going away in the middle of the ISR. We assume * that drivers that are sophisticated enough to use filters don't * need our protection. This also allows us to ensure they *ARE* * called if their filter said they needed to be called. */ if (ih->filt == NULL) { if (!sc->cardok) return; if (!CBB_CARD_PRESENT(cbb_get(sc, CBB_SOCKET_STATE))) { sc->cardok = 0; return; } } /* * Call the registered ithread interrupt handler. This entire routine * will be called with Giant if this isn't an MP safe driver, or not * if it is. Either way, we don't have to worry. */ ih->intr(ih->arg); } /************************************************************************/ /* Generic Power functions */ /************************************************************************/ static uint32_t cbb_detect_voltage(device_t brdev) { struct cbb_softc *sc = device_get_softc(brdev); uint32_t psr; uint32_t vol = CARD_UKN_CARD; psr = cbb_get(sc, CBB_SOCKET_STATE); if (psr & CBB_STATE_5VCARD && psr & CBB_STATE_5VSOCK) vol |= CARD_5V_CARD; if (psr & CBB_STATE_3VCARD && psr & CBB_STATE_3VSOCK) vol |= CARD_3V_CARD; if (psr & CBB_STATE_XVCARD && psr & CBB_STATE_XVSOCK) vol |= CARD_XV_CARD; if (psr & CBB_STATE_YVCARD && psr & CBB_STATE_YVSOCK) vol |= CARD_YV_CARD; return (vol); } static uint8_t cbb_o2micro_power_hack(struct cbb_softc *sc) { uint8_t reg; /* * Issue #2: INT# not qualified with IRQ Routing Bit. An * unexpected PCI INT# may be generated during PC Card * initialization even with the IRQ Routing Bit Set with some * PC Cards. * * This is a two part issue. The first part is that some of * our older controllers have an issue in which the slot's PCI * INT# is NOT qualified by the IRQ routing bit (PCI reg. 3Eh * bit 7). Regardless of the IRQ routing bit, if NO ISA IRQ * is selected (ExCA register 03h bits 3:0, of the slot, are * cleared) we will generate INT# if IREQ# is asserted. The * second part is because some PC Cards prematurally assert * IREQ# before the ExCA registers are fully programmed. This * in turn asserts INT# because ExCA register 03h bits 3:0 * (ISA IRQ Select) are not yet programmed. * * The fix for this issue, which will work for any controller * (old or new), is to set ExCA register 03h bits 3:0 = 0001b * (select IRQ1), of the slot, before turning on slot power. * Selecting IRQ1 will result in INT# NOT being asserted * (because IRQ1 is selected), and IRQ1 won't be asserted * because our controllers don't generate IRQ1. * * Other, non O2Micro controllers will generate irq 1 in some * situations, so we can't do this hack for everybody. Reports of * keyboard controller's interrupts being suppressed occurred when * we did this. */ reg = exca_getb(&sc->exca[0], EXCA_INTR); exca_putb(&sc->exca[0], EXCA_INTR, (reg & 0xf0) | 1); return (reg); } /* * Restore the damage that cbb_o2micro_power_hack does to EXCA_INTR so * we don't have an interrupt storm on power on. This has the effect of * disabling card status change interrupts for the duration of poweron. */ static void cbb_o2micro_power_hack2(struct cbb_softc *sc, uint8_t reg) { exca_putb(&sc->exca[0], EXCA_INTR, reg); } int cbb_power(device_t brdev, int volts) { uint32_t status, sock_ctrl, reg_ctrl, mask; struct cbb_softc *sc = device_get_softc(brdev); int cnt, sane; int retval = 0; int on = 0; uint8_t reg = 0; sock_ctrl = cbb_get(sc, CBB_SOCKET_CONTROL); sock_ctrl &= ~CBB_SOCKET_CTRL_VCCMASK; switch (volts & CARD_VCCMASK) { case 5: sock_ctrl |= CBB_SOCKET_CTRL_VCC_5V; on++; break; case 3: sock_ctrl |= CBB_SOCKET_CTRL_VCC_3V; on++; break; case XV: sock_ctrl |= CBB_SOCKET_CTRL_VCC_XV; on++; break; case YV: sock_ctrl |= CBB_SOCKET_CTRL_VCC_YV; on++; break; case 0: break; default: return (0); /* power NEVER changed */ } /* VPP == VCC */ sock_ctrl &= ~CBB_SOCKET_CTRL_VPPMASK; sock_ctrl |= ((sock_ctrl >> 4) & 0x07); if (cbb_get(sc, CBB_SOCKET_CONTROL) == sock_ctrl) return (1); /* no change necessary */ DEVPRINTF((sc->dev, "cbb_power: %dV\n", volts)); if (volts != 0 && sc->chipset == CB_O2MICRO) reg = cbb_o2micro_power_hack(sc); /* * We have to mask the card change detect interrupt while we're * messing with the power. It is allowed to bounce while we're * messing with power as things settle down. In addition, we mask off * the card's function interrupt by routing it via the ISA bus. This * bit generally only affects 16-bit cards. Some bridges allow one to * set another bit to have it also affect 32-bit cards. Since 32-bit * cards are required to be better behaved, we don't bother to get * into those bridge specific features. * * XXX I wonder if we need to enable the READY bit interrupt in the * EXCA CSC register for 16-bit cards, and disable the CD bit? */ mask = cbb_get(sc, CBB_SOCKET_MASK); mask |= CBB_SOCKET_MASK_POWER; mask &= ~CBB_SOCKET_MASK_CD; cbb_set(sc, CBB_SOCKET_MASK, mask); PCI_MASK_CONFIG(brdev, CBBR_BRIDGECTRL, |CBBM_BRIDGECTRL_INTR_IREQ_ISA_EN, 2); cbb_set(sc, CBB_SOCKET_CONTROL, sock_ctrl); if (on) { mtx_lock(&sc->mtx); cnt = sc->powerintr; /* * We have a shortish timeout of 500ms here. Some bridges do * not generate a POWER_CYCLE event for 16-bit cards. In * those cases, we have to cope the best we can, and having * only a short delay is better than the alternatives. Others * raise the power cycle a smidge before it is really ready. * We deal with those below. */ sane = 10; while (!(cbb_get(sc, CBB_SOCKET_STATE) & CBB_STATE_POWER_CYCLE) && cnt == sc->powerintr && sane-- > 0) msleep(&sc->powerintr, &sc->mtx, 0, "-", hz / 20); mtx_unlock(&sc->mtx); /* * Relax for 100ms. Some bridges appear to assert this signal * right away, but before the card has stabilized. Other * cards need need more time to cope up reliabily. * Experiments with troublesome setups show this to be a * "cheap" way to enhance reliabilty. We need not do this for * "off" since we don't touch the card after we turn it off. */ pause("cbbPwr", min(hz / 10, 1)); /* * The TOPIC95B requires a little bit extra time to get its * act together, so delay for an additional 100ms. Also as * documented below, it doesn't seem to set the POWER_CYCLE * bit, so don't whine if it never came on. */ if (sc->chipset == CB_TOPIC95) pause("cbb95B", hz / 10); else if (sane <= 0) device_printf(sc->dev, "power timeout, doom?\n"); } /* * After the power is good, we can turn off the power interrupt. * However, the PC Card standard says that we must delay turning the * CD bit back on for a bit to allow for bouncyness on power down * (recall that we don't wait above for a power down, since we don't * get an interrupt for that). We're called either from the suspend * code in which case we don't want to turn card change on again, or * we're called from the card insertion code, in which case the cbb * thread will turn it on for us before it waits to be woken by a * change event. * * NB: Topic95B doesn't set the power cycle bit. we assume that * both it and the TOPIC95 behave the same. */ cbb_clrb(sc, CBB_SOCKET_MASK, CBB_SOCKET_MASK_POWER); status = cbb_get(sc, CBB_SOCKET_STATE); if (on && sc->chipset != CB_TOPIC95) { if ((status & CBB_STATE_POWER_CYCLE) == 0) device_printf(sc->dev, "Power not on?\n"); } if (status & CBB_STATE_BAD_VCC_REQ) { device_printf(sc->dev, "Bad Vcc requested\n"); /* * Turn off the power, and try again. Retrigger other * active interrupts via force register. From NetBSD * PR 36652, coded by me to description there. */ sock_ctrl &= ~CBB_SOCKET_CTRL_VCCMASK; sock_ctrl &= ~CBB_SOCKET_CTRL_VPPMASK; cbb_set(sc, CBB_SOCKET_CONTROL, sock_ctrl); status &= ~CBB_STATE_BAD_VCC_REQ; status &= ~CBB_STATE_DATA_LOST; status |= CBB_FORCE_CV_TEST; cbb_set(sc, CBB_SOCKET_FORCE, status); goto done; } if (sc->chipset == CB_TOPIC97) { reg_ctrl = pci_read_config(sc->dev, TOPIC_REG_CTRL, 4); reg_ctrl &= ~TOPIC97_REG_CTRL_TESTMODE; if (on) reg_ctrl |= TOPIC97_REG_CTRL_CLKRUN_ENA; else reg_ctrl &= ~TOPIC97_REG_CTRL_CLKRUN_ENA; pci_write_config(sc->dev, TOPIC_REG_CTRL, reg_ctrl, 4); } PCI_MASK_CONFIG(brdev, CBBR_BRIDGECTRL, & ~CBBM_BRIDGECTRL_INTR_IREQ_ISA_EN, 2); retval = 1; done:; if (volts != 0 && sc->chipset == CB_O2MICRO) cbb_o2micro_power_hack2(sc, reg); return (retval); } static int cbb_current_voltage(device_t brdev) { struct cbb_softc *sc = device_get_softc(brdev); uint32_t ctrl; ctrl = cbb_get(sc, CBB_SOCKET_CONTROL); switch (ctrl & CBB_SOCKET_CTRL_VCCMASK) { case CBB_SOCKET_CTRL_VCC_5V: return CARD_5V_CARD; case CBB_SOCKET_CTRL_VCC_3V: return CARD_3V_CARD; case CBB_SOCKET_CTRL_VCC_XV: return CARD_XV_CARD; case CBB_SOCKET_CTRL_VCC_YV: return CARD_YV_CARD; } return 0; } /* * detect the voltage for the card, and set it. Since the power * used is the square of the voltage, lower voltages is a big win * and what Windows does (and what Microsoft prefers). The MS paper * also talks about preferring the CIS entry as well, but that has * to be done elsewhere. We also optimize power sequencing here * and don't change things if we're already powered up at a supported * voltage. * * In addition, we power up with OE disabled. We'll set it later * in the power up sequence. */ static int cbb_do_power(device_t brdev) { struct cbb_softc *sc = device_get_softc(brdev); uint32_t voltage, curpwr; uint32_t status; /* Don't enable OE (output enable) until power stable */ exca_clrb(&sc->exca[0], EXCA_PWRCTL, EXCA_PWRCTL_OE); voltage = cbb_detect_voltage(brdev); curpwr = cbb_current_voltage(brdev); status = cbb_get(sc, CBB_SOCKET_STATE); if ((status & CBB_STATE_POWER_CYCLE) && (voltage & curpwr)) return 0; /* Prefer lowest voltage supported */ cbb_power(brdev, CARD_OFF); if (voltage & CARD_YV_CARD) cbb_power(brdev, CARD_VCC(YV)); else if (voltage & CARD_XV_CARD) cbb_power(brdev, CARD_VCC(XV)); else if (voltage & CARD_3V_CARD) cbb_power(brdev, CARD_VCC(3)); else if (voltage & CARD_5V_CARD) cbb_power(brdev, CARD_VCC(5)); else { device_printf(brdev, "Unknown card voltage\n"); return (ENXIO); } return (0); } /************************************************************************/ /* CardBus power functions */ /************************************************************************/ static int cbb_cardbus_reset_power(device_t brdev, device_t child, int on) { struct cbb_softc *sc = device_get_softc(brdev); uint32_t b, h; int delay, count, zero_seen, func; /* * Asserting reset for 20ms is necessary for most bridges. For some * reason, the Ricoh RF5C47x bridges need it asserted for 400ms. The * root cause of this is unknown, and NetBSD does the same thing. */ delay = sc->chipset == CB_RF5C47X ? 400 : 20; PCI_MASK_CONFIG(brdev, CBBR_BRIDGECTRL, |CBBM_BRIDGECTRL_RESET, 2); pause("cbbP3", hz * delay / 1000); /* * If a card exists and we're turning it on, take it out of reset. * After clearing reset, wait up to 1.1s for the first configuration * register (vendor/product) configuration register of device 0.0 to * become != 0xffffffff. The PCMCIA PC Card Host System Specification * says that when powering up the card, the PCI Spec v2.1 must be * followed. In PCI spec v2.2 Table 4-6, Trhfa (Reset High to first * Config Access) is at most 2^25 clocks, or just over 1s. Section * 2.2.1 states any card not ready to participate in bus transactions * must tristate its outputs. Therefore, any access to its * configuration registers must be ignored. In that state, the config * reg will read 0xffffffff. Section 6.2.1 states a vendor id of * 0xffff is invalid, so this can never match a real card. Print a * warning if it never returns a real id. The PCMCIA PC Card * Electrical Spec Section 5.2.7.1 implies only device 0 is present on * a cardbus bus, so that's the only register we check here. */ if (on && CBB_CARD_PRESENT(cbb_get(sc, CBB_SOCKET_STATE))) { PCI_MASK_CONFIG(brdev, CBBR_BRIDGECTRL, &~CBBM_BRIDGECTRL_RESET, 2); b = pcib_get_bus(child); count = 1100 / 20; do { pause("cbbP4", hz * 2 / 100); } while (PCIB_READ_CONFIG(brdev, b, 0, 0, PCIR_DEVVENDOR, 4) == 0xfffffffful && --count >= 0); if (count < 0) device_printf(brdev, "Warning: Bus reset timeout\n"); /* * Some cards (so far just an atheros card I have) seem to * come out of reset in a funky state. They report they are * multi-function cards, but have nonsense for some of the * higher functions. So if the card claims to be MFDEV, and * any of the higher functions' ID is 0, then we've hit the * bug and we'll try again. */ h = PCIB_READ_CONFIG(brdev, b, 0, 0, PCIR_HDRTYPE, 1); if ((h & PCIM_MFDEV) == 0) return 0; zero_seen = 0; for (func = 1; func < 8; func++) { h = PCIB_READ_CONFIG(brdev, b, 0, func, PCIR_DEVVENDOR, 4); if (h == 0) zero_seen++; } if (!zero_seen) return 0; return (EINVAL); } return 0; } static int cbb_cardbus_power_disable_socket(device_t brdev, device_t child) { cbb_power(brdev, CARD_OFF); cbb_cardbus_reset_power(brdev, child, 0); return (0); } static int cbb_cardbus_power_enable_socket(device_t brdev, device_t child) { struct cbb_softc *sc = device_get_softc(brdev); int err, count; if (!CBB_CARD_PRESENT(cbb_get(sc, CBB_SOCKET_STATE))) return (ENODEV); count = 10; do { err = cbb_do_power(brdev); if (err) return (err); err = cbb_cardbus_reset_power(brdev, child, 1); if (err) { device_printf(brdev, "Reset failed, trying again.\n"); cbb_cardbus_power_disable_socket(brdev, child); pause("cbbErr1", hz / 10); /* wait 100ms */ } } while (err != 0 && count-- > 0); return (0); } /************************************************************************/ /* CardBus Resource */ /************************************************************************/ static void cbb_activate_window(device_t brdev, int type) { PCI_ENABLE_IO(device_get_parent(brdev), brdev, type); } static int cbb_cardbus_io_open(device_t brdev, int win, uint32_t start, uint32_t end) { int basereg; int limitreg; if ((win < 0) || (win > 1)) { DEVPRINTF((brdev, "cbb_cardbus_io_open: window out of range %d\n", win)); return (EINVAL); } basereg = win * 8 + CBBR_IOBASE0; limitreg = win * 8 + CBBR_IOLIMIT0; pci_write_config(brdev, basereg, start, 4); pci_write_config(brdev, limitreg, end, 4); cbb_activate_window(brdev, SYS_RES_IOPORT); return (0); } static int cbb_cardbus_mem_open(device_t brdev, int win, uint32_t start, uint32_t end) { int basereg; int limitreg; if ((win < 0) || (win > 1)) { DEVPRINTF((brdev, "cbb_cardbus_mem_open: window out of range %d\n", win)); return (EINVAL); } basereg = win * 8 + CBBR_MEMBASE0; limitreg = win * 8 + CBBR_MEMLIMIT0; pci_write_config(brdev, basereg, start, 4); pci_write_config(brdev, limitreg, end, 4); cbb_activate_window(brdev, SYS_RES_MEMORY); return (0); } #define START_NONE 0xffffffff #define END_NONE 0 static void cbb_cardbus_auto_open(struct cbb_softc *sc, int type) { uint32_t starts[2]; uint32_t ends[2]; struct cbb_reslist *rle; int align, i; uint32_t reg; starts[0] = starts[1] = START_NONE; ends[0] = ends[1] = END_NONE; if (type == SYS_RES_MEMORY) align = CBB_MEMALIGN; else if (type == SYS_RES_IOPORT) align = CBB_IOALIGN; else align = 1; SLIST_FOREACH(rle, &sc->rl, link) { if (rle->type != type) continue; if (rle->res == NULL) continue; if (!(rman_get_flags(rle->res) & RF_ACTIVE)) continue; if (rman_get_flags(rle->res) & RF_PREFETCHABLE) i = 1; else i = 0; if (rman_get_start(rle->res) < starts[i]) starts[i] = rman_get_start(rle->res); if (rman_get_end(rle->res) > ends[i]) ends[i] = rman_get_end(rle->res); } for (i = 0; i < 2; i++) { if (starts[i] == START_NONE) continue; starts[i] &= ~(align - 1); ends[i] = roundup2(ends[i], align) - 1; } if (starts[0] != START_NONE && starts[1] != START_NONE) { if (starts[0] < starts[1]) { if (ends[0] > starts[1]) { device_printf(sc->dev, "Overlapping ranges" " for prefetch and non-prefetch memory\n"); return; } } else { if (ends[1] > starts[0]) { device_printf(sc->dev, "Overlapping ranges" " for prefetch and non-prefetch memory\n"); return; } } } if (type == SYS_RES_MEMORY) { cbb_cardbus_mem_open(sc->dev, 0, starts[0], ends[0]); cbb_cardbus_mem_open(sc->dev, 1, starts[1], ends[1]); reg = pci_read_config(sc->dev, CBBR_BRIDGECTRL, 2); reg &= ~(CBBM_BRIDGECTRL_PREFETCH_0 | CBBM_BRIDGECTRL_PREFETCH_1); if (starts[1] != START_NONE) reg |= CBBM_BRIDGECTRL_PREFETCH_1; pci_write_config(sc->dev, CBBR_BRIDGECTRL, reg, 2); if (bootverbose) { device_printf(sc->dev, "Opening memory:\n"); if (starts[0] != START_NONE) device_printf(sc->dev, "Normal: %#x-%#x\n", starts[0], ends[0]); if (starts[1] != START_NONE) device_printf(sc->dev, "Prefetch: %#x-%#x\n", starts[1], ends[1]); } } else if (type == SYS_RES_IOPORT) { cbb_cardbus_io_open(sc->dev, 0, starts[0], ends[0]); cbb_cardbus_io_open(sc->dev, 1, starts[1], ends[1]); if (bootverbose && starts[0] != START_NONE) device_printf(sc->dev, "Opening I/O: %#x-%#x\n", starts[0], ends[0]); } } static int cbb_cardbus_activate_resource(device_t brdev, device_t child, int type, int rid, struct resource *res) { int ret; ret = BUS_ACTIVATE_RESOURCE(device_get_parent(brdev), child, type, rid, res); if (ret != 0) return (ret); cbb_cardbus_auto_open(device_get_softc(brdev), type); return (0); } static int cbb_cardbus_deactivate_resource(device_t brdev, device_t child, int type, int rid, struct resource *res) { int ret; ret = BUS_DEACTIVATE_RESOURCE(device_get_parent(brdev), child, type, rid, res); if (ret != 0) return (ret); cbb_cardbus_auto_open(device_get_softc(brdev), type); return (0); } static struct resource * cbb_cardbus_alloc_resource(device_t brdev, device_t child, int type, int *rid, rman_res_t start, rman_res_t end, rman_res_t count, u_int flags) { struct cbb_softc *sc = device_get_softc(brdev); int tmp; struct resource *res; rman_res_t align; switch (type) { case SYS_RES_IRQ: tmp = rman_get_start(sc->irq_res); if (start > tmp || end < tmp || count != 1) { device_printf(child, "requested interrupt %jd-%jd," "count = %jd not supported by cbb\n", start, end, count); return (NULL); } start = end = tmp; flags |= RF_SHAREABLE; break; case SYS_RES_IOPORT: if (start <= cbb_start_32_io) start = cbb_start_32_io; if (end < start) end = start; if (count > (1 << RF_ALIGNMENT(flags))) flags = (flags & ~RF_ALIGNMENT_MASK) | rman_make_alignment_flags(count); break; case SYS_RES_MEMORY: if (start <= cbb_start_mem) start = cbb_start_mem; if (end < start) end = start; if (count < CBB_MEMALIGN) align = CBB_MEMALIGN; else align = count; if (align > (1 << RF_ALIGNMENT(flags))) flags = (flags & ~RF_ALIGNMENT_MASK) | rman_make_alignment_flags(align); break; } res = BUS_ALLOC_RESOURCE(device_get_parent(brdev), child, type, rid, start, end, count, flags & ~RF_ACTIVE); if (res == NULL) { printf("cbb alloc res fail type %d rid %x\n", type, *rid); return (NULL); } cbb_insert_res(sc, res, type, *rid); if (flags & RF_ACTIVE) if (bus_activate_resource(child, type, *rid, res) != 0) { bus_release_resource(child, type, *rid, res); return (NULL); } return (res); } static int cbb_cardbus_release_resource(device_t brdev, device_t child, int type, int rid, struct resource *res) { struct cbb_softc *sc = device_get_softc(brdev); int error; if (rman_get_flags(res) & RF_ACTIVE) { error = bus_deactivate_resource(child, type, rid, res); if (error != 0) return (error); } cbb_remove_res(sc, res); return (BUS_RELEASE_RESOURCE(device_get_parent(brdev), child, type, rid, res)); } /************************************************************************/ /* PC Card Power Functions */ /************************************************************************/ static int cbb_pcic_power_enable_socket(device_t brdev, device_t child) { struct cbb_softc *sc = device_get_softc(brdev); int err; DPRINTF(("cbb_pcic_socket_enable:\n")); /* power down/up the socket to reset */ err = cbb_do_power(brdev); if (err) return (err); exca_reset(&sc->exca[0], child); return (0); } static int cbb_pcic_power_disable_socket(device_t brdev, device_t child) { struct cbb_softc *sc = device_get_softc(brdev); DPRINTF(("cbb_pcic_socket_disable\n")); /* Turn off the card's interrupt and leave it in reset, wait 10ms */ exca_putb(&sc->exca[0], EXCA_INTR, 0); pause("cbbP1", hz / 100); /* power down the socket */ cbb_power(brdev, CARD_OFF); exca_putb(&sc->exca[0], EXCA_PWRCTL, 0); /* wait 300ms until power fails (Tpf). */ pause("cbbP2", hz * 300 / 1000); /* enable CSC interrupts */ exca_putb(&sc->exca[0], EXCA_INTR, EXCA_INTR_ENABLE); return (0); } /************************************************************************/ /* POWER methods */ /************************************************************************/ int cbb_power_enable_socket(device_t brdev, device_t child) { struct cbb_softc *sc = device_get_softc(brdev); if (sc->flags & CBB_16BIT_CARD) return (cbb_pcic_power_enable_socket(brdev, child)); return (cbb_cardbus_power_enable_socket(brdev, child)); } int cbb_power_disable_socket(device_t brdev, device_t child) { struct cbb_softc *sc = device_get_softc(brdev); if (sc->flags & CBB_16BIT_CARD) return (cbb_pcic_power_disable_socket(brdev, child)); return (cbb_cardbus_power_disable_socket(brdev, child)); } static int cbb_pcic_activate_resource(device_t brdev, device_t child, int type, int rid, struct resource *res) { struct cbb_softc *sc = device_get_softc(brdev); int error; error = exca_activate_resource(&sc->exca[0], child, type, rid, res); if (error == 0) cbb_activate_window(brdev, type); return (error); } static int cbb_pcic_deactivate_resource(device_t brdev, device_t child, int type, int rid, struct resource *res) { struct cbb_softc *sc = device_get_softc(brdev); return (exca_deactivate_resource(&sc->exca[0], child, type, rid, res)); } static struct resource * cbb_pcic_alloc_resource(device_t brdev, device_t child, int type, int *rid, rman_res_t start, rman_res_t end, rman_res_t count, u_int flags) { struct resource *res = NULL; struct cbb_softc *sc = device_get_softc(brdev); int align; int tmp; switch (type) { case SYS_RES_MEMORY: if (start < cbb_start_mem) start = cbb_start_mem; if (end < start) end = start; if (count < CBB_MEMALIGN) align = CBB_MEMALIGN; else align = count; if (align > (1 << RF_ALIGNMENT(flags))) flags = (flags & ~RF_ALIGNMENT_MASK) | rman_make_alignment_flags(align); break; case SYS_RES_IOPORT: if (start < cbb_start_16_io) start = cbb_start_16_io; if (end < start) end = start; break; case SYS_RES_IRQ: tmp = rman_get_start(sc->irq_res); if (start > tmp || end < tmp || count != 1) { device_printf(child, "requested interrupt %jd-%jd," "count = %jd not supported by cbb\n", start, end, count); return (NULL); } flags |= RF_SHAREABLE; start = end = rman_get_start(sc->irq_res); break; } res = BUS_ALLOC_RESOURCE(device_get_parent(brdev), child, type, rid, start, end, count, flags & ~RF_ACTIVE); if (res == NULL) return (NULL); cbb_insert_res(sc, res, type, *rid); if (flags & RF_ACTIVE) { if (bus_activate_resource(child, type, *rid, res) != 0) { bus_release_resource(child, type, *rid, res); return (NULL); } } return (res); } static int cbb_pcic_release_resource(device_t brdev, device_t child, int type, int rid, struct resource *res) { struct cbb_softc *sc = device_get_softc(brdev); int error; if (rman_get_flags(res) & RF_ACTIVE) { error = bus_deactivate_resource(child, type, rid, res); if (error != 0) return (error); } cbb_remove_res(sc, res); return (BUS_RELEASE_RESOURCE(device_get_parent(brdev), child, type, rid, res)); } /************************************************************************/ /* PC Card methods */ /************************************************************************/ int cbb_pcic_set_res_flags(device_t brdev, device_t child, int type, int rid, u_long flags) { struct cbb_softc *sc = device_get_softc(brdev); struct resource *res; if (type != SYS_RES_MEMORY) return (EINVAL); res = cbb_find_res(sc, type, rid); if (res == NULL) { device_printf(brdev, "set_res_flags: specified rid not found\n"); return (ENOENT); } return (exca_mem_set_flags(&sc->exca[0], res, flags)); } int cbb_pcic_set_memory_offset(device_t brdev, device_t child, int rid, uint32_t cardaddr, uint32_t *deltap) { struct cbb_softc *sc = device_get_softc(brdev); struct resource *res; res = cbb_find_res(sc, SYS_RES_MEMORY, rid); if (res == NULL) { device_printf(brdev, "set_memory_offset: specified rid not found\n"); return (ENOENT); } return (exca_mem_set_offset(&sc->exca[0], res, cardaddr, deltap)); } /************************************************************************/ /* BUS Methods */ /************************************************************************/ int cbb_activate_resource(device_t brdev, device_t child, int type, int rid, struct resource *r) { struct cbb_softc *sc = device_get_softc(brdev); if (sc->flags & CBB_16BIT_CARD) return (cbb_pcic_activate_resource(brdev, child, type, rid, r)); else return (cbb_cardbus_activate_resource(brdev, child, type, rid, r)); } int cbb_deactivate_resource(device_t brdev, device_t child, int type, int rid, struct resource *r) { struct cbb_softc *sc = device_get_softc(brdev); if (sc->flags & CBB_16BIT_CARD) return (cbb_pcic_deactivate_resource(brdev, child, type, rid, r)); else return (cbb_cardbus_deactivate_resource(brdev, child, type, rid, r)); } struct resource * cbb_alloc_resource(device_t brdev, device_t child, int type, int *rid, rman_res_t start, rman_res_t end, rman_res_t count, u_int flags) { struct cbb_softc *sc = device_get_softc(brdev); if (sc->flags & CBB_16BIT_CARD) return (cbb_pcic_alloc_resource(brdev, child, type, rid, start, end, count, flags)); else return (cbb_cardbus_alloc_resource(brdev, child, type, rid, start, end, count, flags)); } int cbb_release_resource(device_t brdev, device_t child, int type, int rid, struct resource *r) { struct cbb_softc *sc = device_get_softc(brdev); if (sc->flags & CBB_16BIT_CARD) return (cbb_pcic_release_resource(brdev, child, type, rid, r)); else return (cbb_cardbus_release_resource(brdev, child, type, rid, r)); } int cbb_read_ivar(device_t brdev, device_t child, int which, uintptr_t *result) { struct cbb_softc *sc = device_get_softc(brdev); switch (which) { case PCIB_IVAR_DOMAIN: *result = sc->domain; return (0); case PCIB_IVAR_BUS: *result = sc->bus.sec; return (0); } return (ENOENT); } int cbb_write_ivar(device_t brdev, device_t child, int which, uintptr_t value) { switch (which) { case PCIB_IVAR_DOMAIN: return (EINVAL); case PCIB_IVAR_BUS: return (EINVAL); } return (ENOENT); } int cbb_child_present(device_t parent, device_t child) { struct cbb_softc *sc = (struct cbb_softc *)device_get_softc(parent); uint32_t sockstate; sockstate = cbb_get(sc, CBB_SOCKET_STATE); return (CBB_CARD_PRESENT(sockstate) && sc->cardok); }