summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorimp <imp@FreeBSD.org>2001-09-13 08:26:55 +0000
committerimp <imp@FreeBSD.org>2001-09-13 08:26:55 +0000
commitcd3f175e88a8787dc72b6a169a8cd1a9bc7546c9 (patch)
treefe41baa0bce9c3a433c31fdec1b8afe45e41efa9
parent214349ee3a8fd079e8ada36c86698d61a7706aa5 (diff)
downloadFreeBSD-src-cd3f175e88a8787dc72b6a169a8cd1a9bc7546c9.zip
FreeBSD-src-cd3f175e88a8787dc72b6a169a8cd1a9bc7546c9.tar.gz
<jkh> "Hey Rocky, watch me eject this pccard outta my laptop!" "What,
again? That NEVER works!" "This time for sure!" Minor overhaul of how we do interrupts for the pci interrupt routing case to cope with card ejection better (read: make it not hand on so many cards): o Reintroduce func_intr and func_arg and use the to store the interrupt handler to call. o Create a pcic_pci_func_intr to call the real interrupt handler iff the card hasn't been ejected. o Remove some checks in pcic_setup_intr now that it is used exclusively for isa routed interrupts. o Defer the eject event until later too, but make sure we can't do any client driver ISR calling in the interrum. o Add some simple code to make sure that we don't attach more than one child. This should fix pccardd starting twice problem (ala single user -> multi-user when you started pccardd by hand in SU). MFC: after jkh thinks I've put the crack pipe away.
-rw-r--r--sys/pccard/pccard.c12
-rw-r--r--sys/pccard/pcic.c8
-rw-r--r--sys/pccard/pcic_pci.c93
-rw-r--r--sys/pccard/pcicvar.h2
4 files changed, 92 insertions, 23 deletions
diff --git a/sys/pccard/pccard.c b/sys/pccard/pccard.c
index a7650de..dfd12fe 100644
--- a/sys/pccard/pccard.c
+++ b/sys/pccard/pccard.c
@@ -44,6 +44,7 @@
#include <sys/uio.h>
#include <sys/poll.h>
#include <sys/bus.h>
+#include <sys/proc.h>
#include <machine/bus.h>
#include <pccard/cardinfo.h>
@@ -216,8 +217,19 @@ allocate_driver(struct slot *slt, struct dev_desc *desc)
device_t pccarddev;
int err, irq = 0;
device_t child;
+ device_t *devs;
+ int count;
pccarddev = slt->dev;
+ err = device_get_children(pccarddev, &devs, &count);
+ if (err != 0)
+ return (err);
+ free(devs, M_TEMP);
+ if (count) {
+ device_printf(pccarddev,
+ "Can not attach more than one child.\n");
+ return (EIO);
+ }
irq = ffs(desc->irqmask) - 1;
MALLOC(devi, struct pccard_devinfo *, sizeof(*devi), M_DEVBUF,
M_WAITOK | M_ZERO);
diff --git a/sys/pccard/pcic.c b/sys/pccard/pcic.c
index bffd59f..14ab1db 100644
--- a/sys/pccard/pcic.c
+++ b/sys/pccard/pcic.c
@@ -860,17 +860,9 @@ int
pcic_setup_intr(device_t dev, device_t child, struct resource *irq,
int flags, driver_intr_t *intr, void *arg, void **cookiep)
{
- struct pcic_softc *sc = device_get_softc(dev);
struct pccard_devinfo *devi = device_get_ivars(child);
int err;
-#if __FreeBSD_version >= 500000
- if (sc->csc_route == pcic_iw_pci && (flags & INTR_FAST))
-#else
- if (sc->csc_route == pcic_iw_pci && (flags & INTR_TYPE_FAST))
-#endif
- return (EINVAL);
-
if (((1 << rman_get_start(irq)) & PCIC_INT_MASK_ALLOWED) == 0) {
device_printf(dev, "Hardware does not support irq %ld.\n",
rman_get_start(irq));
diff --git a/sys/pccard/pcic_pci.c b/sys/pccard/pcic_pci.c
index d2240be..0874278 100644
--- a/sys/pccard/pcic_pci.c
+++ b/sys/pccard/pcic_pci.c
@@ -874,13 +874,15 @@ pcic_pci_cardtype(u_int32_t stat)
* in the hardware being initialized many times, only to be torn down
* as well. This may also cause races with pccardd. Instead, we wait
* for the insertion signal to be stable for 0.5 seconds before we declare
- * it to be a real insertion event. Removal is done right away.
+ * it to be a real insertion event. Removal is also debounced. We turn
+ * off interrupt servicing during the settling time to prevent infinite
+ * loops in the driver.
*
* Note: We only handle the card detect change events. We don't handle
* power events and status change events.
*/
static void
-pcic_cd_insert(void *arg)
+pcic_cd_change(void *arg)
{
struct pcic_softc *sc = (struct pcic_softc *) arg;
struct pcic_slot *sp = &sc->slots[0];
@@ -889,9 +891,12 @@ pcic_cd_insert(void *arg)
sc->cd_pending = 0;
stat = bus_space_read_4(sp->bst, sp->bsh, CB_SOCKET_STATE);
- /* Just return if the interrupt handler missed a remove transition. */
- if ((stat & CB_SS_CD) != 0)
+ /* If the card left, remove it from the system. */
+ if ((stat & CB_SS_CD) != 0) {
+ sc->cd_present = 0;
+ pccard_event(sp->slt, card_removed);
return;
+ }
sc->cd_present = 1;
if ((stat & CB_SS_16BIT) == 0)
device_printf(sp->sc->dev, "Card type %s is unsupported\n",
@@ -919,19 +924,17 @@ pcic_pci_intr(void *arg)
present = (stat & CB_SS_CD) == 0;
if (present != sc->cd_present) {
if (sc->cd_pending) {
- untimeout(pcic_cd_insert, arg, sc->cd_ch);
+ untimeout(pcic_cd_change, arg, sc->cd_ch);
sc->cd_pending = 0;
}
/* Delay insert events to debounce noisy signals. */
- if (present) {
- sc->cd_ch = timeout(pcic_cd_insert, arg, hz/2);
- sc->cd_pending = 1;
- } else {
- sc->cd_present = 0;
- pccard_event(sp->slt, card_removed);
- }
+ sc->cd_pending = 1;
+ sc->cd_ch = timeout(pcic_cd_change, arg, hz/2);
+ /* if the card is gone, stop interrupts to it */
+ if (!present)
+ sc->func_intr = NULL;
}
- if (stat & CB_SS_BADVCC)
+ if (bootverbose && (stat & CB_SS_BADVCC) != 0)
device_printf(sc->dev, "BAD Vcc request\n");
/* Ack the interrupt */
@@ -944,6 +947,9 @@ pcic_pci_intr(void *arg)
* and read it to clear the bits. Maybe we should check the status
* ala the ISA interrupt handler, but those changes should be caught
* in the CD change.
+ *
+ * We have to check it every interrupt because these bits can sometimes
+ * show up indepentently of the CB_SOCKET_EVENT register above.
*/
sp->getb(sp, PCIC_STAT_CHG);
}
@@ -1317,6 +1323,63 @@ pcic_pci_gen_mapirq(struct pcic_slot *sp, int irq)
return (sp->sc->chip->func_intr_way(sp, pcic_iw_pci));
}
+static void
+pcic_pci_func_intr(void *arg)
+{
+ struct pcic_softc *sc = (struct pcic_softc *) arg;
+ struct pcic_slot *sp = &sc->slots[0];
+ u_int32_t stat;
+
+ stat = bus_space_read_4(sp->bst, sp->bsh, CB_SOCKET_STATE);
+ if ((stat & CB_SS_CD) == 0 && sc->func_intr != 0)
+ sc->func_intr(sc->func_arg);
+}
+
+static int
+pcic_pci_setup_intr(device_t dev, device_t child, struct resource *irq,
+ int flags, driver_intr_t *intr, void *arg, void **cookiep)
+{
+ struct pcic_softc *sc = device_get_softc(dev);
+ struct pcic_slot *sp = &sc->slots[0];
+ int err;
+
+ if (sc->func_route == pcic_iw_isa)
+ return(pcic_setup_intr(dev, child, irq, flags, intr, arg,
+ cookiep));
+
+#if __FreeBSD_version >= 500000
+ if ((flags & INTR_FAST) != 0)
+#else
+ if ((flags & INTR_TYPE_FAST) != 0)
+#endif
+ return (EINVAL);
+ if (sc->func_intr != NULL) {
+ device_printf(child, "Can't establish another ISR\n");
+ return (EINVAL);
+ }
+
+ err = bus_generic_setup_intr(dev, child, irq, flags,
+ pcic_pci_func_intr, sc, cookiep);
+ if (err != 0)
+ return (err);
+ sc->chip->map_irq(sp, rman_get_start(irq));
+ sc->func_intr = intr;
+ sc->func_arg = arg;
+ return (0);
+}
+
+static int
+pcic_pci_teardown_intr(device_t dev, device_t child, struct resource *irq,
+ void *cookie)
+{
+ struct pcic_softc *sc = device_get_softc(dev);
+
+ if (sc->func_route == pcic_iw_isa)
+ return (pcic_teardown_intr(dev, child, irq, cookie));
+ sc->func_intr = NULL;
+ return (bus_generic_teardown_intr(dev, child, irq, cookie));
+}
+
static device_method_t pcic_pci_methods[] = {
/* Device interface */
DEVMETHOD(device_probe, pcic_pci_probe),
@@ -1332,8 +1395,8 @@ static device_method_t pcic_pci_methods[] = {
DEVMETHOD(bus_release_resource, bus_generic_release_resource),
DEVMETHOD(bus_activate_resource, pcic_activate_resource),
DEVMETHOD(bus_deactivate_resource, pcic_deactivate_resource),
- DEVMETHOD(bus_setup_intr, pcic_setup_intr),
- DEVMETHOD(bus_teardown_intr, pcic_teardown_intr),
+ DEVMETHOD(bus_setup_intr, pcic_pci_setup_intr),
+ DEVMETHOD(bus_teardown_intr, pcic_pci_teardown_intr),
/* Card interface */
DEVMETHOD(card_set_res_flags, pcic_set_res_flags),
diff --git a/sys/pccard/pcicvar.h b/sys/pccard/pcicvar.h
index 154860d..3df95a2 100644
--- a/sys/pccard/pcicvar.h
+++ b/sys/pccard/pcicvar.h
@@ -71,6 +71,8 @@ struct pcic_softc
int cd_present; /* debounced card-present state */
struct callout_handle cd_ch; /* handle for pcic_cd_insert */
struct pcic_chip *chip;
+ driver_intr_t *func_intr;
+ void *func_arg;
};
typedef int (pcic_intr_way_t)(struct pcic_slot *, enum pcic_intr_way);
OpenPOWER on IntegriCloud