summaryrefslogtreecommitdiffstats
path: root/sys/pccard/pcic_pci.c
diff options
context:
space:
mode:
authorimp <imp@FreeBSD.org>2001-08-19 05:01:18 +0000
committerimp <imp@FreeBSD.org>2001-08-19 05:01:18 +0000
commit33c3c53a533a006733f0b78847fcf122188e0d74 (patch)
tree4e63915801d736650dadd97fa7343efa3c989826 /sys/pccard/pcic_pci.c
parentc51d4d8eeeb05a290cd206983dba6d49fa070cf8 (diff)
downloadFreeBSD-src-33c3c53a533a006733f0b78847fcf122188e0d74.zip
FreeBSD-src-33c3c53a533a006733f0b78847fcf122188e0d74.tar.gz
Improve interlocking for card removal. We now can remove the card in
the ISR. We keep track of the card state and don't call the IRS when the card isn't inserted. This helps quite a bit with card ejection problems that Ian was seeing. Submitted by: Ian Dowse MFC upon: re approvel.
Diffstat (limited to 'sys/pccard/pcic_pci.c')
-rw-r--r--sys/pccard/pcic_pci.c82
1 files changed, 56 insertions, 26 deletions
diff --git a/sys/pccard/pcic_pci.c b/sys/pccard/pcic_pci.c
index 00460cd..e48e0f7 100644
--- a/sys/pccard/pcic_pci.c
+++ b/sys/pccard/pcic_pci.c
@@ -438,25 +438,36 @@ pcic_pci_cardtype(u_int32_t stat)
return ("none (can't happen)");
}
+/*
+ * Card insertion and removal code. The insertion events need to be
+ * debounced so that the noisy insertion/removal events don't result
+ * 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.
+ *
+ * Note: We only handle the card detect change events. We don't handle
+ * power events and status change events.
+ */
static void
-pcic_cd_event(void *arg)
+pcic_cd_insert(void *arg)
{
struct pcic_softc *sc = (struct pcic_softc *) arg;
struct pcic_slot *sp = &sc->slots[0];
u_int32_t stat;
-
+
+ sc->cd_pending = 0;
stat = bus_space_read_4(sp->bst, sp->bsh, CB_SOCKET_STATE);
- device_printf(sc->dev, "debounced state is 0x%x\n", stat);
- if ((stat & CB_SS_CD) == 0) {
- if ((stat & CB_SS_16BIT) == 0)
- device_printf(sp->sc->dev, "Unsupported card: %s\n",
- pcic_pci_cardtype(stat));
- else
- pccard_event(sp->slt, card_inserted);
- } else {
- pccard_event(sp->slt, card_removed);
- }
- sc->cd_pending = 0;
+
+ /* Just return if the interrupt handler missed a remove transition. */
+ if ((stat & CB_SS_CD) != 0)
+ return;
+ sc->cd_present = 1;
+ if ((stat & CB_SS_16BIT) == 0)
+ device_printf(sp->sc->dev, "Card type %s is unsupported\n",
+ pcic_pci_cardtype(stat));
+ else
+ pccard_event(sp->slt, card_inserted);
}
static void
@@ -466,20 +477,37 @@ pcic_pci_intr(void *arg)
struct pcic_slot *sp = &sc->slots[0];
u_int32_t event;
u_int32_t stat;
+ int present;
event = bus_space_read_4(sp->bst, sp->bsh, CB_SOCKET_EVENT);
if (event != 0) {
- device_printf(sc->dev, "Event mask 0x%x\n", event);
- if ((event & CB_SE_CD) != 0 && !sc->cd_pending) {
- sc->cd_pending = 1;
- timeout(pcic_cd_event, arg, hz/2);
+ stat = bus_space_read_4(sp->bst, sp->bsh, CB_SOCKET_STATE);
+ if (bootverbose)
+ device_printf(sc->dev, "Event mask 0x%x stat 0x%x\n",
+ event, stat);
+
+ present = (stat & CB_SS_CD) == 0;
+ if (present != sc->cd_present) {
+ if (sc->cd_pending) {
+ untimeout(pcic_cd_insert, 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;
+ sp->intr = NULL;
+ pccard_event(sp->slt, card_removed);
+ }
}
- /* Ack the interrupt, all of them to be safe */
- bus_space_write_4(sp->bst, sp->bsh, 0, 0xffffffff);
+ /* Ack the interrupt */
+ bus_space_write_4(sp->bst, sp->bsh, 0, event);
}
/*
- * TI chips also require us to read the old ExCA register for
+ * Some TI chips also require us to read the old ExCA register for
* card status change when we route CSC via PCI! So, we go ahead
* and read it to clear the bits. Maybe we should check the status
* ala the ISA interrupt handler, but those changes should be caught
@@ -487,12 +515,14 @@ pcic_pci_intr(void *arg)
*/
sp->getb(sp, PCIC_STAT_CHG);
- /* Now call children interrupts if any */
- stat = bus_space_read_4(sp->bst, sp->bsh, CB_SOCKET_STATE);
- if ((stat & CB_SS_CD) == 0) {
- if (sp->intr != NULL)
- sp->intr(sp->argp);
- }
+ /*
+ * If we have a card in the slot with an interrupt handler, then
+ * call it. Note: This means that each card can have at most one
+ * interrupt handler for it. Since multifunction cards aren't
+ * supported, this shouldn't cause a problem in practice.
+ */
+ if (sc->cd_present && sp->intr != NULL)
+ sp->intr(sp->argp);
}
/*
OpenPOWER on IntegriCloud