diff options
-rw-r--r-- | sys/pccard/pccard.c | 12 | ||||
-rw-r--r-- | sys/pccard/pcic.c | 8 | ||||
-rw-r--r-- | sys/pccard/pcic_pci.c | 93 | ||||
-rw-r--r-- | 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 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); |