2009-05-12 18:38:26 +04:00
|
|
|
/* $NetBSD: pcmcia.c,v 1.93 2009/05/12 14:42:19 cegger Exp $ */
|
2004-08-09 03:19:59 +04:00
|
|
|
|
|
|
|
/*
|
|
|
|
* 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.
|
|
|
|
*/
|
1997-10-17 03:27:16 +04:00
|
|
|
|
|
|
|
/*
|
|
|
|
* 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.
|
|
|
|
*/
|
|
|
|
|
2001-11-13 10:24:43 +03:00
|
|
|
#include <sys/cdefs.h>
|
2009-05-12 18:38:26 +04:00
|
|
|
__KERNEL_RCSID(0, "$NetBSD: pcmcia.c,v 1.93 2009/05/12 14:42:19 cegger Exp $");
|
2001-11-13 10:24:43 +03:00
|
|
|
|
1997-10-17 03:27:16 +04:00
|
|
|
#include "opt_pcmciaverbose.h"
|
|
|
|
|
|
|
|
#include <sys/param.h>
|
|
|
|
#include <sys/systm.h>
|
|
|
|
#include <sys/device.h>
|
|
|
|
|
2007-12-09 23:27:42 +03:00
|
|
|
#include <net/if.h>
|
|
|
|
|
1997-10-17 03:27:16 +04:00
|
|
|
#include <dev/pcmcia/pcmciareg.h>
|
|
|
|
#include <dev/pcmcia/pcmciachip.h>
|
|
|
|
#include <dev/pcmcia/pcmciavar.h>
|
2000-02-26 22:02:22 +03:00
|
|
|
#ifdef IT8368E_LEGACY_MODE /* XXX -uch */
|
|
|
|
#include <arch/hpcmips/dev/it8368var.h>
|
|
|
|
#endif
|
1997-10-17 03:27:16 +04:00
|
|
|
|
1997-10-19 18:04:29 +04:00
|
|
|
#include "locators.h"
|
|
|
|
|
1997-10-17 03:27:16 +04:00
|
|
|
#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
|
|
|
|
|
2009-05-12 18:38:26 +04:00
|
|
|
int pcmcia_match(device_t, cfdata_t, void *);
|
|
|
|
void pcmcia_attach(device_t, device_t, void *);
|
2007-12-17 00:28:30 +03:00
|
|
|
int pcmcia_detach(device_t, int);
|
2009-05-12 18:38:26 +04:00
|
|
|
int pcmcia_rescan(device_t, const char *, const int *);
|
|
|
|
void pcmcia_childdetached(device_t, device_t);
|
2005-02-04 05:10:35 +03:00
|
|
|
int pcmcia_print(void *, const char *);
|
1997-10-17 03:27:16 +04:00
|
|
|
|
During shutdown, detach devices in an orderly fashion.
Call the detach routine for every device in the device tree, starting
with the leaves and moving toward the root, expecting that each
(pseudo-)device driver will use the opportunity to gracefully commit
outstandings transactions to the underlying (pseudo-)device and to
relinquish control of the hardware to the system BIOS.
Detaching devices is not suitable for every shutdown: in an emergency,
or if the system state is inconsistent, we should resort to a fast,
simple shutdown that uses only the pmf(9) shutdown hooks and the
(deprecated) shutdownhooks. For now, if the flag RB_NOSYNC is set in
boothowto, opt for the fast, simple shutdown.
Add a device flag, DVF_DETACH_SHUTDOWN, that indicates by its presence
that it is safe to detach a device during shutdown. Introduce macros
CFATTACH_DECL3() and CFATTACH_DECL3_NEW() for creating autoconf
attachments with default device flags. Add DVF_DETACH_SHUTDOWN
to configuration attachments for atabus(4), atw(4) at cardbus(4),
cardbus(4), cardslot(4), com(4) at isa(4), elanpar(4), elanpex(4),
elansc(4), gpio(4), npx(4) at isa(4), nsphyter(4), pci(4), pcib(4),
pcmcia(4), ppb(4), sip(4), wd(4), and wdc(4) at isa(4).
Add a device-detachment "reason" flag, DETACH_SHUTDOWN, that tells the
autoconf code and a device driver that the reason for detachment is
system shutdown.
Add a sysctl, kern.detachall, that tells the system to try to detach
every device at shutdown, regardless of any device's DVF_DETACH_SHUTDOWN
flag. The default for kern.detachall is 0. SET IT TO 1, PLEASE, TO
HELP TEST AND DEBUG DEVICE DETACHMENT AT SHUTDOWN.
This is a work in progress. In future work, I aim to treat
pseudo-devices more thoroughly, and to gracefully tear down a stack of
(pseudo-)disk drivers and filesystems, including cgd(4), vnd(4), and
raid(4) instances at shutdown.
Also commit some changes that are not easily untangled from the rest:
(1) begin to simplify device_t locking: rename struct pmf_private to
device_lock, and incorporate device_lock into struct device.
(2) #include <sys/device.h> in sys/pmf.h in order to get some
definitions that it needs. Stop unnecessarily #including <sys/device.h>
in sys/arch/x86/include/pic.h to keep the amd64, xen, and i386 releases
building.
2009-04-02 04:09:32 +04:00
|
|
|
CFATTACH_DECL3_NEW(pcmcia, sizeof(struct pcmcia_softc),
|
2007-12-17 00:28:30 +03:00
|
|
|
pcmcia_match, pcmcia_attach, pcmcia_detach, NULL,
|
During shutdown, detach devices in an orderly fashion.
Call the detach routine for every device in the device tree, starting
with the leaves and moving toward the root, expecting that each
(pseudo-)device driver will use the opportunity to gracefully commit
outstandings transactions to the underlying (pseudo-)device and to
relinquish control of the hardware to the system BIOS.
Detaching devices is not suitable for every shutdown: in an emergency,
or if the system state is inconsistent, we should resort to a fast,
simple shutdown that uses only the pmf(9) shutdown hooks and the
(deprecated) shutdownhooks. For now, if the flag RB_NOSYNC is set in
boothowto, opt for the fast, simple shutdown.
Add a device flag, DVF_DETACH_SHUTDOWN, that indicates by its presence
that it is safe to detach a device during shutdown. Introduce macros
CFATTACH_DECL3() and CFATTACH_DECL3_NEW() for creating autoconf
attachments with default device flags. Add DVF_DETACH_SHUTDOWN
to configuration attachments for atabus(4), atw(4) at cardbus(4),
cardbus(4), cardslot(4), com(4) at isa(4), elanpar(4), elanpex(4),
elansc(4), gpio(4), npx(4) at isa(4), nsphyter(4), pci(4), pcib(4),
pcmcia(4), ppb(4), sip(4), wd(4), and wdc(4) at isa(4).
Add a device-detachment "reason" flag, DETACH_SHUTDOWN, that tells the
autoconf code and a device driver that the reason for detachment is
system shutdown.
Add a sysctl, kern.detachall, that tells the system to try to detach
every device at shutdown, regardless of any device's DVF_DETACH_SHUTDOWN
flag. The default for kern.detachall is 0. SET IT TO 1, PLEASE, TO
HELP TEST AND DEBUG DEVICE DETACHMENT AT SHUTDOWN.
This is a work in progress. In future work, I aim to treat
pseudo-devices more thoroughly, and to gracefully tear down a stack of
(pseudo-)disk drivers and filesystems, including cgd(4), vnd(4), and
raid(4) instances at shutdown.
Also commit some changes that are not easily untangled from the rest:
(1) begin to simplify device_t locking: rename struct pmf_private to
device_lock, and incorporate device_lock into struct device.
(2) #include <sys/device.h> in sys/pmf.h in order to get some
definitions that it needs. Stop unnecessarily #including <sys/device.h>
in sys/arch/x86/include/pic.h to keep the amd64, xen, and i386 releases
building.
2009-04-02 04:09:32 +04:00
|
|
|
pcmcia_rescan, pcmcia_childdetached, DVF_DETACH_SHUTDOWN);
|
1997-10-17 03:27:16 +04:00
|
|
|
|
|
|
|
int
|
2009-03-14 18:35:58 +03:00
|
|
|
pcmcia_ccr_read(struct pcmcia_function *pf, int ccr)
|
1997-10-17 03:27:16 +04:00
|
|
|
{
|
|
|
|
|
|
|
|
return (bus_space_read_1(pf->pf_ccrt, pf->pf_ccrh,
|
2004-08-09 05:32:04 +04:00
|
|
|
pf->pf_ccr_offset + ccr * 2));
|
1997-10-17 03:27:16 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
void
|
2009-03-14 18:35:58 +03:00
|
|
|
pcmcia_ccr_write(struct pcmcia_function *pf, int ccr, int val)
|
1997-10-17 03:27:16 +04:00
|
|
|
{
|
|
|
|
|
2004-08-09 05:32:04 +04:00
|
|
|
if (pf->ccr_mask & (1 << ccr)) {
|
1997-10-17 03:27:16 +04:00
|
|
|
bus_space_write_1(pf->pf_ccrt, pf->pf_ccrh,
|
2004-08-09 05:32:04 +04:00
|
|
|
pf->pf_ccr_offset + ccr * 2, val);
|
1997-10-17 03:27:16 +04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
int
|
2009-05-12 18:38:26 +04:00
|
|
|
pcmcia_match(device_t parent, cfdata_t match, void *aux)
|
1997-10-17 03:27:16 +04:00
|
|
|
{
|
1999-10-15 10:07:17 +04:00
|
|
|
struct pcmciabus_attach_args *paa = aux;
|
|
|
|
|
2002-09-27 06:24:06 +04:00
|
|
|
if (strcmp(paa->paa_busname, match->cf_name)) {
|
1999-10-15 10:07:17 +04:00
|
|
|
return 0;
|
|
|
|
}
|
1997-10-17 03:27:16 +04:00
|
|
|
/* if the autoconfiguration got this far, there's a socket here */
|
|
|
|
return (1);
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
2009-05-12 18:38:26 +04:00
|
|
|
pcmcia_attach(device_t parent, device_t self, void *aux)
|
1997-10-17 03:27:16 +04:00
|
|
|
{
|
|
|
|
struct pcmciabus_attach_args *paa = aux;
|
2008-07-03 23:07:43 +04:00
|
|
|
struct pcmcia_softc *sc = device_private(self);
|
1997-10-17 03:27:16 +04:00
|
|
|
|
2007-12-01 17:35:51 +03:00
|
|
|
aprint_naive("\n");
|
|
|
|
aprint_normal("\n");
|
1997-10-17 03:27:16 +04:00
|
|
|
|
2008-07-03 23:07:43 +04:00
|
|
|
sc->dev = self;
|
1997-10-17 03:27:16 +04:00
|
|
|
sc->pct = paa->pct;
|
|
|
|
sc->pch = paa->pch;
|
|
|
|
sc->iobase = paa->iobase;
|
|
|
|
sc->iosize = paa->iosize;
|
|
|
|
|
|
|
|
sc->ih = NULL;
|
2007-12-09 23:27:42 +03:00
|
|
|
|
|
|
|
if (!pmf_device_register(self, NULL, NULL))
|
|
|
|
aprint_error_dev(self, "couldn't establish power handler\n");
|
1997-10-17 03:27:16 +04:00
|
|
|
}
|
|
|
|
|
2007-12-17 00:28:30 +03:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
1997-10-17 03:27:16 +04:00
|
|
|
int
|
2009-05-12 18:38:26 +04:00
|
|
|
pcmcia_card_attach(device_t dev)
|
1997-10-17 03:27:16 +04:00
|
|
|
{
|
2008-07-03 23:07:43 +04:00
|
|
|
struct pcmcia_softc *sc = device_private(dev);
|
1997-10-17 03:27:16 +04:00
|
|
|
struct pcmcia_function *pf;
|
2004-08-18 16:01:13 +04:00
|
|
|
int error;
|
2005-08-26 15:01:42 +04:00
|
|
|
static const int wildcard[PCMCIACF_NLOCS] = {
|
2006-10-25 00:54:10 +04:00
|
|
|
PCMCIACF_FUNCTION_DEFAULT
|
2004-08-18 16:01:13 +04:00
|
|
|
};
|
1997-10-17 03:27:16 +04:00
|
|
|
|
|
|
|
/*
|
|
|
|
* this is here so that when socket_enable calls gettype, trt happens
|
|
|
|
*/
|
2002-06-02 03:50:52 +04:00
|
|
|
SIMPLEQ_FIRST(&sc->card.pf_head) = NULL;
|
1997-10-17 03:27:16 +04:00
|
|
|
|
2004-08-11 03:34:06 +04:00
|
|
|
pcmcia_socket_enable(dev);
|
1997-10-17 03:27:16 +04:00
|
|
|
|
|
|
|
pcmcia_read_cis(sc);
|
1998-12-24 07:51:59 +03:00
|
|
|
pcmcia_check_cis_quirks(sc);
|
|
|
|
|
2004-08-18 16:01:13 +04:00
|
|
|
#if 1 /* XXX remove this, done below ??? */
|
1997-10-17 03:27:16 +04:00
|
|
|
/*
|
|
|
|
* bail now if the card has no functions, or if there was an error in
|
|
|
|
* the cis.
|
|
|
|
*/
|
2004-08-12 00:57:40 +04:00
|
|
|
if (sc->card.error ||
|
|
|
|
SIMPLEQ_EMPTY(&sc->card.pf_head)) {
|
|
|
|
printf("%s: card appears to have bogus CIS\n",
|
2008-07-03 23:07:43 +04:00
|
|
|
device_xname(sc->dev));
|
2004-08-18 16:01:13 +04:00
|
|
|
error = EIO;
|
2004-08-11 03:34:06 +04:00
|
|
|
goto done;
|
2004-08-12 00:57:40 +04:00
|
|
|
}
|
2004-08-18 16:01:13 +04:00
|
|
|
#endif
|
1997-10-17 03:27:16 +04:00
|
|
|
|
|
|
|
if (pcmcia_verbose)
|
|
|
|
pcmcia_print_cis(sc);
|
|
|
|
|
2002-06-02 03:50:52 +04:00
|
|
|
SIMPLEQ_FOREACH(pf, &sc->card.pf_head, pf_list) {
|
|
|
|
if (SIMPLEQ_EMPTY(&pf->cfe_head))
|
1997-10-17 03:27:16 +04:00
|
|
|
continue;
|
|
|
|
|
1998-11-17 11:49:11 +03:00
|
|
|
#ifdef DIAGNOSTIC
|
|
|
|
if (pf->child != NULL) {
|
|
|
|
printf("%s: %s still attached to function %d!\n",
|
2008-07-03 23:07:43 +04:00
|
|
|
device_xname(sc->dev), device_xname(pf->child),
|
1998-11-17 11:49:11 +03:00
|
|
|
pf->number);
|
|
|
|
panic("pcmcia_card_attach");
|
|
|
|
}
|
|
|
|
#endif
|
1997-10-17 03:27:16 +04:00
|
|
|
pf->sc = sc;
|
1998-11-17 11:49:11 +03:00
|
|
|
pf->child = NULL;
|
1997-10-17 03:27:16 +04:00
|
|
|
pf->cfe = NULL;
|
2004-08-10 00:02:36 +04:00
|
|
|
pf->pf_ih = NULL;
|
1997-10-17 03:27:16 +04:00
|
|
|
}
|
|
|
|
|
2004-08-18 16:01:13 +04:00
|
|
|
error = pcmcia_rescan(dev, "pcmcia", wildcard);
|
|
|
|
done:
|
|
|
|
pcmcia_socket_disable(dev);
|
|
|
|
return (error);
|
|
|
|
}
|
|
|
|
|
|
|
|
int
|
2009-05-12 18:38:26 +04:00
|
|
|
pcmcia_rescan(device_t self, const char *ifattr,
|
2006-10-12 05:30:41 +04:00
|
|
|
const int *locators)
|
2004-08-18 16:01:13 +04:00
|
|
|
{
|
2008-07-03 23:07:43 +04:00
|
|
|
struct pcmcia_softc *sc = device_private(self);
|
2004-08-18 16:01:13 +04:00
|
|
|
struct pcmcia_function *pf;
|
|
|
|
struct pcmcia_attach_args paa;
|
2005-08-25 22:35:38 +04:00
|
|
|
int locs[PCMCIACF_NLOCS];
|
2004-08-18 16:01:13 +04:00
|
|
|
|
|
|
|
if (sc->card.error ||
|
|
|
|
SIMPLEQ_EMPTY(&sc->card.pf_head)) {
|
|
|
|
/* XXX silently ignore if no card present? */
|
|
|
|
return (EIO);
|
|
|
|
}
|
|
|
|
|
2002-06-02 03:50:52 +04:00
|
|
|
SIMPLEQ_FOREACH(pf, &sc->card.pf_head, pf_list) {
|
|
|
|
if (SIMPLEQ_EMPTY(&pf->cfe_head))
|
1997-10-17 03:27:16 +04:00
|
|
|
continue;
|
|
|
|
|
2004-08-18 16:01:13 +04:00
|
|
|
if ((locators[PCMCIACF_FUNCTION] != PCMCIACF_FUNCTION_DEFAULT)
|
|
|
|
&& (locators[PCMCIACF_FUNCTION] != pf->number))
|
|
|
|
continue;
|
|
|
|
|
|
|
|
if (pf->child)
|
|
|
|
continue;
|
|
|
|
|
2005-08-25 22:35:38 +04:00
|
|
|
locs[PCMCIACF_FUNCTION] = pf->number;
|
2004-08-18 16:01:13 +04:00
|
|
|
|
1997-10-17 03:27:16 +04:00
|
|
|
paa.manufacturer = sc->card.manufacturer;
|
|
|
|
paa.product = sc->card.product;
|
|
|
|
paa.card = &sc->card;
|
|
|
|
paa.pf = pf;
|
|
|
|
|
2005-08-25 22:35:38 +04:00
|
|
|
pf->child = config_found_sm_loc(self, "pcmcia", locs, &paa,
|
2005-08-26 02:33:18 +04:00
|
|
|
pcmcia_print,
|
|
|
|
config_stdsubmatch);
|
1997-10-17 03:27:16 +04:00
|
|
|
}
|
|
|
|
|
2004-08-18 16:01:13 +04:00
|
|
|
return (0);
|
1997-10-17 03:27:16 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
void
|
2009-05-12 18:38:26 +04:00
|
|
|
pcmcia_card_detach(device_t dev, int flags)
|
2009-03-15 00:04:01 +03:00
|
|
|
/* flags: DETACH_* flags */
|
1997-10-17 03:27:16 +04:00
|
|
|
{
|
2008-07-03 23:07:43 +04:00
|
|
|
struct pcmcia_softc *sc = device_private(dev);
|
1998-11-17 11:49:11 +03:00
|
|
|
struct pcmcia_function *pf;
|
|
|
|
int error;
|
1998-11-14 04:54:25 +03:00
|
|
|
|
1998-11-17 11:49:11 +03:00
|
|
|
/*
|
|
|
|
* We are running on either the PCMCIA socket's event thread
|
|
|
|
* or in user context detaching a device by user request.
|
1998-11-14 04:54:25 +03:00
|
|
|
*/
|
2002-06-02 03:50:52 +04:00
|
|
|
SIMPLEQ_FOREACH(pf, &sc->card.pf_head, pf_list) {
|
2004-08-12 21:31:06 +04:00
|
|
|
pf->pf_flags |= PFF_DETACHED;
|
2002-06-02 03:50:52 +04:00
|
|
|
if (SIMPLEQ_EMPTY(&pf->cfe_head))
|
1998-11-17 11:49:11 +03:00
|
|
|
continue;
|
|
|
|
if (pf->child == NULL)
|
|
|
|
continue;
|
|
|
|
DPRINTF(("%s: detaching %s (function %d)\n",
|
2008-07-03 23:07:43 +04:00
|
|
|
device_xname(sc->dev), device_xname(pf->child), pf->number));
|
1998-11-17 11:49:11 +03:00
|
|
|
if ((error = config_detach(pf->child, flags)) != 0) {
|
|
|
|
printf("%s: error %d detaching %s (function %d)\n",
|
2008-07-03 23:07:43 +04:00
|
|
|
device_xname(sc->dev), error, device_xname(pf->child),
|
1998-11-17 11:49:11 +03:00
|
|
|
pf->number);
|
2004-08-18 16:01:13 +04:00
|
|
|
}
|
1998-11-17 11:49:11 +03:00
|
|
|
}
|
2004-08-10 06:50:52 +04:00
|
|
|
|
|
|
|
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;
|
|
|
|
}
|
1998-11-17 11:49:11 +03:00
|
|
|
}
|
|
|
|
|
2004-08-18 16:01:13 +04:00
|
|
|
void
|
2009-05-12 18:38:26 +04:00
|
|
|
pcmcia_childdetached(device_t self, device_t child)
|
2004-08-18 16:01:13 +04:00
|
|
|
{
|
2008-07-03 23:07:43 +04:00
|
|
|
struct pcmcia_softc *sc = device_private(self);
|
2004-08-18 16:01:13 +04:00
|
|
|
struct pcmcia_function *pf;
|
|
|
|
|
|
|
|
SIMPLEQ_FOREACH(pf, &sc->card.pf_head, pf_list) {
|
|
|
|
if (SIMPLEQ_EMPTY(&pf->cfe_head))
|
|
|
|
continue;
|
|
|
|
if (pf->child == child) {
|
2006-03-29 10:00:46 +04:00
|
|
|
KASSERT(device_locator(child, PCMCIACF_FUNCTION)
|
2004-08-18 16:01:13 +04:00
|
|
|
== pf->number);
|
|
|
|
pf->child = NULL;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2008-04-06 01:31:23 +04:00
|
|
|
aprint_error_dev(self, "pcmcia_childdetached: %s not found\n",
|
|
|
|
device_xname(child));
|
2004-08-18 16:01:13 +04:00
|
|
|
}
|
|
|
|
|
1998-11-17 11:49:11 +03:00
|
|
|
void
|
2009-05-12 18:38:26 +04:00
|
|
|
pcmcia_card_deactivate(device_t dev)
|
1998-11-17 11:49:11 +03:00
|
|
|
{
|
2008-07-03 23:07:43 +04:00
|
|
|
struct pcmcia_softc *sc = device_private(dev);
|
1998-11-17 11:49:11 +03:00
|
|
|
struct pcmcia_function *pf;
|
1998-11-14 04:54:25 +03:00
|
|
|
|
1998-11-17 11:49:11 +03:00
|
|
|
/*
|
|
|
|
* 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.
|
|
|
|
*/
|
2002-06-02 03:50:52 +04:00
|
|
|
SIMPLEQ_FOREACH(pf, &sc->card.pf_head, pf_list) {
|
|
|
|
if (SIMPLEQ_EMPTY(&pf->cfe_head))
|
1998-11-17 11:49:11 +03:00
|
|
|
continue;
|
|
|
|
if (pf->child == NULL)
|
|
|
|
continue;
|
|
|
|
DPRINTF(("%s: deactivating %s (function %d)\n",
|
2008-07-03 23:07:43 +04:00
|
|
|
device_xname(sc->dev), device_xname(pf->child), pf->number));
|
1998-11-17 11:49:11 +03:00
|
|
|
config_deactivate(pf->child);
|
|
|
|
}
|
1997-10-17 03:27:16 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
int
|
2009-03-14 18:35:58 +03:00
|
|
|
pcmcia_print(void *arg, const char *pnp)
|
1997-10-17 03:27:16 +04:00
|
|
|
{
|
|
|
|
struct pcmcia_attach_args *pa = arg;
|
|
|
|
struct pcmcia_softc *sc = pa->pf->sc;
|
|
|
|
struct pcmcia_card *card = &sc->card;
|
2000-02-07 12:35:29 +03:00
|
|
|
char devinfo[256];
|
1997-10-17 03:27:16 +04:00
|
|
|
|
2004-07-07 10:43:22 +04:00
|
|
|
if (pnp)
|
|
|
|
aprint_normal("%s", pnp);
|
|
|
|
|
|
|
|
pcmcia_devinfo(card, !!pnp, devinfo, sizeof(devinfo));
|
|
|
|
|
2004-08-10 22:43:49 +04:00
|
|
|
aprint_normal(" function %d: %s\n", pa->pf->number, devinfo);
|
1997-10-17 03:27:16 +04:00
|
|
|
|
|
|
|
return (UNCONF);
|
|
|
|
}
|
|
|
|
|
2000-02-07 12:35:29 +03:00
|
|
|
void
|
2009-03-14 18:35:58 +03:00
|
|
|
pcmcia_devinfo(struct pcmcia_card *card, int showhex, char *cp, size_t cplen)
|
2000-02-07 12:35:29 +03:00
|
|
|
{
|
|
|
|
int i, n;
|
|
|
|
|
2004-07-07 10:08:25 +04:00
|
|
|
if (cplen > 1) {
|
|
|
|
*cp++ = '<';
|
2004-07-07 10:43:22 +04:00
|
|
|
*cp = '\0';
|
2004-07-07 10:08:25 +04:00
|
|
|
cplen--;
|
|
|
|
}
|
|
|
|
|
|
|
|
for (i = 0; i < 4 && card->cis1_info[i] != NULL && cplen > 1; i++) {
|
2004-08-07 00:30:05 +04:00
|
|
|
n = snprintf(cp, cplen, "%s%s", i ? ", " : "",
|
2000-02-07 12:35:29 +03:00
|
|
|
card->cis1_info[i]);
|
|
|
|
cp += n;
|
2004-07-07 10:43:22 +04:00
|
|
|
if (cplen < n)
|
|
|
|
return;
|
2000-02-07 12:35:29 +03:00
|
|
|
cplen -= n;
|
|
|
|
}
|
2004-07-07 10:08:25 +04:00
|
|
|
|
|
|
|
if (cplen > 1) {
|
|
|
|
*cp++ = '>';
|
2004-07-07 10:43:22 +04:00
|
|
|
*cp = '\0';
|
2004-07-07 10:08:25 +04:00
|
|
|
cplen--;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (showhex && cplen > 1)
|
|
|
|
snprintf(cp, cplen, " (manufacturer 0x%04x, product 0x%04x)",
|
|
|
|
card->manufacturer, card->product);
|
2000-02-07 12:35:29 +03:00
|
|
|
}
|
|
|
|
|
2004-08-10 22:39:08 +04:00
|
|
|
const void *
|
2009-03-14 18:35:58 +03:00
|
|
|
pcmcia_product_lookup(struct pcmcia_attach_args *pa, const void *tab, size_t nent, size_t ent_size, pcmcia_product_match_fn matchfn)
|
2000-02-04 04:27:12 +03:00
|
|
|
{
|
2004-08-10 22:39:08 +04:00
|
|
|
const struct pcmcia_product *pp;
|
|
|
|
int n;
|
2000-02-04 04:27:12 +03:00
|
|
|
int matches;
|
|
|
|
|
|
|
|
#ifdef DIAGNOSTIC
|
2004-08-10 22:39:08 +04:00
|
|
|
if (sizeof *pp > ent_size)
|
2005-02-27 03:26:58 +03:00
|
|
|
panic("pcmcia_product_lookup: bogus ent_size %ld",
|
2000-02-05 23:02:42 +03:00
|
|
|
(long) ent_size);
|
2000-02-04 04:27:12 +03:00
|
|
|
#endif
|
|
|
|
|
2004-08-10 22:39:08 +04:00
|
|
|
for (pp = tab, n = nent; n; pp = (const struct pcmcia_product *)
|
|
|
|
((const char *)pp + ent_size), n--) {
|
|
|
|
/* see if it matches vendor/product */
|
2004-08-10 23:15:08 +04:00
|
|
|
matches = 0;
|
2004-08-10 22:39:08 +04:00
|
|
|
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;
|
2004-08-10 23:15:08 +04:00
|
|
|
if ((pp->pp_cisinfo[0] && pa->card->cis1_info[0] &&
|
2004-08-10 22:39:08 +04:00
|
|
|
!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;
|
2000-02-04 04:27:12 +03:00
|
|
|
|
|
|
|
/* if a separate match function is given, let it override */
|
2004-08-10 22:39:08 +04:00
|
|
|
if (matchfn)
|
|
|
|
matches = (*matchfn)(pa, pp, matches);
|
2000-02-04 04:27:12 +03:00
|
|
|
|
|
|
|
if (matches)
|
2004-08-10 22:39:08 +04:00
|
|
|
return (pp);
|
2000-02-04 04:27:12 +03:00
|
|
|
}
|
2004-08-10 22:39:08 +04:00
|
|
|
return (0);
|
2000-02-04 04:27:12 +03:00
|
|
|
}
|
|
|
|
|
2004-08-11 04:18:18 +04:00
|
|
|
void
|
2009-05-12 18:38:26 +04:00
|
|
|
pcmcia_socket_settype(device_t dev, int type)
|
1997-10-17 03:27:16 +04:00
|
|
|
{
|
2008-07-03 23:07:43 +04:00
|
|
|
struct pcmcia_softc *sc = device_private(dev);
|
1997-10-17 03:27:16 +04:00
|
|
|
|
2004-08-11 04:18:18 +04:00
|
|
|
pcmcia_chip_socket_settype(sc->pct, sc->pch, type);
|
1997-10-17 03:27:16 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Initialize a PCMCIA function. May be called as long as the function is
|
|
|
|
* disabled.
|
|
|
|
*/
|
|
|
|
void
|
2009-03-14 18:35:58 +03:00
|
|
|
pcmcia_function_init(struct pcmcia_function *pf, struct pcmcia_config_entry *cfe)
|
1997-10-17 03:27:16 +04:00
|
|
|
{
|
|
|
|
if (pf->pf_flags & PFF_ENABLED)
|
|
|
|
panic("pcmcia_function_init: function is enabled");
|
|
|
|
|
|
|
|
/* Remember which configuration entry we are using. */
|
|
|
|
pf->cfe = cfe;
|
|
|
|
}
|
|
|
|
|
2004-08-11 03:34:06 +04:00
|
|
|
void
|
2009-05-12 18:38:26 +04:00
|
|
|
pcmcia_socket_enable(device_t dev)
|
1998-02-02 02:49:02 +03:00
|
|
|
{
|
2008-07-03 23:07:43 +04:00
|
|
|
struct pcmcia_softc *sc = device_private(dev);
|
2004-08-08 09:33:04 +04:00
|
|
|
|
2004-08-11 03:34:06 +04:00
|
|
|
if (sc->sc_enabled_count++ == 0)
|
|
|
|
pcmcia_chip_socket_enable(sc->pct, sc->pch);
|
2008-07-03 23:07:43 +04:00
|
|
|
DPRINTF(("%s: ++enabled_count = %d\n", device_xname(sc->dev),
|
2004-08-11 03:34:06 +04:00
|
|
|
sc->sc_enabled_count));
|
1998-02-02 02:49:02 +03:00
|
|
|
}
|
|
|
|
|
2004-08-11 03:34:06 +04:00
|
|
|
void
|
2009-05-12 18:38:26 +04:00
|
|
|
pcmcia_socket_disable(device_t dev)
|
1998-02-02 02:49:02 +03:00
|
|
|
{
|
2008-07-03 23:07:43 +04:00
|
|
|
struct pcmcia_softc *sc = device_private(dev);
|
2004-08-08 09:33:04 +04:00
|
|
|
|
2004-08-11 03:34:06 +04:00
|
|
|
if (--sc->sc_enabled_count == 0)
|
|
|
|
pcmcia_chip_socket_disable(sc->pct, sc->pch);
|
2008-07-03 23:07:43 +04:00
|
|
|
DPRINTF(("%s: --enabled_count = %d\n", device_xname(sc->dev),
|
2004-08-11 03:34:06 +04:00
|
|
|
sc->sc_enabled_count));
|
1998-02-02 02:49:02 +03:00
|
|
|
}
|
|
|
|
|
1997-10-17 03:27:16 +04:00
|
|
|
/* Enable a PCMCIA function */
|
|
|
|
int
|
2009-03-14 18:35:58 +03:00
|
|
|
pcmcia_function_enable(struct pcmcia_function *pf)
|
1997-10-17 03:27:16 +04:00
|
|
|
{
|
2004-08-10 06:50:52 +04:00
|
|
|
struct pcmcia_softc *sc = pf->sc;
|
1997-10-17 03:27:16 +04:00
|
|
|
struct pcmcia_function *tmp;
|
|
|
|
int reg;
|
2004-08-10 19:29:56 +04:00
|
|
|
int error;
|
1997-10-17 03:27:16 +04:00
|
|
|
|
|
|
|
if (pf->cfe == NULL)
|
|
|
|
panic("pcmcia_function_enable: function not initialized");
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Increase the reference count on the socket, enabling power, if
|
|
|
|
* necessary.
|
|
|
|
*/
|
2008-07-03 23:07:43 +04:00
|
|
|
pcmcia_socket_enable(sc->dev);
|
|
|
|
pcmcia_socket_settype(sc->dev, pf->cfe->iftype);
|
1998-02-02 02:49:02 +03:00
|
|
|
|
|
|
|
if (pf->pf_flags & PFF_ENABLED) {
|
|
|
|
/*
|
|
|
|
* Don't do anything if we're already enabled.
|
|
|
|
*/
|
|
|
|
return (0);
|
|
|
|
}
|
1997-10-17 03:27:16 +04:00
|
|
|
|
|
|
|
/*
|
|
|
|
* it's possible for different functions' CCRs to be in the same
|
|
|
|
* underlying page. Check for that.
|
|
|
|
*/
|
|
|
|
|
2004-08-10 06:50:52 +04:00
|
|
|
SIMPLEQ_FOREACH(tmp, &sc->card.pf_head, pf_list) {
|
1997-10-17 03:27:16 +04:00
|
|
|
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) {
|
2004-08-10 19:29:56 +04:00
|
|
|
error = pcmcia_mem_alloc(pf, PCMCIA_CCR_SIZE, &pf->pf_pcmh);
|
|
|
|
if (error)
|
1997-10-17 03:27:16 +04:00
|
|
|
goto bad;
|
|
|
|
|
2004-08-10 19:29:56 +04:00
|
|
|
error = pcmcia_mem_map(pf, PCMCIA_MEM_ATTR, pf->ccr_base,
|
1997-10-17 03:27:16 +04:00
|
|
|
PCMCIA_CCR_SIZE, &pf->pf_pcmh, &pf->pf_ccr_offset,
|
2004-08-10 19:29:56 +04:00
|
|
|
&pf->pf_ccr_window);
|
|
|
|
if (error) {
|
1997-10-17 03:27:16 +04:00
|
|
|
pcmcia_mem_free(pf, &pf->pf_pcmh);
|
|
|
|
goto bad;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2004-08-12 21:26:51 +04:00
|
|
|
if (pcmcia_mfc(sc) || 1) {
|
2004-08-09 03:17:12 +04:00
|
|
|
pcmcia_ccr_write(pf, PCMCIA_CCR_IOBASE0,
|
2004-08-12 20:04:20 +04:00
|
|
|
(pf->pf_mfc_iobase >> 0) & 0xff);
|
2004-08-09 03:17:12 +04:00
|
|
|
pcmcia_ccr_write(pf, PCMCIA_CCR_IOBASE1,
|
2004-08-12 20:04:20 +04:00
|
|
|
(pf->pf_mfc_iobase >> 8) & 0xff);
|
2004-08-09 03:17:12 +04:00
|
|
|
pcmcia_ccr_write(pf, PCMCIA_CCR_IOBASE2,
|
2004-08-12 20:04:20 +04:00
|
|
|
(pf->pf_mfc_iobase >> 16) & 0xff);
|
2004-08-09 03:17:12 +04:00
|
|
|
pcmcia_ccr_write(pf, PCMCIA_CCR_IOBASE3,
|
2004-08-12 20:04:20 +04:00
|
|
|
(pf->pf_mfc_iobase >> 24) & 0xff);
|
|
|
|
pcmcia_ccr_write(pf, PCMCIA_CCR_IOLIMIT,
|
|
|
|
pf->pf_mfc_iomax - pf->pf_mfc_iobase);
|
2004-08-09 03:17:12 +04:00
|
|
|
}
|
|
|
|
|
2004-08-12 21:07:52 +04:00
|
|
|
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);
|
|
|
|
|
1997-10-17 03:27:16 +04:00
|
|
|
reg = (pf->cfe->number & PCMCIA_CCR_OPTION_CFINDEX);
|
|
|
|
reg |= PCMCIA_CCR_OPTION_LEVIREQ;
|
2004-08-10 06:50:52 +04:00
|
|
|
if (pcmcia_mfc(sc)) {
|
1998-02-02 02:49:02 +03:00
|
|
|
reg |= (PCMCIA_CCR_OPTION_FUNC_ENABLE |
|
|
|
|
PCMCIA_CCR_OPTION_ADDR_DECODE);
|
2004-08-10 00:02:36 +04:00
|
|
|
if (pf->pf_ih)
|
1998-02-02 02:49:02 +03:00
|
|
|
reg |= PCMCIA_CCR_OPTION_IREQ_ENABLE;
|
|
|
|
|
|
|
|
}
|
1997-10-17 03:27:16 +04:00
|
|
|
pcmcia_ccr_write(pf, PCMCIA_CCR_OPTION, reg);
|
|
|
|
|
1998-12-24 07:50:43 +03:00
|
|
|
#ifdef PCMCIADEBUG
|
|
|
|
if (pcmcia_debug) {
|
2004-08-10 06:50:52 +04:00
|
|
|
SIMPLEQ_FOREACH(tmp, &sc->card.pf_head, pf_list) {
|
1998-12-24 07:50:43 +03:00
|
|
|
printf("%s: function %d CCR at %d offset %lx: "
|
|
|
|
"%x %x %x %x, %x %x %x %x, %x\n",
|
2008-07-03 23:07:43 +04:00
|
|
|
device_xname(tmp->sc->dev), tmp->number,
|
2000-01-23 23:44:04 +03:00
|
|
|
tmp->pf_ccr_window,
|
|
|
|
(unsigned long) tmp->pf_ccr_offset,
|
2004-08-09 05:32:04 +04:00
|
|
|
pcmcia_ccr_read(tmp, 0),
|
|
|
|
pcmcia_ccr_read(tmp, 1),
|
|
|
|
pcmcia_ccr_read(tmp, 2),
|
|
|
|
pcmcia_ccr_read(tmp, 3),
|
1998-12-24 07:50:43 +03:00
|
|
|
|
2004-08-09 05:32:04 +04:00
|
|
|
pcmcia_ccr_read(tmp, 5),
|
2005-02-27 03:26:58 +03:00
|
|
|
pcmcia_ccr_read(tmp, 6),
|
2004-08-09 05:32:04 +04:00
|
|
|
pcmcia_ccr_read(tmp, 7),
|
|
|
|
pcmcia_ccr_read(tmp, 8),
|
1998-12-24 07:50:43 +03:00
|
|
|
|
2004-08-09 05:32:04 +04:00
|
|
|
pcmcia_ccr_read(tmp, 9));
|
1998-12-24 07:50:43 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
#endif
|
1998-02-02 02:49:02 +03:00
|
|
|
|
2000-02-26 22:02:22 +03:00
|
|
|
#ifdef IT8368E_LEGACY_MODE
|
|
|
|
/* return to I/O mode */
|
|
|
|
it8368_mode(pf, IT8368_IO_MODE, IT8368_WIDTH_16);
|
|
|
|
#endif
|
2004-08-12 21:06:49 +04:00
|
|
|
|
|
|
|
pf->pf_flags |= PFF_ENABLED;
|
1997-10-17 03:27:16 +04:00
|
|
|
return (0);
|
|
|
|
|
2004-08-10 19:29:56 +04:00
|
|
|
bad:
|
1997-10-17 03:27:16 +04:00
|
|
|
/*
|
|
|
|
* Decrement the reference count, and power down the socket, if
|
|
|
|
* necessary.
|
|
|
|
*/
|
2008-04-06 01:31:23 +04:00
|
|
|
printf("%s: couldn't map the CCR\n", device_xname(pf->child));
|
2008-07-03 23:07:43 +04:00
|
|
|
pcmcia_socket_disable(sc->dev);
|
1998-02-02 02:49:02 +03:00
|
|
|
|
2004-08-10 19:29:56 +04:00
|
|
|
return (error);
|
1997-10-17 03:27:16 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Disable PCMCIA function. */
|
|
|
|
void
|
2009-03-14 18:35:58 +03:00
|
|
|
pcmcia_function_disable(struct pcmcia_function *pf)
|
1997-10-17 03:27:16 +04:00
|
|
|
{
|
2004-08-11 03:34:06 +04:00
|
|
|
struct pcmcia_softc *sc = pf->sc;
|
1997-10-17 03:27:16 +04:00
|
|
|
struct pcmcia_function *tmp;
|
2004-08-09 22:30:51 +04:00
|
|
|
int reg;
|
1997-10-17 03:27:16 +04:00
|
|
|
|
|
|
|
if (pf->cfe == NULL)
|
|
|
|
panic("pcmcia_function_enable: function not initialized");
|
|
|
|
|
|
|
|
if ((pf->pf_flags & PFF_ENABLED) == 0) {
|
|
|
|
/*
|
2003-10-22 11:46:48 +04:00
|
|
|
* Don't do anything but decrement if we're already disabled.
|
1997-10-17 03:27:16 +04:00
|
|
|
*/
|
2003-10-22 11:46:48 +04:00
|
|
|
goto out;
|
1997-10-17 03:27:16 +04:00
|
|
|
}
|
|
|
|
|
2004-08-12 21:31:06 +04:00
|
|
|
if (pcmcia_mfc(sc) &&
|
|
|
|
(pf->pf_flags & PFF_DETACHED) == 0) {
|
2004-08-09 22:30:51 +04:00
|
|
|
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);
|
|
|
|
}
|
|
|
|
|
1997-10-17 03:27:16 +04:00
|
|
|
/*
|
|
|
|
* 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;
|
2004-08-11 03:34:06 +04:00
|
|
|
SIMPLEQ_FOREACH(tmp, &sc->card.pf_head, pf_list) {
|
1997-10-17 03:27:16 +04:00
|
|
|
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);
|
|
|
|
}
|
|
|
|
|
2003-10-22 11:46:48 +04:00
|
|
|
out:
|
1997-10-17 03:27:16 +04:00
|
|
|
/*
|
|
|
|
* Decrement the reference count, and power down the socket, if
|
|
|
|
* necessary.
|
|
|
|
*/
|
2008-07-03 23:07:43 +04:00
|
|
|
pcmcia_socket_disable(sc->dev);
|
1997-10-17 03:27:16 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
int
|
2009-03-14 18:35:58 +03:00
|
|
|
pcmcia_io_map(struct pcmcia_function *pf, int width, struct pcmcia_io_handle *pcihp, int *windowp)
|
1997-10-17 03:27:16 +04:00
|
|
|
{
|
2004-08-12 21:26:51 +04:00
|
|
|
struct pcmcia_softc *sc = pf->sc;
|
2004-08-09 23:08:19 +04:00
|
|
|
int error;
|
1997-10-17 03:27:16 +04:00
|
|
|
|
2004-08-09 23:08:19 +04:00
|
|
|
if (pf->pf_flags & PFF_ENABLED)
|
2004-08-09 23:33:07 +04:00
|
|
|
printf("pcmcia_io_map: function is enabled!\n");
|
2004-08-09 23:08:19 +04:00
|
|
|
|
2004-08-12 21:26:51 +04:00
|
|
|
error = pcmcia_chip_io_map(sc->pct, sc->pch,
|
2004-08-09 23:08:19 +04:00
|
|
|
width, 0, pcihp->size, pcihp, windowp);
|
|
|
|
if (error)
|
|
|
|
return (error);
|
1997-10-17 03:27:16 +04:00
|
|
|
|
|
|
|
/*
|
|
|
|
* 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
|
|
|
|
*/
|
|
|
|
|
2004-08-12 21:26:51 +04:00
|
|
|
if (pcmcia_mfc(sc) || 1) {
|
2004-08-12 20:04:20 +04:00
|
|
|
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));
|
1997-10-17 03:27:16 +04:00
|
|
|
}
|
2004-08-09 03:17:12 +04:00
|
|
|
|
1997-10-17 03:27:16 +04:00
|
|
|
return (0);
|
|
|
|
}
|
|
|
|
|
1998-11-17 11:49:11 +03:00
|
|
|
void
|
2009-03-14 18:35:58 +03:00
|
|
|
pcmcia_io_unmap(struct pcmcia_function *pf, int window)
|
1998-11-17 11:49:11 +03:00
|
|
|
{
|
2004-08-12 21:26:51 +04:00
|
|
|
struct pcmcia_softc *sc = pf->sc;
|
1998-11-17 11:49:11 +03:00
|
|
|
|
2004-08-09 23:08:19 +04:00
|
|
|
if (pf->pf_flags & PFF_ENABLED)
|
2004-08-09 23:33:07 +04:00
|
|
|
printf("pcmcia_io_unmap: function is enabled!\n");
|
2004-08-09 23:08:19 +04:00
|
|
|
|
2004-08-12 21:26:51 +04:00
|
|
|
pcmcia_chip_io_unmap(sc->pct, sc->pch, window);
|
1998-11-17 11:49:11 +03:00
|
|
|
}
|
|
|
|
|
1997-10-17 03:27:16 +04:00
|
|
|
void *
|
2009-03-15 23:30:05 +03:00
|
|
|
pcmcia_intr_establish(struct pcmcia_function *pf, int ipl,
|
|
|
|
int (*ih_fct)(void *), void *ih_arg)
|
1997-10-17 03:27:16 +04:00
|
|
|
{
|
|
|
|
|
2004-08-09 23:08:19 +04:00
|
|
|
if (pf->pf_flags & PFF_ENABLED)
|
2004-08-09 23:33:07 +04:00
|
|
|
printf("pcmcia_intr_establish: function is enabled!\n");
|
2004-08-10 00:02:36 +04:00
|
|
|
if (pf->pf_ih)
|
|
|
|
panic("pcmcia_intr_establish: already done\n");
|
1997-10-17 03:27:16 +04:00
|
|
|
|
2004-08-10 00:02:36 +04:00
|
|
|
pf->pf_ih = pcmcia_chip_intr_establish(pf->sc->pct, pf->sc->pch,
|
|
|
|
pf, ipl, ih_fct, ih_arg);
|
2004-08-10 19:29:56 +04:00
|
|
|
if (!pf->pf_ih)
|
2008-04-06 01:31:23 +04:00
|
|
|
aprint_error_dev(pf->child, "interrupt establish failed\n");
|
2004-08-10 00:02:36 +04:00
|
|
|
return (pf->pf_ih);
|
1997-10-17 03:27:16 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
void
|
2009-03-14 18:35:58 +03:00
|
|
|
pcmcia_intr_disestablish(struct pcmcia_function *pf, void *ih)
|
1997-10-17 03:27:16 +04:00
|
|
|
{
|
2004-08-10 00:02:36 +04:00
|
|
|
|
2004-08-09 23:08:19 +04:00
|
|
|
if (pf->pf_flags & PFF_ENABLED)
|
2004-08-09 23:33:07 +04:00
|
|
|
printf("pcmcia_intr_disestablish: function is enabled!\n");
|
2004-08-10 00:02:36 +04:00
|
|
|
if (!pf->pf_ih)
|
|
|
|
panic("pcmcia_intr_distestablish: already done\n");
|
1997-10-17 03:27:16 +04:00
|
|
|
|
2004-08-10 00:02:36 +04:00
|
|
|
pcmcia_chip_intr_disestablish(pf->sc->pct, pf->sc->pch, ih);
|
|
|
|
pf->pf_ih = 0;
|
1997-10-17 03:27:16 +04:00
|
|
|
}
|
2004-08-10 06:50:52 +04:00
|
|
|
|
|
|
|
int
|
2009-03-14 18:35:58 +03:00
|
|
|
pcmcia_config_alloc(struct pcmcia_function *pf, struct pcmcia_config_entry *cfe)
|
2004-08-10 06:50:52 +04:00
|
|
|
{
|
2004-08-11 04:32:44 +04:00
|
|
|
int error = 0;
|
2004-08-10 06:50:52 +04:00
|
|
|
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);
|
|
|
|
|
2004-08-10 09:21:59 +04:00
|
|
|
if ((start - skew) == 0 && align < 0x400) {
|
|
|
|
if (skew)
|
|
|
|
printf("Drats! I need a skew!\n");
|
2004-08-10 06:50:52 +04:00
|
|
|
start = 0;
|
2004-08-10 09:21:59 +04:00
|
|
|
}
|
2004-08-10 06:50:52 +04:00
|
|
|
|
|
|
|
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! */
|
2004-08-11 04:32:44 +04:00
|
|
|
return (error);
|
2004-08-10 06:50:52 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
void
|
2009-03-14 18:35:58 +03:00
|
|
|
pcmcia_config_free(struct pcmcia_function *pf)
|
2004-08-10 06:50:52 +04:00
|
|
|
{
|
|
|
|
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
|
2009-03-14 18:35:58 +03:00
|
|
|
pcmcia_config_map(struct pcmcia_function *pf)
|
2004-08-10 06:50:52 +04:00
|
|
|
{
|
|
|
|
struct pcmcia_config_entry *cfe = pf->cfe;
|
2004-08-11 04:32:44 +04:00
|
|
|
int error = 0;
|
2004-08-10 06:50:52 +04:00
|
|
|
int n, m;
|
|
|
|
|
|
|
|
for (n = 0; n < cfe->num_iospace; n++) {
|
|
|
|
int width;
|
|
|
|
|
2004-08-11 08:25:28 +04:00
|
|
|
if (cfe->flags & PCMCIA_CFE_IO16)
|
2004-08-10 06:50:52 +04:00
|
|
|
width = PCMCIA_WIDTH_AUTO;
|
2004-08-11 08:25:28 +04:00
|
|
|
else
|
|
|
|
width = PCMCIA_WIDTH_IO8;
|
2004-08-10 06:50:52 +04:00
|
|
|
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! */
|
2004-08-11 04:32:44 +04:00
|
|
|
return (error);
|
2004-08-10 06:50:52 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
void
|
2009-03-14 18:35:58 +03:00
|
|
|
pcmcia_config_unmap(struct pcmcia_function *pf)
|
2004-08-10 06:50:52 +04:00
|
|
|
{
|
|
|
|
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
|
2009-03-15 23:30:05 +03:00
|
|
|
pcmcia_function_configure(struct pcmcia_function *pf,
|
|
|
|
int (*validator)(struct pcmcia_config_entry *))
|
2004-08-10 06:50:52 +04:00
|
|
|
{
|
|
|
|
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
|
2009-03-14 18:35:58 +03:00
|
|
|
pcmcia_function_unconfigure(struct pcmcia_function *pf)
|
2004-08-10 06:50:52 +04:00
|
|
|
{
|
|
|
|
|
|
|
|
pcmcia_config_unmap(pf);
|
|
|
|
pcmcia_config_free(pf);
|
|
|
|
}
|