* origin/tmp-a909d3e: Linux 4.9-rc3 x86/smpboot: Init apic mapping before usage ACPICA: Dispatcher: Fix interpreter locking around acpi_ev_initialize_region() ACPICA: Dispatcher: Fix an unbalanced lock exit path in acpi_ds_auto_serialize_method() ACPICA: Dispatcher: Fix order issue of method termination ARC: module: print pretty section names ARC: module: elide loop to save reference to .eh_frame ARC: mm: retire ARC_DBG_TLB_MISS_COUNT... ARC: build: retire old toggles ARC: boot log: refactor cpu name/release printing ARC: boot log: remove awkward space comma from MMU line ARC: boot log: don't assume SWAPE instruction support ARC: boot log: refactor printing abt features not captured in BCRs ARCv2: boot log: print IOC exists as well as enabled status ubifs: Fix regression in ubifs_readdir() ubi: fastmap: Fix add_vol() return value test in ubi_attach_fastmap() MAINTAINERS: Add entry for genwqe driver VMCI: Doorbell create and destroy fixes GenWQE: Fix bad page access during abort of resource allocation vme: vme_get_size potentially returning incorrect value on failure tty: serial_core: fix NULL struct tty pointer access in uart_write_wakeup tty: serial_core: Fix serial console crash on port shutdown tty/serial: at91: fix hardware handshake on Atmel platforms perf/x86/intel: Honour the CPUID for number of fixed counters in hypervisors perf/powerpc: Don't call perf_event_disable() from atomic context perf/core: Protect PMU device removal with a 'pmu_bus_running' check, to fix CONFIG_DEBUG_TEST_DRIVER_REMOVE=y kernel panic x86/microcode/AMD: Fix more fallout from CONFIG_RANDOMIZE_MEMORY=y drivers/misc/sgi-gru/grumain.c: remove bogus 0x prefix from printk cris/arch-v32: cryptocop: print a hex number after a 0x prefix ipack: print a hex number after a 0x prefix block: DAC960: print a hex number after a 0x prefix fs: exofs: print a hex number after a 0x prefix lib/genalloc.c: start search from start of chunk mm: memcontrol: do not recurse in direct reclaim CREDITS: update credit information for Martin Kepplinger proc: fix NULL dereference when reading /proc/<pid>/auxv mm: kmemleak: ensure that the task stack is not freed during scanning lib/stackdepot.c: bump stackdepot capacity from 16MB to 128MB latent_entropy: raise CONFIG_FRAME_WARN by default kconfig.h: remove config_enabled() macro ipc: account for kmem usage on mqueue and msg mm/slab: improve performance of gathering slabinfo stats mm: page_alloc: use KERN_CONT where appropriate mm/list_lru.c: avoid error-path NULL pointer deref h8300: fix syscall restarting kcov: properly check if we are in an interrupt mm/slab: fix kmemcg cache creation delayed issue device-dax: fix percpu_ref_exit ordering Allow KASAN and HOTPLUG_MEMORY to co-exist when doing build testing nvdimm: make CONFIG_NVDIMM_DAX 'bool' mm: remove unused variable in memory hotplug btrfs: fix races on root_log_ctx lists mm: remove per-zone hashtable of bitlock waitqueues blk-mq: update hardware and software queues for sleeping alloc driver core: Make Kconfig text for DEBUG_TEST_DRIVER_REMOVE stronger kernfs: Add noop_fsync to supported kernfs_file_fops vt: clear selection before resizing sc16is7xx: always write state when configuring GPIO as an output sh-sci: document R8A7743/5 support tty: serial: 8250: 8250_core: NXP SC16C2552 workaround tty: limit terminal size to 4M chars tty: serial: fsl_lpuart: Fix Tx DMA edge case serial: 8250_lpss: enable MSI for sure serial: core: fix console problems on uart_close serial: 8250_uniphier: fix clearing divisor latch access bit serial: 8250_uniphier: fix more unterminated string serial: pch_uart: add terminate entry for dmi_system_id tables devicetree: bindings: uart: Add new compatible string for ZynqMP serial: xuartps: Add new compatible string for ZynqMP serial: SERIAL_STM32 should depend on HAS_DMA serial: stm32: Fix comparisons with undefined register tty: vt, fix bogus division in csi_J powerpc/64s: relocation, register save fixes for system reset interrupt powerpc/mm/radix: Use tlbiel only if we ever ran on the current cpu powerpc/process: Fix CONFIG_ALIVEC typo in restore_tm_state() ALSA: usb-audio: Add quirk for Syntek STK1160 sched/fair: Remove unused but set variable 'rq' objtool: Fix rare switch jump table pattern detection security/keys: make BIG_KEYS dependent on stdrng. KEYS: Sort out big_key initialisation KEYS: Fix short sprintf buffer in /proc/keys show function arm64: mm: fix __page_to_voff definition arm64/numa: fix incorrect log for memory-less node arm64/numa: fix pcpu_cpu_distance() to get correct CPU proximity block: flush: fix IO hang in case of flood fua req x86: Fix export for mcount and __fentry__ doc: Add missing parameter for msi_setup extcon: qcom-spmi-misc: Sync the extcon state on interrupt drm/drivers: add support for using the arch wc mapping API. x86/io: add interface to reserve io memtype for a resource range. (v1.1) MAINTAINERS: Begin module maintainer transition ahci: fix the single MSI-X case in ahci_init_one timers: Prevent base clock corruption when forwarding timers: Prevent base clock rewind when forwarding clock timers: Lock base for same bucket optimization timers: Plug locking race vs. timer migration ALSA: seq: Fix time account regression i2c: imx: defer probe if bus recovery GPIOs are not ready i2c: designware: Avoid aborted transfers with fast reacting I2C slaves i2c: i801: Fix I2C Block Read on 8-Series/C220 and later i2c: xgene: Avoid dma_buffer overrun i2c: digicolor: Fix module autoload i2c: xlr: Fix module autoload for OF registration i2c: xlp9xx: Fix module autoload i2c: jz4780: Fix module autoload i2c: allow configuration of imx driver for ColdFire architecture i2c: mark device nodes only in case of successful instantiation x86/quirks: Hide maybe-uninitialized warning x86/build: Fix build with older GCC versions x86/unwind: Fix empty stack dereference in guess unwinder i2c: rk3x: Give the tuning value 0 during rk3x_i2c_v0_calc_timings i2c: hix5hd2: allow build with ARCH_HISI usb: chipidea: host: fix NULL ptr dereference during shutdown ALSA: hda - Fix surround output pins for ASRock B150M mobo hv: do not lose pending heartbeat vmbus packets mm: unexport __get_user_pages() proc: don't use FOLL_FORCE for reading cmdline and environment cpufreq: intel_pstate: Always set max P-state in performance mode nbd: fix incorrect unlock of nbd->sock_lock in sock_shutdown orangefs: don't use d_time orangefs: user file_inode() where it is due mei: txe: don't clean an unprocessed interrupt cause. ANDROID: binder: Clear binder and cookie when setting handle in flat binder struct ANDROID: binder: Add strong ref checks ARCv2: IOC: use @ioc_enable not @ioc_exist where intended ARC: syscall for userspace cmpxchg assist dm table: fix missing dm_put_target_type() in dm_table_add_target() xenbus: check return value of xenbus_scanf() xenbus: prefer list_for_each() x86: xen: move cpu_up functions out of ifdef xenbus: advertise control feature flags greybus: fix a leak on error in gb_module_create() greybus: es2: fix error return code in ap_probe() greybus: arche-platform: Add missing of_node_put() in arche_platform_change_state() staging: android: ion: Fix error handling in ion_query_heaps() ARM: imx: mach-imx6q: Fix the PHY ID mask for AR8031 PM / suspend: Fix missing KERN_CONT for suspend message usb: renesas_usbhs: add wait after initialization for R-Car Gen3 ACPI / APEI: Fix incorrect return value of ghes_proc() usb: increase ohci watchdog delay to 275 msec usb: musb: Call pm_runtime from musb_gadget_queue usb: musb: Fix hardirq-safe hardirq-unsafe lock order error usb: ehci-platform: increase EHCI_MAX_RSTS to 4 usb: ohci-at91: Set RemoteWakeupConnected bit explicitly. ACPI/PCI: pci_link: Include PIRQ_PENALTY_PCI_USING for ISA IRQs ACPI/PCI: pci_link: penalize SCI correctly ACPI/PCI/IRQ: assign ISA IRQ directly during early boot stages ARM: dts: vf610: fix IRQ flag of global timer powerpc/64: Fix race condition in setting lock bit in idle/wakeup code powerpc/64: Re-fix race condition between going idle and entering guest s390/mm: fix zone calculation in arch_add_memory() s390/dumpstack: use pr_cont within show_stack and die ARM: imx: gpc: Fix the imx_gpc_genpd_init() error path ARM: imx: gpc: Initialize all power domains xfs: clear cowblocks tag when cow fork is emptied xfs: fix up inode cowblocks tracking tracepoints fs: Do to trim high file position bits in iomap_page_mkwrite_actor cxl: Fix leaking pid refs in some error paths gpio: mpc8xxx: Correct irq handler function gpio: ath79: Fix module autoload arm64: dts: Updated NAND DT properties for NS2 SVK iio: accel: sca3000_core: avoid potentially uninitialized variable iio:chemical:atlas-ph-sensor: Fix use of 32 bit int to hold 16 bit big endian value arm64: dts: uniphier: change MIO node to SD control node ARM: dts: uniphier: change MIO node to SD control node reset: uniphier: rename MIO reset to SD reset for Pro5, PXs2, LD20 SoCs arm64: uniphier: select ARCH_HAS_RESET_CONTROLLER ARM: uniphier: select ARCH_HAS_RESET_CONTROLLER badblocks: badblocks_set/clear update unacked_exist softirq: Display IRQ_POLL for irq-poll statistics powerpc: Convert cmp to cmpd in idle enter sequence KVM: PPC: Book3S HV: Fix build error when SMP=n cpufreq: intel_pstate: Set P-state upfront in performance mode USB: serial: fix potential NULL-dereference at probe arm64: dts: Add timer erratum property for LS2080A and LS1043A gpio: ts4800: Fix module autoload gpio: GPIO_GET_LINEEVENT_IOCTL: Reject invalid line and event flags gpio: GPIO_GET_LINEHANDLE_IOCTL: Reject invalid line flags gpio: GPIOHANDLE_GET_LINE_VALUES_IOCTL: Fix information leak gpio: GPIO_GET_LINEEVENT_IOCTL: Validate line offset gpio: GPIOHANDLE_GET_LINE_VALUES_IOCTL: Fix information leak gpio: GPIO_GET_LINEHANDLE_IOCTL: Validate line offset gpio: GPIO_GET_CHIPINFO_IOCTL: Fix information leak gpio: GPIO_GET_CHIPINFO_IOCTL: Fix line offset validation clk: at91: Fix a return value in case of error ahci: fix nvec check xhci: use default USB_RESUME_TIMEOUT when resuming ports. xhci: workaround for hosts missing CAS bit xhci: add restart quirk for Intel Wildcatpoint PCH gpio / ACPI: fix returned error from acpi_dev_gpio_irq_get() gpio: mockup: add sysfs dependency gpio: stmpe: || vs && typo gpio: mxs: Unmap region obtained by of_iomap gpio/board.txt: point to gpiod_set_value ALSA: hda - Fix headset mic detection problem for two Dell laptops USB: serial: cp210x: fix tiocmget error handling thermal/powerclamp: correct cpu support check thermal: intel_pch_thermal: Enable Haswell PCH thermal: intel_pch_thermal: Add an ACPI passive trip xfs: remove xfs_bunmapi_cow xfs: optimize xfs_reflink_end_cow xfs: optimize xfs_reflink_cancel_cow_blocks xfs: refactor xfs_bunmapi_cow xfs: optimize writes to reflink files xfs: don't bother looking at the refcount tree for reads xfs: handle "raw" delayed extents xfs_reflink_trim_around_shared xfs: add xfs_trim_extent iomap: add IOMAP_REPORT xfs: merge xfs_reflink_remap_range and xfs_file_share_range xfs: remove xfs_file_wait_for_io xfs: move inode locking from xfs_reflink_remap_range to xfs_file_share_range xfs: fix the same_inode check in xfs_file_share_range xfs: remove the same fs check from xfs_file_share_range libxfs: v3 inodes are only valid on crc-enabled filesystems libxfs: clean up _calc_dquots_per_chunk xfs: unset MS_ACTIVE if mount fails xfs: remove pointless error goto in xfs_bmap_remap_alloc xfs: don't take the IOLOCK exclusive for direct I/O page invalidation xfs: add some 'static' annotations xfs: Fix uninitialized variable in xfs_reflink_reserve_cow_range() xfs: remove redundant assignment of ifp ARC: fix build warning in elf.h clk: uniphier: rename MIO clock to SD clock for Pro5, PXs2, LD20 SoCs clk: uniphier: fix memory overrun bug pmem: report error on clear poison failure libnvdimm, namespace: potential NULL deref on allocation error ahci: only try to use multi-MSI mode if there is more than 1 port ARC: Adjust cpuinfo for non-continuous cpu ids perf/x86/intel/cstate: Add C-state residency events for Knights Landing wusb: fix error return code in wusb_prf() hwrng: core - Don't use a stack buffer in add_early_randomness() arm64: dts: rockchip: remove the abuse of keep-power-in-suspend dm rq: clear kworker_task if kthread_run() returned an error dm: free io_barrier after blk_cleanup_queue call ALSA: asihpi: fix kernel memory disclosure Revert "Documentation: devicetree: dwc2: Deprecate g-tx-fifo-size" Revert "usb: dwc2: gadget: fix TX FIFO size and address initialization" Revert "usb: dwc2: gadget: change variable name to more meaningful" ALSA: hda - Adding a new group of pin cfg into ALC295 pin quirk table ALSA: hda - allow 40 bit DMA mask for NVidia devices clk: hi6220: use CLK_OF_DECLARE_DRIVER for sysctrl and mediactrl clock init clk: mvebu: armada-37xx-periph: Fix the clock gate flag clk: bcm2835: Clamp the PLL's requested rate to the hardware limits. clk: max77686: fix number of clocks setup for clk_hw based registration clk: mvebu: armada-37xx-periph: Fix the clock provider registration clk: core: add __init decoration for CLK_OF_DECLARE_DRIVER function clk: mediatek: Add hardware dependency clk: samsung: clk-exynos-audss: Fix module autoload clk: uniphier: fix type of variable passed to regmap_read() clk: uniphier: add system clock support for sLD3 SoC ARM: multi_v7_defconfig: Enable Intel e1000e driver MAINTAINERS: add myself as Marvell berlin SoC maintainer bus: qcom-ebi2: depend on ARCH_QCOM or COMPILE_TEST ARM: dts: fix the SD card on the Snowball dm raid: fix activation of existing raid4/10 devices scsi: NCR5380: no longer mark irq probing as __init scsi: be2iscsi: Replace _bh with _irqsave/irqrestore scsi: libiscsi: Fix locking in __iscsi_conn_send_pdu USB: serial: ftdi_sio: add support for Infineon TriBoard TC2X7 s390/dumpstack: get rid of return_address again s390/disassambler: use pr_cont where appropriate s390/dumpstack: use pr_cont where appropriate s390/dumpstack: restore reliable indicator for call traces wusb: Stop using the stack for sg crypto scratch space usb: dwc3: Fix size used in dma_free_coherent() usb: gadget: f_fs: stop sleeping in ffs_func_eps_disable usb: gadget: f_fs: edit epfile->ep under lock usb: dwc2: Add msleep for host-only s390/mm: use hugetlb_bad_size() s390/cio: don't register chpids in reserved state s390: ignore pkey system calls s390/dasd: avoid undefined behaviour usb: dwc3: gadget: never pre-start Isochronous endpoints usb: dwc3: gadget: properly account queued requests usb: gadget: function: u_ether: don't starve tx request queue usb: gadget: udc: atmel: fix endpoint name staging/lustre/llite: Move unstable_stats from sysfs to debugfs Staging: wilc1000: Fix kernel Oops on opening the device staging: android/ion: testing the wrong variable Staging: greybus: uart: Use gbphy_dev->dev instead of bundle->dev Staging: greybus: gpio: Use gbphy_dev->dev instead of bundle->dev ARC: [build] Support gz, lzma compressed uImage ARCv2: intc: untangle SMP, MCIP and IDU arm64: dts: rockchip: remove always-on and boot-on from vcc_sd dm mirror: use all available legs on multiple failures dm mirror: fix read error on recovery after default leg failure Btrfs: fix incremental send failure caused by balance dm raid: fix compat_features validation iio: adc: ti-adc081c: Select IIO_TRIGGERED_BUFFER to prevent build errors iio: maxim_thermocouple: Align 16 bit big endian value of raw reads arm64: dts: marvell: fix clocksource for CP110 master SPI0 ARM: mvebu: Select corediv clk for all mvebu v7 SoC Conflicts: drivers/android/binder.c drivers/staging/android/ion/ion.c drivers/usb/gadget/function/u_ether.c Change-Id: Id87fbdb0731f6f7681787faa4cebf067a402aa7e Signed-off-by: Channagoud Kadabi <ckadabi@codeaurora.org>
585 lines
15 KiB
C
585 lines
15 KiB
C
/*
|
|
* kernel/power/suspend.c - Suspend to RAM and standby functionality.
|
|
*
|
|
* Copyright (c) 2003 Patrick Mochel
|
|
* Copyright (c) 2003 Open Source Development Lab
|
|
* Copyright (c) 2009 Rafael J. Wysocki <rjw@sisk.pl>, Novell Inc.
|
|
*
|
|
* This file is released under the GPLv2.
|
|
*/
|
|
|
|
#include <linux/string.h>
|
|
#include <linux/delay.h>
|
|
#include <linux/errno.h>
|
|
#include <linux/init.h>
|
|
#include <linux/console.h>
|
|
#include <linux/cpu.h>
|
|
#include <linux/cpuidle.h>
|
|
#include <linux/syscalls.h>
|
|
#include <linux/gfp.h>
|
|
#include <linux/io.h>
|
|
#include <linux/kernel.h>
|
|
#include <linux/list.h>
|
|
#include <linux/mm.h>
|
|
#include <linux/slab.h>
|
|
#include <linux/export.h>
|
|
#include <linux/suspend.h>
|
|
#include <linux/syscore_ops.h>
|
|
#include <linux/ftrace.h>
|
|
#include <linux/rtc.h>
|
|
#include <trace/events/power.h>
|
|
#include <linux/compiler.h>
|
|
#include <linux/moduleparam.h>
|
|
#include <linux/wakeup_reason.h>
|
|
|
|
#include "power.h"
|
|
|
|
const char *pm_labels[] = { "mem", "standby", "freeze", NULL };
|
|
const char *pm_states[PM_SUSPEND_MAX];
|
|
|
|
unsigned int pm_suspend_global_flags;
|
|
EXPORT_SYMBOL_GPL(pm_suspend_global_flags);
|
|
|
|
static const struct platform_suspend_ops *suspend_ops;
|
|
static const struct platform_freeze_ops *freeze_ops;
|
|
static DECLARE_WAIT_QUEUE_HEAD(suspend_freeze_wait_head);
|
|
|
|
enum freeze_state __read_mostly suspend_freeze_state;
|
|
static DEFINE_SPINLOCK(suspend_freeze_lock);
|
|
|
|
void freeze_set_ops(const struct platform_freeze_ops *ops)
|
|
{
|
|
lock_system_sleep();
|
|
freeze_ops = ops;
|
|
unlock_system_sleep();
|
|
}
|
|
|
|
static void freeze_begin(void)
|
|
{
|
|
suspend_freeze_state = FREEZE_STATE_NONE;
|
|
}
|
|
|
|
static void freeze_enter(void)
|
|
{
|
|
spin_lock_irq(&suspend_freeze_lock);
|
|
if (pm_wakeup_pending())
|
|
goto out;
|
|
|
|
suspend_freeze_state = FREEZE_STATE_ENTER;
|
|
spin_unlock_irq(&suspend_freeze_lock);
|
|
|
|
get_online_cpus();
|
|
cpuidle_resume();
|
|
|
|
/* Push all the CPUs into the idle loop. */
|
|
wake_up_all_idle_cpus();
|
|
pr_debug("PM: suspend-to-idle\n");
|
|
/* Make the current CPU wait so it can enter the idle loop too. */
|
|
wait_event(suspend_freeze_wait_head,
|
|
suspend_freeze_state == FREEZE_STATE_WAKE);
|
|
pr_debug("PM: resume from suspend-to-idle\n");
|
|
|
|
cpuidle_pause();
|
|
put_online_cpus();
|
|
|
|
spin_lock_irq(&suspend_freeze_lock);
|
|
|
|
out:
|
|
suspend_freeze_state = FREEZE_STATE_NONE;
|
|
spin_unlock_irq(&suspend_freeze_lock);
|
|
}
|
|
|
|
void freeze_wake(void)
|
|
{
|
|
unsigned long flags;
|
|
|
|
spin_lock_irqsave(&suspend_freeze_lock, flags);
|
|
if (suspend_freeze_state > FREEZE_STATE_NONE) {
|
|
suspend_freeze_state = FREEZE_STATE_WAKE;
|
|
wake_up(&suspend_freeze_wait_head);
|
|
}
|
|
spin_unlock_irqrestore(&suspend_freeze_lock, flags);
|
|
}
|
|
EXPORT_SYMBOL_GPL(freeze_wake);
|
|
|
|
static bool valid_state(suspend_state_t state)
|
|
{
|
|
/*
|
|
* PM_SUSPEND_STANDBY and PM_SUSPEND_MEM states need low level
|
|
* support and need to be valid to the low level
|
|
* implementation, no valid callback implies that none are valid.
|
|
*/
|
|
return suspend_ops && suspend_ops->valid && suspend_ops->valid(state);
|
|
}
|
|
|
|
/*
|
|
* If this is set, the "mem" label always corresponds to the deepest sleep state
|
|
* available, the "standby" label corresponds to the second deepest sleep state
|
|
* available (if any), and the "freeze" label corresponds to the remaining
|
|
* available sleep state (if there is one).
|
|
*/
|
|
static bool relative_states;
|
|
|
|
void __init pm_states_init(void)
|
|
{
|
|
/*
|
|
* freeze state should be supported even without any suspend_ops,
|
|
* initialize pm_states accordingly here
|
|
*/
|
|
pm_states[PM_SUSPEND_FREEZE] = pm_labels[relative_states ? 0 : 2];
|
|
}
|
|
|
|
static int __init sleep_states_setup(char *str)
|
|
{
|
|
relative_states = !strncmp(str, "1", 1);
|
|
return 1;
|
|
}
|
|
|
|
__setup("relative_sleep_states=", sleep_states_setup);
|
|
|
|
/**
|
|
* suspend_set_ops - Set the global suspend method table.
|
|
* @ops: Suspend operations to use.
|
|
*/
|
|
void suspend_set_ops(const struct platform_suspend_ops *ops)
|
|
{
|
|
suspend_state_t i;
|
|
int j = 0;
|
|
|
|
lock_system_sleep();
|
|
|
|
suspend_ops = ops;
|
|
for (i = PM_SUSPEND_MEM; i >= PM_SUSPEND_STANDBY; i--)
|
|
if (valid_state(i)) {
|
|
pm_states[i] = pm_labels[j++];
|
|
} else if (!relative_states) {
|
|
pm_states[i] = NULL;
|
|
j++;
|
|
}
|
|
|
|
pm_states[PM_SUSPEND_FREEZE] = pm_labels[j];
|
|
|
|
unlock_system_sleep();
|
|
}
|
|
EXPORT_SYMBOL_GPL(suspend_set_ops);
|
|
|
|
/**
|
|
* suspend_valid_only_mem - Generic memory-only valid callback.
|
|
*
|
|
* Platform drivers that implement mem suspend only and only need to check for
|
|
* that in their .valid() callback can use this instead of rolling their own
|
|
* .valid() callback.
|
|
*/
|
|
int suspend_valid_only_mem(suspend_state_t state)
|
|
{
|
|
return state == PM_SUSPEND_MEM;
|
|
}
|
|
EXPORT_SYMBOL_GPL(suspend_valid_only_mem);
|
|
|
|
static bool sleep_state_supported(suspend_state_t state)
|
|
{
|
|
return state == PM_SUSPEND_FREEZE || (suspend_ops && suspend_ops->enter);
|
|
}
|
|
|
|
static int platform_suspend_prepare(suspend_state_t state)
|
|
{
|
|
return state != PM_SUSPEND_FREEZE && suspend_ops->prepare ?
|
|
suspend_ops->prepare() : 0;
|
|
}
|
|
|
|
static int platform_suspend_prepare_late(suspend_state_t state)
|
|
{
|
|
return state == PM_SUSPEND_FREEZE && freeze_ops && freeze_ops->prepare ?
|
|
freeze_ops->prepare() : 0;
|
|
}
|
|
|
|
static int platform_suspend_prepare_noirq(suspend_state_t state)
|
|
{
|
|
return state != PM_SUSPEND_FREEZE && suspend_ops->prepare_late ?
|
|
suspend_ops->prepare_late() : 0;
|
|
}
|
|
|
|
static void platform_resume_noirq(suspend_state_t state)
|
|
{
|
|
if (state != PM_SUSPEND_FREEZE && suspend_ops->wake)
|
|
suspend_ops->wake();
|
|
}
|
|
|
|
static void platform_resume_early(suspend_state_t state)
|
|
{
|
|
if (state == PM_SUSPEND_FREEZE && freeze_ops && freeze_ops->restore)
|
|
freeze_ops->restore();
|
|
}
|
|
|
|
static void platform_resume_finish(suspend_state_t state)
|
|
{
|
|
if (state != PM_SUSPEND_FREEZE && suspend_ops->finish)
|
|
suspend_ops->finish();
|
|
}
|
|
|
|
static int platform_suspend_begin(suspend_state_t state)
|
|
{
|
|
if (state == PM_SUSPEND_FREEZE && freeze_ops && freeze_ops->begin)
|
|
return freeze_ops->begin();
|
|
else if (suspend_ops && suspend_ops->begin)
|
|
return suspend_ops->begin(state);
|
|
else
|
|
return 0;
|
|
}
|
|
|
|
static void platform_resume_end(suspend_state_t state)
|
|
{
|
|
if (state == PM_SUSPEND_FREEZE && freeze_ops && freeze_ops->end)
|
|
freeze_ops->end();
|
|
else if (suspend_ops && suspend_ops->end)
|
|
suspend_ops->end();
|
|
}
|
|
|
|
static void platform_recover(suspend_state_t state)
|
|
{
|
|
if (state != PM_SUSPEND_FREEZE && suspend_ops->recover)
|
|
suspend_ops->recover();
|
|
}
|
|
|
|
static bool platform_suspend_again(suspend_state_t state)
|
|
{
|
|
return state != PM_SUSPEND_FREEZE && suspend_ops->suspend_again ?
|
|
suspend_ops->suspend_again() : false;
|
|
}
|
|
|
|
#ifdef CONFIG_PM_DEBUG
|
|
static unsigned int pm_test_delay = 5;
|
|
module_param(pm_test_delay, uint, 0644);
|
|
MODULE_PARM_DESC(pm_test_delay,
|
|
"Number of seconds to wait before resuming from suspend test");
|
|
#endif
|
|
|
|
static int suspend_test(int level)
|
|
{
|
|
#ifdef CONFIG_PM_DEBUG
|
|
if (pm_test_level == level) {
|
|
pr_info("suspend debug: Waiting for %d second(s).\n",
|
|
pm_test_delay);
|
|
mdelay(pm_test_delay * 1000);
|
|
return 1;
|
|
}
|
|
#endif /* !CONFIG_PM_DEBUG */
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* suspend_prepare - Prepare for entering system sleep state.
|
|
*
|
|
* Common code run for every system sleep state that can be entered (except for
|
|
* hibernation). Run suspend notifiers, allocate the "suspend" console and
|
|
* freeze processes.
|
|
*/
|
|
static int suspend_prepare(suspend_state_t state)
|
|
{
|
|
int error, nr_calls = 0;
|
|
|
|
if (!sleep_state_supported(state))
|
|
return -EPERM;
|
|
|
|
pm_prepare_console();
|
|
|
|
error = __pm_notifier_call_chain(PM_SUSPEND_PREPARE, -1, &nr_calls);
|
|
if (error) {
|
|
nr_calls--;
|
|
goto Finish;
|
|
}
|
|
|
|
trace_suspend_resume(TPS("freeze_processes"), 0, true);
|
|
error = suspend_freeze_processes();
|
|
trace_suspend_resume(TPS("freeze_processes"), 0, false);
|
|
if (!error)
|
|
return 0;
|
|
|
|
suspend_stats.failed_freeze++;
|
|
dpm_save_failed_step(SUSPEND_FREEZE);
|
|
Finish:
|
|
__pm_notifier_call_chain(PM_POST_SUSPEND, nr_calls, NULL);
|
|
pm_restore_console();
|
|
return error;
|
|
}
|
|
|
|
/* default implementation */
|
|
void __weak arch_suspend_disable_irqs(void)
|
|
{
|
|
local_irq_disable();
|
|
}
|
|
|
|
/* default implementation */
|
|
void __weak arch_suspend_enable_irqs(void)
|
|
{
|
|
local_irq_enable();
|
|
}
|
|
|
|
/**
|
|
* suspend_enter - Make the system enter the given sleep state.
|
|
* @state: System sleep state to enter.
|
|
* @wakeup: Returns information that the sleep state should not be re-entered.
|
|
*
|
|
* This function should be called after devices have been suspended.
|
|
*/
|
|
static int suspend_enter(suspend_state_t state, bool *wakeup)
|
|
{
|
|
char suspend_abort[MAX_SUSPEND_ABORT_LEN];
|
|
int error, last_dev;
|
|
|
|
error = platform_suspend_prepare(state);
|
|
if (error)
|
|
goto Platform_finish;
|
|
|
|
error = dpm_suspend_late(PMSG_SUSPEND);
|
|
if (error) {
|
|
last_dev = suspend_stats.last_failed_dev + REC_FAILED_NUM - 1;
|
|
last_dev %= REC_FAILED_NUM;
|
|
pr_err("PM: late suspend of devices failed\n");
|
|
log_suspend_abort_reason("%s device failed to power down",
|
|
suspend_stats.failed_devs[last_dev]);
|
|
goto Platform_finish;
|
|
}
|
|
error = platform_suspend_prepare_late(state);
|
|
if (error)
|
|
goto Devices_early_resume;
|
|
|
|
error = dpm_suspend_noirq(PMSG_SUSPEND);
|
|
if (error) {
|
|
last_dev = suspend_stats.last_failed_dev + REC_FAILED_NUM - 1;
|
|
last_dev %= REC_FAILED_NUM;
|
|
pr_err("PM: noirq suspend of devices failed\n");
|
|
log_suspend_abort_reason("noirq suspend of %s device failed",
|
|
suspend_stats.failed_devs[last_dev]);
|
|
goto Platform_early_resume;
|
|
}
|
|
error = platform_suspend_prepare_noirq(state);
|
|
if (error)
|
|
goto Platform_wake;
|
|
|
|
if (suspend_test(TEST_PLATFORM))
|
|
goto Platform_wake;
|
|
|
|
/*
|
|
* PM_SUSPEND_FREEZE equals
|
|
* frozen processes + suspended devices + idle processors.
|
|
* Thus we should invoke freeze_enter() soon after
|
|
* all the devices are suspended.
|
|
*/
|
|
if (state == PM_SUSPEND_FREEZE) {
|
|
trace_suspend_resume(TPS("machine_suspend"), state, true);
|
|
freeze_enter();
|
|
trace_suspend_resume(TPS("machine_suspend"), state, false);
|
|
goto Platform_wake;
|
|
}
|
|
|
|
error = disable_nonboot_cpus();
|
|
if (error || suspend_test(TEST_CPUS)) {
|
|
log_suspend_abort_reason("Disabling non-boot cpus failed");
|
|
goto Enable_cpus;
|
|
}
|
|
|
|
arch_suspend_disable_irqs();
|
|
BUG_ON(!irqs_disabled());
|
|
|
|
error = syscore_suspend();
|
|
if (!error) {
|
|
*wakeup = pm_wakeup_pending();
|
|
if (!(suspend_test(TEST_CORE) || *wakeup)) {
|
|
trace_suspend_resume(TPS("machine_suspend"),
|
|
state, true);
|
|
error = suspend_ops->enter(state);
|
|
trace_suspend_resume(TPS("machine_suspend"),
|
|
state, false);
|
|
events_check_enabled = false;
|
|
} else if (*wakeup) {
|
|
pm_get_active_wakeup_sources(suspend_abort,
|
|
MAX_SUSPEND_ABORT_LEN);
|
|
log_suspend_abort_reason(suspend_abort);
|
|
error = -EBUSY;
|
|
}
|
|
syscore_resume();
|
|
}
|
|
|
|
arch_suspend_enable_irqs();
|
|
BUG_ON(irqs_disabled());
|
|
|
|
Enable_cpus:
|
|
enable_nonboot_cpus();
|
|
|
|
Platform_wake:
|
|
platform_resume_noirq(state);
|
|
dpm_resume_noirq(PMSG_RESUME);
|
|
|
|
Platform_early_resume:
|
|
platform_resume_early(state);
|
|
|
|
Devices_early_resume:
|
|
dpm_resume_early(PMSG_RESUME);
|
|
|
|
Platform_finish:
|
|
platform_resume_finish(state);
|
|
return error;
|
|
}
|
|
|
|
/**
|
|
* suspend_devices_and_enter - Suspend devices and enter system sleep state.
|
|
* @state: System sleep state to enter.
|
|
*/
|
|
int suspend_devices_and_enter(suspend_state_t state)
|
|
{
|
|
int error;
|
|
bool wakeup = false;
|
|
|
|
if (!sleep_state_supported(state))
|
|
return -ENOSYS;
|
|
|
|
error = platform_suspend_begin(state);
|
|
if (error)
|
|
goto Close;
|
|
|
|
suspend_console();
|
|
suspend_test_start();
|
|
error = dpm_suspend_start(PMSG_SUSPEND);
|
|
if (error) {
|
|
pr_err("PM: Some devices failed to suspend, or early wake event detected\n");
|
|
log_suspend_abort_reason("Some devices failed to suspend, or early wake event detected");
|
|
goto Recover_platform;
|
|
}
|
|
suspend_test_finish("suspend devices");
|
|
if (suspend_test(TEST_DEVICES))
|
|
goto Recover_platform;
|
|
|
|
do {
|
|
error = suspend_enter(state, &wakeup);
|
|
} while (!error && !wakeup && platform_suspend_again(state));
|
|
|
|
Resume_devices:
|
|
suspend_test_start();
|
|
dpm_resume_end(PMSG_RESUME);
|
|
suspend_test_finish("resume devices");
|
|
trace_suspend_resume(TPS("resume_console"), state, true);
|
|
resume_console();
|
|
trace_suspend_resume(TPS("resume_console"), state, false);
|
|
|
|
Close:
|
|
platform_resume_end(state);
|
|
return error;
|
|
|
|
Recover_platform:
|
|
platform_recover(state);
|
|
goto Resume_devices;
|
|
}
|
|
|
|
/**
|
|
* suspend_finish - Clean up before finishing the suspend sequence.
|
|
*
|
|
* Call platform code to clean up, restart processes, and free the console that
|
|
* we've allocated. This routine is not called for hibernation.
|
|
*/
|
|
static void suspend_finish(void)
|
|
{
|
|
suspend_thaw_processes();
|
|
pm_notifier_call_chain(PM_POST_SUSPEND);
|
|
pm_restore_console();
|
|
}
|
|
|
|
/**
|
|
* enter_state - Do common work needed to enter system sleep state.
|
|
* @state: System sleep state to enter.
|
|
*
|
|
* Make sure that no one else is trying to put the system into a sleep state.
|
|
* Fail if that's not the case. Otherwise, prepare for system suspend, make the
|
|
* system enter the given sleep state and clean up after wakeup.
|
|
*/
|
|
static int enter_state(suspend_state_t state)
|
|
{
|
|
int error;
|
|
|
|
trace_suspend_resume(TPS("suspend_enter"), state, true);
|
|
if (state == PM_SUSPEND_FREEZE) {
|
|
#ifdef CONFIG_PM_DEBUG
|
|
if (pm_test_level != TEST_NONE && pm_test_level <= TEST_CPUS) {
|
|
pr_warn("PM: Unsupported test mode for suspend to idle, please choose none/freezer/devices/platform.\n");
|
|
return -EAGAIN;
|
|
}
|
|
#endif
|
|
} else if (!valid_state(state)) {
|
|
return -EINVAL;
|
|
}
|
|
if (!mutex_trylock(&pm_mutex))
|
|
return -EBUSY;
|
|
|
|
if (state == PM_SUSPEND_FREEZE)
|
|
freeze_begin();
|
|
|
|
#ifndef CONFIG_SUSPEND_SKIP_SYNC
|
|
trace_suspend_resume(TPS("sync_filesystems"), 0, true);
|
|
pr_info("PM: Syncing filesystems ... ");
|
|
sys_sync();
|
|
pr_cont("done.\n");
|
|
trace_suspend_resume(TPS("sync_filesystems"), 0, false);
|
|
#endif
|
|
|
|
pr_debug("PM: Preparing system for sleep (%s)\n", pm_states[state]);
|
|
pm_suspend_clear_flags();
|
|
error = suspend_prepare(state);
|
|
if (error)
|
|
goto Unlock;
|
|
|
|
if (suspend_test(TEST_FREEZER))
|
|
goto Finish;
|
|
|
|
trace_suspend_resume(TPS("suspend_enter"), state, false);
|
|
pr_debug("PM: Suspending system (%s)\n", pm_states[state]);
|
|
pm_restrict_gfp_mask();
|
|
error = suspend_devices_and_enter(state);
|
|
pm_restore_gfp_mask();
|
|
|
|
Finish:
|
|
pr_debug("PM: Finishing wakeup.\n");
|
|
suspend_finish();
|
|
Unlock:
|
|
mutex_unlock(&pm_mutex);
|
|
return error;
|
|
}
|
|
|
|
static void pm_suspend_marker(char *annotation)
|
|
{
|
|
struct timespec ts;
|
|
struct rtc_time tm;
|
|
|
|
getnstimeofday(&ts);
|
|
rtc_time_to_tm(ts.tv_sec, &tm);
|
|
pr_info("PM: suspend %s %d-%02d-%02d %02d:%02d:%02d.%09lu UTC\n",
|
|
annotation, tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday,
|
|
tm.tm_hour, tm.tm_min, tm.tm_sec, ts.tv_nsec);
|
|
}
|
|
|
|
/**
|
|
* pm_suspend - Externally visible function for suspending the system.
|
|
* @state: System sleep state to enter.
|
|
*
|
|
* Check if the value of @state represents one of the supported states,
|
|
* execute enter_state() and update system suspend statistics.
|
|
*/
|
|
int pm_suspend(suspend_state_t state)
|
|
{
|
|
int error;
|
|
|
|
if (state <= PM_SUSPEND_ON || state >= PM_SUSPEND_MAX)
|
|
return -EINVAL;
|
|
|
|
pm_suspend_marker("entry");
|
|
error = enter_state(state);
|
|
if (error) {
|
|
suspend_stats.fail++;
|
|
dpm_save_failed_errno(error);
|
|
} else {
|
|
suspend_stats.success++;
|
|
}
|
|
pm_suspend_marker("exit");
|
|
return error;
|
|
}
|
|
EXPORT_SYMBOL(pm_suspend);
|