For PCI drivers you will need to add two new functions to _struct pci_driver_ - one will be called at suspend time, the other at resume time (see examples below). All Power Management functions should be surrounded by #ifdef CONFIG_PM and #endif.
#ifdef CONFIG_PM
static int rhine_suspend (struct pci_dev *pdev, u32 state)
{
...
}
static int rhine_resume (struct pci_dev *pdev)
{
...
}
#endif
static struct pci_driver rhine_driver = {
.name = DRV_NAME,
.id_table = rhine_pci_tbl,
.probe = rhine_init_one,
.remove = __devexit_p(rhine_remove_one),
+#ifdef CONFIG_PM
+ .suspend = rhine_suspend,
+ .resume = rhine_resume,
+#endif /* CONFIG_PM */
.driver = {
.shutdown = rhine_shutdown,
}
The suspend function should save PCI state into a private data structure of the driver and turn on state 3 (D3). You can also shut down hardware here or stop whatever work it might do.
pci_save_state (pdev, rp->pci_state);
pci_set_power_state (pdev, 3);
The resume function should reverse that.
pci_set_power_state (pdev, 0);
pci_restore_state (pdev, rp->pci_state);
rp is in this case private data kept in pci_get_drvdata(pdev)->priv.
The resume function should also restore hardware state - set hardware specific settings, etc. In this via-rhine example, this means reinitializing the whole card:
static int rhine_resume (struct pci_dev *pdev)
{
struct net_device *dev = pci_get_drvdata (pdev);
struct rhine_private *rp = dev->priv;
unsigned long flags;
if (!netif_running (dev))
return 0;
/* restore power state 0 */
pci_set_power_state (pdev, 0);
/* restore PCI data */
pci_restore_state (pdev, rp->pci_state);
spin_lock_irqsave (&rp->lock, flags);
rhine_power_init(dev); /* set power registers in via-rhine card */
rhine_hw_init(dev, rp->pioaddr); /* reinitialize hardware */
/* free and allocate ring buffers */
free_tbufs(dev);
free_rbufs(dev);
alloc_tbufs(dev);
alloc_rbufs(dev);
/* init via-rhine hardware registers */
init_registers(dev);
spin_unlock_irqrestore (&rp->lock, flags);
/* attach device again */
netif_device_attach (dev);
return 0;
}
#endif /* CONFIG_PM */
Fixing AGP drivers is much easier. Usually agp drivers already have functions that setup hardware properly so the only thing you need to do is to call them at resume time. For example, the via-agp driver:
static int agp_via_resume(struct pci_dev *pdev)
{
struct agp_bridge_data *bridge = pci_get_drvdata(pdev);
/* set power state 0 and restore PCI space */
pci_set_power_state (pdev, 0);
pci_restore_state(pdev, pdev->saved_config_space);
/* reconfigure AGP hardware again */
if (bridge->driver == &via_agp3_driver)
return via_configure_agp3();
else if (bridge->driver == &via_driver)
return via_configure();
return 0;
}
When trying to fix drivers, take a look at other drivers that already have suspend/resume support. You can learn almost everything from them.




