NetBSD/sys/dev/cardbus/cardslot.c

463 lines
12 KiB
C
Raw Normal View History

/* $NetBSD: cardslot.c,v 1.56 2016/09/24 23:54:49 mrg Exp $ */
/*
* Copyright (c) 1999 and 2000
* HAYAKAWA Koichi. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* 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 15:51:12 +03:00
#include <sys/cdefs.h>
__KERNEL_RCSID(0, "$NetBSD: cardslot.c,v 1.56 2016/09/24 23:54:49 mrg Exp $");
2001-11-13 15:51:12 +03:00
#include "opt_cardslot.h"
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/device.h>
#include <sys/malloc.h>
#include <sys/kernel.h>
#include <sys/syslog.h>
#include <sys/kthread.h>
#include <sys/bus.h>
#include <dev/cardbus/cardslotvar.h>
#include <dev/cardbus/cardbusvar.h>
#include <dev/pcmcia/pcmciavar.h>
#include <dev/pcmcia/pcmciachip.h>
#include "locators.h"
#if defined CARDSLOT_DEBUG
#define STATIC
#define DPRINTF(a) printf a
#else
#define STATIC static
#define DPRINTF(a)
#endif
int pcmcia_error(device_t);
int
pcmcia_error(device_t dev)
{
return 1;
}
__weak_alias(pcmcia_card_attach, pcmcia_error);
__weak_alias(pcmcia_card_deactivate, pcmcia_error);
__weak_alias(pcmcia_card_detach, pcmcia_error);
STATIC void cardslotchilddet(device_t, device_t);
STATIC void cardslotattach(device_t, device_t, void *);
STATIC int cardslotdetach(device_t, int);
STATIC int cardslotmatch(device_t, cfdata_t, void *);
2005-02-04 05:10:35 +03:00
static void cardslot_event_thread(void *arg);
2005-02-04 05:10:35 +03:00
STATIC int cardslot_cb_print(void *aux, const char *pcic);
static int cardslot_16_print(void *, const char *);
static int cardslot_16_submatch(device_t, cfdata_t, const int *, void *);
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(cardslot, sizeof(struct cardslot_softc),
cardslotmatch, cardslotattach, cardslotdetach, NULL, NULL, cardslotchilddet,
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
DVF_DETACH_SHUTDOWN);
STATIC int
cardslotmatch(device_t parent, cfdata_t cf,
void *aux)
{
2001-03-27 14:34:05 +04:00
struct cardslot_attach_args *caa = aux;
2001-03-27 14:34:05 +04:00
if (caa->caa_cb_attach == NULL && caa->caa_16_attach == NULL) {
/* Neither CardBus nor 16-bit PCMCIA are defined. */
return 0;
}
2001-03-27 14:34:05 +04:00
return 1;
}
STATIC void
cardslotchilddet(device_t self, device_t child)
{
struct cardslot_softc *sc = device_private(self);
KASSERT(sc->sc_cb_softc == device_private(child) ||
sc->sc_16_softc == child);
if (sc->sc_cb_softc == device_private(child))
sc->sc_cb_softc = NULL;
else if (sc->sc_16_softc == child)
sc->sc_16_softc = NULL;
}
STATIC void
cardslotattach(device_t parent, device_t self, void *aux)
{
2006-03-29 10:22:38 +04:00
struct cardslot_softc *sc = device_private(self);
2001-03-27 14:34:05 +04:00
struct cardslot_attach_args *caa = aux;
struct cbslot_attach_args *cba = caa->caa_cb_attach;
struct pcmciabus_attach_args *pa = caa->caa_16_attach;
2003-10-25 22:34:14 +04:00
struct cardbus_softc *csc = NULL;
struct pcmcia_softc *psc = NULL;
2001-03-27 14:34:05 +04:00
sc->sc_dev = self;
2001-03-27 14:34:05 +04:00
sc->sc_cb_softc = NULL;
sc->sc_16_softc = NULL;
SIMPLEQ_INIT(&sc->sc_events);
sc->sc_th_enable = 0;
2007-12-01 07:50:50 +03:00
aprint_naive("\n");
aprint_normal("\n");
2001-03-27 14:34:05 +04:00
DPRINTF(("%s attaching CardBus bus...\n", device_xname(self)));
2001-03-27 14:34:05 +04:00
if (cba != NULL) {
csc = device_private(config_found_ia(self, "cbbus", cba,
cardslot_cb_print));
if (csc) {
2001-03-27 14:34:05 +04:00
/* cardbus found */
DPRINTF(("%s: found cardbus on %s\n", __func__,
device_xname(self)));
2001-03-27 14:34:05 +04:00
sc->sc_cb_softc = csc;
}
}
if (pa != NULL) {
sc->sc_16_softc = config_found_sm_loc(self, "pcmciabus", NULL,
pa, cardslot_16_print,
cardslot_16_submatch);
if (sc->sc_16_softc) {
2001-03-27 14:34:05 +04:00
/* pcmcia 16-bit bus found */
DPRINTF(("%s: found 16-bit pcmcia bus\n", __func__));
psc = device_private(sc->sc_16_softc);
2001-03-27 14:34:05 +04:00
}
}
if (csc != NULL || psc != NULL) {
config_pending_incr(self);
if (kthread_create(PRI_NONE, 0, NULL, cardslot_event_thread,
sc, &sc->sc_event_thread, "%s", device_xname(self))) {
aprint_error_dev(sc->sc_dev,
"unable to create thread\n");
panic("cardslotattach");
}
sc->sc_th_enable = 1;
2001-03-27 14:34:05 +04:00
}
if (csc && (csc->sc_cf->cardbus_ctrl)(csc->sc_cc, CARDBUS_CD)) {
DPRINTF(("%s: CardBus card found\n", __func__));
2001-03-27 14:34:05 +04:00
/* attach deferred */
cardslot_event_throw(sc, CARDSLOT_EVENT_INSERTION_CB);
}
if (psc && (psc->pct->card_detect)(psc->pch)) {
DPRINTF(("%s: 16-bit card found\n", __func__));
2001-03-27 14:34:05 +04:00
/* attach deferred */
cardslot_event_throw(sc, CARDSLOT_EVENT_INSERTION_16);
}
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");
}
STATIC int
cardslotdetach(device_t self, int flags)
{
int rc;
struct cardslot_softc *sc = device_private(self);
if ((rc = config_detach_children(self, flags)) != 0)
return rc;
sc->sc_th_enable = 0;
wakeup(&sc->sc_events);
while (sc->sc_event_thread != NULL)
(void)tsleep(sc, PWAIT, "cardslotthd", 0);
if (!SIMPLEQ_EMPTY(&sc->sc_events))
aprint_error_dev(self, "events outstanding");
pmf_device_deregister(self);
return 0;
}
STATIC int
cardslot_cb_print(void *aux, const char *pnp)
{
struct cbslot_attach_args *cba = aux;
if (pnp != NULL) {
aprint_normal("cardbus at %s subordinate bus %d",
pnp, cba->cba_bus);
}
return UNCONF;
}
static int
cardslot_16_submatch(device_t parent, cfdata_t cf,
const int *ldesc, void *aux)
{
2001-03-27 14:34:05 +04:00
if (cf->cf_loc[PCMCIABUSCF_CONTROLLER] != PCMCIABUSCF_CONTROLLER_DEFAULT
&& cf->cf_loc[PCMCIABUSCF_CONTROLLER] != 0) {
return 0;
}
2011-05-24 20:37:04 +04:00
if (cf->cf_loc[PCMCIABUSCF_CONTROLLER] == PCMCIABUSCF_CONTROLLER_DEFAULT) {
return (config_match(parent, cf, aux));
2001-03-27 14:34:05 +04:00
}
2001-03-27 14:34:05 +04:00
return 0;
}
static int
cardslot_16_print(void *arg, const char *pnp)
{
if (pnp != NULL) {
aprint_normal("pcmciabus at %s", pnp);
2001-03-27 14:34:05 +04:00
}
2001-03-27 14:34:05 +04:00
return UNCONF;
}
/*
* void cardslot_event_throw(struct cardslot_softc *sc, int ev)
*
* This function throws an event to the event handler. If the state
* of a slot is changed, it should be noticed using this function.
*/
void
cardslot_event_throw(struct cardslot_softc *sc, int ev)
{
2001-03-27 14:34:05 +04:00
struct cardslot_event *ce;
2001-03-27 14:34:05 +04:00
DPRINTF(("cardslot_event_throw: an event %s comes\n",
ev == CARDSLOT_EVENT_INSERTION_CB ? "CardBus Card inserted" :
ev == CARDSLOT_EVENT_INSERTION_16 ? "16-bit Card inserted" :
ev == CARDSLOT_EVENT_REMOVAL_CB ? "CardBus Card removed" :
ev == CARDSLOT_EVENT_REMOVAL_16 ? "16-bit Card removed" : "???"));
2001-03-27 14:34:05 +04:00
if (NULL == (ce = (struct cardslot_event *)malloc(sizeof (struct cardslot_event), M_TEMP, M_NOWAIT))) {
panic("cardslot_enevt");
}
2001-03-27 14:34:05 +04:00
ce->ce_type = ev;
2001-03-27 14:34:05 +04:00
{
int s = spltty();
SIMPLEQ_INSERT_TAIL(&sc->sc_events, ce, ce_q);
splx(s);
}
2001-03-27 14:34:05 +04:00
wakeup(&sc->sc_events);
2001-03-27 14:34:05 +04:00
return;
}
/*
* static void cardslot_event_thread(void *arg)
*
* This function is the main routine handing cardslot events such as
* insertions and removals.
*
*/
static void
cardslot_event_thread(void *arg)
{
2001-03-27 14:34:05 +04:00
struct cardslot_softc *sc = arg;
struct cardslot_event *ce;
int s, first = 1;
static int antonym_ev[4] = {
CARDSLOT_EVENT_REMOVAL_16, CARDSLOT_EVENT_INSERTION_16,
CARDSLOT_EVENT_REMOVAL_CB, CARDSLOT_EVENT_INSERTION_CB
};
while (sc->sc_th_enable) {
s = spltty();
if ((ce = SIMPLEQ_FIRST(&sc->sc_events)) == NULL) {
splx(s);
if (first) {
first = 0;
config_pending_decr(sc->sc_dev);
2001-03-27 14:34:05 +04:00
}
(void) tsleep(&sc->sc_events, PWAIT, "cardslotev", 0);
continue;
}
SIMPLEQ_REMOVE_HEAD(&sc->sc_events, ce_q);
2001-03-27 14:34:05 +04:00
splx(s);
if (IS_CARDSLOT_INSERT_REMOVE_EV(ce->ce_type)) {
/* Chattering suppression */
2001-03-27 14:34:05 +04:00
s = spltty();
while (1) {
struct cardslot_event *ce1, *ce2;
if ((ce1 = SIMPLEQ_FIRST(&sc->sc_events)) == NULL) {
break;
}
if (ce1->ce_type != antonym_ev[ce->ce_type]) {
break;
}
if ((ce2 = SIMPLEQ_NEXT(ce1, ce_q)) == NULL) {
break;
}
if (ce2->ce_type == ce->ce_type) {
SIMPLEQ_REMOVE_HEAD(&sc->sc_events,
ce_q);
2001-03-27 14:34:05 +04:00
free(ce1, M_TEMP);
SIMPLEQ_REMOVE_HEAD(&sc->sc_events,
ce_q);
2001-03-27 14:34:05 +04:00
free(ce2, M_TEMP);
}
}
splx(s);
}
switch (ce->ce_type) {
case CARDSLOT_EVENT_INSERTION_CB:
if ((CARDSLOT_CARDTYPE(sc->sc_status) == CARDSLOT_STATUS_CARD_CB)
|| (CARDSLOT_CARDTYPE(sc->sc_status) == CARDSLOT_STATUS_CARD_16)) {
if (CARDSLOT_WORK(sc->sc_status) == CARDSLOT_STATUS_WORKING) {
/*
* A card has already been
* inserted and works.
*/
break;
}
}
if (sc->sc_cb_softc) {
CARDSLOT_SET_CARDTYPE(sc->sc_status,
CARDSLOT_STATUS_CARD_CB);
if (cardbus_attach_card(sc->sc_cb_softc) > 0) {
/* at least one function works */
CARDSLOT_SET_WORK(sc->sc_status, CARDSLOT_STATUS_WORKING);
} else {
/*
* no functions work or this
* card is not known
*/
CARDSLOT_SET_WORK(sc->sc_status,
CARDSLOT_STATUS_NOTWORK);
}
} else {
panic("no cardbus on %s",
device_xname(sc->sc_dev));
2001-03-27 14:34:05 +04:00
}
break;
case CARDSLOT_EVENT_INSERTION_16:
if ((CARDSLOT_CARDTYPE(sc->sc_status) == CARDSLOT_STATUS_CARD_CB)
|| (CARDSLOT_CARDTYPE(sc->sc_status) == CARDSLOT_STATUS_CARD_16)) {
if (CARDSLOT_WORK(sc->sc_status) == CARDSLOT_STATUS_WORKING) {
/*
* A card has already been
* inserted and work.
*/
break;
}
}
if (sc->sc_16_softc) {
CARDSLOT_SET_CARDTYPE(sc->sc_status, CARDSLOT_STATUS_CARD_16);
if (pcmcia_card_attach(sc->sc_16_softc)) {
2001-03-27 14:34:05 +04:00
/* Do not attach */
CARDSLOT_SET_WORK(sc->sc_status,
CARDSLOT_STATUS_NOTWORK);
} else {
/* working */
CARDSLOT_SET_WORK(sc->sc_status,
CARDSLOT_STATUS_WORKING);
}
} else {
panic("no 16-bit pcmcia on %s",
device_xname(sc->sc_dev));
2001-03-27 14:34:05 +04:00
}
break;
case CARDSLOT_EVENT_REMOVAL_CB:
if (CARDSLOT_CARDTYPE(sc->sc_status) == CARDSLOT_STATUS_CARD_CB) {
/* CardBus card has not been inserted. */
if (CARDSLOT_WORK(sc->sc_status) == CARDSLOT_STATUS_WORKING) {
cardbus_detach_card(sc->sc_cb_softc);
CARDSLOT_SET_WORK(sc->sc_status,
CARDSLOT_STATUS_NOTWORK);
CARDSLOT_SET_WORK(sc->sc_status,
CARDSLOT_STATUS_CARD_NONE);
}
CARDSLOT_SET_CARDTYPE(sc->sc_status,
CARDSLOT_STATUS_CARD_NONE);
} else if (CARDSLOT_CARDTYPE(sc->sc_status) != CARDSLOT_STATUS_CARD_16) {
/* Unknown card... */
CARDSLOT_SET_CARDTYPE(sc->sc_status,
CARDSLOT_STATUS_CARD_NONE);
}
CARDSLOT_SET_WORK(sc->sc_status,
CARDSLOT_STATUS_NOTWORK);
break;
case CARDSLOT_EVENT_REMOVAL_16:
2008-06-29 13:09:09 +04:00
DPRINTF(("%s: removal event\n", device_xname(sc->sc_dev)));
2001-03-27 14:34:05 +04:00
if (CARDSLOT_CARDTYPE(sc->sc_status) != CARDSLOT_STATUS_CARD_16) {
/* 16-bit card has not been inserted. */
break;
}
if ((sc->sc_16_softc != NULL)
&& (CARDSLOT_WORK(sc->sc_status) == CARDSLOT_STATUS_WORKING)) {
struct pcmcia_softc *psc =
device_private(sc->sc_16_softc);
2001-03-27 14:34:05 +04:00
pcmcia_card_deactivate(sc->sc_16_softc);
2001-03-27 14:34:05 +04:00
pcmcia_chip_socket_disable(psc->pct, psc->pch);
pcmcia_card_detach(sc->sc_16_softc,
DETACH_FORCE);
2001-03-27 14:34:05 +04:00
}
CARDSLOT_SET_CARDTYPE(sc->sc_status, CARDSLOT_STATUS_CARD_NONE);
CARDSLOT_SET_WORK(sc->sc_status, CARDSLOT_STATUS_NOTWORK);
break;
default:
panic("cardslot_event_thread: unknown event %d", ce->ce_type);
}
free(ce, M_TEMP);
}
2001-03-27 14:34:05 +04:00
sc->sc_event_thread = NULL;
/* In case the parent device is waiting for us to exit. */
wakeup(sc);
kthread_exit(0);
}