From 7cf44afd1ae953e419ad25858c14ca58438ad79e Mon Sep 17 00:00:00 2001 From: Warner Losh Date: Thu, 13 Sep 2001 08:26:55 +0000 Subject: [PATCH] "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. --- sys/pccard/pccard.c | 12 ++++++ sys/pccard/pcic.c | 8 ---- sys/pccard/pcic_pci.c | 93 ++++++++++++++++++++++++++++++++++++------- sys/pccard/pcicvar.h | 2 + 4 files changed, 92 insertions(+), 23 deletions(-) diff --git a/sys/pccard/pccard.c b/sys/pccard/pccard.c index a7650dea7345..dfd12fee2b1f 100644 --- a/sys/pccard/pccard.c +++ b/sys/pccard/pccard.c @@ -44,6 +44,7 @@ #include #include #include +#include #include #include @@ -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 bffd59f14a55..14ab1db99abc 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 d2240be82fc9..08742781c75b 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 154860df269c..3df95a2bb97b 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);