linux/kernel/cpu_pm.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0-only
   2/*
   3 * Copyright (C) 2011 Google, Inc.
   4 *
   5 * Author:
   6 *      Colin Cross <ccross@android.com>
   7 */
   8
   9#include <linux/kernel.h>
  10#include <linux/cpu_pm.h>
  11#include <linux/module.h>
  12#include <linux/notifier.h>
  13#include <linux/spinlock.h>
  14#include <linux/syscore_ops.h>
  15
  16static ATOMIC_NOTIFIER_HEAD(cpu_pm_notifier_chain);
  17
  18static int cpu_pm_notify(enum cpu_pm_event event, int nr_to_call, int *nr_calls)
  19{
  20        int ret;
  21
  22        /*
  23         * __atomic_notifier_call_chain has a RCU read critical section, which
  24         * could be disfunctional in cpu idle. Copy RCU_NONIDLE code to let
  25         * RCU know this.
  26         */
  27        rcu_irq_enter_irqson();
  28        ret = __atomic_notifier_call_chain(&cpu_pm_notifier_chain, event, NULL,
  29                nr_to_call, nr_calls);
  30        rcu_irq_exit_irqson();
  31
  32        return notifier_to_errno(ret);
  33}
  34
  35/**
  36 * cpu_pm_register_notifier - register a driver with cpu_pm
  37 * @nb: notifier block to register
  38 *
  39 * Add a driver to a list of drivers that are notified about
  40 * CPU and CPU cluster low power entry and exit.
  41 *
  42 * This function may sleep, and has the same return conditions as
  43 * raw_notifier_chain_register.
  44 */
  45int cpu_pm_register_notifier(struct notifier_block *nb)
  46{
  47        return atomic_notifier_chain_register(&cpu_pm_notifier_chain, nb);
  48}
  49EXPORT_SYMBOL_GPL(cpu_pm_register_notifier);
  50
  51/**
  52 * cpu_pm_unregister_notifier - unregister a driver with cpu_pm
  53 * @nb: notifier block to be unregistered
  54 *
  55 * Remove a driver from the CPU PM notifier list.
  56 *
  57 * This function may sleep, and has the same return conditions as
  58 * raw_notifier_chain_unregister.
  59 */
  60int cpu_pm_unregister_notifier(struct notifier_block *nb)
  61{
  62        return atomic_notifier_chain_unregister(&cpu_pm_notifier_chain, nb);
  63}
  64EXPORT_SYMBOL_GPL(cpu_pm_unregister_notifier);
  65
  66/**
  67 * cpu_pm_enter - CPU low power entry notifier
  68 *
  69 * Notifies listeners that a single CPU is entering a low power state that may
  70 * cause some blocks in the same power domain as the cpu to reset.
  71 *
  72 * Must be called on the affected CPU with interrupts disabled.  Platform is
  73 * responsible for ensuring that cpu_pm_enter is not called twice on the same
  74 * CPU before cpu_pm_exit is called. Notified drivers can include VFP
  75 * co-processor, interrupt controller and its PM extensions, local CPU
  76 * timers context save/restore which shouldn't be interrupted. Hence it
  77 * must be called with interrupts disabled.
  78 *
  79 * Return conditions are same as __raw_notifier_call_chain.
  80 */
  81int cpu_pm_enter(void)
  82{
  83        int nr_calls;
  84        int ret = 0;
  85
  86        ret = cpu_pm_notify(CPU_PM_ENTER, -1, &nr_calls);
  87        if (ret)
  88                /*
  89                 * Inform listeners (nr_calls - 1) about failure of CPU PM
  90                 * PM entry who are notified earlier to prepare for it.
  91                 */
  92                cpu_pm_notify(CPU_PM_ENTER_FAILED, nr_calls - 1, NULL);
  93
  94        return ret;
  95}
  96EXPORT_SYMBOL_GPL(cpu_pm_enter);
  97
  98/**
  99 * cpu_pm_exit - CPU low power exit notifier
 100 *
 101 * Notifies listeners that a single CPU is exiting a low power state that may
 102 * have caused some blocks in the same power domain as the cpu to reset.
 103 *
 104 * Notified drivers can include VFP co-processor, interrupt controller
 105 * and its PM extensions, local CPU timers context save/restore which
 106 * shouldn't be interrupted. Hence it must be called with interrupts disabled.
 107 *
 108 * Return conditions are same as __raw_notifier_call_chain.
 109 */
 110int cpu_pm_exit(void)
 111{
 112        return cpu_pm_notify(CPU_PM_EXIT, -1, NULL);
 113}
 114EXPORT_SYMBOL_GPL(cpu_pm_exit);
 115
 116/**
 117 * cpu_cluster_pm_enter - CPU cluster low power entry notifier
 118 *
 119 * Notifies listeners that all cpus in a power domain are entering a low power
 120 * state that may cause some blocks in the same power domain to reset.
 121 *
 122 * Must be called after cpu_pm_enter has been called on all cpus in the power
 123 * domain, and before cpu_pm_exit has been called on any cpu in the power
 124 * domain. Notified drivers can include VFP co-processor, interrupt controller
 125 * and its PM extensions, local CPU timers context save/restore which
 126 * shouldn't be interrupted. Hence it must be called with interrupts disabled.
 127 *
 128 * Must be called with interrupts disabled.
 129 *
 130 * Return conditions are same as __raw_notifier_call_chain.
 131 */
 132int cpu_cluster_pm_enter(void)
 133{
 134        int nr_calls;
 135        int ret = 0;
 136
 137        ret = cpu_pm_notify(CPU_CLUSTER_PM_ENTER, -1, &nr_calls);
 138        if (ret)
 139                /*
 140                 * Inform listeners (nr_calls - 1) about failure of CPU cluster
 141                 * PM entry who are notified earlier to prepare for it.
 142                 */
 143                cpu_pm_notify(CPU_CLUSTER_PM_ENTER_FAILED, nr_calls - 1, NULL);
 144
 145        return ret;
 146}
 147EXPORT_SYMBOL_GPL(cpu_cluster_pm_enter);
 148
 149/**
 150 * cpu_cluster_pm_exit - CPU cluster low power exit notifier
 151 *
 152 * Notifies listeners that all cpus in a power domain are exiting form a
 153 * low power state that may have caused some blocks in the same power domain
 154 * to reset.
 155 *
 156 * Must be called after cpu_cluster_pm_enter has been called for the power
 157 * domain, and before cpu_pm_exit has been called on any cpu in the power
 158 * domain. Notified drivers can include VFP co-processor, interrupt controller
 159 * and its PM extensions, local CPU timers context save/restore which
 160 * shouldn't be interrupted. Hence it must be called with interrupts disabled.
 161 *
 162 * Return conditions are same as __raw_notifier_call_chain.
 163 */
 164int cpu_cluster_pm_exit(void)
 165{
 166        return cpu_pm_notify(CPU_CLUSTER_PM_EXIT, -1, NULL);
 167}
 168EXPORT_SYMBOL_GPL(cpu_cluster_pm_exit);
 169
 170#ifdef CONFIG_PM
 171static int cpu_pm_suspend(void)
 172{
 173        int ret;
 174
 175        ret = cpu_pm_enter();
 176        if (ret)
 177                return ret;
 178
 179        ret = cpu_cluster_pm_enter();
 180        return ret;
 181}
 182
 183static void cpu_pm_resume(void)
 184{
 185        cpu_cluster_pm_exit();
 186        cpu_pm_exit();
 187}
 188
 189static struct syscore_ops cpu_pm_syscore_ops = {
 190        .suspend = cpu_pm_suspend,
 191        .resume = cpu_pm_resume,
 192};
 193
 194static int cpu_pm_init(void)
 195{
 196        register_syscore_ops(&cpu_pm_syscore_ops);
 197        return 0;
 198}
 199core_initcall(cpu_pm_init);
 200#endif
 201