From 49bc2f4b3a9159404826f143e5485cf3905ee94d Mon Sep 17 00:00:00 2001 From: Niklas Hallqvist Date: Wed, 11 Aug 1999 12:02:08 +0000 Subject: New pcmcia irq prober that really tries to ensure the irq will work --- sys/dev/isa/i82365_isa.c | 59 ++++++++-------- sys/dev/isa/i82365_isasubr.c | 162 ++++++++++++++++++++++++++++++++++--------- sys/dev/isa/i82365_isavar.h | 8 ++- 3 files changed, 166 insertions(+), 63 deletions(-) (limited to 'sys') diff --git a/sys/dev/isa/i82365_isa.c b/sys/dev/isa/i82365_isa.c index 179f448da0b..9939c36c1d9 100644 --- a/sys/dev/isa/i82365_isa.c +++ b/sys/dev/isa/i82365_isa.c @@ -1,4 +1,4 @@ -/* $OpenBSD: i82365_isa.c,v 1.8 1999/07/26 05:43:16 deraadt Exp $ */ +/* $OpenBSD: i82365_isa.c,v 1.9 1999/08/11 12:02:07 niklas Exp $ */ /* $NetBSD: i82365_isa.c,v 1.11 1998/06/09 07:25:00 thorpej Exp $ */ /* @@ -151,14 +151,15 @@ pcic_isa_attach(parent, self, aux) struct device *parent, *self; void *aux; { - struct pcic_softc *sc = (void *) self; + struct pcic_softc *sc = (void *)self; + struct pcic_handle *h; struct isa_attach_args *ia = aux; isa_chipset_tag_t ic = ia->ia_ic; bus_space_tag_t iot = ia->ia_iot; bus_space_tag_t memt = ia->ia_memt; bus_space_handle_t ioh; bus_space_handle_t memh; - int i; + int irq, i; /* Map i/o space. */ if (bus_space_map(iot, ia->ia_iobase, ia->ia_iosize, 0, &ioh)) { @@ -176,7 +177,7 @@ pcic_isa_attach(parent, self, aux) sc->subregionmask = (1 << (ia->ia_msize / PCIC_MEM_PAGESIZE)) - 1; sc->intr_est = ic; - sc->pct = (pcmcia_chipset_tag_t) & pcic_isa_functions; + sc->pct = (pcmcia_chipset_tag_t)&pcic_isa_functions; sc->iot = iot; sc->ioh = ioh; @@ -186,38 +187,38 @@ pcic_isa_attach(parent, self, aux) printf("\n"); pcic_attach(sc); - pcic_isa_bus_width_probe (sc, iot, ioh, ia->ia_iobase, ia->ia_iosize); + pcic_isa_bus_width_probe(sc, iot, ioh, ia->ia_iobase, ia->ia_iosize); pcic_attach_sockets(sc); /* - * We allocate the card event IRQ late because it is more important - * that the cards will get their interrupt than that we get a - * card event interrupt vector that works. - * * Allocate an irq. It will be used by both controllers. I could * use two different interrupts, but interrupts are relatively * scarce, shareable, and for PCIC controllers, very infrequent. */ - - if ((sc->irq = ia->ia_irq) == IRQUNK) { - for (i = 0; i < npcic_isa_intr_list; i++) - if (isa_intr_check(ic, pcic_isa_intr_list[i], - IST_EDGE) == 2) - goto found; - for (i = 0; i < npcic_isa_intr_list; i++) - if (isa_intr_check(ic, pcic_isa_intr_list[i], - IST_EDGE) == 1) - goto found; - printf("%s: no irq\n", sc->dev.dv_xname); - return; -found: - sc->irq = pcic_isa_intr_list[i]; + irq = ia->ia_irq; + if (irq == IRQUNK) + irq = pcic_intr_find(sc, IST_EDGE); + + if (irq) { + sc->ih = isa_intr_establish(ic, irq, IST_EDGE, IPL_TTY, + pcic_intr, sc, sc->dev.dv_xname); + if (!sc->ih) + irq = 0; } - - sc->ih = isa_intr_establish(ic, sc->irq, IST_EDGE, IPL_TTY, - pcic_intr, sc, sc->dev.dv_xname); - if (sc->ih) - printf("%s: irq %d\n", sc->dev.dv_xname, sc->irq); - else + sc->irq = irq; + + if (irq) { + printf("%s: irq %d\n", sc->dev.dv_xname, irq); + + /* Set up the pcic to interrupt on card detect. */ + for (i = 0; i < PCIC_NSLOTS; i++) { + h = &sc->handle[i]; + if (h->flags & PCIC_FLAG_SOCKETP) { + pcic_write(h, PCIC_CSC_INTR, + (sc->irq << PCIC_CSC_INTR_IRQ_SHIFT) | + PCIC_CSC_INTR_CD_ENABLE); + } + } + } else printf("%s: no irq\n", sc->dev.dv_xname); } diff --git a/sys/dev/isa/i82365_isasubr.c b/sys/dev/isa/i82365_isasubr.c index ccca5abf088..5ceb3ef4bd7 100644 --- a/sys/dev/isa/i82365_isasubr.c +++ b/sys/dev/isa/i82365_isasubr.c @@ -1,4 +1,4 @@ -/* $OpenBSD: i82365_isasubr.c,v 1.9 1999/07/26 05:43:16 deraadt Exp $ */ +/* $OpenBSD: i82365_isasubr.c,v 1.10 1999/08/11 12:02:07 niklas Exp $ */ /* $NetBSD: i82365_isasubr.c,v 1.1 1998/06/07 18:28:31 sommerfe Exp $ */ /* @@ -76,6 +76,15 @@ int pcic_isa_alloc_iobase = PCIC_ISA_ALLOC_IOBASE; int pcic_isa_alloc_iosize = PCIC_ISA_ALLOC_IOSIZE; +/* + * I am well aware that some of later irqs below are not for real, but there + * is a way to deal with that in the search loop. For beauty's sake I want + * this list to be a permutation of 0..15. + */ +char pcic_isa_intr_list[] = { + 3, 4, 14, 9, 5, 12, 10, 11, 15, 13, 7, 1, 6, 2, 0, 8 +}; + struct pcic_ranges pcic_isa_addr[] = { { 0x340, 0x040 }, { 0x300, 0x030 }, @@ -84,17 +93,6 @@ struct pcic_ranges pcic_isa_addr[] = { { 0, 0 }, /* terminator */ }; -/* - * Default IRQ allocation bitmask. This defines the range of allowable - * IRQs for PCMCIA slots. Useful if order of probing would screw up other - * devices, or if PCIC hardware/cards have trouble with certain interrupt - * lines. - */ -char pcic_isa_intr_list[] = { - 3, 4, 14, 9, 5, 12, 10, 11, 15, 13, 7 -}; -int npcic_isa_intr_list = - sizeof(pcic_isa_intr_list) / sizeof(pcic_isa_intr_list[0]); /***************************************************************************** * End of configurable parameters. @@ -107,7 +105,10 @@ int pcicsubr_debug = 1 /* XXX */ ; #define DPRINTF(arg) #endif -void pcic_isa_bus_width_probe (sc, iot, ioh, base, length) +static int pcic_intr_seen; + +void +pcic_isa_bus_width_probe(sc, iot, ioh, base, length) struct pcic_softc *sc; bus_space_tag_t iot; bus_space_handle_t ioh; @@ -190,11 +191,10 @@ pcic_isa_chip_intr_establish(pch, pf, ipl, fct, arg) int (*fct) __P((void *)); void *arg; { - struct pcic_handle *h = (struct pcic_handle *) pch; + struct pcic_handle *h = (struct pcic_handle *)pch; isa_chipset_tag_t ic = h->sc->intr_est; int irq, ist; void *ih; - int i, reg; if (pf->cfe->flags & PCMCIA_CFE_IRQLEVEL) ist = IST_LEVEL; @@ -203,26 +203,19 @@ pcic_isa_chip_intr_establish(pch, pf, ipl, fct, arg) else ist = IST_LEVEL; - for (i = 0; i < npcic_isa_intr_list; i++) - if (isa_intr_check(ic, pcic_isa_intr_list[i], ist) == 2) - goto found; - for (i = 0; i < npcic_isa_intr_list; i++) - if (isa_intr_check(ic, pcic_isa_intr_list[i], ist) == 1) - goto found; - return (NULL); - -found: - irq = pcic_isa_intr_list[i]; - if ((ih = isa_intr_establish(ic, irq, ist, ipl, - fct, arg, h->pcmcia->dv_xname)) == NULL) + irq = pcic_intr_find(h->sc, ist); + if (!irq) return (NULL); - reg = pcic_read(h, PCIC_INTR); - reg &= ~PCIC_INTR_IRQ_MASK; - reg |= irq; - pcic_write(h, PCIC_INTR, reg); + ih = isa_intr_establish(ic, irq, ist, ipl, fct, arg, + h->pcmcia->dv_xname); + if (!ih) + return (NULL); h->ih_irq = irq; + pcic_write(h, PCIC_INTR, + (pcic_read(h, PCIC_INTR) & ~PCIC_INTR_IRQ_MASK) | irq); + printf(" irq %d", irq); return (ih); } @@ -244,3 +237,110 @@ pcic_isa_chip_intr_disestablish(pch, ih) isa_intr_disestablish(ic, ih); } + +int +pcic_intr_probe(v) + void *v; +{ + pcic_intr_seen = 1; + return (1); +} + +/* + * Try to find a working interrupt, first by searching for a unique + * irq that is known to work, verified by tickling the pcic, then + * by searching for a shareable irq known to work. If the pcic does + * not allow tickling we then fallback to the same strategy but without + * tickling just assuming the first usable irq found works. + */ +int +pcic_intr_find(sc, ist) + struct pcic_softc *sc; + int ist; +{ + struct pcic_handle *ph = &sc->handle[0]; + isa_chipset_tag_t ic = sc->intr_est; + int i, tickle, check, irq, chosen_irq = 0; + void *ih; + u_int8_t saved_csc_intr; + + /* + * First time, look for entirely free interrupts, last + * time accept shareable ones. + */ + for (tickle = 1; tickle >= 0; tickle--) { + if (tickle) + /* + * Remember card status change interrupt + * configuration. + */ + saved_csc_intr = pcic_read(ph, PCIC_CSC_INTR); + + for (check = 2; check; check--) { + + /* Walk over all possible interrupts. */ + for (i = 0; i < 16; i++) { + irq = pcic_isa_intr_list[i]; + + if (((1 << irq) & + PCIC_CSC_INTR_IRQ_VALIDMASK) == 0) + continue; + + if (isa_intr_check(ic, irq, ist) < check) + continue; + + if (!tickle) { + chosen_irq = irq; + goto out; + } + + /* + * Prepare for an interrupt tickle. + * As this can be called from an + * IPL_TTY context (the card status + * change interrupt) we need to do + * higher. + */ + ih = isa_intr_establish(ic, irq, ist, IPL_IMP, + pcic_intr_probe, 0, NULL); + if (ih == NULL) + continue; + pcic_intr_seen = 0; + pcic_write(ph, PCIC_CSC_INTR, + (saved_csc_intr & ~PCIC_CSC_INTR_IRQ_MASK) + | PCIC_CSC_INTR_CD_ENABLE + | (irq << PCIC_CSC_INTR_IRQ_SHIFT)); + + /* Teehee, you tickle me! ;-) */ + pcic_write(ph, PCIC_CARD_DETECT, + pcic_read(ph, PCIC_CARD_DETECT) | + PCIC_CARD_DETECT_SW_INTR); + + /* + * Delay for 10 ms and then shut the + * probe off. That should be plenty + * of time for the interrupt to be + * handled. + */ + delay(10000); + + /* Acknowledge the interrupt. */ + pcic_read(ph, PCIC_CSC); + + isa_intr_disestablish(ic, ih); + + if (pcic_intr_seen) { + chosen_irq = irq; + goto out; + } + } + + if (tickle) + /* Restore card detection bit. */ + pcic_write(ph, PCIC_CSC_INTR, saved_csc_intr); + } + } + +out: + return (chosen_irq); +} diff --git a/sys/dev/isa/i82365_isavar.h b/sys/dev/isa/i82365_isavar.h index aadca7a8d51..ef94d6a6fb0 100644 --- a/sys/dev/isa/i82365_isavar.h +++ b/sys/dev/isa/i82365_isavar.h @@ -1,4 +1,4 @@ -/* $OpenBSD: i82365_isavar.h,v 1.2 1998/12/27 00:27:19 deraadt Exp $ */ +/* $OpenBSD: i82365_isavar.h,v 1.3 1999/08/11 12:02:07 niklas Exp $ */ /* $NetBSD: i82365_isavar.h,v 1.1 1998/06/07 18:28:31 sommerfe Exp $ */ /* @@ -46,6 +46,8 @@ void pcic_isa_chip_intr_disestablish __P((pcmcia_chipset_handle_t, void *)); * Figure out how wide the ISA bus is... */ -void pcic_isa_bus_width_probe __P((struct pcic_softc *, bus_space_tag_t, - bus_space_handle_t, bus_addr_t, u_int32_t)); +void pcic_isa_bus_width_probe __P((struct pcic_softc *, bus_space_tag_t, + bus_space_handle_t, bus_addr_t, u_int32_t)); +int pcic_intr_probe __P((void *)); +int pcic_intr_find __P((struct pcic_softc *, int)); -- cgit v1.2.3