linux/drivers/acpi/sleep.c
<<
>>
Prefs
   1/*
   2 * sleep.c - ACPI sleep support.
   3 *
   4 * Copyright (c) 2005 Alexey Starikovskiy <alexey.y.starikovskiy@intel.com>
   5 * Copyright (c) 2004 David Shaohua Li <shaohua.li@intel.com>
   6 * Copyright (c) 2000-2003 Patrick Mochel
   7 * Copyright (c) 2003 Open Source Development Lab
   8 *
   9 * This file is released under the GPLv2.
  10 *
  11 */
  12
  13#include <linux/delay.h>
  14#include <linux/irq.h>
  15#include <linux/dmi.h>
  16#include <linux/device.h>
  17#include <linux/suspend.h>
  18#include <linux/reboot.h>
  19#include <linux/acpi.h>
  20#include <linux/module.h>
  21
  22#include <asm/io.h>
  23
  24#include <acpi/acpi_bus.h>
  25#include <acpi/acpi_drivers.h>
  26
  27#include "internal.h"
  28#include "sleep.h"
  29
  30static u8 sleep_states[ACPI_S_STATE_COUNT];
  31
  32static void acpi_sleep_tts_switch(u32 acpi_state)
  33{
  34        union acpi_object in_arg = { ACPI_TYPE_INTEGER };
  35        struct acpi_object_list arg_list = { 1, &in_arg };
  36        acpi_status status = AE_OK;
  37
  38        in_arg.integer.value = acpi_state;
  39        status = acpi_evaluate_object(NULL, "\\_TTS", &arg_list, NULL);
  40        if (ACPI_FAILURE(status) && status != AE_NOT_FOUND) {
  41                /*
  42                 * OS can't evaluate the _TTS object correctly. Some warning
  43                 * message will be printed. But it won't break anything.
  44                 */
  45                printk(KERN_NOTICE "Failure in evaluating _TTS object\n");
  46        }
  47}
  48
  49static int tts_notify_reboot(struct notifier_block *this,
  50                        unsigned long code, void *x)
  51{
  52        acpi_sleep_tts_switch(ACPI_STATE_S5);
  53        return NOTIFY_DONE;
  54}
  55
  56static struct notifier_block tts_notifier = {
  57        .notifier_call  = tts_notify_reboot,
  58        .next           = NULL,
  59        .priority       = 0,
  60};
  61
  62static int acpi_sleep_prepare(u32 acpi_state)
  63{
  64#ifdef CONFIG_ACPI_SLEEP
  65        /* do we have a wakeup address for S2 and S3? */
  66        if (acpi_state == ACPI_STATE_S3) {
  67                if (!acpi_wakeup_address)
  68                        return -EFAULT;
  69                acpi_set_firmware_waking_vector(acpi_wakeup_address);
  70
  71        }
  72        ACPI_FLUSH_CPU_CACHE();
  73#endif
  74        printk(KERN_INFO PREFIX "Preparing to enter system sleep state S%d\n",
  75                acpi_state);
  76        acpi_enable_wakeup_devices(acpi_state);
  77        acpi_enter_sleep_state_prep(acpi_state);
  78        return 0;
  79}
  80
  81#ifdef CONFIG_ACPI_SLEEP
  82static u32 acpi_target_sleep_state = ACPI_STATE_S0;
  83
  84u32 acpi_target_system_state(void)
  85{
  86        return acpi_target_sleep_state;
  87}
  88
  89static bool pwr_btn_event_pending;
  90
  91/*
  92 * The ACPI specification wants us to save NVS memory regions during hibernation
  93 * and to restore them during the subsequent resume.  Windows does that also for
  94 * suspend to RAM.  However, it is known that this mechanism does not work on
  95 * all machines, so we allow the user to disable it with the help of the
  96 * 'acpi_sleep=nonvs' kernel command line option.
  97 */
  98static bool nvs_nosave;
  99
 100void __init acpi_nvs_nosave(void)
 101{
 102        nvs_nosave = true;
 103}
 104
 105/*
 106 * The ACPI specification wants us to save NVS memory regions during hibernation
 107 * but says nothing about saving NVS during S3.  Not all versions of Windows
 108 * save NVS on S3 suspend either, and it is clear that not all systems need
 109 * NVS to be saved at S3 time.  To improve suspend/resume time, allow the
 110 * user to disable saving NVS on S3 if their system does not require it, but
 111 * continue to save/restore NVS for S4 as specified.
 112 */
 113static bool nvs_nosave_s3;
 114
 115void __init acpi_nvs_nosave_s3(void)
 116{
 117        nvs_nosave_s3 = true;
 118}
 119
 120/*
 121 * ACPI 1.0 wants us to execute _PTS before suspending devices, so we allow the
 122 * user to request that behavior by using the 'acpi_old_suspend_ordering'
 123 * kernel command line option that causes the following variable to be set.
 124 */
 125static bool old_suspend_ordering;
 126
 127void __init acpi_old_suspend_ordering(void)
 128{
 129        old_suspend_ordering = true;
 130}
 131
 132static int __init init_old_suspend_ordering(const struct dmi_system_id *d)
 133{
 134        acpi_old_suspend_ordering();
 135        return 0;
 136}
 137
 138static int __init init_nvs_nosave(const struct dmi_system_id *d)
 139{
 140        acpi_nvs_nosave();
 141        return 0;
 142}
 143
 144static struct dmi_system_id __initdata acpisleep_dmi_table[] = {
 145        {
 146        .callback = init_old_suspend_ordering,
 147        .ident = "Abit KN9 (nForce4 variant)",
 148        .matches = {
 149                DMI_MATCH(DMI_BOARD_VENDOR, "http://www.abit.com.tw/"),
 150                DMI_MATCH(DMI_BOARD_NAME, "KN9 Series(NF-CK804)"),
 151                },
 152        },
 153        {
 154        .callback = init_old_suspend_ordering,
 155        .ident = "HP xw4600 Workstation",
 156        .matches = {
 157                DMI_MATCH(DMI_SYS_VENDOR, "Hewlett-Packard"),
 158                DMI_MATCH(DMI_PRODUCT_NAME, "HP xw4600 Workstation"),
 159                },
 160        },
 161        {
 162        .callback = init_old_suspend_ordering,
 163        .ident = "Asus Pundit P1-AH2 (M2N8L motherboard)",
 164        .matches = {
 165                DMI_MATCH(DMI_BOARD_VENDOR, "ASUSTek Computer INC."),
 166                DMI_MATCH(DMI_BOARD_NAME, "M2N8L"),
 167                },
 168        },
 169        {
 170        .callback = init_old_suspend_ordering,
 171        .ident = "Panasonic CF51-2L",
 172        .matches = {
 173                DMI_MATCH(DMI_BOARD_VENDOR,
 174                                "Matsushita Electric Industrial Co.,Ltd."),
 175                DMI_MATCH(DMI_BOARD_NAME, "CF51-2L"),
 176                },
 177        },
 178        {
 179        .callback = init_nvs_nosave,
 180        .ident = "Sony Vaio VGN-FW41E_H",
 181        .matches = {
 182                DMI_MATCH(DMI_SYS_VENDOR, "Sony Corporation"),
 183                DMI_MATCH(DMI_PRODUCT_NAME, "VGN-FW41E_H"),
 184                },
 185        },
 186        {
 187        .callback = init_nvs_nosave,
 188        .ident = "Sony Vaio VGN-FW21E",
 189        .matches = {
 190                DMI_MATCH(DMI_SYS_VENDOR, "Sony Corporation"),
 191                DMI_MATCH(DMI_PRODUCT_NAME, "VGN-FW21E"),
 192                },
 193        },
 194        {
 195        .callback = init_nvs_nosave,
 196        .ident = "Sony Vaio VPCEB17FX",
 197        .matches = {
 198                DMI_MATCH(DMI_SYS_VENDOR, "Sony Corporation"),
 199                DMI_MATCH(DMI_PRODUCT_NAME, "VPCEB17FX"),
 200                },
 201        },
 202        {
 203        .callback = init_nvs_nosave,
 204        .ident = "Sony Vaio VGN-SR11M",
 205        .matches = {
 206                DMI_MATCH(DMI_SYS_VENDOR, "Sony Corporation"),
 207                DMI_MATCH(DMI_PRODUCT_NAME, "VGN-SR11M"),
 208                },
 209        },
 210        {
 211        .callback = init_nvs_nosave,
 212        .ident = "Everex StepNote Series",
 213        .matches = {
 214                DMI_MATCH(DMI_SYS_VENDOR, "Everex Systems, Inc."),
 215                DMI_MATCH(DMI_PRODUCT_NAME, "Everex StepNote Series"),
 216                },
 217        },
 218        {
 219        .callback = init_nvs_nosave,
 220        .ident = "Sony Vaio VPCEB1Z1E",
 221        .matches = {
 222                DMI_MATCH(DMI_SYS_VENDOR, "Sony Corporation"),
 223                DMI_MATCH(DMI_PRODUCT_NAME, "VPCEB1Z1E"),
 224                },
 225        },
 226        {
 227        .callback = init_nvs_nosave,
 228        .ident = "Sony Vaio VGN-NW130D",
 229        .matches = {
 230                DMI_MATCH(DMI_SYS_VENDOR, "Sony Corporation"),
 231                DMI_MATCH(DMI_PRODUCT_NAME, "VGN-NW130D"),
 232                },
 233        },
 234        {
 235        .callback = init_nvs_nosave,
 236        .ident = "Sony Vaio VPCCW29FX",
 237        .matches = {
 238                DMI_MATCH(DMI_SYS_VENDOR, "Sony Corporation"),
 239                DMI_MATCH(DMI_PRODUCT_NAME, "VPCCW29FX"),
 240                },
 241        },
 242        {
 243        .callback = init_nvs_nosave,
 244        .ident = "Averatec AV1020-ED2",
 245        .matches = {
 246                DMI_MATCH(DMI_SYS_VENDOR, "AVERATEC"),
 247                DMI_MATCH(DMI_PRODUCT_NAME, "1000 Series"),
 248                },
 249        },
 250        {
 251        .callback = init_old_suspend_ordering,
 252        .ident = "Asus A8N-SLI DELUXE",
 253        .matches = {
 254                DMI_MATCH(DMI_BOARD_VENDOR, "ASUSTeK Computer INC."),
 255                DMI_MATCH(DMI_BOARD_NAME, "A8N-SLI DELUXE"),
 256                },
 257        },
 258        {
 259        .callback = init_old_suspend_ordering,
 260        .ident = "Asus A8N-SLI Premium",
 261        .matches = {
 262                DMI_MATCH(DMI_BOARD_VENDOR, "ASUSTeK Computer INC."),
 263                DMI_MATCH(DMI_BOARD_NAME, "A8N-SLI Premium"),
 264                },
 265        },
 266        {
 267        .callback = init_nvs_nosave,
 268        .ident = "Sony Vaio VGN-SR26GN_P",
 269        .matches = {
 270                DMI_MATCH(DMI_SYS_VENDOR, "Sony Corporation"),
 271                DMI_MATCH(DMI_PRODUCT_NAME, "VGN-SR26GN_P"),
 272                },
 273        },
 274        {
 275        .callback = init_nvs_nosave,
 276        .ident = "Sony Vaio VPCEB1S1E",
 277        .matches = {
 278                DMI_MATCH(DMI_SYS_VENDOR, "Sony Corporation"),
 279                DMI_MATCH(DMI_PRODUCT_NAME, "VPCEB1S1E"),
 280                },
 281        },
 282        {
 283        .callback = init_nvs_nosave,
 284        .ident = "Sony Vaio VGN-FW520F",
 285        .matches = {
 286                DMI_MATCH(DMI_SYS_VENDOR, "Sony Corporation"),
 287                DMI_MATCH(DMI_PRODUCT_NAME, "VGN-FW520F"),
 288                },
 289        },
 290        {
 291        .callback = init_nvs_nosave,
 292        .ident = "Asus K54C",
 293        .matches = {
 294                DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK Computer Inc."),
 295                DMI_MATCH(DMI_PRODUCT_NAME, "K54C"),
 296                },
 297        },
 298        {
 299        .callback = init_nvs_nosave,
 300        .ident = "Asus K54HR",
 301        .matches = {
 302                DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK Computer Inc."),
 303                DMI_MATCH(DMI_PRODUCT_NAME, "K54HR"),
 304                },
 305        },
 306        {},
 307};
 308
 309static void acpi_sleep_dmi_check(void)
 310{
 311        dmi_check_system(acpisleep_dmi_table);
 312}
 313
 314/**
 315 * acpi_pm_freeze - Disable the GPEs and suspend EC transactions.
 316 */
 317static int acpi_pm_freeze(void)
 318{
 319        acpi_disable_all_gpes();
 320        acpi_os_wait_events_complete();
 321        acpi_ec_block_transactions();
 322        return 0;
 323}
 324
 325/**
 326 * acpi_pre_suspend - Enable wakeup devices, "freeze" EC and save NVS.
 327 */
 328static int acpi_pm_pre_suspend(void)
 329{
 330        acpi_pm_freeze();
 331        return suspend_nvs_save();
 332}
 333
 334/**
 335 *      __acpi_pm_prepare - Prepare the platform to enter the target state.
 336 *
 337 *      If necessary, set the firmware waking vector and do arch-specific
 338 *      nastiness to get the wakeup code to the waking vector.
 339 */
 340static int __acpi_pm_prepare(void)
 341{
 342        int error = acpi_sleep_prepare(acpi_target_sleep_state);
 343        if (error)
 344                acpi_target_sleep_state = ACPI_STATE_S0;
 345
 346        return error;
 347}
 348
 349/**
 350 *      acpi_pm_prepare - Prepare the platform to enter the target sleep
 351 *              state and disable the GPEs.
 352 */
 353static int acpi_pm_prepare(void)
 354{
 355        int error = __acpi_pm_prepare();
 356        if (!error)
 357                error = acpi_pm_pre_suspend();
 358
 359        return error;
 360}
 361
 362static int find_powerf_dev(struct device *dev, void *data)
 363{
 364        struct acpi_device *device = to_acpi_device(dev);
 365        const char *hid = acpi_device_hid(device);
 366
 367        return !strcmp(hid, ACPI_BUTTON_HID_POWERF);
 368}
 369
 370/**
 371 *      acpi_pm_finish - Instruct the platform to leave a sleep state.
 372 *
 373 *      This is called after we wake back up (or if entering the sleep state
 374 *      failed).
 375 */
 376static void acpi_pm_finish(void)
 377{
 378        struct device *pwr_btn_dev;
 379        u32 acpi_state = acpi_target_sleep_state;
 380
 381        acpi_ec_unblock_transactions();
 382        suspend_nvs_free();
 383
 384        if (acpi_state == ACPI_STATE_S0)
 385                return;
 386
 387        printk(KERN_INFO PREFIX "Waking up from system sleep state S%d\n",
 388                acpi_state);
 389        acpi_disable_wakeup_devices(acpi_state);
 390        acpi_leave_sleep_state(acpi_state);
 391
 392        /* reset firmware waking vector */
 393        acpi_set_firmware_waking_vector((acpi_physical_address) 0);
 394
 395        acpi_target_sleep_state = ACPI_STATE_S0;
 396
 397        /* If we were woken with the fixed power button, provide a small
 398         * hint to userspace in the form of a wakeup event on the fixed power
 399         * button device (if it can be found).
 400         *
 401         * We delay the event generation til now, as the PM layer requires
 402         * timekeeping to be running before we generate events. */
 403        if (!pwr_btn_event_pending)
 404                return;
 405
 406        pwr_btn_event_pending = false;
 407        pwr_btn_dev = bus_find_device(&acpi_bus_type, NULL, NULL,
 408                                      find_powerf_dev);
 409        if (pwr_btn_dev) {
 410                pm_wakeup_event(pwr_btn_dev, 0);
 411                put_device(pwr_btn_dev);
 412        }
 413}
 414
 415/**
 416 *      acpi_pm_end - Finish up suspend sequence.
 417 */
 418static void acpi_pm_end(void)
 419{
 420        /*
 421         * This is necessary in case acpi_pm_finish() is not called during a
 422         * failing transition to a sleep state.
 423         */
 424        acpi_target_sleep_state = ACPI_STATE_S0;
 425        acpi_sleep_tts_switch(acpi_target_sleep_state);
 426}
 427#else /* !CONFIG_ACPI_SLEEP */
 428#define acpi_target_sleep_state ACPI_STATE_S0
 429static inline void acpi_sleep_dmi_check(void) {}
 430#endif /* CONFIG_ACPI_SLEEP */
 431
 432#ifdef CONFIG_SUSPEND
 433static u32 acpi_suspend_states[] = {
 434        [PM_SUSPEND_ON] = ACPI_STATE_S0,
 435        [PM_SUSPEND_STANDBY] = ACPI_STATE_S1,
 436        [PM_SUSPEND_MEM] = ACPI_STATE_S3,
 437        [PM_SUSPEND_MAX] = ACPI_STATE_S5
 438};
 439
 440/**
 441 *      acpi_suspend_begin - Set the target system sleep state to the state
 442 *              associated with given @pm_state, if supported.
 443 */
 444static int acpi_suspend_begin(suspend_state_t pm_state)
 445{
 446        u32 acpi_state = acpi_suspend_states[pm_state];
 447        int error = 0;
 448
 449        error = (nvs_nosave || nvs_nosave_s3) ? 0 : suspend_nvs_alloc();
 450        if (error)
 451                return error;
 452
 453        if (sleep_states[acpi_state]) {
 454                acpi_target_sleep_state = acpi_state;
 455                acpi_sleep_tts_switch(acpi_target_sleep_state);
 456        } else {
 457                printk(KERN_ERR "ACPI does not support this state: %d\n",
 458                        pm_state);
 459                error = -ENOSYS;
 460        }
 461        return error;
 462}
 463
 464/**
 465 *      acpi_suspend_enter - Actually enter a sleep state.
 466 *      @pm_state: ignored
 467 *
 468 *      Flush caches and go to sleep. For STR we have to call arch-specific
 469 *      assembly, which in turn call acpi_enter_sleep_state().
 470 *      It's unfortunate, but it works. Please fix if you're feeling frisky.
 471 */
 472static int acpi_suspend_enter(suspend_state_t pm_state)
 473{
 474        acpi_status status = AE_OK;
 475        u32 acpi_state = acpi_target_sleep_state;
 476        int error;
 477
 478        ACPI_FLUSH_CPU_CACHE();
 479
 480        switch (acpi_state) {
 481        case ACPI_STATE_S1:
 482                barrier();
 483                status = acpi_enter_sleep_state(acpi_state);
 484                break;
 485
 486        case ACPI_STATE_S3:
 487                error = acpi_suspend_lowlevel();
 488                if (error)
 489                        return error;
 490                pr_info(PREFIX "Low-level resume complete\n");
 491                break;
 492        }
 493
 494        /* This violates the spec but is required for bug compatibility. */
 495        acpi_write_bit_register(ACPI_BITREG_SCI_ENABLE, 1);
 496
 497        /* Reprogram control registers */
 498        acpi_leave_sleep_state_prep(acpi_state);
 499
 500        /* ACPI 3.0 specs (P62) says that it's the responsibility
 501         * of the OSPM to clear the status bit [ implying that the
 502         * POWER_BUTTON event should not reach userspace ]
 503         *
 504         * However, we do generate a small hint for userspace in the form of
 505         * a wakeup event. We flag this condition for now and generate the
 506         * event later, as we're currently too early in resume to be able to
 507         * generate wakeup events.
 508         */
 509        if (ACPI_SUCCESS(status) && (acpi_state == ACPI_STATE_S3)) {
 510                acpi_event_status pwr_btn_status;
 511
 512                acpi_get_event_status(ACPI_EVENT_POWER_BUTTON, &pwr_btn_status);
 513
 514                if (pwr_btn_status & ACPI_EVENT_FLAG_SET) {
 515                        acpi_clear_event(ACPI_EVENT_POWER_BUTTON);
 516                        /* Flag for later */
 517                        pwr_btn_event_pending = true;
 518                }
 519        }
 520
 521        /*
 522         * Disable and clear GPE status before interrupt is enabled. Some GPEs
 523         * (like wakeup GPE) haven't handler, this can avoid such GPE misfire.
 524         * acpi_leave_sleep_state will reenable specific GPEs later
 525         */
 526        acpi_disable_all_gpes();
 527        /* Allow EC transactions to happen. */
 528        acpi_ec_unblock_transactions_early();
 529
 530        suspend_nvs_restore();
 531
 532        return ACPI_SUCCESS(status) ? 0 : -EFAULT;
 533}
 534
 535static int acpi_suspend_state_valid(suspend_state_t pm_state)
 536{
 537        u32 acpi_state;
 538
 539        switch (pm_state) {
 540        case PM_SUSPEND_ON:
 541        case PM_SUSPEND_STANDBY:
 542        case PM_SUSPEND_MEM:
 543                acpi_state = acpi_suspend_states[pm_state];
 544
 545                return sleep_states[acpi_state];
 546        default:
 547                return 0;
 548        }
 549}
 550
 551static const struct platform_suspend_ops acpi_suspend_ops = {
 552        .valid = acpi_suspend_state_valid,
 553        .begin = acpi_suspend_begin,
 554        .prepare_late = acpi_pm_prepare,
 555        .enter = acpi_suspend_enter,
 556        .wake = acpi_pm_finish,
 557        .end = acpi_pm_end,
 558};
 559
 560/**
 561 *      acpi_suspend_begin_old - Set the target system sleep state to the
 562 *              state associated with given @pm_state, if supported, and
 563 *              execute the _PTS control method.  This function is used if the
 564 *              pre-ACPI 2.0 suspend ordering has been requested.
 565 */
 566static int acpi_suspend_begin_old(suspend_state_t pm_state)
 567{
 568        int error = acpi_suspend_begin(pm_state);
 569        if (!error)
 570                error = __acpi_pm_prepare();
 571
 572        return error;
 573}
 574
 575/*
 576 * The following callbacks are used if the pre-ACPI 2.0 suspend ordering has
 577 * been requested.
 578 */
 579static const struct platform_suspend_ops acpi_suspend_ops_old = {
 580        .valid = acpi_suspend_state_valid,
 581        .begin = acpi_suspend_begin_old,
 582        .prepare_late = acpi_pm_pre_suspend,
 583        .enter = acpi_suspend_enter,
 584        .wake = acpi_pm_finish,
 585        .end = acpi_pm_end,
 586        .recover = acpi_pm_finish,
 587};
 588#endif /* CONFIG_SUSPEND */
 589
 590#ifdef CONFIG_HIBERNATION
 591static unsigned long s4_hardware_signature;
 592static struct acpi_table_facs *facs;
 593static bool nosigcheck;
 594
 595void __init acpi_no_s4_hw_signature(void)
 596{
 597        nosigcheck = true;
 598}
 599
 600static int acpi_hibernation_begin(void)
 601{
 602        int error;
 603
 604        error = nvs_nosave ? 0 : suspend_nvs_alloc();
 605        if (!error) {
 606                acpi_target_sleep_state = ACPI_STATE_S4;
 607                acpi_sleep_tts_switch(acpi_target_sleep_state);
 608        }
 609
 610        return error;
 611}
 612
 613static int acpi_hibernation_enter(void)
 614{
 615        acpi_status status = AE_OK;
 616
 617        ACPI_FLUSH_CPU_CACHE();
 618
 619        /* This shouldn't return.  If it returns, we have a problem */
 620        status = acpi_enter_sleep_state(ACPI_STATE_S4);
 621        /* Reprogram control registers */
 622        acpi_leave_sleep_state_prep(ACPI_STATE_S4);
 623
 624        return ACPI_SUCCESS(status) ? 0 : -EFAULT;
 625}
 626
 627static void acpi_hibernation_leave(void)
 628{
 629        /*
 630         * If ACPI is not enabled by the BIOS and the boot kernel, we need to
 631         * enable it here.
 632         */
 633        acpi_enable();
 634        /* Reprogram control registers */
 635        acpi_leave_sleep_state_prep(ACPI_STATE_S4);
 636        /* Check the hardware signature */
 637        if (facs && s4_hardware_signature != facs->hardware_signature) {
 638                printk(KERN_EMERG "ACPI: Hardware changed while hibernated, "
 639                        "cannot resume!\n");
 640                panic("ACPI S4 hardware signature mismatch");
 641        }
 642        /* Restore the NVS memory area */
 643        suspend_nvs_restore();
 644        /* Allow EC transactions to happen. */
 645        acpi_ec_unblock_transactions_early();
 646}
 647
 648static void acpi_pm_thaw(void)
 649{
 650        acpi_ec_unblock_transactions();
 651        acpi_enable_all_runtime_gpes();
 652}
 653
 654static const struct platform_hibernation_ops acpi_hibernation_ops = {
 655        .begin = acpi_hibernation_begin,
 656        .end = acpi_pm_end,
 657        .pre_snapshot = acpi_pm_prepare,
 658        .finish = acpi_pm_finish,
 659        .prepare = acpi_pm_prepare,
 660        .enter = acpi_hibernation_enter,
 661        .leave = acpi_hibernation_leave,
 662        .pre_restore = acpi_pm_freeze,
 663        .restore_cleanup = acpi_pm_thaw,
 664};
 665
 666/**
 667 *      acpi_hibernation_begin_old - Set the target system sleep state to
 668 *              ACPI_STATE_S4 and execute the _PTS control method.  This
 669 *              function is used if the pre-ACPI 2.0 suspend ordering has been
 670 *              requested.
 671 */
 672static int acpi_hibernation_begin_old(void)
 673{
 674        int error;
 675        /*
 676         * The _TTS object should always be evaluated before the _PTS object.
 677         * When the old_suspended_ordering is true, the _PTS object is
 678         * evaluated in the acpi_sleep_prepare.
 679         */
 680        acpi_sleep_tts_switch(ACPI_STATE_S4);
 681
 682        error = acpi_sleep_prepare(ACPI_STATE_S4);
 683
 684        if (!error) {
 685                if (!nvs_nosave)
 686                        error = suspend_nvs_alloc();
 687                if (!error)
 688                        acpi_target_sleep_state = ACPI_STATE_S4;
 689        }
 690        return error;
 691}
 692
 693/*
 694 * The following callbacks are used if the pre-ACPI 2.0 suspend ordering has
 695 * been requested.
 696 */
 697static const struct platform_hibernation_ops acpi_hibernation_ops_old = {
 698        .begin = acpi_hibernation_begin_old,
 699        .end = acpi_pm_end,
 700        .pre_snapshot = acpi_pm_pre_suspend,
 701        .prepare = acpi_pm_freeze,
 702        .finish = acpi_pm_finish,
 703        .enter = acpi_hibernation_enter,
 704        .leave = acpi_hibernation_leave,
 705        .pre_restore = acpi_pm_freeze,
 706        .restore_cleanup = acpi_pm_thaw,
 707        .recover = acpi_pm_finish,
 708};
 709#endif /* CONFIG_HIBERNATION */
 710
 711int acpi_suspend(u32 acpi_state)
 712{
 713        suspend_state_t states[] = {
 714                [1] = PM_SUSPEND_STANDBY,
 715                [3] = PM_SUSPEND_MEM,
 716                [5] = PM_SUSPEND_MAX
 717        };
 718
 719        if (acpi_state < 6 && states[acpi_state])
 720                return pm_suspend(states[acpi_state]);
 721        if (acpi_state == 4)
 722                return hibernate();
 723        return -EINVAL;
 724}
 725
 726static void acpi_power_off_prepare(void)
 727{
 728        /* Prepare to power off the system */
 729        acpi_sleep_prepare(ACPI_STATE_S5);
 730        acpi_disable_all_gpes();
 731}
 732
 733static void acpi_power_off(void)
 734{
 735        /* acpi_sleep_prepare(ACPI_STATE_S5) should have already been called */
 736        printk(KERN_DEBUG "%s called\n", __func__);
 737        local_irq_disable();
 738        acpi_enter_sleep_state(ACPI_STATE_S5);
 739}
 740
 741int __init acpi_sleep_init(void)
 742{
 743        acpi_status status;
 744        u8 type_a, type_b;
 745#ifdef CONFIG_SUSPEND
 746        int i = 0;
 747#endif
 748
 749        if (acpi_disabled)
 750                return 0;
 751
 752        acpi_sleep_dmi_check();
 753
 754        sleep_states[ACPI_STATE_S0] = 1;
 755        printk(KERN_INFO PREFIX "(supports S0");
 756
 757#ifdef CONFIG_SUSPEND
 758        for (i = ACPI_STATE_S1; i < ACPI_STATE_S4; i++) {
 759                status = acpi_get_sleep_type_data(i, &type_a, &type_b);
 760                if (ACPI_SUCCESS(status)) {
 761                        sleep_states[i] = 1;
 762                        printk(KERN_CONT " S%d", i);
 763                }
 764        }
 765
 766        suspend_set_ops(old_suspend_ordering ?
 767                &acpi_suspend_ops_old : &acpi_suspend_ops);
 768#endif
 769
 770#ifdef CONFIG_HIBERNATION
 771        status = acpi_get_sleep_type_data(ACPI_STATE_S4, &type_a, &type_b);
 772        if (ACPI_SUCCESS(status)) {
 773                hibernation_set_ops(old_suspend_ordering ?
 774                        &acpi_hibernation_ops_old : &acpi_hibernation_ops);
 775                sleep_states[ACPI_STATE_S4] = 1;
 776                printk(KERN_CONT " S4");
 777                if (!nosigcheck) {
 778                        acpi_get_table(ACPI_SIG_FACS, 1,
 779                                (struct acpi_table_header **)&facs);
 780                        if (facs)
 781                                s4_hardware_signature =
 782                                        facs->hardware_signature;
 783                }
 784        }
 785#endif
 786        status = acpi_get_sleep_type_data(ACPI_STATE_S5, &type_a, &type_b);
 787        if (ACPI_SUCCESS(status)) {
 788                sleep_states[ACPI_STATE_S5] = 1;
 789                printk(KERN_CONT " S5");
 790                pm_power_off_prepare = acpi_power_off_prepare;
 791                pm_power_off = acpi_power_off;
 792        }
 793        printk(KERN_CONT ")\n");
 794        /*
 795         * Register the tts_notifier to reboot notifier list so that the _TTS
 796         * object can also be evaluated when the system enters S5.
 797         */
 798        register_reboot_notifier(&tts_notifier);
 799        return 0;
 800}
 801
lxr.linux.no kindly hosted by Redpill Linpro AS, provider of Linux consulting and operations services since 1995.