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