pccard.c revision 153773
166200Simp/* $NetBSD: pcmcia.c,v 1.23 2000/07/28 19:17:02 drochner Exp $ */ 252506Simp 3139749Simp/*- 452506Simp * Copyright (c) 1997 Marc Horowitz. All rights reserved. 552506Simp * 652506Simp * Redistribution and use in source and binary forms, with or without 752506Simp * modification, are permitted provided that the following conditions 852506Simp * are met: 952506Simp * 1. Redistributions of source code must retain the above copyright 1052506Simp * notice, this list of conditions and the following disclaimer. 1152506Simp * 2. Redistributions in binary form must reproduce the above copyright 1252506Simp * notice, this list of conditions and the following disclaimer in the 1352506Simp * documentation and/or other materials provided with the distribution. 1452506Simp * 3. All advertising materials mentioning features or use of this software 1552506Simp * must display the following acknowledgement: 1652506Simp * This product includes software developed by Marc Horowitz. 1752506Simp * 4. The name of the author may not be used to endorse or promote products 1852506Simp * derived from this software without specific prior written permission. 1952506Simp * 2052506Simp * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 2152506Simp * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 2252506Simp * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 2352506Simp * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 2452506Simp * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 2552506Simp * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 2652506Simp * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 2752506Simp * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 2852506Simp * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 2952506Simp * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 3052506Simp */ 3152506Simp 32119418Sobrien#include <sys/cdefs.h> 33119418Sobrien__FBSDID("$FreeBSD: head/sys/dev/pccard/pccard.c 153773 2005-12-28 05:30:09Z imp $"); 34119418Sobrien 3552506Simp#include <sys/param.h> 3652506Simp#include <sys/systm.h> 3752506Simp#include <sys/malloc.h> 3852506Simp#include <sys/module.h> 3952506Simp#include <sys/kernel.h> 4052506Simp#include <sys/queue.h> 4191786Simp#include <sys/sysctl.h> 4252506Simp#include <sys/types.h> 4352506Simp 4452506Simp#include <sys/bus.h> 4552506Simp#include <machine/bus.h> 4652506Simp#include <sys/rman.h> 4752506Simp#include <machine/resource.h> 4852506Simp 4982781Sshiba#include <net/ethernet.h> 5082781Sshiba 5152506Simp#include <dev/pccard/pccardreg.h> 5252506Simp#include <dev/pccard/pccardvar.h> 53150362Simp#include <dev/pccard/pccardvarp.h> 54144930Simp#include <dev/pccard/pccard_cis.h> 5552506Simp 5655500Simp#include "power_if.h" 5759193Simp#include "card_if.h" 5855500Simp 5955500Simp#define PCCARDDEBUG 6055500Simp 6191786Simp/* sysctl vars */ 6291786SimpSYSCTL_NODE(_hw, OID_AUTO, pccard, CTLFLAG_RD, 0, "PCCARD parameters"); 6391786Simp 6491786Simpint pccard_debug = 0; 6591786SimpTUNABLE_INT("hw.pccard.debug", &pccard_debug); 6691786SimpSYSCTL_INT(_hw_pccard, OID_AUTO, debug, CTLFLAG_RW, 6791786Simp &pccard_debug, 0, 6891786Simp "pccard debug"); 6991786Simp 7091786Simpint pccard_cis_debug = 0; 7191786SimpTUNABLE_INT("hw.pccard.cis_debug", &pccard_cis_debug); 7291786SimpSYSCTL_INT(_hw_pccard, OID_AUTO, cis_debug, CTLFLAG_RW, 7391786Simp &pccard_cis_debug, 0, "pccard CIS debug"); 7491786Simp 7552506Simp#ifdef PCCARDDEBUG 7652506Simp#define DPRINTF(arg) if (pccard_debug) printf arg 7755500Simp#define DEVPRINTF(arg) if (pccard_debug) device_printf arg 7867333Simp#define PRVERBOSE(arg) printf arg 7967333Simp#define DEVPRVERBOSE(arg) device_printf arg 8052506Simp#else 8152506Simp#define DPRINTF(arg) 8255500Simp#define DEVPRINTF(arg) 8367333Simp#define PRVERBOSE(arg) if (bootverbose) printf arg 8467333Simp#define DEVPRVERBOSE(arg) if (bootverbose) device_printf arg 8552506Simp#endif 8652506Simp 8782378Sjonstatic int pccard_ccr_read(struct pccard_function *pf, int ccr); 8882378Sjonstatic void pccard_ccr_write(struct pccard_function *pf, int ccr, int val); 8982378Sjonstatic int pccard_attach_card(device_t dev); 90106362Simpstatic int pccard_detach_card(device_t dev); 9182378Sjonstatic void pccard_function_init(struct pccard_function *pf); 9282378Sjonstatic void pccard_function_free(struct pccard_function *pf); 9382378Sjonstatic int pccard_function_enable(struct pccard_function *pf); 9482378Sjonstatic void pccard_function_disable(struct pccard_function *pf); 9582378Sjonstatic int pccard_probe(device_t dev); 9682378Sjonstatic int pccard_attach(device_t dev); 9782378Sjonstatic int pccard_detach(device_t dev); 9882378Sjonstatic void pccard_print_resources(struct resource_list *rl, 9982378Sjon const char *name, int type, int count, const char *format); 10082378Sjonstatic int pccard_print_child(device_t dev, device_t child); 10182378Sjonstatic int pccard_set_resource(device_t dev, device_t child, int type, 10282378Sjon int rid, u_long start, u_long count); 10382378Sjonstatic int pccard_get_resource(device_t dev, device_t child, int type, 10482378Sjon int rid, u_long *startp, u_long *countp); 10582378Sjonstatic void pccard_delete_resource(device_t dev, device_t child, int type, 10682378Sjon int rid); 10782378Sjonstatic int pccard_set_res_flags(device_t dev, device_t child, int type, 108140692Simp int rid, uint32_t flags); 10982378Sjonstatic int pccard_set_memory_offset(device_t dev, device_t child, int rid, 110140692Simp uint32_t offset, uint32_t *deltap); 111104641Simpstatic void pccard_probe_nomatch(device_t cbdev, device_t child); 11282378Sjonstatic int pccard_read_ivar(device_t bus, device_t child, int which, 11382378Sjon u_char *result); 11482378Sjonstatic void pccard_driver_added(device_t dev, driver_t *driver); 11582378Sjonstatic struct resource *pccard_alloc_resource(device_t dev, 11682378Sjon device_t child, int type, int *rid, u_long start, 11782378Sjon u_long end, u_long count, u_int flags); 11882378Sjonstatic int pccard_release_resource(device_t dev, device_t child, int type, 11982378Sjon int rid, struct resource *r); 12082378Sjonstatic void pccard_child_detached(device_t parent, device_t dev); 12182378Sjonstatic void pccard_intr(void *arg); 12282378Sjonstatic int pccard_setup_intr(device_t dev, device_t child, 12382378Sjon struct resource *irq, int flags, driver_intr_t *intr, 12482378Sjon void *arg, void **cookiep); 12582378Sjonstatic int pccard_teardown_intr(device_t dev, device_t child, 12682378Sjon struct resource *r, void *cookie); 12752506Simp 12897613Stakawatastatic const struct pccard_product * 12997613Stakawatapccard_do_product_lookup(device_t bus, device_t dev, 13097613Stakawata const struct pccard_product *tab, size_t ent_size, 13197613Stakawata pccard_product_match_fn matchfn); 13297613Stakawata 13397613Stakawata 13482378Sjonstatic int 13574632Simppccard_ccr_read(struct pccard_function *pf, int ccr) 13652506Simp{ 13752506Simp return (bus_space_read_1(pf->pf_ccrt, pf->pf_ccrh, 13852506Simp pf->pf_ccr_offset + ccr)); 13952506Simp} 14052506Simp 14182378Sjonstatic void 14274632Simppccard_ccr_write(struct pccard_function *pf, int ccr, int val) 14352506Simp{ 14452506Simp if ((pf->ccr_mask) & (1 << (ccr / 2))) { 14552506Simp bus_space_write_1(pf->pf_ccrt, pf->pf_ccrh, 14652506Simp pf->pf_ccr_offset + ccr, val); 14752506Simp } 14852506Simp} 14952506Simp 15059193Simpstatic int 151113242Simppccard_set_default_descr(device_t dev) 152113242Simp{ 153121521Simp const char *vendorstr, *prodstr; 154133865Simp uint32_t vendor, prod; 155121521Simp char *str; 156113242Simp 157113242Simp if (pccard_get_vendor_str(dev, &vendorstr)) 158113242Simp return (0); 159113242Simp if (pccard_get_product_str(dev, &prodstr)) 160113242Simp return (0); 161133865Simp if (vendorstr != NULL && prodstr != NULL) { 162133865Simp str = malloc(strlen(vendorstr) + strlen(prodstr) + 2, M_DEVBUF, 163133865Simp M_WAITOK); 164133865Simp sprintf(str, "%s %s", vendorstr, prodstr); 165133865Simp device_set_desc_copy(dev, str); 166133865Simp free(str, M_DEVBUF); 167133865Simp } else { 168133865Simp if (pccard_get_vendor(dev, &vendor)) 169133865Simp return (0); 170133865Simp if (pccard_get_product(dev, &prod)) 171133865Simp return (0); 172133865Simp str = malloc(100, M_DEVBUF, M_WAITOK); 173133865Simp snprintf(str, 100, "vendor=0x%x product=0x%x", vendor, prod); 174133865Simp device_set_desc_copy(dev, str); 175133865Simp free(str, M_DEVBUF); 176133865Simp } 177113242Simp return (0); 178113242Simp} 179113242Simp 180113242Simpstatic int 18159193Simppccard_attach_card(device_t dev) 18252506Simp{ 18364850Simp struct pccard_softc *sc = PCCARD_SOFTC(dev); 18452506Simp struct pccard_function *pf; 18565917Simp struct pccard_ivar *ivar; 18661788Simp device_t child; 187102713Simp int i; 18852506Simp 189153773Simp if (!STAILQ_EMPTY(&sc->card.pf_head)) { 190153773Simp if (bootverbose || pccard_debug) 191153773Simp device_printf(dev, "Card already inserted.\n"); 192153773Simp } 19352506Simp 19455500Simp DEVPRINTF((dev, "chip_socket_enable\n")); 19555500Simp POWER_ENABLE_SOCKET(device_get_parent(dev), dev); 19652506Simp 19755500Simp DEVPRINTF((dev, "read_cis\n")); 19852506Simp pccard_read_cis(sc); 19952506Simp 20055500Simp DEVPRINTF((dev, "check_cis_quirks\n")); 20152506Simp pccard_check_cis_quirks(dev); 20252506Simp 20352506Simp /* 20452506Simp * bail now if the card has no functions, or if there was an error in 20552506Simp * the cis. 20652506Simp */ 20752506Simp 20870715Sjon if (sc->card.error) { 209102713Simp device_printf(dev, "CARD ERROR!\n"); 21052506Simp return (1); 21170715Sjon } 21270715Sjon if (STAILQ_EMPTY(&sc->card.pf_head)) { 213102713Simp device_printf(dev, "Card has no functions!\n"); 21452506Simp return (1); 21570715Sjon } 21652506Simp 21790436Simp if (bootverbose || pccard_debug) 21852506Simp pccard_print_cis(dev); 21952506Simp 22055500Simp DEVPRINTF((dev, "functions scanning\n")); 221102713Simp i = -1; 22252506Simp STAILQ_FOREACH(pf, &sc->card.pf_head, pf_list) { 223102713Simp i++; 224102713Simp if (STAILQ_EMPTY(&pf->cfe_head)) { 225102713Simp device_printf(dev, 226102713Simp "Function %d has no config entries.!\n", i); 22752506Simp continue; 228102713Simp } 22952506Simp pf->sc = sc; 23052506Simp pf->cfe = NULL; 23164927Simp pf->dev = NULL; 23252506Simp } 233104641Simp DEVPRINTF((dev, "Card has %d functions. pccard_mfc is %d\n", i + 1, 234102713Simp pccard_mfc(sc))); 23582378Sjon 23652506Simp STAILQ_FOREACH(pf, &sc->card.pf_head, pf_list) { 23752506Simp if (STAILQ_EMPTY(&pf->cfe_head)) 23852506Simp continue; 23961788Simp /* 24061788Simp * In NetBSD, the drivers are responsible for activating 24161788Simp * each function of a card. I think that in FreeBSD we 24261788Simp * want to activate them enough for the usual bus_*_resource 24361788Simp * routines will do the right thing. This many mean a 24461788Simp * departure from the current NetBSD model. 24561788Simp * 246104641Simp * This seems to work well in practice for most cards. 247104641Simp * However, there are two cases that are problematic. 248104641Simp * If a driver wishes to pick and chose which config 249104641Simp * entry to use, then this method falls down. These 250104641Simp * are usually older cards. In addition, there are 251104641Simp * some cards that have multiple hardware units on the 252104641Simp * cards, but presents only one CIS chain. These cards 253104641Simp * are combination cards, but only one of these units 254104641Simp * can be on at a time. 25561788Simp */ 25667897Sdwmalone ivar = malloc(sizeof(struct pccard_ivar), M_DEVBUF, 257111119Simp M_WAITOK | M_ZERO); 258143815Simp resource_list_init(&ivar->resources); 25961788Simp child = device_add_child(dev, NULL, -1); 26065917Simp device_set_ivars(child, ivar); 261147963Simp ivar->pf = pf; 26267187Simp pf->dev = child; 26367167Simp /* 264147963Simp * XXX We might want to move the next three lines into 26567167Simp * XXX the pccard interface layer. For the moment, this 26667167Simp * XXX is OK, but some drivers want to pick the config 26767167Simp * XXX entry to use as well as some address tweaks (mostly 26867167Simp * XXX due to bugs in decode logic that makes some 26967167Simp * XXX addresses illegal or broken). 27067167Simp */ 27165917Simp pccard_function_init(pf); 27282378Sjon if (sc->sc_enabled_count == 0) 27382378Sjon POWER_ENABLE_SOCKET(device_get_parent(dev), dev); 27467333Simp if (pccard_function_enable(pf) == 0 && 275113242Simp pccard_set_default_descr(child) == 0 && 27667333Simp device_probe_and_attach(child) == 0) { 27755500Simp DEVPRINTF((sc->dev, "function %d CCR at %d " 278121905Simp "offset %x mask %x: " 279121905Simp "%x %x %x %x, %x %x %x %x, %x\n", 28067167Simp pf->number, pf->pf_ccr_window, pf->pf_ccr_offset, 281121905Simp pf->ccr_mask, pccard_ccr_read(pf, 0x00), 28252506Simp pccard_ccr_read(pf, 0x02), pccard_ccr_read(pf, 0x04), 28352506Simp pccard_ccr_read(pf, 0x06), pccard_ccr_read(pf, 0x0A), 28452506Simp pccard_ccr_read(pf, 0x0C), pccard_ccr_read(pf, 0x0E), 28552506Simp pccard_ccr_read(pf, 0x10), pccard_ccr_read(pf, 0x12))); 28667167Simp } else { 28786907Simp if (pf->cfe != NULL) 28886907Simp pccard_function_disable(pf); 28952506Simp } 29052506Simp } 29174632Simp return (0); 29252506Simp} 29352506Simp 29459193Simpstatic int 295106362Simppccard_detach_card(device_t dev) 29652506Simp{ 29764850Simp struct pccard_softc *sc = PCCARD_SOFTC(dev); 29852506Simp struct pccard_function *pf; 29982378Sjon struct pccard_config_entry *cfe; 300119755Simp int state; 30152506Simp 30252506Simp /* 30352506Simp * We are running on either the PCCARD socket's event thread 30452506Simp * or in user context detaching a device by user request. 30552506Simp */ 30652506Simp STAILQ_FOREACH(pf, &sc->card.pf_head, pf_list) { 307119755Simp if (pf->dev == NULL) 308119755Simp continue; 309119755Simp state = device_get_state(pf->dev); 31082378Sjon if (state == DS_ATTACHED || state == DS_BUSY) 31182378Sjon device_detach(pf->dev); 31286907Simp if (pf->cfe != NULL) 31386907Simp pccard_function_disable(pf); 31482378Sjon pccard_function_free(pf); 315106896Simp device_delete_child(dev, pf->dev); 31652506Simp } 31782378Sjon if (sc->sc_enabled_count == 0) 31882378Sjon POWER_DISABLE_SOCKET(device_get_parent(dev), dev); 31982378Sjon 32082378Sjon while (NULL != (pf = STAILQ_FIRST(&sc->card.pf_head))) { 32182378Sjon while (NULL != (cfe = STAILQ_FIRST(&pf->cfe_head))) { 32282378Sjon STAILQ_REMOVE_HEAD(&pf->cfe_head, cfe_list); 32382378Sjon free(cfe, M_DEVBUF); 32482378Sjon } 32582378Sjon STAILQ_REMOVE_HEAD(&sc->card.pf_head, pf_list); 32682378Sjon free(pf, M_DEVBUF); 32782378Sjon } 328153773Simp STAILQ_INIT(&sc->card.pf_head); 32974632Simp return (0); 33052506Simp} 33152506Simp 33297613Stakawatastatic const struct pccard_product * 33397613Stakawatapccard_do_product_lookup(device_t bus, device_t dev, 334112359Simp const struct pccard_product *tab, size_t ent_size, 335112359Simp pccard_product_match_fn matchfn) 33666200Simp{ 33766200Simp const struct pccard_product *ent; 33866200Simp int matches; 339140692Simp uint32_t vendor; 340140692Simp uint32_t prod; 341121521Simp const char *vendorstr; 342121521Simp const char *prodstr; 343140366Simp const char *cis3str; 344140366Simp const char *cis4str; 34566200Simp 34666200Simp#ifdef DIAGNOSTIC 34766200Simp if (sizeof *ent > ent_size) 348112359Simp panic("pccard_product_lookup: bogus ent_size %jd", 349112359Simp (intmax_t) ent_size); 35066200Simp#endif 35166200Simp if (pccard_get_vendor(dev, &vendor)) 35266200Simp return (NULL); 35366200Simp if (pccard_get_product(dev, &prod)) 35466200Simp return (NULL); 35566200Simp if (pccard_get_vendor_str(dev, &vendorstr)) 35666200Simp return (NULL); 35766200Simp if (pccard_get_product_str(dev, &prodstr)) 35866200Simp return (NULL); 359140366Simp if (pccard_get_cis3_str(dev, &cis3str)) 360140366Simp return (NULL); 361140366Simp if (pccard_get_cis4_str(dev, &cis4str)) 362140366Simp return (NULL); 363113313Simp for (ent = tab; ent->pp_vendor != 0; ent = 36482378Sjon (const struct pccard_product *) ((const char *) ent + ent_size)) { 36566200Simp matches = 1; 36686642Simp if (ent->pp_vendor == PCCARD_VENDOR_ANY && 367113078Ssanpei ent->pp_product == PCCARD_PRODUCT_ANY && 36886642Simp ent->pp_cis[0] == NULL && 36986642Simp ent->pp_cis[1] == NULL) { 370113300Simp if (ent->pp_name) 371113300Simp device_printf(dev, 372113300Simp "Total wildcard entry ignored for %s\n", 373113300Simp ent->pp_name); 37486642Simp continue; 37586642Simp } 37666200Simp if (matches && ent->pp_vendor != PCCARD_VENDOR_ANY && 37766200Simp vendor != ent->pp_vendor) 37866200Simp matches = 0; 37966200Simp if (matches && ent->pp_product != PCCARD_PRODUCT_ANY && 38066200Simp prod != ent->pp_product) 38166200Simp matches = 0; 38271322Simp if (matches && ent->pp_cis[0] && 383133865Simp (vendorstr == NULL || 384133865Simp strcmp(ent->pp_cis[0], vendorstr) != 0)) 38566200Simp matches = 0; 38671322Simp if (matches && ent->pp_cis[1] && 387133865Simp (prodstr == NULL || 388133865Simp strcmp(ent->pp_cis[1], prodstr) != 0)) 38966200Simp matches = 0; 390140366Simp if (matches && ent->pp_cis[2] && 391140366Simp (cis3str == NULL || 392140366Simp strcmp(ent->pp_cis[2], cis3str) != 0)) 393140366Simp matches = 0; 394140366Simp if (matches && ent->pp_cis[3] && 395140366Simp (cis4str == NULL || 396140366Simp strcmp(ent->pp_cis[3], cis4str) != 0)) 397140366Simp matches = 0; 39866200Simp if (matchfn != NULL) 39966200Simp matches = (*matchfn)(dev, ent, matches); 40066200Simp if (matches) 40166200Simp return (ent); 40266200Simp } 40366200Simp return (NULL); 40466200Simp} 40566200Simp 40652506Simp/* 40752506Simp * Initialize a PCCARD function. May be called as long as the function is 40852506Simp * disabled. 40982382Simp * 41082382Simp * Note: pccard_function_init should not keep resources allocated. It should 41182382Simp * only set them up ala isa pnp, set the values in the rl lists, and return. 41282382Simp * Any resource held after pccard_function_init is called is a bug. However, 41382382Simp * the bus routines to get the resources also assume that pccard_function_init 41482382Simp * does this, so they need to be fixed too. 41552506Simp */ 41682378Sjonstatic void 41770715Sjonpccard_function_init(struct pccard_function *pf) 41852506Simp{ 41965917Simp struct pccard_config_entry *cfe; 42067242Simp struct pccard_ivar *devi = PCCARD_IVAR(pf->dev); 42167242Simp struct resource_list *rl = &devi->resources; 42270762Simp struct resource_list_entry *rle; 42367242Simp struct resource *r = 0; 42467242Simp device_t bus; 425144955Simp u_long start, end, len; 426144955Simp int i, rid, spaces; 42765917Simp 42870715Sjon if (pf->pf_flags & PFF_ENABLED) { 42970715Sjon printf("pccard_function_init: function is enabled"); 43070715Sjon return; 43170715Sjon } 43267242Simp bus = device_get_parent(pf->dev); 43352506Simp /* Remember which configuration entry we are using. */ 43472012Sphk STAILQ_FOREACH(cfe, &pf->cfe_head, cfe_list) { 435144930Simp if (cfe->iftype != PCCARD_IFTYPE_IO) 436144930Simp continue; 43790897Simp spaces = 0; 43867187Simp for (i = 0; i < cfe->num_iospace; i++) { 43967424Simp start = cfe->iospace[i].start; 44067424Simp if (start) 44167424Simp end = start + cfe->iospace[i].length - 1; 44267424Simp else 443144930Simp end = ~0UL; 444144955Simp DEVPRINTF((bus, "I/O rid %d start %lx end %lx\n", 44590897Simp i, start, end)); 446144930Simp rid = i; 447144955Simp len = cfe->iospace[i].length; 448144955Simp r = bus_alloc_resource(bus, SYS_RES_IOPORT, &rid, 449144955Simp start, end, len, rman_make_alignment_flags(len)); 450144955Simp if (r == NULL) 45167187Simp goto not_this_one; 452144927Simp rle = resource_list_add(rl, SYS_RES_IOPORT, 453144930Simp rid, rman_get_start(r), rman_get_end(r), 45467242Simp cfe->iospace[i].length); 455144927Simp if (rle == NULL) 456144930Simp panic("Cannot add resource rid %d IOPORT", rid); 45776424Simp rle->res = r; 45890897Simp spaces++; 45967187Simp } 460144930Simp for (i = 0; i < cfe->num_memspace; i++) { 461144930Simp start = cfe->memspace[i].hostaddr; 462144930Simp if (start) 463144930Simp end = start + cfe->memspace[i].length - 1; 464144930Simp else 465144930Simp end = ~0UL; 466144955Simp DEVPRINTF((bus, "Memory rid %d start %lx end %lx\n", 467144930Simp i, start, end)); 468144930Simp rid = i; 469144955Simp len = cfe->memspace[i].length; 470144955Simp r = bus_alloc_resource(bus, SYS_RES_MEMORY, &rid, 471144955Simp start, end, len, rman_make_alignment_flags(len)); 472144955Simp if (r == NULL) 473144930Simp goto not_this_one; 474144930Simp rle = resource_list_add(rl, SYS_RES_MEMORY, 475144930Simp rid, rman_get_start(r), rman_get_end(r), 476144930Simp cfe->memspace[i].length); 477144930Simp if (rle == NULL) 478144930Simp panic("Cannot add resource rid %d MEM", rid); 479144930Simp rle->res = r; 480144930Simp spaces++; 48167187Simp } 48290897Simp if (spaces == 0) { 483140488Simp DEVPRINTF((bus, "Neither memory nor I/O mapped\n")); 48490897Simp goto not_this_one; 48590897Simp } 48667187Simp if (cfe->irqmask) { 487144930Simp rid = 0; 488144955Simp r = bus_alloc_resource_any(bus, SYS_RES_IRQ, &rid, 489144955Simp RF_SHAREABLE); 490144955Simp if (r == NULL) 49167187Simp goto not_this_one; 492144930Simp rle = resource_list_add(rl, SYS_RES_IRQ, rid, 49367242Simp rman_get_start(r), rman_get_end(r), 1); 494144927Simp if (rle == NULL) 495144930Simp panic("Cannot add resource rid %d IRQ", rid); 49676424Simp rle->res = r; 49767187Simp } 49867187Simp /* If we get to here, we've allocated all we need */ 49967167Simp pf->cfe = cfe; 50067187Simp break; 50167187Simp not_this_one:; 50267424Simp DEVPRVERBOSE((bus, "Allocation failed for cfe %d\n", 50367424Simp cfe->number)); 504144955Simp resource_list_purge(rl); 50567167Simp } 50652506Simp} 50752506Simp 50882378Sjon/* 50982378Sjon * Free resources allocated by pccard_function_init(), May be called as long 51082378Sjon * as the function is disabled. 51182382Simp * 51282382Simp * NOTE: This function should be unnecessary. pccard_function_init should 51382382Simp * never keep resources initialized. 51482378Sjon */ 51582378Sjonstatic void 51682378Sjonpccard_function_free(struct pccard_function *pf) 51782378Sjon{ 51882378Sjon struct pccard_ivar *devi = PCCARD_IVAR(pf->dev); 51982378Sjon struct resource_list_entry *rle; 52082378Sjon 52182378Sjon if (pf->pf_flags & PFF_ENABLED) { 52282378Sjon printf("pccard_function_init: function is enabled"); 52382378Sjon return; 52482378Sjon } 52582378Sjon 526143785Simp STAILQ_FOREACH(rle, &devi->resources, link) { 52782378Sjon if (rle->res) { 528113242Simp if (rman_get_device(rle->res) != pf->sc->dev) 52982378Sjon device_printf(pf->sc->dev, 53082378Sjon "function_free: Resource still owned by " 53182378Sjon "child, oops. " 53282378Sjon "(type=%d, rid=%d, addr=%lx)\n", 53382378Sjon rle->type, rle->rid, 53482378Sjon rman_get_start(rle->res)); 53582378Sjon BUS_RELEASE_RESOURCE(device_get_parent(pf->sc->dev), 536113242Simp pf->sc->dev, rle->type, rle->rid, rle->res); 53782378Sjon rle->res = NULL; 53882378Sjon } 53982378Sjon } 54082378Sjon resource_list_free(&devi->resources); 54182378Sjon} 54282378Sjon 543121905Simpstatic void 544121905Simppccard_mfc_adjust_iobase(struct pccard_function *pf, bus_addr_t addr, 545121905Simp bus_addr_t offset, bus_size_t size) 546121905Simp{ 547121958Simp bus_size_t iosize, tmp; 548121905Simp 549121905Simp if (addr != 0) { 550121905Simp if (pf->pf_mfc_iomax == 0) { 551121905Simp pf->pf_mfc_iobase = addr + offset; 552121905Simp pf->pf_mfc_iomax = pf->pf_mfc_iobase + size; 553121905Simp } else { 554121905Simp /* this makes the assumption that nothing overlaps */ 555121905Simp if (pf->pf_mfc_iobase > addr + offset) 556121905Simp pf->pf_mfc_iobase = addr + offset; 557121905Simp if (pf->pf_mfc_iomax < addr + offset + size) 558121905Simp pf->pf_mfc_iomax = addr + offset + size; 559121905Simp } 560121905Simp } 561121905Simp 562121905Simp tmp = pf->pf_mfc_iomax - pf->pf_mfc_iobase; 563121905Simp /* round up to nearest (2^n)-1 */ 564121905Simp for (iosize = 1; iosize < tmp; iosize <<= 1) 565121905Simp ; 566121905Simp iosize--; 567121905Simp 568122032Simp DEVPRINTF((pf->dev, "MFC: I/O base %#jx IOSIZE %#jx\n", 569122032Simp (uintmax_t)pf->pf_mfc_iobase, (uintmax_t)(iosize + 1))); 570121905Simp pccard_ccr_write(pf, PCCARD_CCR_IOBASE0, 571121905Simp pf->pf_mfc_iobase & 0xff); 572121905Simp pccard_ccr_write(pf, PCCARD_CCR_IOBASE1, 573121905Simp (pf->pf_mfc_iobase >> 8) & 0xff); 574121905Simp pccard_ccr_write(pf, PCCARD_CCR_IOBASE2, 0); 575121905Simp pccard_ccr_write(pf, PCCARD_CCR_IOBASE3, 0); 576121905Simp pccard_ccr_write(pf, PCCARD_CCR_IOSIZE, iosize); 577121905Simp} 578121905Simp 57952506Simp/* Enable a PCCARD function */ 58082378Sjonstatic int 58155720Simppccard_function_enable(struct pccard_function *pf) 58252506Simp{ 58352506Simp struct pccard_function *tmp; 58452506Simp int reg; 58555720Simp device_t dev = pf->sc->dev; 58670746Simp 58767333Simp if (pf->cfe == NULL) { 58867333Simp DEVPRVERBOSE((dev, "No config entry could be allocated.\n")); 58974632Simp return (ENOMEM); 59067333Simp } 59152506Simp 59252506Simp /* 59352506Simp * Increase the reference count on the socket, enabling power, if 59452506Simp * necessary. 59552506Simp */ 59682378Sjon pf->sc->sc_enabled_count++; 59752506Simp 59852506Simp if (pf->pf_flags & PFF_ENABLED) { 59952506Simp /* 60052506Simp * Don't do anything if we're already enabled. 60152506Simp */ 60252506Simp return (0); 60352506Simp } 60452506Simp 60552506Simp /* 60652506Simp * it's possible for different functions' CCRs to be in the same 60752506Simp * underlying page. Check for that. 60852506Simp */ 60952506Simp STAILQ_FOREACH(tmp, &pf->sc->card.pf_head, pf_list) { 61052506Simp if ((tmp->pf_flags & PFF_ENABLED) && 61152506Simp (pf->ccr_base >= (tmp->ccr_base - tmp->pf_ccr_offset)) && 61252506Simp ((pf->ccr_base + PCCARD_CCR_SIZE) <= 61382378Sjon (tmp->ccr_base - tmp->pf_ccr_offset + 61482378Sjon tmp->pf_ccr_realsize))) { 61552506Simp pf->pf_ccrt = tmp->pf_ccrt; 61652506Simp pf->pf_ccrh = tmp->pf_ccrh; 61752506Simp pf->pf_ccr_realsize = tmp->pf_ccr_realsize; 61852506Simp 61952506Simp /* 62052506Simp * pf->pf_ccr_offset = (tmp->pf_ccr_offset - 62152506Simp * tmp->ccr_base) + pf->ccr_base; 62252506Simp */ 62370715Sjon /* pf->pf_ccr_offset = 62452506Simp (tmp->pf_ccr_offset + pf->ccr_base) - 62570715Sjon tmp->ccr_base; */ 62652506Simp pf->pf_ccr_window = tmp->pf_ccr_window; 62752506Simp break; 62852506Simp } 62952506Simp } 63052506Simp if (tmp == NULL) { 63155720Simp pf->ccr_rid = 0; 63255720Simp pf->ccr_res = bus_alloc_resource(dev, SYS_RES_MEMORY, 633150097Simp &pf->ccr_rid, 0, ~0, PCCARD_MEM_PAGE_SIZE, RF_ACTIVE); 63470715Sjon if (!pf->ccr_res) 63552506Simp goto bad; 636106914Smux DEVPRINTF((dev, "ccr_res == %lx-%lx, base=%x\n", 63770746Simp rman_get_start(pf->ccr_res), rman_get_end(pf->ccr_res), 63870746Simp pf->ccr_base)); 63961788Simp CARD_SET_RES_FLAGS(device_get_parent(dev), dev, SYS_RES_MEMORY, 64061788Simp pf->ccr_rid, PCCARD_A_MEM_ATTR); 64170715Sjon CARD_SET_MEMORY_OFFSET(device_get_parent(dev), dev, 64270748Simp pf->ccr_rid, pf->ccr_base, &pf->pf_ccr_offset); 64355720Simp pf->pf_ccrt = rman_get_bustag(pf->ccr_res); 64455720Simp pf->pf_ccrh = rman_get_bushandle(pf->ccr_res); 64555720Simp pf->pf_ccr_realsize = 1; 64652506Simp } 64752506Simp 64852506Simp reg = (pf->cfe->number & PCCARD_CCR_OPTION_CFINDEX); 64952506Simp reg |= PCCARD_CCR_OPTION_LEVIREQ; 65052506Simp if (pccard_mfc(pf->sc)) { 65152506Simp reg |= (PCCARD_CCR_OPTION_FUNC_ENABLE | 65252506Simp PCCARD_CCR_OPTION_ADDR_DECODE); 65382383Simp /* PCCARD_CCR_OPTION_IRQ_ENABLE set elsewhere as needed */ 65452506Simp } 65552506Simp pccard_ccr_write(pf, PCCARD_CCR_OPTION, reg); 65652506Simp 65752506Simp reg = 0; 65852506Simp if ((pf->cfe->flags & PCCARD_CFE_IO16) == 0) 65952506Simp reg |= PCCARD_CCR_STATUS_IOIS8; 66052506Simp if (pf->cfe->flags & PCCARD_CFE_AUDIO) 66152506Simp reg |= PCCARD_CCR_STATUS_AUDIO; 66252506Simp pccard_ccr_write(pf, PCCARD_CCR_STATUS, reg); 66352506Simp 66452506Simp pccard_ccr_write(pf, PCCARD_CCR_SOCKETCOPY, 0); 66552506Simp 666121905Simp if (pccard_mfc(pf->sc)) 667121905Simp pccard_mfc_adjust_iobase(pf, 0, 0, 0); 66852506Simp 66952506Simp#ifdef PCCARDDEBUG 67052506Simp if (pccard_debug) { 67152506Simp STAILQ_FOREACH(tmp, &pf->sc->card.pf_head, pf_list) { 67270715Sjon device_printf(tmp->sc->dev, 67355500Simp "function %d CCR at %d offset %x: " 67455500Simp "%x %x %x %x, %x %x %x %x, %x\n", 67570715Sjon tmp->number, tmp->pf_ccr_window, 67655500Simp tmp->pf_ccr_offset, 67755500Simp pccard_ccr_read(tmp, 0x00), 67855500Simp pccard_ccr_read(tmp, 0x02), 67955500Simp pccard_ccr_read(tmp, 0x04), 68055500Simp pccard_ccr_read(tmp, 0x06), 68155500Simp pccard_ccr_read(tmp, 0x0A), 68270715Sjon pccard_ccr_read(tmp, 0x0C), 68355500Simp pccard_ccr_read(tmp, 0x0E), 68455500Simp pccard_ccr_read(tmp, 0x10), 68555500Simp pccard_ccr_read(tmp, 0x12)); 68652506Simp } 68752506Simp } 68852506Simp#endif 68952506Simp pf->pf_flags |= PFF_ENABLED; 69052506Simp return (0); 69152506Simp 69252506Simp bad: 69352506Simp /* 69452506Simp * Decrement the reference count, and power down the socket, if 69552506Simp * necessary. 69652506Simp */ 69782378Sjon pf->sc->sc_enabled_count--; 69865098Simp DEVPRINTF((dev, "bad --enabled_count = %d\n", pf->sc->sc_enabled_count)); 69952506Simp 70052506Simp return (1); 70152506Simp} 70252506Simp 70352506Simp/* Disable PCCARD function. */ 70482378Sjonstatic void 70555720Simppccard_function_disable(struct pccard_function *pf) 70652506Simp{ 70752506Simp struct pccard_function *tmp; 70855720Simp device_t dev = pf->sc->dev; 70952506Simp 71052506Simp if (pf->cfe == NULL) 71161788Simp panic("pccard_function_disable: function not initialized"); 71252506Simp 71352506Simp if ((pf->pf_flags & PFF_ENABLED) == 0) { 71452506Simp /* 71552506Simp * Don't do anything if we're already disabled. 71652506Simp */ 71752506Simp return; 71852506Simp } 71952506Simp 72070715Sjon if (pf->intr_handler != NULL) { 72182378Sjon struct pccard_ivar *devi = PCCARD_IVAR(pf->dev); 72282378Sjon struct resource_list_entry *rle = 72382378Sjon resource_list_find(&devi->resources, SYS_RES_IRQ, 0); 724144927Simp if (rle == NULL) 725144927Simp panic("Can't disable an interrupt with no IRQ res\n"); 72682378Sjon BUS_TEARDOWN_INTR(dev, pf->dev, rle->res, 72782378Sjon pf->intr_handler_cookie); 72870715Sjon } 72970715Sjon 73052506Simp /* 73152506Simp * it's possible for different functions' CCRs to be in the same 73252506Simp * underlying page. Check for that. Note we mark us as disabled 73352506Simp * first to avoid matching ourself. 73452506Simp */ 73552506Simp 73652506Simp pf->pf_flags &= ~PFF_ENABLED; 73752506Simp STAILQ_FOREACH(tmp, &pf->sc->card.pf_head, pf_list) { 73852506Simp if ((tmp->pf_flags & PFF_ENABLED) && 73952506Simp (pf->ccr_base >= (tmp->ccr_base - tmp->pf_ccr_offset)) && 74052506Simp ((pf->ccr_base + PCCARD_CCR_SIZE) <= 74182378Sjon (tmp->ccr_base - tmp->pf_ccr_offset + 74282378Sjon tmp->pf_ccr_realsize))) 74352506Simp break; 74452506Simp } 74552506Simp 74652506Simp /* Not used by anyone else; unmap the CCR. */ 74752506Simp if (tmp == NULL) { 74870715Sjon bus_release_resource(dev, SYS_RES_MEMORY, pf->ccr_rid, 74955720Simp pf->ccr_res); 75055720Simp pf->ccr_res = NULL; 75152506Simp } 75252506Simp 75352506Simp /* 75452506Simp * Decrement the reference count, and power down the socket, if 75552506Simp * necessary. 75652506Simp */ 75782378Sjon pf->sc->sc_enabled_count--; 75852506Simp} 75952506Simp 76053873Simp#define PCCARD_NPORT 2 76153873Simp#define PCCARD_NMEM 5 76253873Simp#define PCCARD_NIRQ 1 76353873Simp#define PCCARD_NDRQ 0 76453873Simp 76552506Simpstatic int 76652506Simppccard_probe(device_t dev) 76752506Simp{ 76867333Simp device_set_desc(dev, "16-bit PCCard bus"); 769150391Simp return (0); 77052506Simp} 77152506Simp 77259193Simpstatic int 77359193Simppccard_attach(device_t dev) 77459193Simp{ 77564850Simp struct pccard_softc *sc = PCCARD_SOFTC(dev); 776150362Simp int err; 77761788Simp 77859193Simp sc->dev = dev; 77961788Simp sc->sc_enabled_count = 0; 780150362Simp if ((err = pccard_device_create(sc)) != 0) 781150362Simp return (err); 782153773Simp STAILQ_INIT(&sc->card.pf_head); 78374632Simp return (bus_generic_attach(dev)); 78459193Simp} 78559193Simp 78682378Sjonstatic int 78782378Sjonpccard_detach(device_t dev) 78882378Sjon{ 789106362Simp pccard_detach_card(dev); 790150362Simp pccard_device_destroy(device_get_softc(dev)); 791150362Simp return (0); 79282378Sjon} 79382378Sjon 79487975Simpstatic int 79587975Simppccard_suspend(device_t self) 79687975Simp{ 797106362Simp pccard_detach_card(self); 79887975Simp return (0); 79987975Simp} 80087975Simp 80187975Simpstatic 80287975Simpint 80387975Simppccard_resume(device_t self) 80487975Simp{ 80587975Simp return (0); 80687975Simp} 80787975Simp 80853873Simpstatic void 80953873Simppccard_print_resources(struct resource_list *rl, const char *name, int type, 81053873Simp int count, const char *format) 81153873Simp{ 81253873Simp struct resource_list_entry *rle; 81353873Simp int printed; 81453873Simp int i; 81553873Simp 81653873Simp printed = 0; 81753873Simp for (i = 0; i < count; i++) { 81853873Simp rle = resource_list_find(rl, type, i); 81976424Simp if (rle != NULL) { 82053873Simp if (printed == 0) 82153873Simp printf(" %s ", name); 82253873Simp else if (printed > 0) 82353873Simp printf(","); 82453873Simp printed++; 82553873Simp printf(format, rle->start); 82653873Simp if (rle->count > 1) { 82753873Simp printf("-"); 82853873Simp printf(format, rle->start + rle->count - 1); 82953873Simp } 83053873Simp } else if (i > 3) { 83153873Simp /* check the first few regardless */ 83253873Simp break; 83353873Simp } 83453873Simp } 83553873Simp} 83653873Simp 83753873Simpstatic int 83853873Simppccard_print_child(device_t dev, device_t child) 83953873Simp{ 84066847Simp struct pccard_ivar *devi = PCCARD_IVAR(child); 84153873Simp struct resource_list *rl = &devi->resources; 84253873Simp int retval = 0; 84353873Simp 84453873Simp retval += bus_print_child_header(dev, child); 84553873Simp retval += printf(" at"); 84653873Simp 84776424Simp if (devi != NULL) { 84853873Simp pccard_print_resources(rl, "port", SYS_RES_IOPORT, 84953873Simp PCCARD_NPORT, "%#lx"); 85053873Simp pccard_print_resources(rl, "iomem", SYS_RES_MEMORY, 85153873Simp PCCARD_NMEM, "%#lx"); 85253873Simp pccard_print_resources(rl, "irq", SYS_RES_IRQ, PCCARD_NIRQ, 85353873Simp "%ld"); 85470715Sjon pccard_print_resources(rl, "drq", SYS_RES_DRQ, PCCARD_NDRQ, 85553873Simp "%ld"); 856147963Simp retval += printf(" function %d config %d", devi->pf->number, 857147963Simp devi->pf->cfe->number); 85853873Simp } 85953873Simp 86053873Simp retval += bus_print_child_footer(dev, child); 86153873Simp 86253873Simp return (retval); 86353873Simp} 86453873Simp 86553873Simpstatic int 86653873Simppccard_set_resource(device_t dev, device_t child, int type, int rid, 867147963Simp u_long start, u_long count) 86853873Simp{ 86966847Simp struct pccard_ivar *devi = PCCARD_IVAR(child); 87053873Simp struct resource_list *rl = &devi->resources; 87153873Simp 87253873Simp if (type != SYS_RES_IOPORT && type != SYS_RES_MEMORY 87353873Simp && type != SYS_RES_IRQ && type != SYS_RES_DRQ) 87474632Simp return (EINVAL); 87553873Simp if (rid < 0) 87674632Simp return (EINVAL); 87753873Simp if (type == SYS_RES_IOPORT && rid >= PCCARD_NPORT) 87874632Simp return (EINVAL); 87953873Simp if (type == SYS_RES_MEMORY && rid >= PCCARD_NMEM) 88074632Simp return (EINVAL); 88153873Simp if (type == SYS_RES_IRQ && rid >= PCCARD_NIRQ) 88274632Simp return (EINVAL); 88353873Simp if (type == SYS_RES_DRQ && rid >= PCCARD_NDRQ) 88474632Simp return (EINVAL); 88553873Simp 88653873Simp resource_list_add(rl, type, rid, start, start + count - 1, count); 88782378Sjon if (NULL != resource_list_alloc(rl, device_get_parent(dev), dev, 88882378Sjon type, &rid, start, start + count - 1, count, 0)) 88982378Sjon return 0; 89082378Sjon else 89182378Sjon return ENOMEM; 89253873Simp} 89353873Simp 89453873Simpstatic int 89553873Simppccard_get_resource(device_t dev, device_t child, int type, int rid, 89653873Simp u_long *startp, u_long *countp) 89753873Simp{ 89866847Simp struct pccard_ivar *devi = PCCARD_IVAR(child); 89953873Simp struct resource_list *rl = &devi->resources; 90053873Simp struct resource_list_entry *rle; 90153873Simp 90253873Simp rle = resource_list_find(rl, type, rid); 90376424Simp if (rle == NULL) 90474632Simp return (ENOENT); 90570715Sjon 90676424Simp if (startp != NULL) 90753873Simp *startp = rle->start; 90876424Simp if (countp != NULL) 90953873Simp *countp = rle->count; 91053873Simp 91174632Simp return (0); 91253873Simp} 91353873Simp 91453873Simpstatic void 91553873Simppccard_delete_resource(device_t dev, device_t child, int type, int rid) 91653873Simp{ 91766847Simp struct pccard_ivar *devi = PCCARD_IVAR(child); 91853873Simp struct resource_list *rl = &devi->resources; 91953873Simp resource_list_delete(rl, type, rid); 92053873Simp} 92153873Simp 92259193Simpstatic int 92359193Simppccard_set_res_flags(device_t dev, device_t child, int type, int rid, 924140692Simp uint32_t flags) 92559193Simp{ 92674632Simp return (CARD_SET_RES_FLAGS(device_get_parent(dev), child, type, 92774632Simp rid, flags)); 92859193Simp} 92959193Simp 93059193Simpstatic int 93159193Simppccard_set_memory_offset(device_t dev, device_t child, int rid, 932140692Simp uint32_t offset, uint32_t *deltap) 93370715Sjon 93459193Simp{ 93574632Simp return (CARD_SET_MEMORY_OFFSET(device_get_parent(dev), child, rid, 93674632Simp offset, deltap)); 93759193Simp} 93859193Simp 939104641Simpstatic void 940104641Simppccard_probe_nomatch(device_t bus, device_t child) 941104641Simp{ 942104641Simp struct pccard_ivar *devi = PCCARD_IVAR(child); 943147963Simp struct pccard_function *pf = devi->pf; 944104641Simp struct pccard_softc *sc = PCCARD_SOFTC(bus); 945148019Simp int i; 946104641Simp 947104641Simp device_printf(bus, "<unknown card>"); 948148012Simp printf(" (manufacturer=0x%04x, product=0x%04x, function_type=%d) " 949148012Simp "at function %d\n", sc->card.manufacturer, sc->card.product, 950148012Simp pf->function, pf->number); 951148012Simp device_printf(bus, " CIS info: "); 952148012Simp for (i = 0; sc->card.cis1_info[i] != NULL && i < 4; i++) 953148012Simp printf("%s%s", i > 0 ? ", " : "", sc->card.cis1_info[i]); 954148012Simp printf("\n"); 955104641Simp return; 956104641Simp} 957104641Simp 95866058Simpstatic int 959104641Simppccard_child_location_str(device_t bus, device_t child, char *buf, 960104641Simp size_t buflen) 961104641Simp{ 962104641Simp struct pccard_ivar *devi = PCCARD_IVAR(child); 963147963Simp struct pccard_function *pf = devi->pf; 964104641Simp 965147963Simp snprintf(buf, buflen, "function=%d", pf->number); 966104641Simp return (0); 967104641Simp} 968104641Simp 969147963Simp/* XXX Maybe this should be in subr_bus? */ 970147963Simpstatic void 971147963Simppccard_safe_quote(char *dst, const char *src, size_t len) 972147963Simp{ 973147963Simp char *walker = dst, *ep = dst + len - 1; 974147963Simp 975147963Simp if (len == 0) 976147963Simp return; 977147963Simp while (walker < ep) 978147963Simp { 979147963Simp if (*src == '"') { 980147963Simp if (ep - walker < 2) 981147963Simp break; 982147963Simp *walker++ = '\\'; 983147963Simp } 984147963Simp *walker++ = *src++; 985147963Simp } 986147963Simp *walker = '\0'; 987147963Simp} 988147963Simp 989104641Simpstatic int 990104641Simppccard_child_pnpinfo_str(device_t bus, device_t child, char *buf, 991104641Simp size_t buflen) 992104641Simp{ 993104641Simp struct pccard_ivar *devi = PCCARD_IVAR(child); 994147963Simp struct pccard_function *pf = devi->pf; 995104641Simp struct pccard_softc *sc = PCCARD_SOFTC(bus); 996147963Simp char cis0[128], cis1[128]; 997104641Simp 998147963Simp pccard_safe_quote(cis0, sc->card.cis1_info[0], sizeof(cis0)); 999147963Simp pccard_safe_quote(cis1, sc->card.cis1_info[1], sizeof(cis1)); 1000104641Simp snprintf(buf, buflen, "manufacturer=0x%04x product=0x%04x " 1001104641Simp "cisvendor=\"%s\" cisproduct=\"%s\" function_type=%d", 1002147963Simp sc->card.manufacturer, sc->card.product, cis0, cis1, pf->function); 1003104641Simp return (0); 1004104641Simp} 1005104641Simp 1006104641Simpstatic int 100766058Simppccard_read_ivar(device_t bus, device_t child, int which, u_char *result) 100866058Simp{ 100966847Simp struct pccard_ivar *devi = PCCARD_IVAR(child); 1010147963Simp struct pccard_function *pf = devi->pf; 101166779Simp struct pccard_softc *sc = PCCARD_SOFTC(bus); 101266779Simp 1013147963Simp if (!pf) 1014147963Simp panic("No pccard function pointer"); 101566779Simp switch (which) { 101666779Simp default: 1017147963Simp return (EINVAL); 101866779Simp case PCCARD_IVAR_ETHADDR: 1019147963Simp bcopy(pf->pf_funce_lan_nid, result, ETHER_ADDR_LEN); 102066779Simp break; 102166779Simp case PCCARD_IVAR_VENDOR: 1022147963Simp *(uint32_t *)result = sc->card.manufacturer; 102366779Simp break; 102466779Simp case PCCARD_IVAR_PRODUCT: 1025147963Simp *(uint32_t *)result = sc->card.product; 102666779Simp break; 102790964Sshiba case PCCARD_IVAR_PRODEXT: 1028147963Simp *(uint16_t *)result = sc->card.prodext; 102990964Sshiba break; 103075761Simp case PCCARD_IVAR_FUNCTION: 1031147963Simp *(uint32_t *)result = pf->function; 103275761Simp break; 103366779Simp case PCCARD_IVAR_FUNCTION_NUMBER: 1034147963Simp *(uint32_t *)result = pf->number; 103566779Simp break; 103666779Simp case PCCARD_IVAR_VENDOR_STR: 1037147963Simp *(const char **)result = sc->card.cis1_info[0]; 103866779Simp break; 103966779Simp case PCCARD_IVAR_PRODUCT_STR: 1040147963Simp *(const char **)result = sc->card.cis1_info[1]; 104166779Simp break; 104266779Simp case PCCARD_IVAR_CIS3_STR: 1043147963Simp *(const char **)result = sc->card.cis1_info[2]; 104466779Simp break; 104567167Simp case PCCARD_IVAR_CIS4_STR: 1046147963Simp *(const char **)result = sc->card.cis1_info[3]; 104767167Simp break; 104866779Simp } 104966779Simp return (0); 105066058Simp} 105166058Simp 105266779Simpstatic void 105366779Simppccard_driver_added(device_t dev, driver_t *driver) 105466779Simp{ 105582378Sjon struct pccard_softc *sc = PCCARD_SOFTC(dev); 105682378Sjon struct pccard_function *pf; 105782378Sjon device_t child; 105882378Sjon 105982378Sjon STAILQ_FOREACH(pf, &sc->card.pf_head, pf_list) { 106082378Sjon if (STAILQ_EMPTY(&pf->cfe_head)) 106182378Sjon continue; 106282378Sjon child = pf->dev; 106382378Sjon if (device_get_state(child) != DS_NOTPRESENT) 106482378Sjon continue; 106582378Sjon if (pccard_function_enable(pf) == 0 && 106682378Sjon device_probe_and_attach(child) == 0) { 106782378Sjon DEVPRINTF((sc->dev, "function %d CCR at %d " 106882378Sjon "offset %x: %x %x %x %x, %x %x %x %x, %x\n", 106982378Sjon pf->number, pf->pf_ccr_window, pf->pf_ccr_offset, 107082378Sjon pccard_ccr_read(pf, 0x00), 107182378Sjon pccard_ccr_read(pf, 0x02), pccard_ccr_read(pf, 0x04), 107282378Sjon pccard_ccr_read(pf, 0x06), pccard_ccr_read(pf, 0x0A), 107382378Sjon pccard_ccr_read(pf, 0x0C), pccard_ccr_read(pf, 0x0E), 107482378Sjon pccard_ccr_read(pf, 0x10), pccard_ccr_read(pf, 0x12))); 107582378Sjon } else { 107686907Simp if (pf->cfe != NULL) 107786907Simp pccard_function_disable(pf); 107882378Sjon } 107982378Sjon } 108082378Sjon return; 108166779Simp} 108266058Simp 108367242Simpstatic struct resource * 108467242Simppccard_alloc_resource(device_t dev, device_t child, int type, int *rid, 108567242Simp u_long start, u_long end, u_long count, u_int flags) 108667242Simp{ 108782378Sjon struct pccard_ivar *dinfo; 108882378Sjon struct resource_list_entry *rle = 0; 108982378Sjon int passthrough = (device_get_parent(child) != dev); 1090121905Simp int isdefault = (start == 0 && end == ~0UL && count == 1); 1091104641Simp struct resource *r = NULL; 109267242Simp 1093121905Simp /* XXX I'm no longer sure this is right */ 109482378Sjon if (passthrough) { 109582378Sjon return (BUS_ALLOC_RESOURCE(device_get_parent(dev), child, 109682378Sjon type, rid, start, end, count, flags)); 109782378Sjon } 109870715Sjon 109982378Sjon dinfo = device_get_ivars(child); 110082378Sjon rle = resource_list_find(&dinfo->resources, type, *rid); 110170715Sjon 1102121905Simp if (rle == NULL && isdefault) 1103121905Simp return (NULL); /* no resource of that type/rid */ 1104121905Simp if (rle == NULL || rle->res == NULL) { 1105144955Simp /* XXX Need to adjust flags */ 1106121905Simp r = bus_alloc_resource(dev, type, rid, start, end, 1107144955Simp count, flags); 1108121905Simp if (r == NULL) 1109121905Simp goto bad; 1110121905Simp resource_list_add(&dinfo->resources, type, *rid, 1111121905Simp rman_get_start(r), rman_get_end(r), count); 1112121905Simp rle = resource_list_find(&dinfo->resources, type, *rid); 1113121905Simp if (!rle) 1114121905Simp goto bad; 1115121905Simp rle->res = r; 111667269Simp } 1117121905Simp /* 1118144955Simp * If dev doesn't own the device, then we can't give this device 1119144955Simp * out. 1120121905Simp */ 1121113242Simp if (rman_get_device(rle->res) != dev) 1122104641Simp return (NULL); 1123144955Simp rman_set_device(rle->res, child); 1124144955Simp if (flags & RF_ACTIVE) 1125144955Simp BUS_ACTIVATE_RESOURCE(dev, child, type, *rid, rle->res); 1126104641Simp return (rle->res); 1127104641Simpbad:; 1128104641Simp device_printf(dev, "WARNING: Resource not reserved by pccard\n"); 1129104641Simp return (NULL); 113067242Simp} 113167242Simp 113267242Simpstatic int 113367242Simppccard_release_resource(device_t dev, device_t child, int type, int rid, 113467242Simp struct resource *r) 113567242Simp{ 113682378Sjon struct pccard_ivar *dinfo; 113782378Sjon int passthrough = (device_get_parent(child) != dev); 113882378Sjon struct resource_list_entry *rle = 0; 113970715Sjon 114082378Sjon if (passthrough) 114182378Sjon return BUS_RELEASE_RESOURCE(device_get_parent(dev), child, 114282378Sjon type, rid, r); 114370715Sjon 114482378Sjon dinfo = device_get_ivars(child); 114570715Sjon 114682378Sjon rle = resource_list_find(&dinfo->resources, type, rid); 114770715Sjon 114882378Sjon if (!rle) { 114982378Sjon device_printf(dev, "Allocated resource not found, " 115082378Sjon "%d %x %lx %lx\n", 115182378Sjon type, rid, rman_get_start(r), rman_get_size(r)); 115282378Sjon return ENOENT; 115370715Sjon } 115482378Sjon if (!rle->res) { 115582378Sjon device_printf(dev, "Allocated resource not recorded\n"); 115682378Sjon return ENOENT; 115770715Sjon } 1158144955Simp /* 1159144955Simp * Deactivate the resource (since it is being released), and 1160144955Simp * assign it to the bus. 1161144955Simp */ 1162144955Simp BUS_DEACTIVATE_RESOURCE(dev, child, type, rid, rle->res); 1163144955Simp rman_set_device(rle->res, dev); 1164144955Simp return (0); 116567242Simp} 116667242Simp 116767333Simpstatic void 116867333Simppccard_child_detached(device_t parent, device_t dev) 116967333Simp{ 117067333Simp struct pccard_ivar *ivar = PCCARD_IVAR(dev); 1171147963Simp struct pccard_function *pf = ivar->pf; 117267333Simp 117382378Sjon pccard_function_disable(pf); 117467333Simp} 117567333Simp 117670715Sjonstatic void 117770762Simppccard_intr(void *arg) 117870762Simp{ 117982378Sjon struct pccard_function *pf = (struct pccard_function*) arg; 118082378Sjon int reg; 1181102923Simp int doisr = 1; 118282378Sjon 118382383Simp /* 1184102923Simp * MFC cards know if they interrupted, so we have to ack the 1185102923Simp * interrupt and call the ISR. Non-MFC cards don't have these 1186102923Simp * bits, so they always get called. Many non-MFC cards have 1187102923Simp * this bit set always upon read, but some do not. 1188102923Simp * 1189102923Simp * We always ack the interrupt, even if there's no ISR 1190102923Simp * for the card. This is done on the theory that acking 1191102923Simp * the interrupt will pacify the card enough to keep an 1192102923Simp * interrupt storm from happening. Of course this won't 1193102923Simp * help in the non-MFC case. 1194116311Simp * 1195116311Simp * This has no impact for MPSAFEness of the client drivers. 1196116311Simp * We register this with whatever flags the intr_handler 1197116311Simp * was registered with. All these functions are MPSAFE. 119882383Simp */ 1199102923Simp if (pccard_mfc(pf->sc)) { 1200102923Simp reg = pccard_ccr_read(pf, PCCARD_CCR_STATUS); 1201102923Simp if (reg & PCCARD_CCR_STATUS_INTR) 1202102923Simp pccard_ccr_write(pf, PCCARD_CCR_STATUS, 1203102923Simp reg & ~PCCARD_CCR_STATUS_INTR); 1204102923Simp else 1205102923Simp doisr = 0; 1206102923Simp } 1207102923Simp if (pf->intr_handler != NULL && doisr) 120882378Sjon pf->intr_handler(pf->intr_handler_arg); 120970715Sjon} 121070715Sjon 121170715Sjonstatic int 121270762Simppccard_setup_intr(device_t dev, device_t child, struct resource *irq, 121370762Simp int flags, driver_intr_t *intr, void *arg, void **cookiep) 121470715Sjon{ 1215102713Simp struct pccard_softc *sc = PCCARD_SOFTC(dev); 121670715Sjon struct pccard_ivar *ivar = PCCARD_IVAR(child); 1217147963Simp struct pccard_function *pf = ivar->pf; 121890445Simp int err; 121970715Sjon 1220147963Simp if (pf->intr_handler != NULL) 1221101762Simp panic("Only one interrupt handler per function allowed"); 122290445Simp err = bus_generic_setup_intr(dev, child, irq, flags, pccard_intr, 1223147963Simp pf, cookiep); 122490445Simp if (err != 0) 122590445Simp return (err); 1226147963Simp pf->intr_handler = intr; 1227147963Simp pf->intr_handler_arg = arg; 1228147963Simp pf->intr_handler_cookie = *cookiep; 1229102713Simp if (pccard_mfc(sc)) { 1230147963Simp pccard_ccr_write(pf, PCCARD_CCR_OPTION, 1231147963Simp pccard_ccr_read(pf, PCCARD_CCR_OPTION) | 1232102713Simp PCCARD_CCR_OPTION_IREQ_ENABLE); 1233102713Simp } 123474632Simp return (0); 123570715Sjon} 123670715Sjon 123770715Sjonstatic int 123870762Simppccard_teardown_intr(device_t dev, device_t child, struct resource *r, 123970762Simp void *cookie) 124070715Sjon{ 1241102713Simp struct pccard_softc *sc = PCCARD_SOFTC(dev); 124270715Sjon struct pccard_ivar *ivar = PCCARD_IVAR(child); 1243147963Simp struct pccard_function *pf = ivar->pf; 124482378Sjon int ret; 124570715Sjon 1246102713Simp if (pccard_mfc(sc)) { 1247147963Simp pccard_ccr_write(pf, PCCARD_CCR_OPTION, 1248147963Simp pccard_ccr_read(pf, PCCARD_CCR_OPTION) & 1249102713Simp ~PCCARD_CCR_OPTION_IREQ_ENABLE); 1250102713Simp } 125190445Simp ret = bus_generic_teardown_intr(dev, child, r, cookie); 125282378Sjon if (ret == 0) { 1253147963Simp pf->intr_handler = NULL; 1254147963Simp pf->intr_handler_arg = NULL; 1255147963Simp pf->intr_handler_cookie = NULL; 125682378Sjon } 125770715Sjon 125882378Sjon return (ret); 125970715Sjon} 126070715Sjon 1261121905Simpstatic int 1262121905Simppccard_activate_resource(device_t brdev, device_t child, int type, int rid, 1263121905Simp struct resource *r) 1264121905Simp{ 1265121905Simp struct pccard_ivar *ivar = PCCARD_IVAR(child); 1266147963Simp struct pccard_function *pf = ivar->pf; 1267121905Simp 1268121905Simp switch(type) { 1269121905Simp case SYS_RES_IOPORT: 1270121905Simp /* 1271121905Simp * We need to adjust IOBASE[01] and IOSIZE if we're an MFC 1272121905Simp * card. 1273121905Simp */ 1274121905Simp if (pccard_mfc(pf->sc)) 1275121905Simp pccard_mfc_adjust_iobase(pf, rman_get_start(r), 0, 1276121905Simp rman_get_size(r)); 1277121905Simp break; 1278121905Simp default: 1279121905Simp break; 1280121905Simp } 1281121905Simp return (bus_generic_activate_resource(brdev, child, type, rid, r)); 1282121905Simp} 1283121905Simp 1284121905Simpstatic int 1285121905Simppccard_deactivate_resource(device_t brdev, device_t child, int type, 1286121905Simp int rid, struct resource *r) 1287121905Simp{ 1288121905Simp /* XXX undo pccard_activate_resource? XXX */ 1289121905Simp return (bus_generic_deactivate_resource(brdev, child, type, rid, r)); 1290121905Simp} 1291121905Simp 1292150098Simpstatic int 1293150098Simppccard_attr_read_impl(device_t brdev, device_t child, uint32_t offset, 1294150098Simp uint8_t *val) 1295150098Simp{ 1296150098Simp struct pccard_ivar *devi = PCCARD_IVAR(child); 1297150098Simp struct pccard_function *pf = devi->pf; 1298150098Simp 1299150098Simp /* 1300150098Simp * Optimization. Most of the time, devices want to access 1301150098Simp * the same page of the attribute memory that the CCR is in. 1302150098Simp * We take advantage of this fact here. 1303150098Simp */ 1304150098Simp if (offset / PCCARD_MEM_PAGE_SIZE == 1305150098Simp pf->ccr_base / PCCARD_MEM_PAGE_SIZE) 1306150098Simp *val = bus_space_read_1(pf->pf_ccrt, pf->pf_ccrh, 1307150098Simp offset % PCCARD_MEM_PAGE_SIZE); 1308150098Simp else { 1309150098Simp CARD_SET_MEMORY_OFFSET(brdev, child, pf->ccr_rid, offset, 1310150098Simp &offset); 1311150098Simp *val = bus_space_read_1(pf->pf_ccrt, pf->pf_ccrh, offset); 1312150098Simp CARD_SET_MEMORY_OFFSET(brdev, child, pf->ccr_rid, pf->ccr_base, 1313150098Simp &offset); 1314150098Simp } 1315150098Simp return 0; 1316150098Simp} 1317150098Simp 1318150098Simpstatic int 1319150098Simppccard_attr_write_impl(device_t brdev, device_t child, uint32_t offset, 1320150098Simp uint8_t val) 1321150098Simp{ 1322150098Simp struct pccard_ivar *devi = PCCARD_IVAR(child); 1323150098Simp struct pccard_function *pf = devi->pf; 1324150098Simp 1325150098Simp /* 1326150098Simp * Optimization. Most of the time, devices want to access 1327150098Simp * the same page of the attribute memory that the CCR is in. 1328150098Simp * We take advantage of this fact here. 1329150098Simp */ 1330150098Simp if (offset / PCCARD_MEM_PAGE_SIZE == 1331150098Simp pf->ccr_base / PCCARD_MEM_PAGE_SIZE) 1332150098Simp bus_space_write_1(pf->pf_ccrt, pf->pf_ccrh, 1333150098Simp offset % PCCARD_MEM_PAGE_SIZE, val); 1334150098Simp else { 1335150098Simp CARD_SET_MEMORY_OFFSET(brdev, child, pf->ccr_rid, offset, 1336150098Simp &offset); 1337150098Simp bus_space_write_1(pf->pf_ccrt, pf->pf_ccrh, offset, val); 1338150098Simp CARD_SET_MEMORY_OFFSET(brdev, child, pf->ccr_rid, pf->ccr_base, 1339150098Simp &offset); 1340150098Simp } 1341150098Simp 1342150098Simp return 0; 1343150098Simp} 1344150098Simp 1345150098Simpstatic int 1346150098Simppccard_ccr_read_impl(device_t brdev, device_t child, uint32_t offset, 1347150098Simp uint8_t *val) 1348150098Simp{ 1349150098Simp struct pccard_ivar *devi = PCCARD_IVAR(child); 1350150098Simp 1351150098Simp *val = pccard_ccr_read(devi->pf, offset); 1352150098Simp device_printf(child, "ccr_read of %#x (%#x) is %#x\n", offset, 1353150098Simp devi->pf->pf_ccr_offset, *val); 1354150098Simp return 0; 1355150098Simp} 1356150098Simp 1357150098Simpstatic int 1358150098Simppccard_ccr_write_impl(device_t brdev, device_t child, uint32_t offset, 1359150098Simp uint8_t val) 1360150098Simp{ 1361150098Simp struct pccard_ivar *devi = PCCARD_IVAR(child); 1362150098Simp struct pccard_function *pf = devi->pf; 1363150098Simp 1364150098Simp /* 1365150098Simp * Can't use pccard_ccr_write since client drivers may access 1366150098Simp * registers not contained in the 'mask' if they are non-standard. 1367150098Simp */ 1368150098Simp device_printf(child, "ccr_write of %#x to %#x (%#x)\n", val, offset, 1369150098Simp devi->pf->pf_ccr_offset); 1370150098Simp bus_space_write_1(pf->pf_ccrt, pf->pf_ccrh, pf->pf_ccr_offset + offset, 1371150098Simp val); 1372150098Simp return 0; 1373150098Simp} 1374150098Simp 1375150098Simp 137652506Simpstatic device_method_t pccard_methods[] = { 137752506Simp /* Device interface */ 137852506Simp DEVMETHOD(device_probe, pccard_probe), 137959193Simp DEVMETHOD(device_attach, pccard_attach), 138082378Sjon DEVMETHOD(device_detach, pccard_detach), 138152506Simp DEVMETHOD(device_shutdown, bus_generic_shutdown), 138287975Simp DEVMETHOD(device_suspend, pccard_suspend), 138387975Simp DEVMETHOD(device_resume, pccard_resume), 138452506Simp 138552506Simp /* Bus interface */ 138652506Simp DEVMETHOD(bus_print_child, pccard_print_child), 138766779Simp DEVMETHOD(bus_driver_added, pccard_driver_added), 138867333Simp DEVMETHOD(bus_child_detached, pccard_child_detached), 138967242Simp DEVMETHOD(bus_alloc_resource, pccard_alloc_resource), 139067242Simp DEVMETHOD(bus_release_resource, pccard_release_resource), 1391121905Simp DEVMETHOD(bus_activate_resource, pccard_activate_resource), 1392121905Simp DEVMETHOD(bus_deactivate_resource, pccard_deactivate_resource), 139370715Sjon DEVMETHOD(bus_setup_intr, pccard_setup_intr), 139470715Sjon DEVMETHOD(bus_teardown_intr, pccard_teardown_intr), 139552506Simp DEVMETHOD(bus_set_resource, pccard_set_resource), 139652506Simp DEVMETHOD(bus_get_resource, pccard_get_resource), 139752506Simp DEVMETHOD(bus_delete_resource, pccard_delete_resource), 1398104641Simp DEVMETHOD(bus_probe_nomatch, pccard_probe_nomatch), 139966058Simp DEVMETHOD(bus_read_ivar, pccard_read_ivar), 1400104641Simp DEVMETHOD(bus_child_pnpinfo_str, pccard_child_pnpinfo_str), 1401104641Simp DEVMETHOD(bus_child_location_str, pccard_child_location_str), 140252506Simp 140359193Simp /* Card Interface */ 140459193Simp DEVMETHOD(card_set_res_flags, pccard_set_res_flags), 140559193Simp DEVMETHOD(card_set_memory_offset, pccard_set_memory_offset), 140659193Simp DEVMETHOD(card_attach_card, pccard_attach_card), 140759193Simp DEVMETHOD(card_detach_card, pccard_detach_card), 1408150098Simp DEVMETHOD(card_do_product_lookup, pccard_do_product_lookup), 1409147711Simp DEVMETHOD(card_cis_scan, pccard_scan_cis), 1410150098Simp DEVMETHOD(card_attr_read, pccard_attr_read_impl), 1411150098Simp DEVMETHOD(card_attr_write, pccard_attr_write_impl), 1412150098Simp DEVMETHOD(card_ccr_read, pccard_ccr_read_impl), 1413150098Simp DEVMETHOD(card_ccr_write, pccard_ccr_write_impl), 141459193Simp 141552506Simp { 0, 0 } 141652506Simp}; 141752506Simp 141852506Simpstatic driver_t pccard_driver = { 141952506Simp "pccard", 142052506Simp pccard_methods, 142164850Simp sizeof(struct pccard_softc) 142252506Simp}; 142352506Simp 142452506Simpdevclass_t pccard_devclass; 142552506Simp 1426101905Simp/* Maybe we need to have a slot device? */ 142753873SimpDRIVER_MODULE(pccard, pcic, pccard_driver, pccard_devclass, 0, 0); 1428101905SimpDRIVER_MODULE(pccard, cbb, pccard_driver, pccard_devclass, 0, 0); 142964927SimpMODULE_VERSION(pccard, 1); 1430