Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0-only
0002 /*
0003  * Copyright (C) 2011 Google, Inc.
0004  *
0005  * Author:
0006  *  Colin Cross <ccross@android.com>
0007  */
0008 
0009 #include <linux/kernel.h>
0010 #include <linux/cpu_pm.h>
0011 #include <linux/module.h>
0012 #include <linux/notifier.h>
0013 #include <linux/spinlock.h>
0014 #include <linux/syscore_ops.h>
0015 
0016 /*
0017  * atomic_notifiers use a spinlock_t, which can block under PREEMPT_RT.
0018  * Notifications for cpu_pm will be issued by the idle task itself, which can
0019  * never block, IOW it requires using a raw_spinlock_t.
0020  */
0021 static struct {
0022     struct raw_notifier_head chain;
0023     raw_spinlock_t lock;
0024 } cpu_pm_notifier = {
0025     .chain = RAW_NOTIFIER_INIT(cpu_pm_notifier.chain),
0026     .lock  = __RAW_SPIN_LOCK_UNLOCKED(cpu_pm_notifier.lock),
0027 };
0028 
0029 static int cpu_pm_notify(enum cpu_pm_event event)
0030 {
0031     int ret;
0032 
0033     /*
0034      * This introduces a RCU read critical section, which could be
0035      * disfunctional in cpu idle. Copy RCU_NONIDLE code to let RCU know
0036      * this.
0037      */
0038     ct_irq_enter_irqson();
0039     rcu_read_lock();
0040     ret = raw_notifier_call_chain(&cpu_pm_notifier.chain, event, NULL);
0041     rcu_read_unlock();
0042     ct_irq_exit_irqson();
0043 
0044     return notifier_to_errno(ret);
0045 }
0046 
0047 static int cpu_pm_notify_robust(enum cpu_pm_event event_up, enum cpu_pm_event event_down)
0048 {
0049     unsigned long flags;
0050     int ret;
0051 
0052     ct_irq_enter_irqson();
0053     raw_spin_lock_irqsave(&cpu_pm_notifier.lock, flags);
0054     ret = raw_notifier_call_chain_robust(&cpu_pm_notifier.chain, event_up, event_down, NULL);
0055     raw_spin_unlock_irqrestore(&cpu_pm_notifier.lock, flags);
0056     ct_irq_exit_irqson();
0057 
0058     return notifier_to_errno(ret);
0059 }
0060 
0061 /**
0062  * cpu_pm_register_notifier - register a driver with cpu_pm
0063  * @nb: notifier block to register
0064  *
0065  * Add a driver to a list of drivers that are notified about
0066  * CPU and CPU cluster low power entry and exit.
0067  *
0068  * This function has the same return conditions as raw_notifier_chain_register.
0069  */
0070 int cpu_pm_register_notifier(struct notifier_block *nb)
0071 {
0072     unsigned long flags;
0073     int ret;
0074 
0075     raw_spin_lock_irqsave(&cpu_pm_notifier.lock, flags);
0076     ret = raw_notifier_chain_register(&cpu_pm_notifier.chain, nb);
0077     raw_spin_unlock_irqrestore(&cpu_pm_notifier.lock, flags);
0078     return ret;
0079 }
0080 EXPORT_SYMBOL_GPL(cpu_pm_register_notifier);
0081 
0082 /**
0083  * cpu_pm_unregister_notifier - unregister a driver with cpu_pm
0084  * @nb: notifier block to be unregistered
0085  *
0086  * Remove a driver from the CPU PM notifier list.
0087  *
0088  * This function has the same return conditions as raw_notifier_chain_unregister.
0089  */
0090 int cpu_pm_unregister_notifier(struct notifier_block *nb)
0091 {
0092     unsigned long flags;
0093     int ret;
0094 
0095     raw_spin_lock_irqsave(&cpu_pm_notifier.lock, flags);
0096     ret = raw_notifier_chain_unregister(&cpu_pm_notifier.chain, nb);
0097     raw_spin_unlock_irqrestore(&cpu_pm_notifier.lock, flags);
0098     return ret;
0099 }
0100 EXPORT_SYMBOL_GPL(cpu_pm_unregister_notifier);
0101 
0102 /**
0103  * cpu_pm_enter - CPU low power entry notifier
0104  *
0105  * Notifies listeners that a single CPU is entering a low power state that may
0106  * cause some blocks in the same power domain as the cpu to reset.
0107  *
0108  * Must be called on the affected CPU with interrupts disabled.  Platform is
0109  * responsible for ensuring that cpu_pm_enter is not called twice on the same
0110  * CPU before cpu_pm_exit is called. Notified drivers can include VFP
0111  * co-processor, interrupt controller and its PM extensions, local CPU
0112  * timers context save/restore which shouldn't be interrupted. Hence it
0113  * must be called with interrupts disabled.
0114  *
0115  * Return conditions are same as __raw_notifier_call_chain.
0116  */
0117 int cpu_pm_enter(void)
0118 {
0119     return cpu_pm_notify_robust(CPU_PM_ENTER, CPU_PM_ENTER_FAILED);
0120 }
0121 EXPORT_SYMBOL_GPL(cpu_pm_enter);
0122 
0123 /**
0124  * cpu_pm_exit - CPU low power exit notifier
0125  *
0126  * Notifies listeners that a single CPU is exiting a low power state that may
0127  * have caused some blocks in the same power domain as the cpu to reset.
0128  *
0129  * Notified drivers can include VFP co-processor, interrupt controller
0130  * and its PM extensions, local CPU timers context save/restore which
0131  * shouldn't be interrupted. Hence it must be called with interrupts disabled.
0132  *
0133  * Return conditions are same as __raw_notifier_call_chain.
0134  */
0135 int cpu_pm_exit(void)
0136 {
0137     return cpu_pm_notify(CPU_PM_EXIT);
0138 }
0139 EXPORT_SYMBOL_GPL(cpu_pm_exit);
0140 
0141 /**
0142  * cpu_cluster_pm_enter - CPU cluster low power entry notifier
0143  *
0144  * Notifies listeners that all cpus in a power domain are entering a low power
0145  * state that may cause some blocks in the same power domain to reset.
0146  *
0147  * Must be called after cpu_pm_enter has been called on all cpus in the power
0148  * domain, and before cpu_pm_exit has been called on any cpu in the power
0149  * domain. Notified drivers can include VFP co-processor, interrupt controller
0150  * and its PM extensions, local CPU timers context save/restore which
0151  * shouldn't be interrupted. Hence it must be called with interrupts disabled.
0152  *
0153  * Must be called with interrupts disabled.
0154  *
0155  * Return conditions are same as __raw_notifier_call_chain.
0156  */
0157 int cpu_cluster_pm_enter(void)
0158 {
0159     return cpu_pm_notify_robust(CPU_CLUSTER_PM_ENTER, CPU_CLUSTER_PM_ENTER_FAILED);
0160 }
0161 EXPORT_SYMBOL_GPL(cpu_cluster_pm_enter);
0162 
0163 /**
0164  * cpu_cluster_pm_exit - CPU cluster low power exit notifier
0165  *
0166  * Notifies listeners that all cpus in a power domain are exiting form a
0167  * low power state that may have caused some blocks in the same power domain
0168  * to reset.
0169  *
0170  * Must be called after cpu_cluster_pm_enter has been called for the power
0171  * domain, and before cpu_pm_exit has been called on any cpu in the power
0172  * domain. Notified drivers can include VFP co-processor, interrupt controller
0173  * and its PM extensions, local CPU timers context save/restore which
0174  * shouldn't be interrupted. Hence it must be called with interrupts disabled.
0175  *
0176  * Return conditions are same as __raw_notifier_call_chain.
0177  */
0178 int cpu_cluster_pm_exit(void)
0179 {
0180     return cpu_pm_notify(CPU_CLUSTER_PM_EXIT);
0181 }
0182 EXPORT_SYMBOL_GPL(cpu_cluster_pm_exit);
0183 
0184 #ifdef CONFIG_PM
0185 static int cpu_pm_suspend(void)
0186 {
0187     int ret;
0188 
0189     ret = cpu_pm_enter();
0190     if (ret)
0191         return ret;
0192 
0193     ret = cpu_cluster_pm_enter();
0194     return ret;
0195 }
0196 
0197 static void cpu_pm_resume(void)
0198 {
0199     cpu_cluster_pm_exit();
0200     cpu_pm_exit();
0201 }
0202 
0203 static struct syscore_ops cpu_pm_syscore_ops = {
0204     .suspend = cpu_pm_suspend,
0205     .resume = cpu_pm_resume,
0206 };
0207 
0208 static int cpu_pm_init(void)
0209 {
0210     register_syscore_ops(&cpu_pm_syscore_ops);
0211     return 0;
0212 }
0213 core_initcall(cpu_pm_init);
0214 #endif