pcmcia: simplify locking
[safe/jmp/linux-2.6] / drivers / pcmcia / cs.c
index 934d4be..13277ee 100644 (file)
@@ -61,17 +61,6 @@ INT_MODULE_PARM(unreset_limit,       30);            /* unreset_check's */
 /* Access speed for attribute memory windows */
 INT_MODULE_PARM(cis_speed,     300);           /* ns */
 
-#ifdef CONFIG_PCMCIA_DEBUG
-static int pc_debug;
-
-module_param(pc_debug, int, 0644);
-
-int cs_debug_level(int level)
-{
-       return pc_debug > level;
-}
-#endif
-
 
 socket_state_t dead_socket = {
        .csc_mask       = SS_DETECT,
@@ -98,10 +87,13 @@ EXPORT_SYMBOL(pcmcia_socket_list_rwsem);
  * 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);
 
-int pcmcia_socket_dev_suspend(struct device *dev)
+static void pcmcia_socket_dev_run(struct device *dev,
+                                 int (*cb)(struct pcmcia_socket *))
 {
        struct pcmcia_socket *socket;
 
@@ -110,52 +102,51 @@ int pcmcia_socket_dev_suspend(struct device *dev)
                if (socket->dev.parent != dev)
                        continue;
                mutex_lock(&socket->skt_mutex);
-               socket_suspend(socket);
+               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);
 
-int pcmcia_socket_dev_resume(struct device *dev)
+void pcmcia_socket_dev_early_resume(struct device *dev)
 {
-       struct pcmcia_socket *socket;
+       pcmcia_socket_dev_run(dev, socket_early_resume);
+}
+EXPORT_SYMBOL(pcmcia_socket_dev_early_resume);
 
-       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);
-               socket_resume(socket);
-               mutex_unlock(&socket->skt_mutex);
-       }
-       up_read(&pcmcia_socket_list_rwsem);
+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 pcmcia_socket *pcmcia_get_socket(struct pcmcia_socket *skt)
 {
        struct device *dev = get_device(&skt->dev);
        if (!dev)
                return NULL;
-       skt = dev_get_drvdata(dev);
-       if (!try_module_get(skt->owner)) {
-               put_device(&skt->dev);
-               return NULL;
-       }
-       return (skt);
+       return dev_get_drvdata(dev);
 }
 EXPORT_SYMBOL(pcmcia_get_socket);
 
 
 void pcmcia_put_socket(struct pcmcia_socket *skt)
 {
-       module_put(skt->owner);
        put_device(&skt->dev);
 }
 EXPORT_SYMBOL(pcmcia_put_socket);
@@ -182,9 +173,7 @@ int pcmcia_register_socket(struct pcmcia_socket *socket)
        if (!socket || !socket->ops || !socket->dev.parent || !socket->resource_ops)
                return -EINVAL;
 
-       cs_dbg(socket, 0, "pcmcia_register_socket(0x%p)\n", socket->ops);
-
-       spin_lock_init(&socket->lock);
+       dev_dbg(&socket->dev, "pcmcia_register_socket(0x%p)\n", socket->ops);
 
        /* try to obtain a socket number [yes, it gets ugly if we
         * register more than 2^sizeof(unsigned int) pcmcia
@@ -231,6 +220,7 @@ int pcmcia_register_socket(struct pcmcia_socket *socket)
        init_completion(&socket->socket_released);
        init_completion(&socket->thread_done);
        mutex_init(&socket->skt_mutex);
+       mutex_init(&socket->ops_mutex);
        spin_lock_init(&socket->thread_lock);
 
        if (socket->resource_ops->init) {
@@ -254,6 +244,13 @@ int pcmcia_register_socket(struct pcmcia_socket *socket)
 
        pcmcia_parse_events(socket, SS_DETECT);
 
+       /*
+        * Let's try to get the PCMCIA module for 16-bit PCMCIA support.
+        * If it fails, it doesn't matter -- we still have 32-bit CardBus
+        * support to offer, so this is not a failure mode.
+        */
+       request_module_nowait("pcmcia");
+
        return 0;
 
  err:
@@ -274,26 +271,25 @@ void pcmcia_unregister_socket(struct pcmcia_socket *socket)
        if (!socket)
                return;
 
-       cs_dbg(socket, 0, "pcmcia_unregister_socket(0x%p)\n", socket->ops);
+       dev_dbg(&socket->dev, "pcmcia_unregister_socket(0x%p)\n", socket->ops);
 
        if (socket->thread)
                kthread_stop(socket->thread);
 
-       release_cis_mem(socket);
-
        /* remove from our own list */
        down_write(&pcmcia_socket_list_rwsem);
        list_del(&socket->socket_list);
        up_write(&pcmcia_socket_list_rwsem);
 
        /* wait for sysfs to drop all references */
-       release_resource_db(socket);
+       if (socket->resource_ops->exit)
+               socket->resource_ops->exit(socket);
        wait_for_completion(&socket->socket_released);
 } /* pcmcia_unregister_socket */
 EXPORT_SYMBOL(pcmcia_unregister_socket);
 
 
-struct pcmcia_socket * pcmcia_get_socket_by_nr(unsigned int nr)
+struct pcmcia_socket *pcmcia_get_socket_by_nr(unsigned int nr)
 {
        struct pcmcia_socket *s;
 
@@ -324,10 +320,10 @@ static int send_event(struct pcmcia_socket *s, event_t event, int priority)
 {
        int ret;
 
-       if (s->state & SOCKET_CARDBUS)
+       if ((s->state & SOCKET_CARDBUS) && (event != CS_EVENT_CARD_REMOVAL))
                return 0;
 
-       cs_dbg(s, 1, "send_event(event %d, pri %d, callback 0x%p)\n",
+       dev_dbg(&s->dev, "send_event(event %d, pri %d, callback 0x%p)\n",
           event, priority, s->callback);
 
        if (!s->callback)
@@ -342,18 +338,11 @@ static int send_event(struct pcmcia_socket *s, event_t event, int priority)
        return ret;
 }
 
-static void socket_remove_drivers(struct pcmcia_socket *skt)
-{
-       cs_dbg(skt, 4, "remove_drivers\n");
-
-       send_event(skt, CS_EVENT_CARD_REMOVAL, CS_EVENT_PRI_HIGH);
-}
-
 static int socket_reset(struct pcmcia_socket *skt)
 {
        int status, i;
 
-       cs_dbg(skt, 4, "reset\n");
+       dev_dbg(&skt->dev, "reset\n");
 
        skt->socket.flags |= SS_OUTPUT_ENA | SS_RESET;
        skt->ops->set_socket(skt, &skt->socket);
@@ -375,7 +364,7 @@ static int socket_reset(struct pcmcia_socket *skt)
                msleep(unreset_check * 10);
        }
 
-       cs_err(skt, "time out after reset.\n");
+       dev_printk(KERN_ERR, &skt->dev, "time out after reset.\n");
        return -ETIMEDOUT;
 }
 
@@ -389,9 +378,11 @@ static void socket_shutdown(struct pcmcia_socket *s)
 {
        int status;
 
-       cs_dbg(s, 4, "shutdown\n");
+       dev_dbg(&s->dev, "shutdown\n");
 
-       socket_remove_drivers(s);
+       send_event(s, CS_EVENT_CARD_REMOVAL, CS_EVENT_PRI_HIGH);
+
+       mutex_lock(&s->ops_mutex);
        s->state &= SOCKET_INUSE | SOCKET_PRESENT;
        msleep(shutdown_delay * 10);
        s->state &= SOCKET_INUSE;
@@ -402,7 +393,8 @@ static void socket_shutdown(struct pcmcia_socket *s)
        s->ops->set_socket(s, &s->socket);
        s->irq.AssignedIRQ = s->irq.Config = 0;
        s->lock_count = 0;
-       destroy_cis_cache(s);
+       kfree(s->fake_cis);
+       s->fake_cis = NULL;
 #ifdef CONFIG_CARDBUS
        cb_free(s);
 #endif
@@ -417,14 +409,15 @@ static void socket_shutdown(struct pcmcia_socket *s)
                           "*** DANGER *** unable to remove socket power\n");
        }
 
-       cs_socket_put(s);
+       s->state &= ~SOCKET_INUSE;
+       mutex_unlock(&s->ops_mutex);
 }
 
 static int socket_setup(struct pcmcia_socket *skt, int initial_delay)
 {
        int status, i;
 
-       cs_dbg(skt, 4, "setup\n");
+       dev_dbg(&skt->dev, "setup\n");
 
        skt->ops->get_status(skt, &status);
        if (!(status & SS_DETECT))
@@ -444,17 +437,20 @@ static int socket_setup(struct pcmcia_socket *skt, int initial_delay)
        }
 
        if (status & SS_PENDING) {
-               cs_err(skt, "voltage interrogation timed out.\n");
+               dev_printk(KERN_ERR, &skt->dev,
+                          "voltage interrogation timed out.\n");
                return -ETIMEDOUT;
        }
 
        if (status & SS_CARDBUS) {
                if (!(skt->features & SS_CAP_CARDBUS)) {
-                       cs_err(skt, "cardbus cards are not supported.\n");
+                       dev_printk(KERN_ERR, &skt->dev,
+                               "cardbus cards are not supported.\n");
                        return -EINVAL;
                }
                skt->state |= SOCKET_CARDBUS;
-       }
+       } else
+               skt->state &= ~SOCKET_CARDBUS;
 
        /*
         * Decode the card voltage requirements, and apply power to the card.
@@ -464,7 +460,7 @@ static int socket_setup(struct pcmcia_socket *skt, int initial_delay)
        else if (!(status & SS_XVCARD))
                skt->socket.Vcc = skt->socket.Vpp = 50;
        else {
-               cs_err(skt, "unsupported voltage key.\n");
+               dev_printk(KERN_ERR, &skt->dev, "unsupported voltage key.\n");
                return -EIO;
        }
 
@@ -481,7 +477,7 @@ static int socket_setup(struct pcmcia_socket *skt, int initial_delay)
 
        skt->ops->get_status(skt, &status);
        if (!(status & SS_POWERON)) {
-               cs_err(skt, "unable to apply power.\n");
+               dev_printk(KERN_ERR, &skt->dev, "unable to apply power.\n");
                return -EIO;
        }
 
@@ -501,10 +497,11 @@ static int socket_insert(struct pcmcia_socket *skt)
 {
        int ret;
 
-       cs_dbg(skt, 4, "insert\n");
+       dev_dbg(&skt->dev, "insert\n");
 
-       if (!cs_socket_get(skt))
-               return -ENODEV;
+       mutex_lock(&skt->ops_mutex);
+       WARN_ON(skt->state & SOCKET_INUSE);
+       skt->state |= SOCKET_INUSE;
 
        ret = socket_setup(skt, setup_delay);
        if (ret == 0) {
@@ -521,10 +518,12 @@ static int socket_insert(struct pcmcia_socket *skt)
                        skt->state |= SOCKET_CARDBUS_CONFIG;
                }
 #endif
-               cs_dbg(skt, 4, "insert done\n");
+               dev_dbg(&skt->dev, "insert done\n");
+               mutex_unlock(&skt->ops_mutex);
 
                send_event(skt, CS_EVENT_CARD_INSERTION, CS_EVENT_PRI_LOW);
        } else {
+               mutex_unlock(&skt->ops_mutex);
                socket_shutdown(skt);
        }
 
@@ -536,66 +535,83 @@ static int socket_suspend(struct pcmcia_socket *skt)
        if (skt->state & SOCKET_SUSPEND)
                return -EBUSY;
 
+       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)
                skt->ops->suspend(skt);
        skt->state |= SOCKET_SUSPEND;
-
+       mutex_unlock(&skt->ops_mutex);
        return 0;
 }
 
-/*
- * Resume a socket.  If a card is present, verify its CIS against
- * our cached copy.  If they are different, the card has been
- * replaced, and we need to tell the drivers.
- */
-static int socket_resume(struct pcmcia_socket *skt)
+static int socket_early_resume(struct pcmcia_socket *skt)
 {
-       int ret;
-
-       if (!(skt->state & SOCKET_SUSPEND))
-               return -EBUSY;
-
+       mutex_lock(&skt->ops_mutex);
        skt->socket = dead_socket;
        skt->ops->init(skt);
        skt->ops->set_socket(skt, &skt->socket);
+       if (skt->state & SOCKET_PRESENT)
+               skt->resume_status = socket_setup(skt, resume_delay);
+       mutex_unlock(&skt->ops_mutex);
+       return 0;
+}
 
-       if (!(skt->state & SOCKET_PRESENT)) {
-               skt->state &= ~SOCKET_SUSPEND;
+static int socket_late_resume(struct pcmcia_socket *skt)
+{
+       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->resume_status) {
+               socket_shutdown(skt);
+               return 0;
        }
 
-       ret = socket_setup(skt, resume_delay);
-       if (ret == 0) {
-               /*
-                * FIXME: need a better check here for cardbus cards.
-                */
-               if (verify_cis_cache(skt) != 0) {
-                       cs_dbg(skt, 4, "cis mismatch - different card\n");
-                       socket_remove_drivers(skt);
-                       destroy_cis_cache(skt);
-                       /*
-                        * Workaround: give DS time to schedule removal.
-                        * Remove me once the 100ms delay is eliminated
-                        * in ds.c
-                        */
-                       msleep(200);
-                       send_event(skt, CS_EVENT_CARD_INSERTION, CS_EVENT_PRI_LOW);
-               } else {
-                       cs_dbg(skt, 4, "cis matches cache\n");
-                       send_event(skt, CS_EVENT_PM_RESUME, CS_EVENT_PRI_LOW);
-               }
-       } else {
+       if (skt->suspended_state != skt->state) {
+               dev_dbg(&skt->dev,
+                       "suspend state 0x%x != resume state 0x%x\n",
+                       skt->suspended_state, skt->state);
+
                socket_shutdown(skt);
+               return socket_insert(skt);
        }
 
-       skt->state &= ~SOCKET_SUSPEND;
+#ifdef CONFIG_CARDBUS
+       if (skt->state & SOCKET_CARDBUS) {
+               /* We can't be sure the CardBus card is the same
+                * as the one previously inserted. Therefore, remove
+                * and re-add... */
+               cb_free(skt);
+               cb_alloc(skt);
+               return 0;
+       }
+#endif
 
+       send_event(skt, CS_EVENT_PM_RESUME, CS_EVENT_PRI_LOW);
        return 0;
 }
 
+/*
+ * Resume a socket.  If a card is present, verify its CIS against
+ * our cached copy.  If they are different, the card has been
+ * replaced, and we need to tell the drivers.
+ */
+static int socket_resume(struct pcmcia_socket *skt)
+{
+       if (!(skt->state & SOCKET_SUSPEND))
+               return -EBUSY;
+
+       socket_early_resume(skt);
+       return socket_late_resume(skt);
+}
+
 static void socket_remove(struct pcmcia_socket *skt)
 {
        dev_printk(KERN_NOTICE, &skt->dev,
@@ -692,6 +708,13 @@ static int pccardd(void *__skt)
        /* make sure we are running before we exit */
        set_current_state(TASK_RUNNING);
 
+       /* shut down socket, if a device is still present */
+       if (skt->state & SOCKET_PRESENT) {
+               mutex_lock(&skt->skt_mutex);
+               socket_remove(skt);
+               mutex_unlock(&skt->skt_mutex);
+       }
+
        /* remove from the device core */
        pccard_sysfs_remove_socket(&skt->dev);
        device_unregister(&skt->dev);
@@ -706,7 +729,7 @@ static int pccardd(void *__skt)
 void pcmcia_parse_events(struct pcmcia_socket *s, u_int events)
 {
        unsigned long flags;
-       cs_dbg(s, 4, "parse_events: events %08x\n", events);
+       dev_dbg(&s->dev, "parse_events: events %08x\n", events);
        if (s->thread) {
                spin_lock_irqsave(&s->thread_lock, flags);
                s->thread_events |= events;
@@ -721,7 +744,7 @@ EXPORT_SYMBOL(pcmcia_parse_events);
 /* register pcmcia_callback */
 int pccard_register_pcmcia(struct pcmcia_socket *s, struct pcmcia_callback *c)
 {
-        int ret = 0;
+       int ret = 0;
 
        /* s->skt_mutex also protects s->callback */
        mutex_lock(&s->skt_mutex);
@@ -756,19 +779,22 @@ int pcmcia_reset_card(struct pcmcia_socket *skt)
 {
        int ret;
 
-       cs_dbg(skt, 1, "resetting socket\n");
+       dev_dbg(&skt->dev, "resetting socket\n");
 
        mutex_lock(&skt->skt_mutex);
        do {
                if (!(skt->state & SOCKET_PRESENT)) {
+                       dev_dbg(&skt->dev, "can't reset, not present\n");
                        ret = -ENODEV;
                        break;
                }
                if (skt->state & SOCKET_SUSPEND) {
+                       dev_dbg(&skt->dev, "can't reset, suspended\n");
                        ret = -EBUSY;
                        break;
                }
                if (skt->state & SOCKET_CARDBUS) {
+                       dev_dbg(&skt->dev, "can't reset, is cardbus\n");
                        ret = -EPERM;
                        break;
                }
@@ -778,7 +804,10 @@ int pcmcia_reset_card(struct pcmcia_socket *skt)
                        send_event(skt, CS_EVENT_RESET_PHYSICAL, CS_EVENT_PRI_LOW);
                        if (skt->callback)
                                skt->callback->suspend(skt);
-                       if (socket_reset(skt) == 0) {
+                       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);
@@ -801,7 +830,7 @@ int pcmcia_suspend_card(struct pcmcia_socket *skt)
 {
        int ret;
 
-       cs_dbg(skt, 1, "suspending socket\n");
+       dev_dbg(&skt->dev, "suspending socket\n");
 
        mutex_lock(&skt->skt_mutex);
        do {
@@ -830,8 +859,8 @@ EXPORT_SYMBOL(pcmcia_suspend_card);
 int pcmcia_resume_card(struct pcmcia_socket *skt)
 {
        int ret;
-    
-       cs_dbg(skt, 1, "waking up socket\n");
+
+       dev_dbg(&skt->dev, "waking up socket\n");
 
        mutex_lock(&skt->skt_mutex);
        do {
@@ -858,8 +887,8 @@ EXPORT_SYMBOL(pcmcia_resume_card);
 int pcmcia_eject_card(struct pcmcia_socket *skt)
 {
        int ret;
-    
-       cs_dbg(skt, 1, "user eject request\n");
+
+       dev_dbg(&skt->dev, "user eject request\n");
 
        mutex_lock(&skt->skt_mutex);
        do {
@@ -888,7 +917,7 @@ int pcmcia_insert_card(struct pcmcia_socket *skt)
 {
        int ret;
 
-       cs_dbg(skt, 1, "user insert request\n");
+       dev_dbg(&skt->dev, "user insert request\n");
 
        mutex_lock(&skt->skt_mutex);
        do {