Commit 9905d1b4 authored by Rafael J. Wysocki's avatar Rafael J. Wysocki

PM / yenta: Split resume into early and late parts (rev. 4)

Commit 0c570cde
(PM / yenta: Fix cardbus suspend/resume regression) caused resume to
fail on systems with two CardBus bridges.  While the exact nature
of the failure is not known at the moment, it can be worked around by
splitting the yenta resume into an early part, executed during the
early phase of resume, that will only resume the socket and power it
up if there was a card in it during suspend, and a late part,
executed during "regular" resume, that will carry out all of the
remaining yenta resume operations.

Fixes http://bugzilla.kernel.org/show_bug.cgi?id=14334, which is a
listed regression from 2.6.31.
Signed-off-by: default avatarRafael J. Wysocki <rjw@sisk.pl>
Acked-by: default avatarDominik Brodowski <linux@dominikbrodowski.net>
Reported-by: default avatarStephen J. Gowdy <gowdy@cern.ch>
Tested-by: default avatarJose Marino <braket@hotmail.com>
parent 1836d959
...@@ -98,10 +98,13 @@ EXPORT_SYMBOL(pcmcia_socket_list_rwsem); ...@@ -98,10 +98,13 @@ EXPORT_SYMBOL(pcmcia_socket_list_rwsem);
* These functions check for the appropriate struct pcmcia_soket arrays, * These functions check for the appropriate struct pcmcia_soket arrays,
* and pass them to the low-level functions pcmcia_{suspend,resume}_socket * 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_resume(struct pcmcia_socket *skt);
static int socket_suspend(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; struct pcmcia_socket *socket;
...@@ -110,29 +113,34 @@ int pcmcia_socket_dev_suspend(struct device *dev) ...@@ -110,29 +113,34 @@ int pcmcia_socket_dev_suspend(struct device *dev)
if (socket->dev.parent != dev) if (socket->dev.parent != dev)
continue; continue;
mutex_lock(&socket->skt_mutex); mutex_lock(&socket->skt_mutex);
socket_suspend(socket); cb(socket);
mutex_unlock(&socket->skt_mutex); mutex_unlock(&socket->skt_mutex);
} }
up_read(&pcmcia_socket_list_rwsem); up_read(&pcmcia_socket_list_rwsem);
}
int pcmcia_socket_dev_suspend(struct device *dev)
{
pcmcia_socket_dev_run(dev, socket_suspend);
return 0; return 0;
} }
EXPORT_SYMBOL(pcmcia_socket_dev_suspend); 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); void pcmcia_socket_dev_late_resume(struct device *dev)
list_for_each_entry(socket, &pcmcia_socket_list, socket_list) { {
if (socket->dev.parent != dev) pcmcia_socket_dev_run(dev, socket_late_resume);
continue; }
mutex_lock(&socket->skt_mutex); EXPORT_SYMBOL(pcmcia_socket_dev_late_resume);
socket_resume(socket);
mutex_unlock(&socket->skt_mutex);
}
up_read(&pcmcia_socket_list_rwsem);
int pcmcia_socket_dev_resume(struct device *dev)
{
pcmcia_socket_dev_run(dev, socket_resume);
return 0; return 0;
} }
EXPORT_SYMBOL(pcmcia_socket_dev_resume); EXPORT_SYMBOL(pcmcia_socket_dev_resume);
...@@ -546,29 +554,24 @@ static int socket_suspend(struct pcmcia_socket *skt) ...@@ -546,29 +554,24 @@ static int socket_suspend(struct pcmcia_socket *skt)
return 0; return 0;
} }
/* static int socket_early_resume(struct pcmcia_socket *skt)
* 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)
{ {
int ret;
if (!(skt->state & SOCKET_SUSPEND))
return -EBUSY;
skt->socket = dead_socket; skt->socket = dead_socket;
skt->ops->init(skt); skt->ops->init(skt);
skt->ops->set_socket(skt, &skt->socket); skt->ops->set_socket(skt, &skt->socket);
if (skt->state & SOCKET_PRESENT)
skt->resume_status = socket_setup(skt, resume_delay);
return 0;
}
static int socket_late_resume(struct pcmcia_socket *skt)
{
if (!(skt->state & SOCKET_PRESENT)) { if (!(skt->state & SOCKET_PRESENT)) {
skt->state &= ~SOCKET_SUSPEND; skt->state &= ~SOCKET_SUSPEND;
return socket_insert(skt); return socket_insert(skt);
} }
ret = socket_setup(skt, resume_delay); if (skt->resume_status == 0) {
if (ret == 0) {
/* /*
* FIXME: need a better check here for cardbus cards. * FIXME: need a better check here for cardbus cards.
*/ */
...@@ -596,6 +599,20 @@ static int socket_resume(struct pcmcia_socket *skt) ...@@ -596,6 +599,20 @@ static int socket_resume(struct pcmcia_socket *skt)
return 0; 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) static void socket_remove(struct pcmcia_socket *skt)
{ {
dev_printk(KERN_NOTICE, &skt->dev, dev_printk(KERN_NOTICE, &skt->dev,
......
...@@ -1275,16 +1275,26 @@ static int yenta_dev_resume_noirq(struct device *dev) ...@@ -1275,16 +1275,26 @@ static int yenta_dev_resume_noirq(struct device *dev)
if (socket->type && socket->type->restore_state) if (socket->type && socket->type->restore_state)
socket->type->restore_state(socket); socket->type->restore_state(socket);
return pcmcia_socket_dev_resume(dev); pcmcia_socket_dev_early_resume(dev);
return 0;
}
static int yenta_dev_resume(struct device *dev)
{
pcmcia_socket_dev_late_resume(dev);
return 0;
} }
static struct dev_pm_ops yenta_pm_ops = { static struct dev_pm_ops yenta_pm_ops = {
.suspend_noirq = yenta_dev_suspend_noirq, .suspend_noirq = yenta_dev_suspend_noirq,
.resume_noirq = yenta_dev_resume_noirq, .resume_noirq = yenta_dev_resume_noirq,
.resume = yenta_dev_resume,
.freeze_noirq = yenta_dev_suspend_noirq, .freeze_noirq = yenta_dev_suspend_noirq,
.thaw_noirq = yenta_dev_resume_noirq, .thaw_noirq = yenta_dev_resume_noirq,
.thaw = yenta_dev_resume,
.poweroff_noirq = yenta_dev_suspend_noirq, .poweroff_noirq = yenta_dev_suspend_noirq,
.restore_noirq = yenta_dev_resume_noirq, .restore_noirq = yenta_dev_resume_noirq,
.restore = yenta_dev_resume,
}; };
#define YENTA_PM_OPS (&yenta_pm_ops) #define YENTA_PM_OPS (&yenta_pm_ops)
......
...@@ -262,6 +262,8 @@ struct pcmcia_socket { ...@@ -262,6 +262,8 @@ struct pcmcia_socket {
struct device dev; struct device dev;
/* data internal to the socket driver */ /* data internal to the socket driver */
void *driver_data; void *driver_data;
/* status of the card during resume from a system sleep state */
int resume_status;
}; };
...@@ -280,6 +282,8 @@ extern struct pccard_resource_ops pccard_nonstatic_ops; ...@@ -280,6 +282,8 @@ extern struct pccard_resource_ops pccard_nonstatic_ops;
/* socket drivers are expected to use these callbacks in their .drv struct */ /* socket drivers are expected to use these callbacks in their .drv struct */
extern int pcmcia_socket_dev_suspend(struct device *dev); extern int pcmcia_socket_dev_suspend(struct device *dev);
extern void pcmcia_socket_dev_early_resume(struct device *dev);
extern void pcmcia_socket_dev_late_resume(struct device *dev);
extern int pcmcia_socket_dev_resume(struct device *dev); extern int pcmcia_socket_dev_resume(struct device *dev);
/* socket drivers use this callback in their IRQ handler */ /* socket drivers use this callback in their IRQ handler */
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment