aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorRafael J. Wysocki <rjw@sisk.pl>2008-02-23 13:13:25 -0500
committerLinus Torvalds <torvalds@woody.linux-foundation.org>2008-02-23 13:40:04 -0500
commit3a2d5b700132f35401f1d9e22fe3c2cab02c2549 (patch)
treead991428c41aee92a5f78b06bf73430af0e6f7ae
parent39273b58a409cd6d65c9732bdca00bacd1626672 (diff)
PM: Introduce PM_EVENT_HIBERNATE callback state
During the last step of hibernation in the "platform" mode (with the help of ACPI) we use the suspend code, including the devices' ->suspend() methods, to prepare the system for entering the ACPI S4 system sleep state. But at least for some devices the operations performed by the ->suspend() callback in that case must be different from its operations during regular suspend. For this reason, introduce the new PM event type PM_EVENT_HIBERNATE and pass it to the device drivers' ->suspend() methods during the last phase of hibernation, so that they can distinguish this case and handle it as appropriate. Modify the drivers that handle PM_EVENT_SUSPEND in a special way and need to handle PM_EVENT_HIBERNATE in the same way. These changes are necessary to fix a hibernation regression related to the i915 driver (ref. http://lkml.org/lkml/2008/2/22/488). Signed-off-by: Rafael J. Wysocki <rjw@sisk.pl> Acked-by: Pavel Machek <pavel@ucw.cz> Tested-by: Jeff Chua <jeff.chua.linux@gmail.com> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
-rw-r--r--Documentation/power/devices.txt13
-rw-r--r--drivers/ata/ahci.c2
-rw-r--r--drivers/ata/ata_piix.c2
-rw-r--r--drivers/ata/libata-core.c2
-rw-r--r--drivers/ide/ppc/pmac.c4
-rw-r--r--drivers/macintosh/mediabay.c3
-rw-r--r--drivers/pci/pci.c1
-rw-r--r--drivers/scsi/aic7xxx/aic79xx_osm_pci.c2
-rw-r--r--drivers/scsi/aic7xxx/aic7xxx_osm_pci.c2
-rw-r--r--drivers/scsi/mesh.c1
-rw-r--r--drivers/scsi/sd.c3
-rw-r--r--drivers/usb/host/sl811-hcd.c1
-rw-r--r--drivers/usb/host/u132-hcd.c11
-rw-r--r--drivers/video/chipsfb.c2
-rw-r--r--drivers/video/nvidia/nvidia.c2
-rw-r--r--include/linux/pm.h9
-rw-r--r--kernel/power/disk.c4
-rw-r--r--net/rfkill/rfkill.c2
18 files changed, 42 insertions, 24 deletions
diff --git a/Documentation/power/devices.txt b/Documentation/power/devices.txt
index c53d26361919..461e4f1dbec4 100644
--- a/Documentation/power/devices.txt
+++ b/Documentation/power/devices.txt
@@ -310,9 +310,12 @@ used with suspend-to-disk:
310 PM_EVENT_SUSPEND -- quiesce the driver and put hardware into a low-power 310 PM_EVENT_SUSPEND -- quiesce the driver and put hardware into a low-power
311 state. When used with system sleep states like "suspend-to-RAM" or 311 state. When used with system sleep states like "suspend-to-RAM" or
312 "standby", the upcoming resume() call will often be able to rely on 312 "standby", the upcoming resume() call will often be able to rely on
313 state kept in hardware, or issue system wakeup events. When used 313 state kept in hardware, or issue system wakeup events.
314 instead with suspend-to-disk, few devices support this capability; 314
315 most are completely powered off. 315 PM_EVENT_HIBERNATE -- Put hardware into a low-power state and enable wakeup
316 events as appropriate. It is only used with hibernation
317 (suspend-to-disk) and few devices are able to wake up the system from
318 this state; most are completely powered off.
316 319
317 PM_EVENT_FREEZE -- quiesce the driver, but don't necessarily change into 320 PM_EVENT_FREEZE -- quiesce the driver, but don't necessarily change into
318 any low power mode. A system snapshot is about to be taken, often 321 any low power mode. A system snapshot is about to be taken, often
@@ -329,8 +332,8 @@ used with suspend-to-disk:
329 wakeup events nor DMA are allowed. 332 wakeup events nor DMA are allowed.
330 333
331To enter "standby" (ACPI S1) or "Suspend to RAM" (STR, ACPI S3) states, or 334To enter "standby" (ACPI S1) or "Suspend to RAM" (STR, ACPI S3) states, or
332the similarly named APM states, only PM_EVENT_SUSPEND is used; for "Suspend 335the similarly named APM states, only PM_EVENT_SUSPEND is used; the other event
333to Disk" (STD, hibernate, ACPI S4), all of those event codes are used. 336codes are used for hibernation ("Suspend to Disk", STD, ACPI S4).
334 337
335There's also PM_EVENT_ON, a value which never appears as a suspend event 338There's also PM_EVENT_ON, a value which never appears as a suspend event
336but is sometimes used to record the "not suspended" device state. 339but is sometimes used to record the "not suspended" device state.
diff --git a/drivers/ata/ahci.c b/drivers/ata/ahci.c
index 3c06e457b4dc..6dd12f7019a0 100644
--- a/drivers/ata/ahci.c
+++ b/drivers/ata/ahci.c
@@ -1932,7 +1932,7 @@ static int ahci_pci_device_suspend(struct pci_dev *pdev, pm_message_t mesg)
1932 void __iomem *mmio = host->iomap[AHCI_PCI_BAR]; 1932 void __iomem *mmio = host->iomap[AHCI_PCI_BAR];
1933 u32 ctl; 1933 u32 ctl;
1934 1934
1935 if (mesg.event == PM_EVENT_SUSPEND) { 1935 if (mesg.event & PM_EVENT_SLEEP) {
1936 /* AHCI spec rev1.1 section 8.3.3: 1936 /* AHCI spec rev1.1 section 8.3.3:
1937 * Software must disable interrupts prior to requesting a 1937 * Software must disable interrupts prior to requesting a
1938 * transition of the HBA to D3 state. 1938 * transition of the HBA to D3 state.
diff --git a/drivers/ata/ata_piix.c b/drivers/ata/ata_piix.c
index 752e7d2f3b2f..fae8404254c0 100644
--- a/drivers/ata/ata_piix.c
+++ b/drivers/ata/ata_piix.c
@@ -1339,7 +1339,7 @@ static int piix_pci_device_suspend(struct pci_dev *pdev, pm_message_t mesg)
1339 * cycles and power trying to do something to the sleeping 1339 * cycles and power trying to do something to the sleeping
1340 * beauty. 1340 * beauty.
1341 */ 1341 */
1342 if (piix_broken_suspend() && mesg.event == PM_EVENT_SUSPEND) { 1342 if (piix_broken_suspend() && (mesg.event & PM_EVENT_SLEEP)) {
1343 pci_save_state(pdev); 1343 pci_save_state(pdev);
1344 1344
1345 /* mark its power state as "unknown", since we don't 1345 /* mark its power state as "unknown", since we don't
diff --git a/drivers/ata/libata-core.c b/drivers/ata/libata-core.c
index 60d1bb556973..4cf8662df99e 100644
--- a/drivers/ata/libata-core.c
+++ b/drivers/ata/libata-core.c
@@ -7368,7 +7368,7 @@ void ata_pci_device_do_suspend(struct pci_dev *pdev, pm_message_t mesg)
7368 pci_save_state(pdev); 7368 pci_save_state(pdev);
7369 pci_disable_device(pdev); 7369 pci_disable_device(pdev);
7370 7370
7371 if (mesg.event == PM_EVENT_SUSPEND) 7371 if (mesg.event & PM_EVENT_SLEEP)
7372 pci_set_power_state(pdev, PCI_D3hot); 7372 pci_set_power_state(pdev, PCI_D3hot);
7373} 7373}
7374 7374
diff --git a/drivers/ide/ppc/pmac.c b/drivers/ide/ppc/pmac.c
index 12ac3bfb4f9a..78c9eeb85634 100644
--- a/drivers/ide/ppc/pmac.c
+++ b/drivers/ide/ppc/pmac.c
@@ -1254,7 +1254,7 @@ pmac_ide_macio_suspend(struct macio_dev *mdev, pm_message_t mesg)
1254 int rc = 0; 1254 int rc = 0;
1255 1255
1256 if (mesg.event != mdev->ofdev.dev.power.power_state.event 1256 if (mesg.event != mdev->ofdev.dev.power.power_state.event
1257 && mesg.event == PM_EVENT_SUSPEND) { 1257 && (mesg.event & PM_EVENT_SLEEP)) {
1258 rc = pmac_ide_do_suspend(hwif); 1258 rc = pmac_ide_do_suspend(hwif);
1259 if (rc == 0) 1259 if (rc == 0)
1260 mdev->ofdev.dev.power.power_state = mesg; 1260 mdev->ofdev.dev.power.power_state = mesg;
@@ -1364,7 +1364,7 @@ pmac_ide_pci_suspend(struct pci_dev *pdev, pm_message_t mesg)
1364 int rc = 0; 1364 int rc = 0;
1365 1365
1366 if (mesg.event != pdev->dev.power.power_state.event 1366 if (mesg.event != pdev->dev.power.power_state.event
1367 && mesg.event == PM_EVENT_SUSPEND) { 1367 && (mesg.event & PM_EVENT_SLEEP)) {
1368 rc = pmac_ide_do_suspend(hwif); 1368 rc = pmac_ide_do_suspend(hwif);
1369 if (rc == 0) 1369 if (rc == 0)
1370 pdev->dev.power.power_state = mesg; 1370 pdev->dev.power.power_state = mesg;
diff --git a/drivers/macintosh/mediabay.c b/drivers/macintosh/mediabay.c
index 51a112815f46..bd8a1d14b45d 100644
--- a/drivers/macintosh/mediabay.c
+++ b/drivers/macintosh/mediabay.c
@@ -698,7 +698,8 @@ static int media_bay_suspend(struct macio_dev *mdev, pm_message_t state)
698{ 698{
699 struct media_bay_info *bay = macio_get_drvdata(mdev); 699 struct media_bay_info *bay = macio_get_drvdata(mdev);
700 700
701 if (state.event != mdev->ofdev.dev.power.power_state.event && state.event == PM_EVENT_SUSPEND) { 701 if (state.event != mdev->ofdev.dev.power.power_state.event
702 && (state.event & PM_EVENT_SLEEP)) {
702 down(&bay->lock); 703 down(&bay->lock);
703 bay->sleeping = 1; 704 bay->sleeping = 1;
704 set_mb_power(bay, 0); 705 set_mb_power(bay, 0);
diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c
index ae3df46eaabf..183fddaa38b7 100644
--- a/drivers/pci/pci.c
+++ b/drivers/pci/pci.c
@@ -554,6 +554,7 @@ pci_power_t pci_choose_state(struct pci_dev *dev, pm_message_t state)
554 case PM_EVENT_PRETHAW: 554 case PM_EVENT_PRETHAW:
555 /* REVISIT both freeze and pre-thaw "should" use D0 */ 555 /* REVISIT both freeze and pre-thaw "should" use D0 */
556 case PM_EVENT_SUSPEND: 556 case PM_EVENT_SUSPEND:
557 case PM_EVENT_HIBERNATE:
557 return PCI_D3hot; 558 return PCI_D3hot;
558 default: 559 default:
559 printk("Unrecognized suspend event %d\n", state.event); 560 printk("Unrecognized suspend event %d\n", state.event);
diff --git a/drivers/scsi/aic7xxx/aic79xx_osm_pci.c b/drivers/scsi/aic7xxx/aic79xx_osm_pci.c
index 4150c8a8fdc2..dfaaae5e73ae 100644
--- a/drivers/scsi/aic7xxx/aic79xx_osm_pci.c
+++ b/drivers/scsi/aic7xxx/aic79xx_osm_pci.c
@@ -89,7 +89,7 @@ ahd_linux_pci_dev_suspend(struct pci_dev *pdev, pm_message_t mesg)
89 pci_save_state(pdev); 89 pci_save_state(pdev);
90 pci_disable_device(pdev); 90 pci_disable_device(pdev);
91 91
92 if (mesg.event == PM_EVENT_SUSPEND) 92 if (mesg.event & PM_EVENT_SLEEP)
93 pci_set_power_state(pdev, PCI_D3hot); 93 pci_set_power_state(pdev, PCI_D3hot);
94 94
95 return rc; 95 return rc;
diff --git a/drivers/scsi/aic7xxx/aic7xxx_osm_pci.c b/drivers/scsi/aic7xxx/aic7xxx_osm_pci.c
index dd6e21d6f1dd..3d3eaef65fb3 100644
--- a/drivers/scsi/aic7xxx/aic7xxx_osm_pci.c
+++ b/drivers/scsi/aic7xxx/aic7xxx_osm_pci.c
@@ -134,7 +134,7 @@ ahc_linux_pci_dev_suspend(struct pci_dev *pdev, pm_message_t mesg)
134 pci_save_state(pdev); 134 pci_save_state(pdev);
135 pci_disable_device(pdev); 135 pci_disable_device(pdev);
136 136
137 if (mesg.event == PM_EVENT_SUSPEND) 137 if (mesg.event & PM_EVENT_SLEEP)
138 pci_set_power_state(pdev, PCI_D3hot); 138 pci_set_power_state(pdev, PCI_D3hot);
139 139
140 return rc; 140 return rc;
diff --git a/drivers/scsi/mesh.c b/drivers/scsi/mesh.c
index 651d09b08f2a..fd63b06d9ef1 100644
--- a/drivers/scsi/mesh.c
+++ b/drivers/scsi/mesh.c
@@ -1759,6 +1759,7 @@ static int mesh_suspend(struct macio_dev *mdev, pm_message_t mesg)
1759 1759
1760 switch (mesg.event) { 1760 switch (mesg.event) {
1761 case PM_EVENT_SUSPEND: 1761 case PM_EVENT_SUSPEND:
1762 case PM_EVENT_HIBERNATE:
1762 case PM_EVENT_FREEZE: 1763 case PM_EVENT_FREEZE:
1763 break; 1764 break;
1764 default: 1765 default:
diff --git a/drivers/scsi/sd.c b/drivers/scsi/sd.c
index 37df8bbe7f46..7aee64dbfbeb 100644
--- a/drivers/scsi/sd.c
+++ b/drivers/scsi/sd.c
@@ -1835,8 +1835,7 @@ static int sd_suspend(struct device *dev, pm_message_t mesg)
1835 goto done; 1835 goto done;
1836 } 1836 }
1837 1837
1838 if (mesg.event == PM_EVENT_SUSPEND && 1838 if ((mesg.event & PM_EVENT_SLEEP) && sdkp->device->manage_start_stop) {
1839 sdkp->device->manage_start_stop) {
1840 sd_printk(KERN_NOTICE, sdkp, "Stopping disk\n"); 1839 sd_printk(KERN_NOTICE, sdkp, "Stopping disk\n");
1841 ret = sd_start_stop_device(sdkp, 0); 1840 ret = sd_start_stop_device(sdkp, 0);
1842 } 1841 }
diff --git a/drivers/usb/host/sl811-hcd.c b/drivers/usb/host/sl811-hcd.c
index ba370c56172c..59be276ccd9d 100644
--- a/drivers/usb/host/sl811-hcd.c
+++ b/drivers/usb/host/sl811-hcd.c
@@ -1766,6 +1766,7 @@ sl811h_suspend(struct platform_device *dev, pm_message_t state)
1766 retval = sl811h_bus_suspend(hcd); 1766 retval = sl811h_bus_suspend(hcd);
1767 break; 1767 break;
1768 case PM_EVENT_SUSPEND: 1768 case PM_EVENT_SUSPEND:
1769 case PM_EVENT_HIBERNATE:
1769 case PM_EVENT_PRETHAW: /* explicitly discard hw state */ 1770 case PM_EVENT_PRETHAW: /* explicitly discard hw state */
1770 port_power(sl811, 0); 1771 port_power(sl811, 0);
1771 break; 1772 break;
diff --git a/drivers/usb/host/u132-hcd.c b/drivers/usb/host/u132-hcd.c
index ac283b09a63f..6fca06961559 100644
--- a/drivers/usb/host/u132-hcd.c
+++ b/drivers/usb/host/u132-hcd.c
@@ -3214,14 +3214,19 @@ static int u132_suspend(struct platform_device *pdev, pm_message_t state)
3214 return -ESHUTDOWN; 3214 return -ESHUTDOWN;
3215 } else { 3215 } else {
3216 int retval = 0; 3216 int retval = 0;
3217 if (state.event == PM_EVENT_FREEZE) { 3217
3218 switch (state.event) {
3219 case PM_EVENT_FREEZE:
3218 retval = u132_bus_suspend(hcd); 3220 retval = u132_bus_suspend(hcd);
3219 } else if (state.event == PM_EVENT_SUSPEND) { 3221 break;
3222 case PM_EVENT_SUSPEND:
3223 case PM_EVENT_HIBERNATE:
3220 int ports = MAX_U132_PORTS; 3224 int ports = MAX_U132_PORTS;
3221 while (ports-- > 0) { 3225 while (ports-- > 0) {
3222 port_power(u132, ports, 0); 3226 port_power(u132, ports, 0);
3223 } 3227 }
3224 } 3228 break;
3229 }
3225 if (retval == 0) 3230 if (retval == 0)
3226 pdev->dev.power.power_state = state; 3231 pdev->dev.power.power_state = state;
3227 return retval; 3232 return retval;
diff --git a/drivers/video/chipsfb.c b/drivers/video/chipsfb.c
index 6796ba62c3c6..777389c40988 100644
--- a/drivers/video/chipsfb.c
+++ b/drivers/video/chipsfb.c
@@ -459,7 +459,7 @@ static int chipsfb_pci_suspend(struct pci_dev *pdev, pm_message_t state)
459 459
460 if (state.event == pdev->dev.power.power_state.event) 460 if (state.event == pdev->dev.power.power_state.event)
461 return 0; 461 return 0;
462 if (state.event != PM_EVENT_SUSPEND) 462 if (!(state.event & PM_EVENT_SLEEP))
463 goto done; 463 goto done;
464 464
465 acquire_console_sem(); 465 acquire_console_sem();
diff --git a/drivers/video/nvidia/nvidia.c b/drivers/video/nvidia/nvidia.c
index 74517b1b26a6..596652d2831f 100644
--- a/drivers/video/nvidia/nvidia.c
+++ b/drivers/video/nvidia/nvidia.c
@@ -1066,7 +1066,7 @@ static int nvidiafb_suspend(struct pci_dev *dev, pm_message_t mesg)
1066 acquire_console_sem(); 1066 acquire_console_sem();
1067 par->pm_state = mesg.event; 1067 par->pm_state = mesg.event;
1068 1068
1069 if (mesg.event == PM_EVENT_SUSPEND) { 1069 if (mesg.event & PM_EVENT_SLEEP) {
1070 fb_set_suspend(info, 1); 1070 fb_set_suspend(info, 1);
1071 nvidiafb_blank(FB_BLANK_POWERDOWN, info); 1071 nvidiafb_blank(FB_BLANK_POWERDOWN, info);
1072 nvidia_write_regs(par, &par->SavedReg); 1072 nvidia_write_regs(par, &par->SavedReg);
diff --git a/include/linux/pm.h b/include/linux/pm.h
index eccf59ea2a77..015b735811b4 100644
--- a/include/linux/pm.h
+++ b/include/linux/pm.h
@@ -143,6 +143,9 @@ typedef struct pm_message {
143 * the upcoming system state (such as PCI_D3hot), and enable 143 * the upcoming system state (such as PCI_D3hot), and enable
144 * wakeup events as appropriate. 144 * wakeup events as appropriate.
145 * 145 *
146 * HIBERNATE Enter a low power device state appropriate for the hibernation
147 * state (eg. ACPI S4) and enable wakeup events as appropriate.
148 *
146 * FREEZE Quiesce operations so that a consistent image can be saved; 149 * FREEZE Quiesce operations so that a consistent image can be saved;
147 * but do NOT otherwise enter a low power device state, and do 150 * but do NOT otherwise enter a low power device state, and do
148 * NOT emit system wakeup events. 151 * NOT emit system wakeup events.
@@ -166,11 +169,15 @@ typedef struct pm_message {
166#define PM_EVENT_ON 0 169#define PM_EVENT_ON 0
167#define PM_EVENT_FREEZE 1 170#define PM_EVENT_FREEZE 1
168#define PM_EVENT_SUSPEND 2 171#define PM_EVENT_SUSPEND 2
169#define PM_EVENT_PRETHAW 3 172#define PM_EVENT_HIBERNATE 4
173#define PM_EVENT_PRETHAW 8
174
175#define PM_EVENT_SLEEP (PM_EVENT_SUSPEND | PM_EVENT_HIBERNATE)
170 176
171#define PMSG_FREEZE ((struct pm_message){ .event = PM_EVENT_FREEZE, }) 177#define PMSG_FREEZE ((struct pm_message){ .event = PM_EVENT_FREEZE, })
172#define PMSG_PRETHAW ((struct pm_message){ .event = PM_EVENT_PRETHAW, }) 178#define PMSG_PRETHAW ((struct pm_message){ .event = PM_EVENT_PRETHAW, })
173#define PMSG_SUSPEND ((struct pm_message){ .event = PM_EVENT_SUSPEND, }) 179#define PMSG_SUSPEND ((struct pm_message){ .event = PM_EVENT_SUSPEND, })
180#define PMSG_HIBERNATE ((struct pm_message){ .event = PM_EVENT_HIBERNATE, })
174#define PMSG_ON ((struct pm_message){ .event = PM_EVENT_ON, }) 181#define PMSG_ON ((struct pm_message){ .event = PM_EVENT_ON, })
175 182
176struct dev_pm_info { 183struct dev_pm_info {
diff --git a/kernel/power/disk.c b/kernel/power/disk.c
index 859a8e59773a..14a656cdc652 100644
--- a/kernel/power/disk.c
+++ b/kernel/power/disk.c
@@ -391,7 +391,7 @@ int hibernation_platform_enter(void)
391 goto Close; 391 goto Close;
392 392
393 suspend_console(); 393 suspend_console();
394 error = device_suspend(PMSG_SUSPEND); 394 error = device_suspend(PMSG_HIBERNATE);
395 if (error) 395 if (error)
396 goto Resume_console; 396 goto Resume_console;
397 397
@@ -404,7 +404,7 @@ int hibernation_platform_enter(void)
404 goto Finish; 404 goto Finish;
405 405
406 local_irq_disable(); 406 local_irq_disable();
407 error = device_power_down(PMSG_SUSPEND); 407 error = device_power_down(PMSG_HIBERNATE);
408 if (!error) { 408 if (!error) {
409 hibernation_ops->enter(); 409 hibernation_ops->enter();
410 /* We should never get here */ 410 /* We should never get here */
diff --git a/net/rfkill/rfkill.c b/net/rfkill/rfkill.c
index 1a47f5d1be17..140a0a8c6b02 100644
--- a/net/rfkill/rfkill.c
+++ b/net/rfkill/rfkill.c
@@ -232,7 +232,7 @@ static int rfkill_suspend(struct device *dev, pm_message_t state)
232 struct rfkill *rfkill = to_rfkill(dev); 232 struct rfkill *rfkill = to_rfkill(dev);
233 233
234 if (dev->power.power_state.event != state.event) { 234 if (dev->power.power_state.event != state.event) {
235 if (state.event == PM_EVENT_SUSPEND) { 235 if (state.event & PM_EVENT_SLEEP) {
236 mutex_lock(&rfkill->mutex); 236 mutex_lock(&rfkill->mutex);
237 237
238 if (rfkill->state == RFKILL_STATE_ON) 238 if (rfkill->state == RFKILL_STATE_ON)