NetBSD/sys/dev/pcmcia/pcmcia.c
dyoung cf784f4497 Improve pci, cbb, cardslot, cardbus, and pcmcia to support detachment
of this entire device tree:

pci0 at mainbus0
        elansc0 at pci0
                gpio0 at elansc0
        cbb0 at pci0
                cardslot0 at cbb0
                        cardbus0 at cardslot0
                        pcmcia0 at cardslot0
        cbb1 at pci0
                cardslot1 at cbb1
                        cardbus1 at cardslot1
                                rtw0 at cardbus1
                        pcmcia1 at cardslot1
        sip0 at pci0
                nsphyter0 at sip0
        sip1 at pci0
                nsphyter1 at sip1

Whew!
2007-12-16 21:28:30 +00:00

968 lines
24 KiB
C

/* $NetBSD: pcmcia.c,v 1.85 2007/12/16 21:28:30 dyoung Exp $ */
/*
* Copyright (c) 2004 Charles M. Hannum. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. All advertising materials mentioning features or use of this software
* must display the following acknowledgement:
* This product includes software developed by Charles M. Hannum.
* 4. The name of the author may not be used to endorse or promote products
* derived from this software without specific prior written permission.
*/
/*
* 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 <sys/cdefs.h>
__KERNEL_RCSID(0, "$NetBSD: pcmcia.c,v 1.85 2007/12/16 21:28:30 dyoung Exp $");
#include "opt_pcmciaverbose.h"
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/device.h>
#include <net/if.h>
#include <dev/pcmcia/pcmciareg.h>
#include <dev/pcmcia/pcmciachip.h>
#include <dev/pcmcia/pcmciavar.h>
#ifdef IT8368E_LEGACY_MODE /* XXX -uch */
#include <arch/hpcmips/dev/it8368var.h>
#endif
#include "locators.h"
#ifdef PCMCIADEBUG
int pcmcia_debug = 0;
#define DPRINTF(arg) if (pcmcia_debug) printf arg
#else
#define DPRINTF(arg)
#endif
#ifdef PCMCIAVERBOSE
int pcmcia_verbose = 1;
#else
int pcmcia_verbose = 0;
#endif
int pcmcia_match(struct device *, struct cfdata *, void *);
void pcmcia_attach(struct device *, struct device *, void *);
int pcmcia_detach(device_t, int);
int pcmcia_rescan(struct device *, const char *, const int *);
void pcmcia_childdetached(struct device *, struct device *);
int pcmcia_print(void *, const char *);
CFATTACH_DECL2(pcmcia, sizeof(struct pcmcia_softc),
pcmcia_match, pcmcia_attach, pcmcia_detach, NULL,
pcmcia_rescan, pcmcia_childdetached);
int
pcmcia_ccr_read(pf, ccr)
struct pcmcia_function *pf;
int ccr;
{
return (bus_space_read_1(pf->pf_ccrt, pf->pf_ccrh,
pf->pf_ccr_offset + ccr * 2));
}
void
pcmcia_ccr_write(pf, ccr, val)
struct pcmcia_function *pf;
int ccr;
int val;
{
if (pf->ccr_mask & (1 << ccr)) {
bus_space_write_1(pf->pf_ccrt, pf->pf_ccrh,
pf->pf_ccr_offset + ccr * 2, val);
}
}
int
pcmcia_match(struct device *parent, struct cfdata *match, void *aux)
{
struct pcmciabus_attach_args *paa = aux;
if (strcmp(paa->paa_busname, match->cf_name)) {
return 0;
}
/* if the autoconfiguration got this far, there's a socket here */
return (1);
}
void
pcmcia_attach(struct device *parent, struct device *self, void *aux)
{
struct pcmciabus_attach_args *paa = aux;
struct pcmcia_softc *sc = (struct pcmcia_softc *) self;
aprint_naive("\n");
aprint_normal("\n");
sc->pct = paa->pct;
sc->pch = paa->pch;
sc->iobase = paa->iobase;
sc->iosize = paa->iosize;
sc->ih = NULL;
if (!pmf_device_register(self, NULL, NULL))
aprint_error_dev(self, "couldn't establish power handler\n");
}
int
pcmcia_detach(device_t self, int flags)
{
int rc;
if ((rc = config_detach_children(self, flags)) != 0)
return rc;
pmf_device_deregister(self);
return 0;
}
int
pcmcia_card_attach(dev)
struct device *dev;
{
struct pcmcia_softc *sc = (struct pcmcia_softc *) dev;
struct pcmcia_function *pf;
int error;
static const int wildcard[PCMCIACF_NLOCS] = {
PCMCIACF_FUNCTION_DEFAULT
};
/*
* this is here so that when socket_enable calls gettype, trt happens
*/
SIMPLEQ_FIRST(&sc->card.pf_head) = NULL;
pcmcia_socket_enable(dev);
pcmcia_read_cis(sc);
pcmcia_check_cis_quirks(sc);
#if 1 /* XXX remove this, done below ??? */
/*
* bail now if the card has no functions, or if there was an error in
* the cis.
*/
if (sc->card.error ||
SIMPLEQ_EMPTY(&sc->card.pf_head)) {
printf("%s: card appears to have bogus CIS\n",
sc->dev.dv_xname);
error = EIO;
goto done;
}
#endif
if (pcmcia_verbose)
pcmcia_print_cis(sc);
SIMPLEQ_FOREACH(pf, &sc->card.pf_head, pf_list) {
if (SIMPLEQ_EMPTY(&pf->cfe_head))
continue;
#ifdef DIAGNOSTIC
if (pf->child != NULL) {
printf("%s: %s still attached to function %d!\n",
sc->dev.dv_xname, pf->child->dv_xname,
pf->number);
panic("pcmcia_card_attach");
}
#endif
pf->sc = sc;
pf->child = NULL;
pf->cfe = NULL;
pf->pf_ih = NULL;
}
error = pcmcia_rescan(dev, "pcmcia", wildcard);
done:
pcmcia_socket_disable(dev);
return (error);
}
int
pcmcia_rescan(struct device *self, const char *ifattr,
const int *locators)
{
struct pcmcia_softc *sc = (struct pcmcia_softc *)self;
struct pcmcia_function *pf;
struct pcmcia_attach_args paa;
int locs[PCMCIACF_NLOCS];
if (sc->card.error ||
SIMPLEQ_EMPTY(&sc->card.pf_head)) {
/* XXX silently ignore if no card present? */
return (EIO);
}
SIMPLEQ_FOREACH(pf, &sc->card.pf_head, pf_list) {
if (SIMPLEQ_EMPTY(&pf->cfe_head))
continue;
if ((locators[PCMCIACF_FUNCTION] != PCMCIACF_FUNCTION_DEFAULT)
&& (locators[PCMCIACF_FUNCTION] != pf->number))
continue;
if (pf->child)
continue;
locs[PCMCIACF_FUNCTION] = pf->number;
paa.manufacturer = sc->card.manufacturer;
paa.product = sc->card.product;
paa.card = &sc->card;
paa.pf = pf;
pf->child = config_found_sm_loc(self, "pcmcia", locs, &paa,
pcmcia_print,
config_stdsubmatch);
}
return (0);
}
void
pcmcia_card_detach(dev, flags)
struct device *dev;
int flags; /* DETACH_* flags */
{
struct pcmcia_softc *sc = (struct pcmcia_softc *) dev;
struct pcmcia_function *pf;
int error;
/*
* We are running on either the PCMCIA socket's event thread
* or in user context detaching a device by user request.
*/
SIMPLEQ_FOREACH(pf, &sc->card.pf_head, pf_list) {
pf->pf_flags |= PFF_DETACHED;
if (SIMPLEQ_EMPTY(&pf->cfe_head))
continue;
if (pf->child == NULL)
continue;
DPRINTF(("%s: detaching %s (function %d)\n",
sc->dev.dv_xname, pf->child->dv_xname, pf->number));
if ((error = config_detach(pf->child, flags)) != 0) {
printf("%s: error %d detaching %s (function %d)\n",
sc->dev.dv_xname, error, pf->child->dv_xname,
pf->number);
}
}
if (sc->sc_enabled_count != 0) {
#ifdef DIAGNOSTIC
printf("pcmcia_card_detach: enabled_count should be 0 here??\n");
#endif
pcmcia_chip_socket_disable(sc->pct, sc->pch);
sc->sc_enabled_count = 0;
}
}
void
pcmcia_childdetached(struct device *self, struct device *child)
{
struct pcmcia_softc *sc = (struct pcmcia_softc *)self;
struct pcmcia_function *pf;
SIMPLEQ_FOREACH(pf, &sc->card.pf_head, pf_list) {
if (SIMPLEQ_EMPTY(&pf->cfe_head))
continue;
if (pf->child == child) {
KASSERT(device_locator(child, PCMCIACF_FUNCTION)
== pf->number);
pf->child = NULL;
return;
}
}
printf("%s: pcmcia_childdetached: %s not found\n",
self->dv_xname, child->dv_xname);
}
void
pcmcia_card_deactivate(dev)
struct device *dev;
{
struct pcmcia_softc *sc = (struct pcmcia_softc *) dev;
struct pcmcia_function *pf;
/*
* We're in the chip's card removal interrupt handler.
* Deactivate the child driver. The PCMCIA socket's
* event thread will run later to finish the detach.
*/
SIMPLEQ_FOREACH(pf, &sc->card.pf_head, pf_list) {
if (SIMPLEQ_EMPTY(&pf->cfe_head))
continue;
if (pf->child == NULL)
continue;
DPRINTF(("%s: deactivating %s (function %d)\n",
sc->dev.dv_xname, pf->child->dv_xname, pf->number));
config_deactivate(pf->child);
}
}
int
pcmcia_print(arg, pnp)
void *arg;
const char *pnp;
{
struct pcmcia_attach_args *pa = arg;
struct pcmcia_softc *sc = pa->pf->sc;
struct pcmcia_card *card = &sc->card;
char devinfo[256];
if (pnp)
aprint_normal("%s", pnp);
pcmcia_devinfo(card, !!pnp, devinfo, sizeof(devinfo));
aprint_normal(" function %d: %s\n", pa->pf->number, devinfo);
return (UNCONF);
}
void
pcmcia_devinfo(card, showhex, cp, cplen)
struct pcmcia_card *card;
int showhex;
char *cp;
size_t cplen;
{
int i, n;
if (cplen > 1) {
*cp++ = '<';
*cp = '\0';
cplen--;
}
for (i = 0; i < 4 && card->cis1_info[i] != NULL && cplen > 1; i++) {
n = snprintf(cp, cplen, "%s%s", i ? ", " : "",
card->cis1_info[i]);
cp += n;
if (cplen < n)
return;
cplen -= n;
}
if (cplen > 1) {
*cp++ = '>';
*cp = '\0';
cplen--;
}
if (showhex && cplen > 1)
snprintf(cp, cplen, " (manufacturer 0x%04x, product 0x%04x)",
card->manufacturer, card->product);
}
const void *
pcmcia_product_lookup(pa, tab, nent, ent_size, matchfn)
struct pcmcia_attach_args *pa;
const void *tab;
size_t nent;
size_t ent_size;
pcmcia_product_match_fn matchfn;
{
const struct pcmcia_product *pp;
int n;
int matches;
#ifdef DIAGNOSTIC
if (sizeof *pp > ent_size)
panic("pcmcia_product_lookup: bogus ent_size %ld",
(long) ent_size);
#endif
for (pp = tab, n = nent; n; pp = (const struct pcmcia_product *)
((const char *)pp + ent_size), n--) {
/* see if it matches vendor/product */
matches = 0;
if ((pp->pp_vendor != PCMCIA_VENDOR_INVALID &&
pp->pp_vendor == pa->manufacturer) &&
(pp->pp_product != PCMCIA_PRODUCT_INVALID &&
pp->pp_product == pa->product))
matches = 1;
if ((pp->pp_cisinfo[0] && pa->card->cis1_info[0] &&
!strcmp(pp->pp_cisinfo[0], pa->card->cis1_info[0])) &&
(pp->pp_cisinfo[1] && pa->card->cis1_info[1] &&
!strcmp(pp->pp_cisinfo[1], pa->card->cis1_info[1])) &&
(!pp->pp_cisinfo[2] || (pa->card->cis1_info[2] &&
!strcmp(pp->pp_cisinfo[2], pa->card->cis1_info[2]))) &&
(!pp->pp_cisinfo[3] || (pa->card->cis1_info[3] &&
!strcmp(pp->pp_cisinfo[3], pa->card->cis1_info[3]))))
matches = 1;
/* if a separate match function is given, let it override */
if (matchfn)
matches = (*matchfn)(pa, pp, matches);
if (matches)
return (pp);
}
return (0);
}
void
pcmcia_socket_settype(dev, type)
struct device *dev;
int type;
{
struct pcmcia_softc *sc = (void *)dev;
pcmcia_chip_socket_settype(sc->pct, sc->pch, type);
}
/*
* Initialize a PCMCIA function. May be called as long as the function is
* disabled.
*/
void
pcmcia_function_init(pf, cfe)
struct pcmcia_function *pf;
struct pcmcia_config_entry *cfe;
{
if (pf->pf_flags & PFF_ENABLED)
panic("pcmcia_function_init: function is enabled");
/* Remember which configuration entry we are using. */
pf->cfe = cfe;
}
void
pcmcia_socket_enable(dev)
struct device *dev;
{
struct pcmcia_softc *sc = (void *)dev;
if (sc->sc_enabled_count++ == 0)
pcmcia_chip_socket_enable(sc->pct, sc->pch);
DPRINTF(("%s: ++enabled_count = %d\n", sc->dev.dv_xname,
sc->sc_enabled_count));
}
void
pcmcia_socket_disable(dev)
struct device *dev;
{
struct pcmcia_softc *sc = (void *)dev;
if (--sc->sc_enabled_count == 0)
pcmcia_chip_socket_disable(sc->pct, sc->pch);
DPRINTF(("%s: --enabled_count = %d\n", sc->dev.dv_xname,
sc->sc_enabled_count));
}
/* Enable a PCMCIA function */
int
pcmcia_function_enable(pf)
struct pcmcia_function *pf;
{
struct pcmcia_softc *sc = pf->sc;
struct pcmcia_function *tmp;
int reg;
int error;
if (pf->cfe == NULL)
panic("pcmcia_function_enable: function not initialized");
/*
* Increase the reference count on the socket, enabling power, if
* necessary.
*/
pcmcia_socket_enable(&sc->dev);
pcmcia_socket_settype(&sc->dev, pf->cfe->iftype);
if (pf->pf_flags & PFF_ENABLED) {
/*
* Don't do anything if we're already enabled.
*/
return (0);
}
/*
* it's possible for different functions' CCRs to be in the same
* underlying page. Check for that.
*/
SIMPLEQ_FOREACH(tmp, &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 + PCMCIA_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) {
error = pcmcia_mem_alloc(pf, PCMCIA_CCR_SIZE, &pf->pf_pcmh);
if (error)
goto bad;
error = pcmcia_mem_map(pf, PCMCIA_MEM_ATTR, pf->ccr_base,
PCMCIA_CCR_SIZE, &pf->pf_pcmh, &pf->pf_ccr_offset,
&pf->pf_ccr_window);
if (error) {
pcmcia_mem_free(pf, &pf->pf_pcmh);
goto bad;
}
}
if (pcmcia_mfc(sc) || 1) {
pcmcia_ccr_write(pf, PCMCIA_CCR_IOBASE0,
(pf->pf_mfc_iobase >> 0) & 0xff);
pcmcia_ccr_write(pf, PCMCIA_CCR_IOBASE1,
(pf->pf_mfc_iobase >> 8) & 0xff);
pcmcia_ccr_write(pf, PCMCIA_CCR_IOBASE2,
(pf->pf_mfc_iobase >> 16) & 0xff);
pcmcia_ccr_write(pf, PCMCIA_CCR_IOBASE3,
(pf->pf_mfc_iobase >> 24) & 0xff);
pcmcia_ccr_write(pf, PCMCIA_CCR_IOLIMIT,
pf->pf_mfc_iomax - pf->pf_mfc_iobase);
}
reg = 0;
if (pf->cfe->flags & PCMCIA_CFE_AUDIO)
reg |= PCMCIA_CCR_STATUS_AUDIO;
pcmcia_ccr_write(pf, PCMCIA_CCR_STATUS, reg);
pcmcia_ccr_write(pf, PCMCIA_CCR_SOCKETCOPY, 0);
reg = (pf->cfe->number & PCMCIA_CCR_OPTION_CFINDEX);
reg |= PCMCIA_CCR_OPTION_LEVIREQ;
if (pcmcia_mfc(sc)) {
reg |= (PCMCIA_CCR_OPTION_FUNC_ENABLE |
PCMCIA_CCR_OPTION_ADDR_DECODE);
if (pf->pf_ih)
reg |= PCMCIA_CCR_OPTION_IREQ_ENABLE;
}
pcmcia_ccr_write(pf, PCMCIA_CCR_OPTION, reg);
#ifdef PCMCIADEBUG
if (pcmcia_debug) {
SIMPLEQ_FOREACH(tmp, &sc->card.pf_head, pf_list) {
printf("%s: function %d CCR at %d offset %lx: "
"%x %x %x %x, %x %x %x %x, %x\n",
tmp->sc->dev.dv_xname, tmp->number,
tmp->pf_ccr_window,
(unsigned long) tmp->pf_ccr_offset,
pcmcia_ccr_read(tmp, 0),
pcmcia_ccr_read(tmp, 1),
pcmcia_ccr_read(tmp, 2),
pcmcia_ccr_read(tmp, 3),
pcmcia_ccr_read(tmp, 5),
pcmcia_ccr_read(tmp, 6),
pcmcia_ccr_read(tmp, 7),
pcmcia_ccr_read(tmp, 8),
pcmcia_ccr_read(tmp, 9));
}
}
#endif
#ifdef IT8368E_LEGACY_MODE
/* return to I/O mode */
it8368_mode(pf, IT8368_IO_MODE, IT8368_WIDTH_16);
#endif
pf->pf_flags |= PFF_ENABLED;
return (0);
bad:
/*
* Decrement the reference count, and power down the socket, if
* necessary.
*/
printf("%s: couldn't map the CCR\n", pf->child->dv_xname);
pcmcia_socket_disable(&sc->dev);
return (error);
}
/* Disable PCMCIA function. */
void
pcmcia_function_disable(pf)
struct pcmcia_function *pf;
{
struct pcmcia_softc *sc = pf->sc;
struct pcmcia_function *tmp;
int reg;
if (pf->cfe == NULL)
panic("pcmcia_function_enable: function not initialized");
if ((pf->pf_flags & PFF_ENABLED) == 0) {
/*
* Don't do anything but decrement if we're already disabled.
*/
goto out;
}
if (pcmcia_mfc(sc) &&
(pf->pf_flags & PFF_DETACHED) == 0) {
reg = pcmcia_ccr_read(pf, PCMCIA_CCR_OPTION);
reg &= ~(PCMCIA_CCR_OPTION_FUNC_ENABLE|
PCMCIA_CCR_OPTION_ADDR_DECODE|
PCMCIA_CCR_OPTION_IREQ_ENABLE);
pcmcia_ccr_write(pf, PCMCIA_CCR_OPTION, reg);
}
/*
* 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;
SIMPLEQ_FOREACH(tmp, &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 + PCMCIA_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) {
pcmcia_mem_unmap(pf, pf->pf_ccr_window);
pcmcia_mem_free(pf, &pf->pf_pcmh);
}
out:
/*
* Decrement the reference count, and power down the socket, if
* necessary.
*/
pcmcia_socket_disable(&sc->dev);
}
int
pcmcia_io_map(pf, width, pcihp, windowp)
struct pcmcia_function *pf;
int width;
struct pcmcia_io_handle *pcihp;
int *windowp;
{
struct pcmcia_softc *sc = pf->sc;
int error;
if (pf->pf_flags & PFF_ENABLED)
printf("pcmcia_io_map: function is enabled!\n");
error = pcmcia_chip_io_map(sc->pct, sc->pch,
width, 0, pcihp->size, pcihp, windowp);
if (error)
return (error);
/*
* XXX in the multifunction multi-iospace-per-function case, this
* needs to cooperate with io_alloc to make sure that the spaces
* don't overlap, and that the ccr's are set correctly
*/
if (pcmcia_mfc(sc) || 1) {
bus_addr_t iobase = pcihp->addr;
bus_addr_t iomax = pcihp->addr + pcihp->size - 1;
DPRINTF(("window iobase %lx iomax %lx\n", (long)iobase,
(long)iomax));
if (pf->pf_mfc_iobase == 0) {
pf->pf_mfc_iobase = iobase;
pf->pf_mfc_iomax = iomax;
} else {
if (iobase < pf->pf_mfc_iobase)
pf->pf_mfc_iobase = iobase;
if (iomax > pf->pf_mfc_iomax)
pf->pf_mfc_iomax = iomax;
}
DPRINTF(("function iobase %lx iomax %lx\n",
(long)pf->pf_mfc_iobase, (long)pf->pf_mfc_iomax));
}
return (0);
}
void
pcmcia_io_unmap(pf, window)
struct pcmcia_function *pf;
int window;
{
struct pcmcia_softc *sc = pf->sc;
if (pf->pf_flags & PFF_ENABLED)
printf("pcmcia_io_unmap: function is enabled!\n");
pcmcia_chip_io_unmap(sc->pct, sc->pch, window);
}
void *
pcmcia_intr_establish(pf, ipl, ih_fct, ih_arg)
struct pcmcia_function *pf;
int ipl;
int (*ih_fct)(void *);
void *ih_arg;
{
if (pf->pf_flags & PFF_ENABLED)
printf("pcmcia_intr_establish: function is enabled!\n");
if (pf->pf_ih)
panic("pcmcia_intr_establish: already done\n");
pf->pf_ih = pcmcia_chip_intr_establish(pf->sc->pct, pf->sc->pch,
pf, ipl, ih_fct, ih_arg);
if (!pf->pf_ih)
printf("%s: interrupt establish failed\n", pf->child->dv_xname);
return (pf->pf_ih);
}
void
pcmcia_intr_disestablish(pf, ih)
struct pcmcia_function *pf;
void *ih;
{
if (pf->pf_flags & PFF_ENABLED)
printf("pcmcia_intr_disestablish: function is enabled!\n");
if (!pf->pf_ih)
panic("pcmcia_intr_distestablish: already done\n");
pcmcia_chip_intr_disestablish(pf->sc->pct, pf->sc->pch, ih);
pf->pf_ih = 0;
}
int
pcmcia_config_alloc(pf, cfe)
struct pcmcia_function *pf;
struct pcmcia_config_entry *cfe;
{
int error = 0;
int n, m;
for (n = 0; n < cfe->num_iospace; n++) {
bus_addr_t start = cfe->iospace[n].start;
bus_size_t length = cfe->iospace[n].length;
bus_size_t align = cfe->iomask ? (1 << cfe->iomask) :
length;
bus_size_t skew = start & (align - 1);
if ((start - skew) == 0 && align < 0x400) {
if (skew)
printf("Drats! I need a skew!\n");
start = 0;
}
DPRINTF(("pcmcia_config_alloc: io %d start=%lx length=%lx align=%lx skew=%lx\n",
n, (long)start, (long)length, (long)align, (long)skew));
error = pcmcia_io_alloc(pf, start, length, align,
&cfe->iospace[n].handle);
if (error)
break;
}
if (n < cfe->num_iospace) {
for (m = 0; m < n; m++)
pcmcia_io_free(pf, &cfe->iospace[m].handle);
return (error);
}
for (n = 0; n < cfe->num_memspace; n++) {
bus_size_t length = cfe->memspace[n].length;
DPRINTF(("pcmcia_config_alloc: mem %d length %lx\n", n,
(long)length));
error = pcmcia_mem_alloc(pf, length, &cfe->memspace[n].handle);
if (error)
break;
}
if (n < cfe->num_memspace) {
for (m = 0; m < cfe->num_iospace; m++)
pcmcia_io_free(pf, &cfe->iospace[m].handle);
for (m = 0; m < n; m++)
pcmcia_mem_free(pf, &cfe->memspace[m].handle);
return (error);
}
/* This one's good! */
return (error);
}
void
pcmcia_config_free(pf)
struct pcmcia_function *pf;
{
struct pcmcia_config_entry *cfe = pf->cfe;
int m;
for (m = 0; m < cfe->num_iospace; m++)
pcmcia_io_free(pf, &cfe->iospace[m].handle);
for (m = 0; m < cfe->num_memspace; m++)
pcmcia_mem_free(pf, &cfe->memspace[m].handle);
}
int
pcmcia_config_map(pf)
struct pcmcia_function *pf;
{
struct pcmcia_config_entry *cfe = pf->cfe;
int error = 0;
int n, m;
for (n = 0; n < cfe->num_iospace; n++) {
int width;
if (cfe->flags & PCMCIA_CFE_IO16)
width = PCMCIA_WIDTH_AUTO;
else
width = PCMCIA_WIDTH_IO8;
error = pcmcia_io_map(pf, width, &cfe->iospace[n].handle,
&cfe->iospace[n].window);
if (error)
break;
}
if (n < cfe->num_iospace) {
for (m = 0; m < n; m++)
pcmcia_io_unmap(pf, cfe->iospace[m].window);
return (error);
}
for (n = 0; n < cfe->num_memspace; n++) {
bus_size_t length = cfe->memspace[n].length;
int width;
DPRINTF(("pcmcia_config_alloc: mem %d length %lx\n", n,
(long)length));
/*XXX*/
width = PCMCIA_WIDTH_MEM8|PCMCIA_MEM_COMMON;
error = pcmcia_mem_map(pf, width, 0, length,
&cfe->memspace[n].handle, &cfe->memspace[n].offset,
&cfe->memspace[n].window);
if (error)
break;
}
if (n < cfe->num_memspace) {
for (m = 0; m < cfe->num_iospace; m++)
pcmcia_io_unmap(pf, cfe->iospace[m].window);
for (m = 0; m < n; m++)
pcmcia_mem_unmap(pf, cfe->memspace[m].window);
return (error);
}
/* This one's good! */
return (error);
}
void
pcmcia_config_unmap(pf)
struct pcmcia_function *pf;
{
struct pcmcia_config_entry *cfe = pf->cfe;
int m;
for (m = 0; m < cfe->num_iospace; m++)
pcmcia_io_unmap(pf, cfe->iospace[m].window);
for (m = 0; m < cfe->num_memspace; m++)
pcmcia_mem_unmap(pf, cfe->memspace[m].window);
}
int
pcmcia_function_configure(pf, validator)
struct pcmcia_function *pf;
int (*validator)(struct pcmcia_config_entry *);
{
struct pcmcia_config_entry *cfe;
int error = ENOENT;
SIMPLEQ_FOREACH(cfe, &pf->cfe_head, cfe_list) {
error = validator(cfe);
if (error)
continue;
error = pcmcia_config_alloc(pf, cfe);
if (!error)
break;
}
if (!cfe) {
DPRINTF(("pcmcia_function_configure: no config entry found, error=%d\n",
error));
return (error);
}
/* Remember which configuration entry we are using. */
pf->cfe = cfe;
error = pcmcia_config_map(pf);
if (error) {
DPRINTF(("pcmcia_function_configure: map failed, error=%d\n",
error));
return (error);
}
return (0);
}
void
pcmcia_function_unconfigure(pf)
struct pcmcia_function *pf;
{
pcmcia_config_unmap(pf);
pcmcia_config_free(pf);
}