]> www.pilppa.org Git - linux-2.6-omap-h63xx.git/commitdiff
PM: Introduce PM_EVENT_HIBERNATE callback state
authorRafael J. Wysocki <rjw@sisk.pl>
Sat, 23 Feb 2008 18:13:25 +0000 (19:13 +0100)
committerLinus Torvalds <torvalds@woody.linux-foundation.org>
Sat, 23 Feb 2008 18:40:04 +0000 (10:40 -0800)
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>
18 files changed:
Documentation/power/devices.txt
drivers/ata/ahci.c
drivers/ata/ata_piix.c
drivers/ata/libata-core.c
drivers/ide/ppc/pmac.c
drivers/macintosh/mediabay.c
drivers/pci/pci.c
drivers/scsi/aic7xxx/aic79xx_osm_pci.c
drivers/scsi/aic7xxx/aic7xxx_osm_pci.c
drivers/scsi/mesh.c
drivers/scsi/sd.c
drivers/usb/host/sl811-hcd.c
drivers/usb/host/u132-hcd.c
drivers/video/chipsfb.c
drivers/video/nvidia/nvidia.c
include/linux/pm.h
kernel/power/disk.c
net/rfkill/rfkill.c

index c53d263619194f7e04719e0a7250944b0134d598..461e4f1dbec418156d2fe5809e49e6f3d8e60031 100644 (file)
@@ -310,9 +310,12 @@ used with suspend-to-disk:
     PM_EVENT_SUSPEND -- quiesce the driver and put hardware into a low-power
        state.  When used with system sleep states like "suspend-to-RAM" or
        "standby", the upcoming resume() call will often be able to rely on
     PM_EVENT_SUSPEND -- quiesce the driver and put hardware into a low-power
        state.  When used with system sleep states like "suspend-to-RAM" or
        "standby", the upcoming resume() call will often be able to rely on
-       state kept in hardware, or issue system wakeup events.  When used
-       instead with suspend-to-disk, few devices support this capability;
-       most are completely powered off.
+       state kept in hardware, or issue system wakeup events.
+
+    PM_EVENT_HIBERNATE -- Put hardware into a low-power state and enable wakeup
+       events as appropriate.  It is only used with hibernation
+       (suspend-to-disk) and few devices are able to wake up the system from
+       this state; most are completely powered off.
 
     PM_EVENT_FREEZE -- quiesce the driver, but don't necessarily change into
        any low power mode.  A system snapshot is about to be taken, often
 
     PM_EVENT_FREEZE -- quiesce the driver, but don't necessarily change into
        any low power mode.  A system snapshot is about to be taken, often
@@ -329,8 +332,8 @@ used with suspend-to-disk:
        wakeup events nor DMA are allowed.
 
 To enter "standby" (ACPI S1) or "Suspend to RAM" (STR, ACPI S3) states, or
        wakeup events nor DMA are allowed.
 
 To enter "standby" (ACPI S1) or "Suspend to RAM" (STR, ACPI S3) states, or
-the similarly named APM states, only PM_EVENT_SUSPEND is used; for "Suspend
-to Disk" (STD, hibernate, ACPI S4), all of those event codes are used.
+the similarly named APM states, only PM_EVENT_SUSPEND is used; the other event
+codes are used for hibernation ("Suspend to Disk", STD, ACPI S4).
 
 There's also PM_EVENT_ON, a value which never appears as a suspend event
 but is sometimes used to record the "not suspended" device state.
 
 There's also PM_EVENT_ON, a value which never appears as a suspend event
 but is sometimes used to record the "not suspended" device state.
index 3c06e457b4dc86929949e700292fd3149fff79fd..6dd12f7019a006cefc64a7a5b1b10d3b7a747160 100644 (file)
@@ -1932,7 +1932,7 @@ static int ahci_pci_device_suspend(struct pci_dev *pdev, pm_message_t mesg)
        void __iomem *mmio = host->iomap[AHCI_PCI_BAR];
        u32 ctl;
 
        void __iomem *mmio = host->iomap[AHCI_PCI_BAR];
        u32 ctl;
 
-       if (mesg.event == PM_EVENT_SUSPEND) {
+       if (mesg.event & PM_EVENT_SLEEP) {
                /* AHCI spec rev1.1 section 8.3.3:
                 * Software must disable interrupts prior to requesting a
                 * transition of the HBA to D3 state.
                /* AHCI spec rev1.1 section 8.3.3:
                 * Software must disable interrupts prior to requesting a
                 * transition of the HBA to D3 state.
index 752e7d2f3b2f5e934ce1941b039ae524b118109e..fae8404254c022dcaba09085eedfc9864657fa78 100644 (file)
@@ -1339,7 +1339,7 @@ static int piix_pci_device_suspend(struct pci_dev *pdev, pm_message_t mesg)
         * cycles and power trying to do something to the sleeping
         * beauty.
         */
         * cycles and power trying to do something to the sleeping
         * beauty.
         */
-       if (piix_broken_suspend() && mesg.event == PM_EVENT_SUSPEND) {
+       if (piix_broken_suspend() && (mesg.event & PM_EVENT_SLEEP)) {
                pci_save_state(pdev);
 
                /* mark its power state as "unknown", since we don't
                pci_save_state(pdev);
 
                /* mark its power state as "unknown", since we don't
index 60d1bb556973b95e6133ac3830e36a06b9d45573..4cf8662df99ea875feb85ffa7f743f48423def45 100644 (file)
@@ -7368,7 +7368,7 @@ void ata_pci_device_do_suspend(struct pci_dev *pdev, pm_message_t mesg)
        pci_save_state(pdev);
        pci_disable_device(pdev);
 
        pci_save_state(pdev);
        pci_disable_device(pdev);
 
-       if (mesg.event == PM_EVENT_SUSPEND)
+       if (mesg.event & PM_EVENT_SLEEP)
                pci_set_power_state(pdev, PCI_D3hot);
 }
 
                pci_set_power_state(pdev, PCI_D3hot);
 }
 
index 12ac3bfb4f9ac1d098cae0738521b44442e992b3..78c9eeb85634876a428166668db3bcc65e8c6bb0 100644 (file)
@@ -1254,7 +1254,7 @@ pmac_ide_macio_suspend(struct macio_dev *mdev, pm_message_t mesg)
        int             rc = 0;
 
        if (mesg.event != mdev->ofdev.dev.power.power_state.event
        int             rc = 0;
 
        if (mesg.event != mdev->ofdev.dev.power.power_state.event
-                       && mesg.event == PM_EVENT_SUSPEND) {
+                       && (mesg.event & PM_EVENT_SLEEP)) {
                rc = pmac_ide_do_suspend(hwif);
                if (rc == 0)
                        mdev->ofdev.dev.power.power_state = mesg;
                rc = pmac_ide_do_suspend(hwif);
                if (rc == 0)
                        mdev->ofdev.dev.power.power_state = mesg;
@@ -1364,7 +1364,7 @@ pmac_ide_pci_suspend(struct pci_dev *pdev, pm_message_t mesg)
        int             rc = 0;
        
        if (mesg.event != pdev->dev.power.power_state.event
        int             rc = 0;
        
        if (mesg.event != pdev->dev.power.power_state.event
-                       && mesg.event == PM_EVENT_SUSPEND) {
+                       && (mesg.event & PM_EVENT_SLEEP)) {
                rc = pmac_ide_do_suspend(hwif);
                if (rc == 0)
                        pdev->dev.power.power_state = mesg;
                rc = pmac_ide_do_suspend(hwif);
                if (rc == 0)
                        pdev->dev.power.power_state = mesg;
index 51a112815f461988e5932b3af4c3dd8277cfe070..bd8a1d14b45d1e1273f0271f5b195a9ae7665092 100644 (file)
@@ -698,7 +698,8 @@ static int media_bay_suspend(struct macio_dev *mdev, pm_message_t state)
 {
        struct media_bay_info   *bay = macio_get_drvdata(mdev);
 
 {
        struct media_bay_info   *bay = macio_get_drvdata(mdev);
 
-       if (state.event != mdev->ofdev.dev.power.power_state.event && state.event == PM_EVENT_SUSPEND) {
+       if (state.event != mdev->ofdev.dev.power.power_state.event
+           && (state.event & PM_EVENT_SLEEP)) {
                down(&bay->lock);
                bay->sleeping = 1;
                set_mb_power(bay, 0);
                down(&bay->lock);
                bay->sleeping = 1;
                set_mb_power(bay, 0);
index ae3df46eaabf29db0f1af949e8926f0abbb8ec1a..183fddaa38b74e5854e386c18266b81814eaa5b9 100644 (file)
@@ -554,6 +554,7 @@ pci_power_t pci_choose_state(struct pci_dev *dev, pm_message_t state)
        case PM_EVENT_PRETHAW:
                /* REVISIT both freeze and pre-thaw "should" use D0 */
        case PM_EVENT_SUSPEND:
        case PM_EVENT_PRETHAW:
                /* REVISIT both freeze and pre-thaw "should" use D0 */
        case PM_EVENT_SUSPEND:
+       case PM_EVENT_HIBERNATE:
                return PCI_D3hot;
        default:
                printk("Unrecognized suspend event %d\n", state.event);
                return PCI_D3hot;
        default:
                printk("Unrecognized suspend event %d\n", state.event);
index 4150c8a8fdc2546cdd21224dd0f1dea987ee2416..dfaaae5e73aebdee4a8fa873117f1ce23db4c3f7 100644 (file)
@@ -89,7 +89,7 @@ ahd_linux_pci_dev_suspend(struct pci_dev *pdev, pm_message_t mesg)
        pci_save_state(pdev);
        pci_disable_device(pdev);
 
        pci_save_state(pdev);
        pci_disable_device(pdev);
 
-       if (mesg.event == PM_EVENT_SUSPEND)
+       if (mesg.event & PM_EVENT_SLEEP)
                pci_set_power_state(pdev, PCI_D3hot);
 
        return rc;
                pci_set_power_state(pdev, PCI_D3hot);
 
        return rc;
index dd6e21d6f1dd36ae12ec4b18eebb1e7e53c6ce9b..3d3eaef65fb3e6ded30a548eb7e1d25eab1e5300 100644 (file)
@@ -134,7 +134,7 @@ ahc_linux_pci_dev_suspend(struct pci_dev *pdev, pm_message_t mesg)
        pci_save_state(pdev);
        pci_disable_device(pdev);
 
        pci_save_state(pdev);
        pci_disable_device(pdev);
 
-       if (mesg.event == PM_EVENT_SUSPEND)
+       if (mesg.event & PM_EVENT_SLEEP)
                pci_set_power_state(pdev, PCI_D3hot);
 
        return rc;
                pci_set_power_state(pdev, PCI_D3hot);
 
        return rc;
index 651d09b08f2a22eb6283da21830736271d1d4cd8..fd63b06d9ef15d64351f235144ba982486cc5c50 100644 (file)
@@ -1759,6 +1759,7 @@ static int mesh_suspend(struct macio_dev *mdev, pm_message_t mesg)
 
        switch (mesg.event) {
        case PM_EVENT_SUSPEND:
 
        switch (mesg.event) {
        case PM_EVENT_SUSPEND:
+       case PM_EVENT_HIBERNATE:
        case PM_EVENT_FREEZE:
                break;
        default:
        case PM_EVENT_FREEZE:
                break;
        default:
index 37df8bbe7f462b3c74228abef8f9ed71a6e6e731..7aee64dbfbeb2391678d555a4fb0e4e999255080 100644 (file)
@@ -1835,8 +1835,7 @@ static int sd_suspend(struct device *dev, pm_message_t mesg)
                        goto done;
        }
 
                        goto done;
        }
 
-       if (mesg.event == PM_EVENT_SUSPEND &&
-           sdkp->device->manage_start_stop) {
+       if ((mesg.event & PM_EVENT_SLEEP) && sdkp->device->manage_start_stop) {
                sd_printk(KERN_NOTICE, sdkp, "Stopping disk\n");
                ret = sd_start_stop_device(sdkp, 0);
        }
                sd_printk(KERN_NOTICE, sdkp, "Stopping disk\n");
                ret = sd_start_stop_device(sdkp, 0);
        }
index ba370c56172c372d7b435669a9b6792a0ac3f83c..59be276ccd9dc112b883e8954734d74936e78f14 100644 (file)
@@ -1766,6 +1766,7 @@ sl811h_suspend(struct platform_device *dev, pm_message_t state)
                retval = sl811h_bus_suspend(hcd);
                break;
        case PM_EVENT_SUSPEND:
                retval = sl811h_bus_suspend(hcd);
                break;
        case PM_EVENT_SUSPEND:
+       case PM_EVENT_HIBERNATE:
        case PM_EVENT_PRETHAW:          /* explicitly discard hw state */
                port_power(sl811, 0);
                break;
        case PM_EVENT_PRETHAW:          /* explicitly discard hw state */
                port_power(sl811, 0);
                break;
index ac283b09a63f873c7d362f0305bdc957617a66c8..6fca0696155930dd5f0ddea7b4401f960dd84f33 100644 (file)
@@ -3214,14 +3214,19 @@ static int u132_suspend(struct platform_device *pdev, pm_message_t state)
                 return -ESHUTDOWN;
         } else {
                 int retval = 0;
                 return -ESHUTDOWN;
         } else {
                 int retval = 0;
-                if (state.event == PM_EVENT_FREEZE) {
+
+               switch (state.event) {
+               case PM_EVENT_FREEZE:
                         retval = u132_bus_suspend(hcd);
                         retval = u132_bus_suspend(hcd);
-                } else if (state.event == PM_EVENT_SUSPEND) {
+                       break;
+               case PM_EVENT_SUSPEND:
+               case PM_EVENT_HIBERNATE:
                         int ports = MAX_U132_PORTS;
                         while (ports-- > 0) {
                                 port_power(u132, ports, 0);
                         }
                         int ports = MAX_U132_PORTS;
                         while (ports-- > 0) {
                                 port_power(u132, ports, 0);
                         }
-                }
+                       break;
+               }
                 if (retval == 0)
                         pdev->dev.power.power_state = state;
                 return retval;
                 if (retval == 0)
                         pdev->dev.power.power_state = state;
                 return retval;
index 6796ba62c3c6ca849d599e1ff74075168aad6855..777389c40988812c29f5ad31c7c04cc700f6f690 100644 (file)
@@ -459,7 +459,7 @@ static int chipsfb_pci_suspend(struct pci_dev *pdev, pm_message_t state)
 
        if (state.event == pdev->dev.power.power_state.event)
                return 0;
 
        if (state.event == pdev->dev.power.power_state.event)
                return 0;
-       if (state.event != PM_EVENT_SUSPEND)
+       if (!(state.event & PM_EVENT_SLEEP))
                goto done;
 
        acquire_console_sem();
                goto done;
 
        acquire_console_sem();
index 74517b1b26a6d2bfd8c7c79fa018b86df2b20dc3..596652d2831ff0c8849672fca177912f247a915c 100644 (file)
@@ -1066,7 +1066,7 @@ static int nvidiafb_suspend(struct pci_dev *dev, pm_message_t mesg)
        acquire_console_sem();
        par->pm_state = mesg.event;
 
        acquire_console_sem();
        par->pm_state = mesg.event;
 
-       if (mesg.event == PM_EVENT_SUSPEND) {
+       if (mesg.event & PM_EVENT_SLEEP) {
                fb_set_suspend(info, 1);
                nvidiafb_blank(FB_BLANK_POWERDOWN, info);
                nvidia_write_regs(par, &par->SavedReg);
                fb_set_suspend(info, 1);
                nvidiafb_blank(FB_BLANK_POWERDOWN, info);
                nvidia_write_regs(par, &par->SavedReg);
index eccf59ea2a77ba107359fd74288aa6043ff4140a..015b735811b436e87a99ffbc5bb27f4ede1226cc 100644 (file)
@@ -143,6 +143,9 @@ typedef struct pm_message {
  *             the upcoming system state (such as PCI_D3hot), and enable
  *             wakeup events as appropriate.
  *
  *             the upcoming system state (such as PCI_D3hot), and enable
  *             wakeup events as appropriate.
  *
+ * HIBERNATE   Enter a low power device state appropriate for the hibernation
+ *             state (eg. ACPI S4) and enable wakeup events as appropriate.
+ *
  * FREEZE      Quiesce operations so that a consistent image can be saved;
  *             but do NOT otherwise enter a low power device state, and do
  *             NOT emit system wakeup events.
  * FREEZE      Quiesce operations so that a consistent image can be saved;
  *             but do NOT otherwise enter a low power device state, and do
  *             NOT emit system wakeup events.
@@ -166,11 +169,15 @@ typedef struct pm_message {
 #define PM_EVENT_ON 0
 #define PM_EVENT_FREEZE 1
 #define PM_EVENT_SUSPEND 2
 #define PM_EVENT_ON 0
 #define PM_EVENT_FREEZE 1
 #define PM_EVENT_SUSPEND 2
-#define PM_EVENT_PRETHAW 3
+#define PM_EVENT_HIBERNATE 4
+#define PM_EVENT_PRETHAW 8
+
+#define PM_EVENT_SLEEP (PM_EVENT_SUSPEND | PM_EVENT_HIBERNATE)
 
 #define PMSG_FREEZE    ((struct pm_message){ .event = PM_EVENT_FREEZE, })
 #define PMSG_PRETHAW   ((struct pm_message){ .event = PM_EVENT_PRETHAW, })
 #define PMSG_SUSPEND   ((struct pm_message){ .event = PM_EVENT_SUSPEND, })
 
 #define PMSG_FREEZE    ((struct pm_message){ .event = PM_EVENT_FREEZE, })
 #define PMSG_PRETHAW   ((struct pm_message){ .event = PM_EVENT_PRETHAW, })
 #define PMSG_SUSPEND   ((struct pm_message){ .event = PM_EVENT_SUSPEND, })
+#define PMSG_HIBERNATE ((struct pm_message){ .event = PM_EVENT_HIBERNATE, })
 #define PMSG_ON                ((struct pm_message){ .event = PM_EVENT_ON, })
 
 struct dev_pm_info {
 #define PMSG_ON                ((struct pm_message){ .event = PM_EVENT_ON, })
 
 struct dev_pm_info {
index 859a8e59773a3a1822bf68700aac39600ff9a60f..14a656cdc6523bf64d9c5b64c3a628529b56bab5 100644 (file)
@@ -391,7 +391,7 @@ int hibernation_platform_enter(void)
                goto Close;
 
        suspend_console();
                goto Close;
 
        suspend_console();
-       error = device_suspend(PMSG_SUSPEND);
+       error = device_suspend(PMSG_HIBERNATE);
        if (error)
                goto Resume_console;
 
        if (error)
                goto Resume_console;
 
@@ -404,7 +404,7 @@ int hibernation_platform_enter(void)
                goto Finish;
 
        local_irq_disable();
                goto Finish;
 
        local_irq_disable();
-       error = device_power_down(PMSG_SUSPEND);
+       error = device_power_down(PMSG_HIBERNATE);
        if (!error) {
                hibernation_ops->enter();
                /* We should never get here */
        if (!error) {
                hibernation_ops->enter();
                /* We should never get here */
index 1a47f5d1be17c42814d83f705bd375de497c2b80..140a0a8c6b02fe5e5f4f4d0177a947aeb51d1692 100644 (file)
@@ -232,7 +232,7 @@ static int rfkill_suspend(struct device *dev, pm_message_t state)
        struct rfkill *rfkill = to_rfkill(dev);
 
        if (dev->power.power_state.event != state.event) {
        struct rfkill *rfkill = to_rfkill(dev);
 
        if (dev->power.power_state.event != state.event) {
-               if (state.event == PM_EVENT_SUSPEND) {
+               if (state.event & PM_EVENT_SLEEP) {
                        mutex_lock(&rfkill->mutex);
 
                        if (rfkill->state == RFKILL_STATE_ON)
                        mutex_lock(&rfkill->mutex);
 
                        if (rfkill->state == RFKILL_STATE_ON)