summaryrefslogtreecommitdiffstats
path: root/drivers/pcmcia
diff options
context:
space:
mode:
authorDominik Brodowski <linux@dominikbrodowski.net>2006-01-06 00:02:03 +0100
committerDominik Brodowski <linux@dominikbrodowski.net>2006-01-06 00:02:03 +0100
commit8e9e793d68fcda6cc84c18cedf85ca0f91d801a8 (patch)
tree3dcceaaa676f1221e532e24106bb661143733eaf /drivers/pcmcia
parent98e4c28b7ec390c2dad6a4c69d69629c0f7e8b10 (diff)
downloadlinux-8e9e793d68fcda6cc84c18cedf85ca0f91d801a8.tar.gz
linux-8e9e793d68fcda6cc84c18cedf85ca0f91d801a8.tar.bz2
linux-8e9e793d68fcda6cc84c18cedf85ca0f91d801a8.zip
[PATCH] pcmcia: merge suspend into device model
Merge the suspend and resume methods for 16-bit PCMCIA cards into the device model -- for both runtime power management and suspend to ram/disk. Bugfix in ds.c by Richard Purdie Signed-Off-By: Richard Purdie <rpurdie@rpsys.net> Signed-off-by: Dominik Brodowski <linux@dominikbrodowski.net>
Diffstat (limited to 'drivers/pcmcia')
-rw-r--r--drivers/pcmcia/cs.c14
-rw-r--r--drivers/pcmcia/cs_internal.h2
-rw-r--r--drivers/pcmcia/ds.c94
3 files changed, 99 insertions, 11 deletions
diff --git a/drivers/pcmcia/cs.c b/drivers/pcmcia/cs.c
index 7cf09084ef61..83d2753814c2 100644
--- a/drivers/pcmcia/cs.c
+++ b/drivers/pcmcia/cs.c
@@ -780,8 +780,13 @@ int pccard_reset_card(struct pcmcia_socket *skt)
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 (socket_reset(skt) == CS_SUCCESS)
+ if (skt->callback)
+ skt->callback->suspend(skt);
+ if (socket_reset(skt) == CS_SUCCESS) {
send_event(skt, CS_EVENT_CARD_RESET, CS_EVENT_PRI_LOW);
+ if (skt->callback)
+ skt->callback->resume(skt);
+ }
}
ret = CS_SUCCESS;
@@ -812,6 +817,11 @@ int pcmcia_suspend_card(struct pcmcia_socket *skt)
ret = CS_UNSUPPORTED_FUNCTION;
break;
}
+ if (skt->callback) {
+ ret = skt->callback->suspend(skt);
+ if (ret)
+ break;
+ }
ret = socket_suspend(skt);
} while (0);
up(&skt->skt_sem);
@@ -838,6 +848,8 @@ int pcmcia_resume_card(struct pcmcia_socket *skt)
break;
}
ret = socket_resume(skt);
+ if (!ret && skt->callback)
+ skt->callback->resume(skt);
} while (0);
up(&skt->skt_sem);
diff --git a/drivers/pcmcia/cs_internal.h b/drivers/pcmcia/cs_internal.h
index 634426b78f2c..7b37eba35bf1 100644
--- a/drivers/pcmcia/cs_internal.h
+++ b/drivers/pcmcia/cs_internal.h
@@ -143,6 +143,8 @@ struct pcmcia_callback{
struct module *owner;
int (*event) (struct pcmcia_socket *s, event_t event, int priority);
void (*requery) (struct pcmcia_socket *s);
+ int (*suspend) (struct pcmcia_socket *s);
+ int (*resume) (struct pcmcia_socket *s);
};
int pccard_register_pcmcia(struct pcmcia_socket *s, struct pcmcia_callback *c);
diff --git a/drivers/pcmcia/ds.c b/drivers/pcmcia/ds.c
index a802c65c3534..5223395b246a 100644
--- a/drivers/pcmcia/ds.c
+++ b/drivers/pcmcia/ds.c
@@ -920,6 +920,78 @@ static struct device_attribute pcmcia_dev_attrs[] = {
__ATTR_NULL,
};
+/* PM support, also needed for reset */
+
+static int pcmcia_dev_suspend(struct device * dev, pm_message_t state)
+{
+ struct pcmcia_device *p_dev = to_pcmcia_dev(dev);
+ struct pcmcia_driver *p_drv = NULL;
+
+ if (dev->driver)
+ p_drv = to_pcmcia_drv(dev->driver);
+
+ if (p_drv && p_drv->suspend)
+ return p_drv->suspend(p_dev);
+
+ return 0;
+}
+
+
+static int pcmcia_dev_resume(struct device * dev)
+{
+ struct pcmcia_device *p_dev = to_pcmcia_dev(dev);
+ struct pcmcia_driver *p_drv = NULL;
+
+ if (dev->driver)
+ p_drv = to_pcmcia_drv(dev->driver);
+
+ if (p_drv && p_drv->resume)
+ return p_drv->resume(p_dev);
+
+ return 0;
+}
+
+
+static int pcmcia_bus_suspend_callback(struct device *dev, void * _data)
+{
+ struct pcmcia_socket *skt = _data;
+ struct pcmcia_device *p_dev = to_pcmcia_dev(dev);
+
+ if (p_dev->socket != skt)
+ return 0;
+
+ return dpm_runtime_suspend(dev, PMSG_SUSPEND);
+}
+
+static int pcmcia_bus_resume_callback(struct device *dev, void * _data)
+{
+ struct pcmcia_socket *skt = _data;
+ struct pcmcia_device *p_dev = to_pcmcia_dev(dev);
+
+ if (p_dev->socket != skt)
+ return 0;
+
+ dpm_runtime_resume(dev);
+
+ return 0;
+}
+
+static int pcmcia_bus_resume(struct pcmcia_socket *skt)
+{
+ bus_for_each_dev(&pcmcia_bus_type, NULL, skt, pcmcia_bus_resume_callback);
+ return 0;
+}
+
+static int pcmcia_bus_suspend(struct pcmcia_socket *skt)
+{
+ if (bus_for_each_dev(&pcmcia_bus_type, NULL, skt,
+ pcmcia_bus_suspend_callback)) {
+ pcmcia_bus_resume(skt);
+ return -EIO;
+ }
+ return 0;
+}
+
/*======================================================================
@@ -951,16 +1023,6 @@ static int send_event_callback(struct device *dev, void * _data)
if (p_dev->state & (CLIENT_UNBOUND|CLIENT_STALE))
return 0;
- if ((data->event == CS_EVENT_PM_SUSPEND) ||
- (data->event == CS_EVENT_RESET_PHYSICAL)) {
- if (p_drv->suspend)
- return p_drv->suspend(p_dev);
- } else if ((data->event == CS_EVENT_PM_RESUME) ||
- (data->event == CS_EVENT_CARD_RESET)) {
- if (p_drv->resume)
- return p_drv->resume(p_dev);
- }
-
if (p_drv->event)
return p_drv->event(data->event, data->priority,
&p_dev->event_callback_args);
@@ -1012,6 +1074,13 @@ static int ds_event(struct pcmcia_socket *skt, event_t event, int priority)
ret = send_event(skt, event, priority);
break;
+ case CS_EVENT_PM_SUSPEND:
+ case CS_EVENT_PM_RESUME:
+ case CS_EVENT_RESET_PHYSICAL:
+ case CS_EVENT_CARD_RESET:
+ handle_event(skt, event);
+ break;
+
default:
handle_event(skt, event);
send_event(skt, event, priority);
@@ -1166,10 +1235,13 @@ int pcmcia_deregister_client(struct pcmcia_device *p_dev)
} /* deregister_client */
EXPORT_SYMBOL(pcmcia_deregister_client);
+
static struct pcmcia_callback pcmcia_bus_callback = {
.owner = THIS_MODULE,
.event = ds_event,
.requery = pcmcia_bus_rescan,
+ .suspend = pcmcia_bus_suspend,
+ .resume = pcmcia_bus_resume,
};
static int __devinit pcmcia_bus_add_socket(struct class_device *class_dev,
@@ -1238,6 +1310,8 @@ struct bus_type pcmcia_bus_type = {
.uevent = pcmcia_bus_uevent,
.match = pcmcia_bus_match,
.dev_attrs = pcmcia_dev_attrs,
+ .suspend = pcmcia_dev_suspend,
+ .resume = pcmcia_dev_resume,
};