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-FW21E",
 181        .matches = {
 182                DMI_MATCH(DMI_SYS_VENDOR, "Sony Corporation"),
 183                DMI_MATCH(DMI_PRODUCT_NAME, "VGN-FW21E"),
 184                },
 185        },
 186        {
 187        .callback = init_nvs_nosave,
 188        .ident = "Sony Vaio VPCEB17FX",
 189        .matches = {
 190                DMI_MATCH(DMI_SYS_VENDOR, "Sony Corporation"),
 191                DMI_MATCH(DMI_PRODUCT_NAME, "VPCEB17FX"),
 192                },
 193        },
 194        {
 195        .callback = init_nvs_nosave,
 196        .ident = "Sony Vaio VGN-SR11M",
 197        .matches = {
 198                DMI_MATCH(DMI_SYS_VENDOR, "Sony Corporation"),
 199                DMI_MATCH(DMI_PRODUCT_NAME, "VGN-SR11M"),
 200                },
 201        },
 202        {
 203        .callback = init_nvs_nosave,
 204        .ident = "Everex StepNote Series",
 205        .matches = {
 206                DMI_MATCH(DMI_SYS_VENDOR, "Everex Systems, Inc."),
 207                DMI_MATCH(DMI_PRODUCT_NAME, "Everex StepNote Series"),
 208                },
 209        },
 210        {
 211        .callback = init_nvs_nosave,
 212        .ident = "Sony Vaio VPCEB1Z1E",
 213        .matches = {
 214                DMI_MATCH(DMI_SYS_VENDOR, "Sony Corporation"),
 215                DMI_MATCH(DMI_PRODUCT_NAME, "VPCEB1Z1E"),
 216                },
 217        },
 218        {
 219        .callback = init_nvs_nosave,
 220        .ident = "Sony Vaio VGN-NW130D",
 221        .matches = {
 222                DMI_MATCH(DMI_SYS_VENDOR, "Sony Corporation"),
 223                DMI_MATCH(DMI_PRODUCT_NAME, "VGN-NW130D"),
 224                },
 225        },
 226        {
 227        .callback = init_nvs_nosave,
 228        .ident = "Sony Vaio VPCCW29FX",
 229        .matches = {
 230                DMI_MATCH(DMI_SYS_VENDOR, "Sony Corporation"),
 231                DMI_MATCH(DMI_PRODUCT_NAME, "VPCCW29FX"),
 232                },
 233        },
 234        {
 235        .callback = init_nvs_nosave,
 236        .ident = "Averatec AV1020-ED2",
 237        .matches = {
 238                DMI_MATCH(DMI_SYS_VENDOR, "AVERATEC"),
 239                DMI_MATCH(DMI_PRODUCT_NAME, "1000 Series"),
 240                },
 241        },
 242        {
 243        .callback = init_old_suspend_ordering,
 244        .ident = "Asus A8N-SLI DELUXE",
 245        .matches = {
 246                DMI_MATCH(DMI_BOARD_VENDOR, "ASUSTeK Computer INC."),
 247                DMI_MATCH(DMI_BOARD_NAME, "A8N-SLI DELUXE"),
 248                },
 249        },
 250        {
 251        .callback = init_old_suspend_ordering,
 252        .ident = "Asus A8N-SLI Premium",
 253        .matches = {
 254                DMI_MATCH(DMI_BOARD_VENDOR, "ASUSTeK Computer INC."),
 255                DMI_MATCH(DMI_BOARD_NAME, "A8N-SLI Premium"),
 256                },
 257        },
 258        {
 259        .callback = init_nvs_nosave,
 260        .ident = "Sony Vaio VGN-SR26GN_P",
 261        .matches = {
 262                DMI_MATCH(DMI_SYS_VENDOR, "Sony Corporation"),
 263                DMI_MATCH(DMI_PRODUCT_NAME, "VGN-SR26GN_P"),
 264                },
 265        },
 266        {
 267        .callback = init_nvs_nosave,
 268        .ident = "Sony Vaio VPCEB1S1E",
 269        .matches = {
 270                DMI_MATCH(DMI_SYS_VENDOR, "Sony Corporation"),
 271                DMI_MATCH(DMI_PRODUCT_NAME, "VPCEB1S1E"),
 272                },
 273        },
 274        {
 275        .callback = init_nvs_nosave,
 276        .ident = "Sony Vaio VGN-FW520F",
 277        .matches = {
 278                DMI_MATCH(DMI_SYS_VENDOR, "Sony Corporation"),
 279                DMI_MATCH(DMI_PRODUCT_NAME, "VGN-FW520F"),
 280                },
 281        },
 282        {
 283        .callback = init_nvs_nosave,
 284        .ident = "Asus K54C",
 285        .matches = {
 286                DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK Computer Inc."),
 287                DMI_MATCH(DMI_PRODUCT_NAME, "K54C"),
 288                },
 289        },
 290        {
 291        .callback = init_nvs_nosave,
 292        .ident = "Asus K54HR",
 293        .matches = {
 294                DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK Computer Inc."),
 295                DMI_MATCH(DMI_PRODUCT_NAME, "K54HR"),
 296                },
 297        },
 298        {},
 299};
 300
 301static void acpi_sleep_dmi_check(void)
 302{
 303        dmi_check_system(acpisleep_dmi_table);
 304}
 305
 306/**
 307 * acpi_pm_freeze - Disable the GPEs and suspend EC transactions.
 308 */
 309static int acpi_pm_freeze(void)
 310{
 311        acpi_disable_all_gpes();
 312        acpi_os_wait_events_complete();
 313        acpi_ec_block_transactions();
 314        return 0;
 315}
 316
 317/**
 318 * acpi_pre_suspend - Enable wakeup devices, "freeze" EC and save NVS.
 319 */
 320static int acpi_pm_pre_suspend(void)
 321{
 322        acpi_pm_freeze();
 323        return suspend_nvs_save();
 324}
 325
 326/**
 327 *      __acpi_pm_prepare - Prepare the platform to enter the target state.
 328 *
 329 *      If necessary, set the firmware waking vector and do arch-specific
 330 *      nastiness to get the wakeup code to the waking vector.
 331 */
 332static int __acpi_pm_prepare(void)
 333{
 334        int error = acpi_sleep_prepare(acpi_target_sleep_state);
 335        if (error)
 336                acpi_target_sleep_state = ACPI_STATE_S0;
 337
 338        return error;
 339}
 340
 341/**
 342 *      acpi_pm_prepare - Prepare the platform to enter the target sleep
 343 *              state and disable the GPEs.
 344 */
 345static int acpi_pm_prepare(void)
 346{
 347        int error = __acpi_pm_prepare();
 348        if (!error)
 349                error = acpi_pm_pre_suspend();
 350
 351        return error;
 352}
 353
 354static int find_powerf_dev(struct device *dev, void *data)
 355{
 356        struct acpi_device *device = to_acpi_device(dev);
 357        const char *hid = acpi_device_hid(device);
 358
 359        return !strcmp(hid, ACPI_BUTTON_HID_POWERF);
 360}
 361
 362/**
 363 *      acpi_pm_finish - Instruct the platform to leave a sleep state.
 364 *
 365 *      This is called after we wake back up (or if entering the sleep state
 366 *      failed).
 367 */
 368static void acpi_pm_finish(void)
 369{
 370        struct device *pwr_btn_dev;
 371        u32 acpi_state = acpi_target_sleep_state;
 372
 373        acpi_ec_unblock_transactions();
 374        suspend_nvs_free();
 375
 376        if (acpi_state == ACPI_STATE_S0)
 377                return;
 378
 379        printk(KERN_INFO PREFIX "Waking up from system sleep state S%d\n",
 380                acpi_state);
 381        acpi_disable_wakeup_devices(acpi_state);
 382        acpi_leave_sleep_state(acpi_state);
 383
 384        /* reset firmware waking vector */
 385        acpi_set_firmware_waking_vector((acpi_physical_address) 0);
 386
 387        acpi_target_sleep_state = ACPI_STATE_S0;
 388
 389        /* If we were woken with the fixed power button, provide a small
 390         * hint to userspace in the form of a wakeup event on the fixed power
 391         * button device (if it can be found).
 392         *
 393         * We delay the event generation til now, as the PM layer requires
 394         * timekeeping to be running before we generate events. */
 395        if (!pwr_btn_event_pending)
 396                return;
 397
 398        pwr_btn_event_pending = false;
 399        pwr_btn_dev = bus_find_device(&acpi_bus_type, NULL, NULL,
 400                                      find_powerf_dev);
 401        if (pwr_btn_dev) {
 402                pm_wakeup_event(pwr_btn_dev, 0);
 403                put_device(pwr_btn_dev);
 404        }
 405}
 406
 407/**
 408 *      acpi_pm_end - Finish up suspend sequence.
 409 */
 410static void acpi_pm_end(void)
 411{
 412        /*
 413         * This is necessary in case acpi_pm_finish() is not called during a
 414         * failing transition to a sleep state.
 415         */
 416        acpi_target_sleep_state = ACPI_STATE_S0;
 417        acpi_sleep_tts_switch(acpi_target_sleep_state);
 418}
 419#else /* !CONFIG_ACPI_SLEEP */
 420#define acpi_target_sleep_state ACPI_STATE_S0
 421static inline void acpi_sleep_dmi_check(void) {}
 422#endif /* CONFIG_ACPI_SLEEP */
 423
 424#ifdef CONFIG_SUSPEND
 425static u32 acpi_suspend_states[] = {
 426        [PM_SUSPEND_ON] = ACPI_STATE_S0,
 427        [PM_SUSPEND_STANDBY] = ACPI_STATE_S1,
 428        [PM_SUSPEND_MEM] = ACPI_STATE_S3,
 429        [PM_SUSPEND_MAX] = ACPI_STATE_S5
 430};
 431
 432/**
 433 *      acpi_suspend_begin - Set the target system sleep state to the state
 434 *              associated with given @pm_state, if supported.
 435 */
 436static int acpi_suspend_begin(suspend_state_t pm_state)
 437{
 438        u32 acpi_state = acpi_suspend_states[pm_state];
 439        int error = 0;
 440
 441        error = (nvs_nosave || nvs_nosave_s3) ? 0 : suspend_nvs_alloc();
 442        if (error)
 443                return error;
 444
 445        if (sleep_states[acpi_state]) {
 446                acpi_target_sleep_state = acpi_state;
 447                acpi_sleep_tts_switch(acpi_target_sleep_state);
 448        } else {
 449                printk(KERN_ERR "ACPI does not support this state: %d\n",
 450                        pm_state);
 451                error = -ENOSYS;
 452        }
 453        return error;
 454}
 455
 456/**
 457 *      acpi_suspend_enter - Actually enter a sleep state.
 458 *      @pm_state: ignored
 459 *
 460 *      Flush caches and go to sleep. For STR we have to call arch-specific
 461 *      assembly, which in turn call acpi_enter_sleep_state().
 462 *      It's unfortunate, but it works. Please fix if you're feeling frisky.
 463 */
 464static int acpi_suspend_enter(suspend_state_t pm_state)
 465{
 466        acpi_status status = AE_OK;
 467        u32 acpi_state = acpi_target_sleep_state;
 468        int error;
 469
 470        ACPI_FLUSH_CPU_CACHE();
 471
 472        switch (acpi_state) {
 473        case ACPI_STATE_S1:
 474                barrier();
 475                status = acpi_enter_sleep_state(acpi_state);
 476                break;
 477
 478        case ACPI_STATE_S3:
 479                error = acpi_suspend_lowlevel();
 480                if (error)
 481                        return error;
 482                pr_info(PREFIX "Low-level resume complete\n");
 483                break;
 484        }
 485
 486        /* This violates the spec but is required for bug compatibility. */
 487        acpi_write_bit_register(ACPI_BITREG_SCI_ENABLE, 1);
 488
 489        /* Reprogram control registers */
 490        acpi_leave_sleep_state_prep(acpi_state);
 491
 492        /* ACPI 3.0 specs (P62) says that it's the responsibility
 493         * of the OSPM to clear the status bit [ implying that the
 494         * POWER_BUTTON event should not reach userspace ]
 495         *
 496         * However, we do generate a small hint for userspace in the form of
 497         * a wakeup event. We flag this condition for now and generate the
 498         * event later, as we're currently too early in resume to be able to
 499         * generate wakeup events.
 500         */
 501        if (ACPI_SUCCESS(status) && (acpi_state == ACPI_STATE_S3)) {
 502                acpi_event_status pwr_btn_status;
 503
 504                acpi_get_event_status(ACPI_EVENT_POWER_BUTTON, &pwr_btn_status);
 505
 506                if (pwr_btn_status & ACPI_EVENT_FLAG_SET) {
 507                        acpi_clear_event(ACPI_EVENT_POWER_BUTTON);
 508                        /* Flag for later */
 509                        pwr_btn_event_pending = true;
 510                }
 511        }
 512
 513        /*
 514         * Disable and clear GPE status before interrupt is enabled. Some GPEs
 515         * (like wakeup GPE) haven't handler, this can avoid such GPE misfire.
 516         * acpi_leave_sleep_state will reenable specific GPEs later
 517         */
 518        acpi_disable_all_gpes();
 519        /* Allow EC transactions to happen. */
 520        acpi_ec_unblock_transactions_early();
 521
 522        suspend_nvs_restore();
 523
 524        return ACPI_SUCCESS(status) ? 0 : -EFAULT;
 525}
 526
 527static int acpi_suspend_state_valid(suspend_state_t pm_state)
 528{
 529        u32 acpi_state;
 530
 531        switch (pm_state) {
 532        case PM_SUSPEND_ON:
 533        case PM_SUSPEND_STANDBY:
 534        case PM_SUSPEND_MEM:
 535                acpi_state = acpi_suspend_states[pm_state];
 536
 537                return sleep_states[acpi_state];
 538        default:
 539                return 0;
 540        }
 541}
 542
 543static const struct platform_suspend_ops acpi_suspend_ops = {
 544        .valid = acpi_suspend_state_valid,
 545        .begin = acpi_suspend_begin,
 546        .prepare_late = acpi_pm_prepare,
 547        .enter = acpi_suspend_enter,
 548        .wake = acpi_pm_finish,
 549        .end = acpi_pm_end,
 550};
 551
 552/**
 553 *      acpi_suspend_begin_old - Set the target system sleep state to the
 554 *              state associated with given @pm_state, if supported, and
 555 *              execute the _PTS control method.  This function is used if the
 556 *              pre-ACPI 2.0 suspend ordering has been requested.
 557 */
 558static int acpi_suspend_begin_old(suspend_state_t pm_state)
 559{
 560        int error = acpi_suspend_begin(pm_state);
 561        if (!error)
 562                error = __acpi_pm_prepare();
 563
 564        return error;
 565}
 566
 567/*
 568 * The following callbacks are used if the pre-ACPI 2.0 suspend ordering has
 569 * been requested.
 570 */
 571static const struct platform_suspend_ops acpi_suspend_ops_old = {
 572        .valid = acpi_suspend_state_valid,
 573        .begin = acpi_suspend_begin_old,
 574        .prepare_late = acpi_pm_pre_suspend,
 575        .enter = acpi_suspend_enter,
 576        .wake = acpi_pm_finish,
 577        .end = acpi_pm_end,
 578        .recover = acpi_pm_finish,
 579};
 580#endif /* CONFIG_SUSPEND */
 581
 582#ifdef CONFIG_HIBERNATION
 583static unsigned long s4_hardware_signature;
 584static struct acpi_table_facs *facs;
 585static bool nosigcheck;
 586
 587void __init acpi_no_s4_hw_signature(void)
 588{
 589        nosigcheck = true;
 590}
 591
 592static int acpi_hibernation_begin(void)
 593{
 594        int error;
 595
 596        error = nvs_nosave ? 0 : suspend_nvs_alloc();
 597        if (!error) {
 598                acpi_target_sleep_state = ACPI_STATE_S4;
 599                acpi_sleep_tts_switch(acpi_target_sleep_state);
 600        }
 601
 602        return error;
 603}
 604
 605static int acpi_hibernation_enter(void)
 606{
 607        acpi_status status = AE_OK;
 608
 609        ACPI_FLUSH_CPU_CACHE();
 610
 611        /* This shouldn't return.  If it returns, we have a problem */
 612        status = acpi_enter_sleep_state(ACPI_STATE_S4);
 613        /* Reprogram control registers */
 614        acpi_leave_sleep_state_prep(ACPI_STATE_S4);
 615
 616        return ACPI_SUCCESS(status) ? 0 : -EFAULT;
 617}
 618
 619static void acpi_hibernation_leave(void)
 620{
 621        /*
 622         * If ACPI is not enabled by the BIOS and the boot kernel, we need to
 623         * enable it here.
 624         */
 625        acpi_enable();
 626        /* Reprogram control registers */
 627        acpi_leave_sleep_state_prep(ACPI_STATE_S4);
 628        /* Check the hardware signature */
 629        if (facs && s4_hardware_signature != facs->hardware_signature) {
 630                printk(KERN_EMERG "ACPI: Hardware changed while hibernated, "
 631                        "cannot resume!\n");
 632                panic("ACPI S4 hardware signature mismatch");
 633        }
 634        /* Restore the NVS memory area */
 635        suspend_nvs_restore();
 636        /* Allow EC transactions to happen. */
 637        acpi_ec_unblock_transactions_early();
 638}
 639
 640static void acpi_pm_thaw(void)
 641{
 642        acpi_ec_unblock_transactions();
 643        acpi_enable_all_runtime_gpes();
 644}
 645
 646static const struct platform_hibernation_ops acpi_hibernation_ops = {
 647        .begin = acpi_hibernation_begin,
 648        .end = acpi_pm_end,
 649        .pre_snapshot = acpi_pm_prepare,
 650        .finish = acpi_pm_finish,
 651        .prepare = acpi_pm_prepare,
 652        .enter = acpi_hibernation_enter,
 653        .leave = acpi_hibernation_leave,
 654        .pre_restore = acpi_pm_freeze,
 655        .restore_cleanup = acpi_pm_thaw,
 656};
 657
 658/**
 659 *      acpi_hibernation_begin_old - Set the target system sleep state to
 660 *              ACPI_STATE_S4 and execute the _PTS control method.  This
 661 *              function is used if the pre-ACPI 2.0 suspend ordering has been
 662 *              requested.
 663 */
 664static int acpi_hibernation_begin_old(void)
 665{
 666        int error;
 667        /*
 668         * The _TTS object should always be evaluated before the _PTS object.
 669         * When the old_suspended_ordering is true, the _PTS object is
 670         * evaluated in the acpi_sleep_prepare.
 671         */
 672        acpi_sleep_tts_switch(ACPI_STATE_S4);
 673
 674        error = acpi_sleep_prepare(ACPI_STATE_S4);
 675
 676        if (!error) {
 677                if (!nvs_nosave)
 678                        error = suspend_nvs_alloc();
 679                if (!error)
 680                        acpi_target_sleep_state = ACPI_STATE_S4;
 681        }
 682        return error;
 683}
 684
 685/*
 686 * The following callbacks are used if the pre-ACPI 2.0 suspend ordering has
 687 * been requested.
 688 */
 689static const struct platform_hibernation_ops acpi_hibernation_ops_old = {
 690        .begin = acpi_hibernation_begin_old,
 691        .end = acpi_pm_end,
 692        .pre_snapshot = acpi_pm_pre_suspend,
 693        .prepare = acpi_pm_freeze,
 694        .finish = acpi_pm_finish,
 695        .enter = acpi_hibernation_enter,
 696        .leave = acpi_hibernation_leave,
 697        .pre_restore = acpi_pm_freeze,
 698        .restore_cleanup = acpi_pm_thaw,
 699        .recover = acpi_pm_finish,
 700};
 701#endif /* CONFIG_HIBERNATION */
 702
 703int acpi_suspend(u32 acpi_state)
 704{
 705        suspend_state_t states[] = {
 706                [1] = PM_SUSPEND_STANDBY,
 707                [3] = PM_SUSPEND_MEM,
 708                [5] = PM_SUSPEND_MAX
 709        };
 710
 711        if (acpi_state < 6 && states[acpi_state])
 712                return pm_suspend(states[acpi_state]);
 713        if (acpi_state == 4)
 714                return hibernate();
 715        return -EINVAL;
 716}
 717
 718static void acpi_power_off_prepare(void)
 719{
 720        /* Prepare to power off the system */
 721        acpi_sleep_prepare(ACPI_STATE_S5);
 722        acpi_disable_all_gpes();
 723}
 724
 725static void acpi_power_off(void)
 726{
 727        /* acpi_sleep_prepare(ACPI_STATE_S5) should have already been called */
 728        printk(KERN_DEBUG "%s called\n", __func__);
 729        local_irq_disable();
 730        acpi_enter_sleep_state(ACPI_STATE_S5);
 731}
 732
 733int __init acpi_sleep_init(void)
 734{
 735        acpi_status status;
 736        u8 type_a, type_b;
 737#ifdef CONFIG_SUSPEND
 738        int i = 0;
 739#endif
 740
 741        if (acpi_disabled)
 742                return 0;
 743
 744        acpi_sleep_dmi_check();
 745
 746        sleep_states[ACPI_STATE_S0] = 1;
 747        printk(KERN_INFO PREFIX "(supports S0");
 748
 749#ifdef CONFIG_SUSPEND
 750        for (i = ACPI_STATE_S1; i < ACPI_STATE_S4; i++) {
 751                status = acpi_get_sleep_type_data(i, &type_a, &type_b);
 752                if (ACPI_SUCCESS(status)) {
 753                        sleep_states[i] = 1;
 754                        printk(KERN_CONT " S%d", i);
 755                }
 756        }
 757
 758        suspend_set_ops(old_suspend_ordering ?
 759                &acpi_suspend_ops_old : &acpi_suspend_ops);
 760#endif
 761
 762#ifdef CONFIG_HIBERNATION
 763        status = acpi_get_sleep_type_data(ACPI_STATE_S4, &type_a, &type_b);
 764        if (ACPI_SUCCESS(status)) {
 765                hibernation_set_ops(old_suspend_ordering ?
 766                        &acpi_hibernation_ops_old : &acpi_hibernation_ops);
 767                sleep_states[ACPI_STATE_S4] = 1;
 768                printk(KERN_CONT " S4");
 769                if (!nosigcheck) {
 770                        acpi_get_table(ACPI_SIG_FACS, 1,
 771                                (struct acpi_table_header **)&facs);
 772                        if (facs)
 773                                s4_hardware_signature =
 774                                        facs->hardware_signature;
 775                }
 776        }
 777#endif
 778        status = acpi_get_sleep_type_data(ACPI_STATE_S5, &type_a, &type_b);
 779        if (ACPI_SUCCESS(status)) {
 780                sleep_states[ACPI_STATE_S5] = 1;
 781                printk(KERN_CONT " S5");
 782                pm_power_off_prepare = acpi_power_off_prepare;
 783                pm_power_off = acpi_power_off;
 784        }
 785        printk(KERN_CONT ")\n");
 786        /*
 787         * Register the tts_notifier to reboot notifier list so that the _TTS
 788         * object can also be evaluated when the system enters S5.
 789         */
 790        register_reboot_notifier(&tts_notifier);
 791        return 0;
 792}
 793
lxr.linux.no kindly hosted by Redpill Linpro AS, provider of Linux consulting and operations services since 1995.