PCMCIA: sa11x0: cerf: convert to use new irq/gpio management
[linux-2.6.git] / drivers / pcmcia / cs.c
index 9a49c39..d9ea192 100644 (file)
@@ -32,9 +32,7 @@
 #include <asm/system.h>
 #include <asm/irq.h>
 
-#include <pcmcia/cs_types.h>
 #include <pcmcia/ss.h>
-#include <pcmcia/cs.h>
 #include <pcmcia/cistpl.h>
 #include <pcmcia/cisreg.h>
 #include <pcmcia/ds.h>
@@ -76,65 +74,6 @@ DECLARE_RWSEM(pcmcia_socket_list_rwsem);
 EXPORT_SYMBOL(pcmcia_socket_list_rwsem);
 
 
-/*
- * Low-level PCMCIA socket drivers need to register with the PCCard
- * core using pcmcia_register_socket.
- *
- * socket drivers are expected to use the following callbacks in their
- * .drv struct:
- *  - pcmcia_socket_dev_suspend
- *  - pcmcia_socket_dev_resume
- * These functions check for the appropriate struct pcmcia_soket arrays,
- * and pass them to the low-level functions pcmcia_{suspend,resume}_socket
- */
-static int socket_early_resume(struct pcmcia_socket *skt);
-static int socket_late_resume(struct pcmcia_socket *skt);
-static int socket_resume(struct pcmcia_socket *skt);
-static int socket_suspend(struct pcmcia_socket *skt);
-
-static void pcmcia_socket_dev_run(struct device *dev,
-                                 int (*cb)(struct pcmcia_socket *))
-{
-       struct pcmcia_socket *socket;
-
-       down_read(&pcmcia_socket_list_rwsem);
-       list_for_each_entry(socket, &pcmcia_socket_list, socket_list) {
-               if (socket->dev.parent != dev)
-                       continue;
-               mutex_lock(&socket->skt_mutex);
-               cb(socket);
-               mutex_unlock(&socket->skt_mutex);
-       }
-       up_read(&pcmcia_socket_list_rwsem);
-}
-
-int pcmcia_socket_dev_suspend(struct device *dev)
-{
-       pcmcia_socket_dev_run(dev, socket_suspend);
-       return 0;
-}
-EXPORT_SYMBOL(pcmcia_socket_dev_suspend);
-
-void pcmcia_socket_dev_early_resume(struct device *dev)
-{
-       pcmcia_socket_dev_run(dev, socket_early_resume);
-}
-EXPORT_SYMBOL(pcmcia_socket_dev_early_resume);
-
-void pcmcia_socket_dev_late_resume(struct device *dev)
-{
-       pcmcia_socket_dev_run(dev, socket_late_resume);
-}
-EXPORT_SYMBOL(pcmcia_socket_dev_late_resume);
-
-int pcmcia_socket_dev_resume(struct device *dev)
-{
-       pcmcia_socket_dev_run(dev, socket_resume);
-       return 0;
-}
-EXPORT_SYMBOL(pcmcia_socket_dev_resume);
-
-
 struct pcmcia_socket *pcmcia_get_socket(struct pcmcia_socket *skt)
 {
        struct device *dev = get_device(&skt->dev);
@@ -311,38 +250,6 @@ struct pcmcia_socket *pcmcia_get_socket_by_nr(unsigned int nr)
 }
 EXPORT_SYMBOL(pcmcia_get_socket_by_nr);
 
-/*
- * The central event handler.  Send_event() sends an event to the
- * 16-bit subsystem, which then calls the relevant device drivers.
- * Parse_events() interprets the event bits from
- * a card status change report.  Do_shutdown() handles the high
- * priority stuff associated with a card removal.
- */
-
-/* NOTE: send_event needs to be called with skt->sem held. */
-
-static int send_event(struct pcmcia_socket *s, event_t event, int priority)
-{
-       int ret;
-
-       if ((s->state & SOCKET_CARDBUS) && (event != CS_EVENT_CARD_REMOVAL))
-               return 0;
-
-       dev_dbg(&s->dev, "send_event(event %d, pri %d, callback 0x%p)\n",
-          event, priority, s->callback);
-
-       if (!s->callback)
-               return 0;
-       if (!try_module_get(s->callback->owner))
-               return 0;
-
-       ret = s->callback->event(s, event, priority);
-
-       module_put(s->callback->owner);
-
-       return ret;
-}
-
 static int socket_reset(struct pcmcia_socket *skt)
 {
        int status, i;
@@ -385,7 +292,8 @@ static void socket_shutdown(struct pcmcia_socket *s)
 
        dev_dbg(&s->dev, "shutdown\n");
 
-       send_event(s, CS_EVENT_CARD_REMOVAL, CS_EVENT_PRI_HIGH);
+       if (s->callback)
+               s->callback->remove(s);
 
        mutex_lock(&s->ops_mutex);
        s->state &= SOCKET_INUSE | SOCKET_PRESENT;
@@ -396,7 +304,6 @@ static void socket_shutdown(struct pcmcia_socket *s)
        s->socket = dead_socket;
        s->ops->init(s);
        s->ops->set_socket(s, &s->socket);
-       s->irq.AssignedIRQ = s->irq.Config = 0;
        s->lock_count = 0;
        kfree(s->fake_cis);
        s->fake_cis = NULL;
@@ -537,7 +444,8 @@ static int socket_insert(struct pcmcia_socket *skt)
                dev_dbg(&skt->dev, "insert done\n");
                mutex_unlock(&skt->ops_mutex);
 
-               send_event(skt, CS_EVENT_CARD_INSERTION, CS_EVENT_PRI_LOW);
+               if (!(skt->state & SOCKET_CARDBUS) && (skt->callback))
+                       skt->callback->add(skt);
        } else {
                mutex_unlock(&skt->ops_mutex);
                socket_shutdown(skt);
@@ -554,7 +462,6 @@ static int socket_suspend(struct pcmcia_socket *skt)
        mutex_lock(&skt->ops_mutex);
        skt->suspended_state = skt->state;
 
-       send_event(skt, CS_EVENT_PM_SUSPEND, CS_EVENT_PRI_LOW);
        skt->socket = dead_socket;
        skt->ops->set_socket(skt, &skt->socket);
        if (skt->ops->suspend)
@@ -578,12 +485,18 @@ static int socket_early_resume(struct pcmcia_socket *skt)
 
 static int socket_late_resume(struct pcmcia_socket *skt)
 {
+       int ret;
+
        mutex_lock(&skt->ops_mutex);
        skt->state &= ~SOCKET_SUSPEND;
        mutex_unlock(&skt->ops_mutex);
 
-       if (!(skt->state & SOCKET_PRESENT))
-               return socket_insert(skt);
+       if (!(skt->state & SOCKET_PRESENT)) {
+               ret = socket_insert(skt);
+               if (ret == -ENODEV)
+                       ret = 0;
+               return ret;
+       }
 
        if (skt->resume_status) {
                socket_shutdown(skt);
@@ -609,8 +522,8 @@ static int socket_late_resume(struct pcmcia_socket *skt)
                return 0;
        }
 #endif
-
-       send_event(skt, CS_EVENT_PM_RESUME, CS_EVENT_PRI_LOW);
+       if (!(skt->state & SOCKET_CARDBUS) && (skt->callback))
+               skt->callback->early_resume(skt);
        return 0;
 }
 
@@ -689,6 +602,9 @@ static int pccardd(void *__skt)
 
        complete(&skt->thread_done);
 
+       /* wait for userspace to catch up */
+       msleep(250);
+
        set_freezable();
        for (;;) {
                unsigned long flags;
@@ -705,36 +621,30 @@ static int pccardd(void *__skt)
                spin_unlock_irqrestore(&skt->thread_lock, flags);
 
                mutex_lock(&skt->skt_mutex);
-               if (events) {
-                       if (events & SS_DETECT)
-                               socket_detect_change(skt);
-                       if (events & SS_BATDEAD)
-                               send_event(skt, CS_EVENT_BATTERY_DEAD, CS_EVENT_PRI_LOW);
-                       if (events & SS_BATWARN)
-                               send_event(skt, CS_EVENT_BATTERY_LOW, CS_EVENT_PRI_LOW);
-                       if (events & SS_READY)
-                               send_event(skt, CS_EVENT_READY_CHANGE, CS_EVENT_PRI_LOW);
-               }
+               if (events & SS_DETECT)
+                       socket_detect_change(skt);
 
                if (sysfs_events) {
                        if (sysfs_events & PCMCIA_UEVENT_EJECT)
                                socket_remove(skt);
                        if (sysfs_events & PCMCIA_UEVENT_INSERT)
                                socket_insert(skt);
-                       if ((sysfs_events & PCMCIA_UEVENT_RESUME) &&
-                               !(skt->state & SOCKET_CARDBUS)) {
-                               ret = socket_resume(skt);
-                               if (!ret && skt->callback)
-                                       skt->callback->resume(skt);
-                       }
                        if ((sysfs_events & PCMCIA_UEVENT_SUSPEND) &&
                                !(skt->state & SOCKET_CARDBUS)) {
                                if (skt->callback)
                                        ret = skt->callback->suspend(skt);
                                else
                                        ret = 0;
-                               if (!ret)
+                               if (!ret) {
                                        socket_suspend(skt);
+                                       msleep(100);
+                               }
+                       }
+                       if ((sysfs_events & PCMCIA_UEVENT_RESUME) &&
+                               !(skt->state & SOCKET_CARDBUS)) {
+                               ret = socket_resume(skt);
+                               if (!ret && skt->callback)
+                                       skt->callback->resume(skt);
                        }
                        if ((sysfs_events & PCMCIA_UEVENT_REQUERY) &&
                                !(skt->state & SOCKET_CARDBUS)) {
@@ -832,7 +742,7 @@ int pccard_register_pcmcia(struct pcmcia_socket *s, struct pcmcia_callback *c)
                s->callback = c;
 
                if ((s->state & (SOCKET_PRESENT|SOCKET_CARDBUS)) == SOCKET_PRESENT)
-                       send_event(s, CS_EVENT_CARD_INSERTION, CS_EVENT_PRI_LOW);
+                       s->callback->add(s);
        } else
                s->callback = NULL;
  err:
@@ -872,20 +782,13 @@ int pcmcia_reset_card(struct pcmcia_socket *skt)
                        break;
                }
 
-               ret = send_event(skt, CS_EVENT_RESET_REQUEST, CS_EVENT_PRI_LOW);
-               if (ret == 0) {
-                       send_event(skt, CS_EVENT_RESET_PHYSICAL, CS_EVENT_PRI_LOW);
-                       if (skt->callback)
-                               skt->callback->suspend(skt);
-                       mutex_lock(&skt->ops_mutex);
-                       ret = socket_reset(skt);
-                       mutex_unlock(&skt->ops_mutex);
-                       if (ret == 0) {
-                               send_event(skt, CS_EVENT_CARD_RESET, CS_EVENT_PRI_LOW);
-                               if (skt->callback)
-                                       skt->callback->resume(skt);
-                       }
-               }
+               if (skt->callback)
+                       skt->callback->suspend(skt);
+               mutex_lock(&skt->ops_mutex);
+               ret = socket_reset(skt);
+               mutex_unlock(&skt->ops_mutex);
+               if ((ret == 0) && (skt->callback))
+                       skt->callback->resume(skt);
 
                ret = 0;
        } while (0);
@@ -916,11 +819,66 @@ static void pcmcia_release_socket_class(struct class *data)
 }
 
 
+#ifdef CONFIG_PM
+
+static int __pcmcia_pm_op(struct device *dev,
+                         int (*callback) (struct pcmcia_socket *skt))
+{
+       struct pcmcia_socket *s = container_of(dev, struct pcmcia_socket, dev);
+       int ret;
+
+       mutex_lock(&s->skt_mutex);
+       ret = callback(s);
+       mutex_unlock(&s->skt_mutex);
+
+       return ret;
+}
+
+static int pcmcia_socket_dev_suspend_noirq(struct device *dev)
+{
+       return __pcmcia_pm_op(dev, socket_suspend);
+}
+
+static int pcmcia_socket_dev_resume_noirq(struct device *dev)
+{
+       return __pcmcia_pm_op(dev, socket_early_resume);
+}
+
+static int __used pcmcia_socket_dev_resume(struct device *dev)
+{
+       return __pcmcia_pm_op(dev, socket_late_resume);
+}
+
+static const struct dev_pm_ops pcmcia_socket_pm_ops = {
+       /* dev_resume may be called with IRQs enabled */
+       SET_SYSTEM_SLEEP_PM_OPS(NULL,
+                               pcmcia_socket_dev_resume)
+
+       /* late suspend must be called with IRQs disabled */
+       .suspend_noirq = pcmcia_socket_dev_suspend_noirq,
+       .freeze_noirq = pcmcia_socket_dev_suspend_noirq,
+       .poweroff_noirq = pcmcia_socket_dev_suspend_noirq,
+
+       /* early resume must be called with IRQs disabled */
+       .resume_noirq = pcmcia_socket_dev_resume_noirq,
+       .thaw_noirq = pcmcia_socket_dev_resume_noirq,
+       .restore_noirq = pcmcia_socket_dev_resume_noirq,
+};
+
+#define PCMCIA_SOCKET_CLASS_PM_OPS (&pcmcia_socket_pm_ops)
+
+#else /* CONFIG_PM */
+
+#define PCMCIA_SOCKET_CLASS_PM_OPS NULL
+
+#endif /* CONFIG_PM */
+
 struct class pcmcia_socket_class = {
        .name = "pcmcia_socket",
        .dev_uevent = pcmcia_socket_uevent,
        .dev_release = pcmcia_release_socket,
        .class_release = pcmcia_release_socket_class,
+       .pm = PCMCIA_SOCKET_CLASS_PM_OPS,
 };
 EXPORT_SYMBOL(pcmcia_socket_class);