Commit 8f719499 authored by Benjamin Herrenschmidt's avatar Benjamin Herrenschmidt Committed by Linus Torvalds

[PATCH] ppc32: uninorth-agp suspend support

This patch adds suspend/resume support to the Apple UniNorth AGP bridge to
make sure AGP is properly disabled when the machine goes to sleep.  Without
this, the r300 based laptops will fail to wakeup from sleep when using the
new experimental r300 DRI driver.  It should also improve reliablility in
general with other chips.

Unfortunately, uninorth-agp is just a "sibling" of the video chip on the
PCI bus, and thus ends up beeing called either before the video chip
suspend routine, or after, depending on the HW layout or other random
things.

To make sure the device side of AGP is always disabled first and that we
never touch the device after having put it into D2 state (which can be
deadly), I also need the separate patch to radeonfb and aty128fb which will
make them disabled their own side if not already done by the bridge driver.
Signed-off-by: default avatarBenjamin Herrenschmidt <benh@kernel.crashing.org>
Signed-off-by: default avatarPaul Mackerras <paulus@samba.org>
Signed-off-by: default avatarAndrew Morton <akpm@osdl.org>
Signed-off-by: default avatarLinus Torvalds <torvalds@osdl.org>
parent 58a40ef7
...@@ -6,6 +6,7 @@ ...@@ -6,6 +6,7 @@
#include <linux/init.h> #include <linux/init.h>
#include <linux/pagemap.h> #include <linux/pagemap.h>
#include <linux/agp_backend.h> #include <linux/agp_backend.h>
#include <linux/delay.h>
#include <asm/uninorth.h> #include <asm/uninorth.h>
#include <asm/pci-bridge.h> #include <asm/pci-bridge.h>
#include "agp.h" #include "agp.h"
...@@ -51,6 +52,11 @@ static void uninorth_tlbflush(struct agp_memory *mem) ...@@ -51,6 +52,11 @@ static void uninorth_tlbflush(struct agp_memory *mem)
static void uninorth_cleanup(void) static void uninorth_cleanup(void)
{ {
u32 tmp;
pci_read_config_dword(agp_bridge->dev, UNI_N_CFG_GART_CTRL, &tmp);
if (!(tmp & UNI_N_CFG_GART_ENABLE))
return;
pci_write_config_dword(agp_bridge->dev, UNI_N_CFG_GART_CTRL, pci_write_config_dword(agp_bridge->dev, UNI_N_CFG_GART_CTRL,
UNI_N_CFG_GART_ENABLE | UNI_N_CFG_GART_INVAL); UNI_N_CFG_GART_ENABLE | UNI_N_CFG_GART_INVAL);
pci_write_config_dword(agp_bridge->dev, UNI_N_CFG_GART_CTRL, pci_write_config_dword(agp_bridge->dev, UNI_N_CFG_GART_CTRL,
...@@ -155,6 +161,62 @@ static void uninorth_agp_enable(struct agp_bridge_data *bridge, u32 mode) ...@@ -155,6 +161,62 @@ static void uninorth_agp_enable(struct agp_bridge_data *bridge, u32 mode)
uninorth_tlbflush(NULL); uninorth_tlbflush(NULL);
} }
#ifdef CONFIG_PM
static int agp_uninorth_suspend(struct pci_dev *pdev, pm_message_t state)
{
u32 cmd;
u8 agp;
struct pci_dev *device = NULL;
if (state != PMSG_SUSPEND)
return 0;
/* turn off AGP on the video chip, if it was enabled */
for_each_pci_dev(device) {
/* Don't touch the bridge yet, device first */
if (device == pdev)
continue;
/* Only deal with devices on the same bus here, no Mac has a P2P
* bridge on the AGP port, and mucking around the entire PCI
* tree is source of problems on some machines because of a bug
* in some versions of pci_find_capability() when hitting a dead
* device
*/
if (device->bus != pdev->bus)
continue;
agp = pci_find_capability(device, PCI_CAP_ID_AGP);
if (!agp)
continue;
pci_read_config_dword(device, agp + PCI_AGP_COMMAND, &cmd);
if (!(cmd & PCI_AGP_COMMAND_AGP))
continue;
printk("uninorth-agp: disabling AGP on device %s\n",
pci_name(device));
cmd &= ~PCI_AGP_COMMAND_AGP;
pci_write_config_dword(device, agp + PCI_AGP_COMMAND, cmd);
}
/* turn off AGP on the bridge */
agp = pci_find_capability(pdev, PCI_CAP_ID_AGP);
pci_read_config_dword(pdev, agp + PCI_AGP_COMMAND, &cmd);
if (cmd & PCI_AGP_COMMAND_AGP) {
printk("uninorth-agp: disabling AGP on bridge %s\n",
pci_name(pdev));
cmd &= ~PCI_AGP_COMMAND_AGP;
pci_write_config_dword(pdev, agp + PCI_AGP_COMMAND, cmd);
}
/* turn off the GART */
uninorth_cleanup();
return 0;
}
static int agp_uninorth_resume(struct pci_dev *pdev)
{
return 0;
}
#endif
static int uninorth_create_gatt_table(struct agp_bridge_data *bridge) static int uninorth_create_gatt_table(struct agp_bridge_data *bridge)
{ {
char *table; char *table;
...@@ -369,6 +431,10 @@ static struct pci_driver agp_uninorth_pci_driver = { ...@@ -369,6 +431,10 @@ static struct pci_driver agp_uninorth_pci_driver = {
.id_table = agp_uninorth_pci_table, .id_table = agp_uninorth_pci_table,
.probe = agp_uninorth_probe, .probe = agp_uninorth_probe,
.remove = agp_uninorth_remove, .remove = agp_uninorth_remove,
#ifdef CONFIG_PM
.suspend = agp_uninorth_suspend,
.resume = agp_uninorth_resume,
#endif
}; };
static int __init agp_uninorth_init(void) static int __init agp_uninorth_init(void)
......
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