summaryrefslogtreecommitdiff
path: root/sys/dev
diff options
context:
space:
mode:
authorAaron Campbell <aaron@cvs.openbsd.org>2000-06-28 17:48:11 +0000
committerAaron Campbell <aaron@cvs.openbsd.org>2000-06-28 17:48:11 +0000
commit936fdbed9f068647891331d9aa6e8b5a3438a274 (patch)
treee7172b8fbfa6e379f4f2a361a496d59e02ed1010 /sys/dev
parentd778f0732491665c2fcfde91332f64c6fe9f2764 (diff)
Make suspend -> eject card -> resume work.
On suspend, the apm code calls each function in its powerhook list. This list contains pointers to functions that are to be executed on PWR_SUSPEND and PWR_RESUME. One of these functions is pcmcia_power() which calls xxx_activate() for each PC Card that is connected with the action DVACT_ACTIVATE (where xxx is the name of the driver for the PC Card). On resume, the same thing happens, except the action is DVACT_ACTIVATE. Clearly, if a card is gone when we resume, trying to activate it is a bad idea. This commit adds a pcic_power() function that runs before pcmcia_power(). On resume, it uses pcic_intr_socket() to detect any events that occurred while we were suspended. (I had to split pcic_event_thread() into a threaded part and a non-threaded part, since the thread will not run when we are in interrupt context, but I need the events to be processed before pcmcia_power() runs.) So, by the time pcic_power() is through, all events that occurred during suspend have been processed, and pcmcia_power() will not try to activate cards that are not there since they have already been completely and properly detached. Note that event handling for card removals first calls the deactivate functions on the card then the detach functions. When we suspend, apm does the deactivate for us. So on resume, if we detect a card has been removed, we skip the deactivation step and just detach. We use the DVF_ACTIVE bit to determine whether or not deactivation has already occurred. Deactivating a device that has already been deactivated causes a panic.
Diffstat (limited to 'sys/dev')
-rw-r--r--sys/dev/ic/i82365.c179
-rw-r--r--sys/dev/ic/i82365var.h4
-rw-r--r--sys/dev/isa/i82365_isa.c3
3 files changed, 112 insertions, 74 deletions
diff --git a/sys/dev/ic/i82365.c b/sys/dev/ic/i82365.c
index 92546233e4d..3e5cb36f5c5 100644
--- a/sys/dev/ic/i82365.c
+++ b/sys/dev/ic/i82365.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: i82365.c,v 1.14 2000/06/23 16:53:07 aaron Exp $ */
+/* $OpenBSD: i82365.c,v 1.15 2000/06/28 17:48:10 aaron Exp $ */
/* $NetBSD: i82365.c,v 1.10 1998/06/09 07:36:55 thorpej Exp $ */
/*
@@ -99,7 +99,7 @@ void pcic_chip_do_io_map __P((struct pcic_handle *, int));
void pcic_create_event_thread __P((void *));
void pcic_event_thread __P((void *));
-
+void pcic_event_process __P((struct pcic_handle *, struct pcic_event *));
void pcic_queue_event __P((struct pcic_handle *, int));
void pcic_wait_ready __P((struct pcic_handle *));
@@ -434,78 +434,81 @@ pcic_event_thread(arg)
(void) tsleep((caddr_t)pcic_event_thread, PWAIT,
"pcicss", hz/4);
}
+ pcic_event_process(h, pe);
+ }
+
+ h->event_thread = NULL;
+
+ /* In case parent is waiting for us to exit. */
+ wakeup(sc);
+
+ kthread_exit(0);
+}
+
+void
+pcic_event_process(h, pe)
+ struct pcic_handle *h;
+ struct pcic_event *pe;
+{
+ int s;
+
+ s = splhigh();
+ SIMPLEQ_REMOVE_HEAD(&h->events, pe, pe_q);
+ splx(s);
+
+ switch (pe->pe_type) {
+ case PCIC_EVENT_INSERTION:
s = splhigh();
- SIMPLEQ_REMOVE_HEAD(&h->events, pe, pe_q);
- splx(s);
+ while (1) {
+ struct pcic_event *pe1, *pe2;
- switch (pe->pe_type) {
- case PCIC_EVENT_INSERTION:
- s = splhigh();
- while (1) {
- struct pcic_event *pe1, *pe2;
-
- if ((pe1 = SIMPLEQ_FIRST(&h->events)) == NULL)
- break;
- if (pe1->pe_type != PCIC_EVENT_REMOVAL)
- break;
- if ((pe2 = SIMPLEQ_NEXT(pe1, pe_q)) == NULL)
- break;
- if (pe2->pe_type == PCIC_EVENT_INSERTION) {
- SIMPLEQ_REMOVE_HEAD(&h->events, pe1,
- pe_q);
- free(pe1, M_TEMP);
- SIMPLEQ_REMOVE_HEAD(&h->events, pe2,
- pe_q);
- free(pe2, M_TEMP);
- }
+ if ((pe1 = SIMPLEQ_FIRST(&h->events)) == NULL)
+ break;
+ if (pe1->pe_type != PCIC_EVENT_REMOVAL)
+ break;
+ if ((pe2 = SIMPLEQ_NEXT(pe1, pe_q)) == NULL)
+ break;
+ if (pe2->pe_type == PCIC_EVENT_INSERTION) {
+ SIMPLEQ_REMOVE_HEAD(&h->events, pe1, pe_q);
+ free(pe1, M_TEMP);
+ SIMPLEQ_REMOVE_HEAD(&h->events, pe2, pe_q);
+ free(pe2, M_TEMP);
}
- splx(s);
+ }
+ splx(s);
- DPRINTF(("%s: insertion event\n",
- h->ph_parent->dv_xname));
- pcic_attach_card(h);
- break;
-
- case PCIC_EVENT_REMOVAL:
- s = splhigh();
- while (1) {
- struct pcic_event *pe1, *pe2;
-
- if ((pe1 = SIMPLEQ_FIRST(&h->events)) == NULL)
- break;
- if (pe1->pe_type != PCIC_EVENT_INSERTION)
- break;
- if ((pe2 = SIMPLEQ_NEXT(pe1, pe_q)) == NULL)
- break;
- if (pe2->pe_type == PCIC_EVENT_REMOVAL) {
- SIMPLEQ_REMOVE_HEAD(&h->events, pe1,
- pe_q);
- free(pe1, M_TEMP);
- SIMPLEQ_REMOVE_HEAD(&h->events, pe2,
- pe_q);
- free(pe2, M_TEMP);
- }
- }
- splx(s);
+ DPRINTF(("%s: insertion event\n", h->ph_parent->dv_xname));
+ pcic_attach_card(h);
+ break;
- DPRINTF(("%s: removal event\n",
- h->ph_parent->dv_xname));
- pcic_detach_card(h, DETACH_FORCE);
- break;
+ case PCIC_EVENT_REMOVAL:
+ s = splhigh();
+ while (1) {
+ struct pcic_event *pe1, *pe2;
- default:
- panic("pcic_event_thread: unknown event %d",
- pe->pe_type);
+ if ((pe1 = SIMPLEQ_FIRST(&h->events)) == NULL)
+ break;
+ if (pe1->pe_type != PCIC_EVENT_INSERTION)
+ break;
+ if ((pe2 = SIMPLEQ_NEXT(pe1, pe_q)) == NULL)
+ break;
+ if (pe2->pe_type == PCIC_EVENT_REMOVAL) {
+ SIMPLEQ_REMOVE_HEAD(&h->events, pe1, pe_q);
+ free(pe1, M_TEMP);
+ SIMPLEQ_REMOVE_HEAD(&h->events, pe2, pe_q);
+ free(pe2, M_TEMP);
+ }
}
- free(pe, M_TEMP);
- }
-
- h->event_thread = NULL;
+ splx(s);
- /* In case parent is waiting for us to exit. */
- wakeup(sc);
+ DPRINTF(("%s: removal event\n", h->ph_parent->dv_xname));
+ pcic_detach_card(h, DETACH_FORCE);
+ break;
- kthread_exit(0);
+ default:
+ panic("pcic_event_thread: unknown event %d", pe->pe_type);
+ }
+ free(pe, M_TEMP);
}
void
@@ -669,16 +672,13 @@ pcic_poll_intr(arg)
void *arg;
{
struct pcic_softc *sc = arg;
- int i, s;
-
- s = spltty();
+ int i;
for (i = 0; i < PCIC_NSLOTS; i++)
if (sc->handle[i].flags & PCIC_FLAG_SOCKETP)
pcic_intr_socket(&sc->handle[i]);
- timeout_add(&sc->poll_timeout, hz / 2 );
- splx(s);
+ timeout_add(&sc->poll_timeout, hz / 2);
}
int
@@ -797,9 +797,15 @@ void
pcic_deactivate_card(h)
struct pcic_handle *h;
{
+ struct device *dev = (struct device *)h->pcmcia;
- /* call the MI deactivate function */
- pcmcia_card_deactivate(h->pcmcia);
+ /*
+ * At suspend, apm deactivates any connected cards. If we've woken up
+ * to find a previously-connected device missing, and we're detaching
+ * it, we don't want to deactivate it again.
+ */
+ if (dev->dv_flags & DVF_ACTIVE)
+ pcmcia_card_deactivate(h->pcmcia);
/* power down the socket */
pcic_write(h, PCIC_PWRCTL, 0);
@@ -808,6 +814,35 @@ pcic_deactivate_card(h)
pcic_write(h, PCIC_INTR, 0);
}
+/*
+ * The pcic_power() function must execute BEFORE the pcmcia_power() hooks.
+ * During suspend, a card may have been ejected. If so, we must detach it
+ * completely before pcmcia_power() tries to activate it. Attempting to
+ * activate a card that isn't there is bad news.
+ */
+void
+pcic_power(why, arg)
+ int why;
+ void *arg;
+{
+ struct pcic_handle *h = (struct pcic_handle *)arg;
+ struct pcic_softc *sc = (struct pcic_softc *)h->ph_parent;
+ struct pcic_event *pe;
+
+ if (why != PWR_RESUME) {
+ if (timeout_pending(&sc->poll_timeout))
+ timeout_del(&sc->poll_timeout);
+ }
+ else {
+ pcic_intr_socket(h);
+
+ while ((pe = SIMPLEQ_FIRST(&h->events)))
+ pcic_event_process(h, pe);
+
+ timeout_add(&sc->poll_timeout, hz / 2);
+ }
+}
+
int
pcic_chip_mem_alloc(pch, size, pcmhp)
pcmcia_chipset_handle_t pch;
diff --git a/sys/dev/ic/i82365var.h b/sys/dev/ic/i82365var.h
index e152b09bf1c..dab9934bcf3 100644
--- a/sys/dev/ic/i82365var.h
+++ b/sys/dev/ic/i82365var.h
@@ -1,4 +1,4 @@
-/* $OpenBSD: i82365var.h,v 1.8 2000/06/23 16:53:08 aaron Exp $ */
+/* $OpenBSD: i82365var.h,v 1.9 2000/06/28 17:48:10 aaron Exp $ */
/* $NetBSD: i82365var.h,v 1.4 1998/05/23 18:32:29 matt Exp $ */
/*
@@ -176,6 +176,8 @@ void pcic_chip_io_unmap __P((pcmcia_chipset_handle_t, int));
void pcic_chip_socket_enable __P((pcmcia_chipset_handle_t));
void pcic_chip_socket_disable __P((pcmcia_chipset_handle_t));
+void pcic_power __P((int, void *));
+
#define pcic_read(h, idx) \
(*(h)->ph_read)((h), (idx))
diff --git a/sys/dev/isa/i82365_isa.c b/sys/dev/isa/i82365_isa.c
index 5ef310aa130..97cb4899bc2 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.10 2000/06/23 16:53:08 aaron Exp $ */
+/* $OpenBSD: i82365_isa.c,v 1.11 2000/06/28 17:48:10 aaron Exp $ */
/* $NetBSD: i82365_isa.c,v 1.11 1998/06/09 07:25:00 thorpej Exp $ */
/*
@@ -218,6 +218,7 @@ pcic_isa_attach(parent, self, aux)
pcic_write(h, PCIC_CSC_INTR,
(sc->irq << PCIC_CSC_INTR_IRQ_SHIFT) |
PCIC_CSC_INTR_CD_ENABLE);
+ powerhook_establish(pcic_power, h);
}
}
} else