PM / yenta: Fix cardbus suspend/resume regression
Since 2.6.29 the PCI PM core have been restoring the standard
configuration registers of PCI devices in the early phase of
resume. In particular, PCI devices without drivers have been handled
this way since commit 355a72d75b
(PCI: Rework default handling of suspend and resume). Unfortunately,
this leads to post-resume problems with CardBus devices which cannot
be accessed in the early phase of resume, because the sockets they
are on have not been woken up yet at that point.
To solve this problem, move the yenta socket resume to the early
phase of resume and, analogously, move the suspend of it to the late
phase of suspend. Additionally, remove some unnecessary PCI code
from the yenta socket's resume routine.
Fixes http://bugzilla.kernel.org/show_bug.cgi?id=13092, which is a
post-2.6.28 regression.
Signed-off-by: Rafael J. Wysocki <rjw@sisk.pl>
Reported-by: Florian <fs-kernelbugzilla@spline.de>
Cc: stable@kernel.org
This commit is contained in:
parent
827b4649d4
commit
0c570cdeb8
|
@ -1225,60 +1225,71 @@ static int __devinit yenta_probe (struct pci_dev *dev, const struct pci_device_i
|
|||
}
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
static int yenta_dev_suspend (struct pci_dev *dev, pm_message_t state)
|
||||
static int yenta_dev_suspend_noirq(struct device *dev)
|
||||
{
|
||||
struct yenta_socket *socket = pci_get_drvdata(dev);
|
||||
struct pci_dev *pdev = to_pci_dev(dev);
|
||||
struct yenta_socket *socket = pci_get_drvdata(pdev);
|
||||
int ret;
|
||||
|
||||
ret = pcmcia_socket_dev_suspend(&dev->dev);
|
||||
ret = pcmcia_socket_dev_suspend(dev);
|
||||
|
||||
if (socket) {
|
||||
if (socket->type && socket->type->save_state)
|
||||
socket->type->save_state(socket);
|
||||
if (!socket)
|
||||
return ret;
|
||||
|
||||
/* FIXME: pci_save_state needs to have a better interface */
|
||||
pci_save_state(dev);
|
||||
pci_read_config_dword(dev, 16*4, &socket->saved_state[0]);
|
||||
pci_read_config_dword(dev, 17*4, &socket->saved_state[1]);
|
||||
pci_disable_device(dev);
|
||||
if (socket->type && socket->type->save_state)
|
||||
socket->type->save_state(socket);
|
||||
|
||||
/*
|
||||
* Some laptops (IBM T22) do not like us putting the Cardbus
|
||||
* bridge into D3. At a guess, some other laptop will
|
||||
* probably require this, so leave it commented out for now.
|
||||
*/
|
||||
/* pci_set_power_state(dev, 3); */
|
||||
}
|
||||
pci_save_state(pdev);
|
||||
pci_read_config_dword(pdev, 16*4, &socket->saved_state[0]);
|
||||
pci_read_config_dword(pdev, 17*4, &socket->saved_state[1]);
|
||||
pci_disable_device(pdev);
|
||||
|
||||
/*
|
||||
* Some laptops (IBM T22) do not like us putting the Cardbus
|
||||
* bridge into D3. At a guess, some other laptop will
|
||||
* probably require this, so leave it commented out for now.
|
||||
*/
|
||||
/* pci_set_power_state(dev, 3); */
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
static int yenta_dev_resume (struct pci_dev *dev)
|
||||
static int yenta_dev_resume_noirq(struct device *dev)
|
||||
{
|
||||
struct yenta_socket *socket = pci_get_drvdata(dev);
|
||||
struct pci_dev *pdev = to_pci_dev(dev);
|
||||
struct yenta_socket *socket = pci_get_drvdata(pdev);
|
||||
int ret;
|
||||
|
||||
if (socket) {
|
||||
int rc;
|
||||
if (!socket)
|
||||
return 0;
|
||||
|
||||
pci_set_power_state(dev, 0);
|
||||
/* FIXME: pci_restore_state needs to have a better interface */
|
||||
pci_restore_state(dev);
|
||||
pci_write_config_dword(dev, 16*4, socket->saved_state[0]);
|
||||
pci_write_config_dword(dev, 17*4, socket->saved_state[1]);
|
||||
pci_write_config_dword(pdev, 16*4, socket->saved_state[0]);
|
||||
pci_write_config_dword(pdev, 17*4, socket->saved_state[1]);
|
||||
|
||||
rc = pci_enable_device(dev);
|
||||
if (rc)
|
||||
return rc;
|
||||
ret = pci_enable_device(pdev);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
pci_set_master(dev);
|
||||
pci_set_master(pdev);
|
||||
|
||||
if (socket->type && socket->type->restore_state)
|
||||
socket->type->restore_state(socket);
|
||||
}
|
||||
if (socket->type && socket->type->restore_state)
|
||||
socket->type->restore_state(socket);
|
||||
|
||||
return pcmcia_socket_dev_resume(&dev->dev);
|
||||
return pcmcia_socket_dev_resume(dev);
|
||||
}
|
||||
|
||||
static struct dev_pm_ops yenta_pm_ops = {
|
||||
.suspend_noirq = yenta_dev_suspend_noirq,
|
||||
.resume_noirq = yenta_dev_resume_noirq,
|
||||
.freeze_noirq = yenta_dev_suspend_noirq,
|
||||
.thaw_noirq = yenta_dev_resume_noirq,
|
||||
.poweroff_noirq = yenta_dev_suspend_noirq,
|
||||
.restore_noirq = yenta_dev_resume_noirq,
|
||||
};
|
||||
|
||||
#define YENTA_PM_OPS (¥ta_pm_ops)
|
||||
#else
|
||||
#define YENTA_PM_OPS NULL
|
||||
#endif
|
||||
|
||||
#define CB_ID(vend,dev,type) \
|
||||
|
@ -1376,10 +1387,7 @@ static struct pci_driver yenta_cardbus_driver = {
|
|||
.id_table = yenta_table,
|
||||
.probe = yenta_probe,
|
||||
.remove = __devexit_p(yenta_close),
|
||||
#ifdef CONFIG_PM
|
||||
.suspend = yenta_dev_suspend,
|
||||
.resume = yenta_dev_resume,
|
||||
#endif
|
||||
.driver.pm = YENTA_PM_OPS,
|
||||
};
|
||||
|
||||
|
||||
|
|
Loading…
Reference in New Issue