2019-05-29 17:17:56 +03:00
// SPDX-License-Identifier: GPL-2.0-only
2011-02-10 13:04:45 +03:00
/*
* Copyright ( C ) 2011 Google , Inc .
*
* Author :
* Colin Cross < ccross @ android . com >
*/
# include <linux/kernel.h>
# include <linux/cpu_pm.h>
# include <linux/module.h>
# include <linux/notifier.h>
# include <linux/spinlock.h>
2011-07-23 01:57:09 +04:00
# include <linux/syscore_ops.h>
2011-02-10 13:04:45 +03:00
2017-07-28 10:09:25 +03:00
static ATOMIC_NOTIFIER_HEAD ( cpu_pm_notifier_chain ) ;
2011-02-10 13:04:45 +03:00
static int cpu_pm_notify ( enum cpu_pm_event event , int nr_to_call , int * nr_calls )
{
int ret ;
2017-07-28 10:09:25 +03:00
/*
* __atomic_notifier_call_chain has a RCU read critical section , which
* could be disfunctional in cpu idle . Copy RCU_NONIDLE code to let
* RCU know this .
*/
rcu_irq_enter_irqson ( ) ;
ret = __atomic_notifier_call_chain ( & cpu_pm_notifier_chain , event , NULL ,
2011-02-10 13:04:45 +03:00
nr_to_call , nr_calls ) ;
2017-07-28 10:09:25 +03:00
rcu_irq_exit_irqson ( ) ;
2011-02-10 13:04:45 +03:00
return notifier_to_errno ( ret ) ;
}
/**
* cpu_pm_register_notifier - register a driver with cpu_pm
* @ nb : notifier block to register
*
* Add a driver to a list of drivers that are notified about
* CPU and CPU cluster low power entry and exit .
*
* This function may sleep , and has the same return conditions as
* raw_notifier_chain_register .
*/
int cpu_pm_register_notifier ( struct notifier_block * nb )
{
2017-07-28 10:09:25 +03:00
return atomic_notifier_chain_register ( & cpu_pm_notifier_chain , nb ) ;
2011-02-10 13:04:45 +03:00
}
EXPORT_SYMBOL_GPL ( cpu_pm_register_notifier ) ;
/**
* cpu_pm_unregister_notifier - unregister a driver with cpu_pm
* @ nb : notifier block to be unregistered
*
* Remove a driver from the CPU PM notifier list .
*
* This function may sleep , and has the same return conditions as
* raw_notifier_chain_unregister .
*/
int cpu_pm_unregister_notifier ( struct notifier_block * nb )
{
2017-07-28 10:09:25 +03:00
return atomic_notifier_chain_unregister ( & cpu_pm_notifier_chain , nb ) ;
2011-02-10 13:04:45 +03:00
}
EXPORT_SYMBOL_GPL ( cpu_pm_unregister_notifier ) ;
/**
2012-06-01 03:26:07 +04:00
* cpu_pm_enter - CPU low power entry notifier
2011-02-10 13:04:45 +03:00
*
* Notifies listeners that a single CPU is entering a low power state that may
* cause some blocks in the same power domain as the cpu to reset .
*
* Must be called on the affected CPU with interrupts disabled . Platform is
* responsible for ensuring that cpu_pm_enter is not called twice on the same
* CPU before cpu_pm_exit is called . Notified drivers can include VFP
2012-06-01 03:26:07 +04:00
* co - processor , interrupt controller and its PM extensions , local CPU
2011-02-10 13:04:45 +03:00
* timers context save / restore which shouldn ' t be interrupted . Hence it
* must be called with interrupts disabled .
*
* Return conditions are same as __raw_notifier_call_chain .
*/
int cpu_pm_enter ( void )
{
int nr_calls ;
int ret = 0 ;
ret = cpu_pm_notify ( CPU_PM_ENTER , - 1 , & nr_calls ) ;
if ( ret )
/*
* Inform listeners ( nr_calls - 1 ) about failure of CPU PM
* PM entry who are notified earlier to prepare for it .
*/
cpu_pm_notify ( CPU_PM_ENTER_FAILED , nr_calls - 1 , NULL ) ;
return ret ;
}
EXPORT_SYMBOL_GPL ( cpu_pm_enter ) ;
/**
2012-06-01 03:26:07 +04:00
* cpu_pm_exit - CPU low power exit notifier
2011-02-10 13:04:45 +03:00
*
* Notifies listeners that a single CPU is exiting a low power state that may
* have caused some blocks in the same power domain as the cpu to reset .
*
* Notified drivers can include VFP co - processor , interrupt controller
2012-06-01 03:26:07 +04:00
* and its PM extensions , local CPU timers context save / restore which
2011-02-10 13:04:45 +03:00
* shouldn ' t be interrupted . Hence it must be called with interrupts disabled .
*
* Return conditions are same as __raw_notifier_call_chain .
*/
int cpu_pm_exit ( void )
{
2017-07-28 10:09:25 +03:00
return cpu_pm_notify ( CPU_PM_EXIT , - 1 , NULL ) ;
2011-02-10 13:04:45 +03:00
}
EXPORT_SYMBOL_GPL ( cpu_pm_exit ) ;
/**
2012-06-01 03:26:07 +04:00
* cpu_cluster_pm_enter - CPU cluster low power entry notifier
2011-02-10 13:04:45 +03:00
*
* Notifies listeners that all cpus in a power domain are entering a low power
* state that may cause some blocks in the same power domain to reset .
*
* Must be called after cpu_pm_enter has been called on all cpus in the power
* domain , and before cpu_pm_exit has been called on any cpu in the power
* domain . Notified drivers can include VFP co - processor , interrupt controller
2012-06-01 03:26:07 +04:00
* and its PM extensions , local CPU timers context save / restore which
2011-02-10 13:04:45 +03:00
* shouldn ' t be interrupted . Hence it must be called with interrupts disabled .
*
* Must be called with interrupts disabled .
*
* Return conditions are same as __raw_notifier_call_chain .
*/
int cpu_cluster_pm_enter ( void )
{
int nr_calls ;
int ret = 0 ;
ret = cpu_pm_notify ( CPU_CLUSTER_PM_ENTER , - 1 , & nr_calls ) ;
if ( ret )
/*
* Inform listeners ( nr_calls - 1 ) about failure of CPU cluster
* PM entry who are notified earlier to prepare for it .
*/
cpu_pm_notify ( CPU_CLUSTER_PM_ENTER_FAILED , nr_calls - 1 , NULL ) ;
return ret ;
}
EXPORT_SYMBOL_GPL ( cpu_cluster_pm_enter ) ;
/**
2012-06-01 03:26:07 +04:00
* cpu_cluster_pm_exit - CPU cluster low power exit notifier
2011-02-10 13:04:45 +03:00
*
* Notifies listeners that all cpus in a power domain are exiting form a
* low power state that may have caused some blocks in the same power domain
* to reset .
*
2015-09-03 01:18:57 +03:00
* Must be called after cpu_cluster_pm_enter has been called for the power
2011-02-10 13:04:45 +03:00
* domain , and before cpu_pm_exit has been called on any cpu in the power
* domain . Notified drivers can include VFP co - processor , interrupt controller
2012-06-01 03:26:07 +04:00
* and its PM extensions , local CPU timers context save / restore which
2011-02-10 13:04:45 +03:00
* shouldn ' t be interrupted . Hence it must be called with interrupts disabled .
*
* Return conditions are same as __raw_notifier_call_chain .
*/
int cpu_cluster_pm_exit ( void )
{
2017-07-28 10:09:25 +03:00
return cpu_pm_notify ( CPU_CLUSTER_PM_EXIT , - 1 , NULL ) ;
2011-02-10 13:04:45 +03:00
}
EXPORT_SYMBOL_GPL ( cpu_cluster_pm_exit ) ;
2011-07-23 01:57:09 +04:00
# ifdef CONFIG_PM
static int cpu_pm_suspend ( void )
{
int ret ;
ret = cpu_pm_enter ( ) ;
if ( ret )
return ret ;
ret = cpu_cluster_pm_enter ( ) ;
return ret ;
}
static void cpu_pm_resume ( void )
{
cpu_cluster_pm_exit ( ) ;
cpu_pm_exit ( ) ;
}
static struct syscore_ops cpu_pm_syscore_ops = {
. suspend = cpu_pm_suspend ,
. resume = cpu_pm_resume ,
} ;
static int cpu_pm_init ( void )
{
register_syscore_ops ( & cpu_pm_syscore_ops ) ;
return 0 ;
}
core_initcall ( cpu_pm_init ) ;
# endif