pm: pm_idle add support for SMP
Signed-off-by: buxiasen <buxiasen@xiaomi.com>
This commit is contained in:
parent
4b7c36554c
commit
43176bbade
|
@ -23,6 +23,8 @@ config PM_NDOMAINS
|
|||
For example, you may want to separately manage the power from the
|
||||
Network domain, shutting down the network when it is not be used,
|
||||
from the UI domain, shutting down the UI when it is not in use.
|
||||
For SMP case, should add CONFIG_SMP_NCPU manually, or will lead to
|
||||
a static assert.
|
||||
|
||||
config PM_PROCFS
|
||||
bool "PM proc fs support"
|
||||
|
|
|
@ -26,18 +26,197 @@
|
|||
#include <nuttx/power/pm.h>
|
||||
#include <sched/sched.h>
|
||||
|
||||
/****************************************************************************
|
||||
* Pre-processor Definitions
|
||||
****************************************************************************/
|
||||
|
||||
#ifdef CONFIG_SMP
|
||||
|
||||
# define PM_SMP_ALL_CPUS ((1 << CONFIG_SMP_NCPUS) - 1)
|
||||
# define PM_SMP_CPU_DOMAIN(cpu) (CONFIG_PM_NDOMAINS - CONFIG_SMP_NCPUS + (cpu))
|
||||
|
||||
static_assert(CONFIG_PM_NDOMAINS >= (CONFIG_SMP_NCPUS + 1),
|
||||
"No enough domain for PM SMP to occupy");
|
||||
|
||||
/****************************************************************************
|
||||
* Private Type Declarations
|
||||
****************************************************************************/
|
||||
|
||||
struct pm_idle_s
|
||||
{
|
||||
spinlock_t lock;
|
||||
cpu_set_t running;
|
||||
cpu_set_t firstdone;
|
||||
};
|
||||
|
||||
/****************************************************************************
|
||||
* Private Data
|
||||
****************************************************************************/
|
||||
|
||||
static struct pm_idle_s g_pm_idle =
|
||||
{
|
||||
SP_UNLOCKED,
|
||||
PM_SMP_ALL_CPUS,
|
||||
0,
|
||||
};
|
||||
|
||||
/****************************************************************************
|
||||
* Public Functions
|
||||
****************************************************************************/
|
||||
|
||||
/****************************************************************************
|
||||
* Name: pm_idle_unlock
|
||||
*
|
||||
* Description:
|
||||
* Release SMP idle cpus lock, allow other cpu continue idle process.
|
||||
*
|
||||
* Input Parameters:
|
||||
* None.
|
||||
*
|
||||
* Returned Value:
|
||||
* None.
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
void pm_idle_unlock(void)
|
||||
{
|
||||
spin_unlock(&g_pm_idle.lock);
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
* Name: pm_idle_lock
|
||||
*
|
||||
* Description:
|
||||
* Claim SMP idle cpus lock, other cpu have to wait until released.
|
||||
*
|
||||
* Input Parameters:
|
||||
* cpu - The current CPU, used to update cpu_set_t
|
||||
*
|
||||
* Returned Value:
|
||||
* true - Current CPU is the first one woken from sleep, should handle
|
||||
* system domain restore process also.
|
||||
* false - Current CPU is not the first one woken from sleep, should only
|
||||
* handle cpu domain restore process.
|
||||
*
|
||||
* Assumptions:
|
||||
* Restore operation pm_changestate(, PM_RESTORE) will done inside pm_idle.
|
||||
* Handler don't have to care about it.
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
bool pm_idle_lock(int cpu)
|
||||
{
|
||||
bool first;
|
||||
spin_lock(&g_pm_idle.lock);
|
||||
first = (g_pm_idle.running == 0);
|
||||
CPU_SET(cpu, &g_pm_idle.running);
|
||||
return first;
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
* Name: pm_idle
|
||||
*
|
||||
* Description:
|
||||
* Standard pm idle work flow for up_idle, for not smp case.
|
||||
* Standard pm idle work flow for up_idle.
|
||||
* pm_idle_handler_t will be different prototype when SMP.
|
||||
*
|
||||
* Input Parameters:
|
||||
* handler - The execution after PM_IDLE_DOMAIN state changed.
|
||||
* handler - The execution after cpu and system domain state changed.
|
||||
*
|
||||
* Returned Value:
|
||||
* None.
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
void pm_idle(pm_idle_handler_t handler)
|
||||
{
|
||||
enum pm_state_e systemstate;
|
||||
enum pm_state_e oldstate;
|
||||
enum pm_state_e newstate;
|
||||
irqstate_t flags;
|
||||
int domain;
|
||||
bool first;
|
||||
bool last;
|
||||
int cpu;
|
||||
int ret;
|
||||
|
||||
systemstate = PM_RESTORE;
|
||||
cpu = this_cpu();
|
||||
domain = PM_SMP_CPU_DOMAIN(cpu);
|
||||
|
||||
/* If sched lock before irq save, and irq handler do post, scheduler will
|
||||
* be delayed after WFI until next sched unlock. which is not acceptable.
|
||||
*/
|
||||
|
||||
flags = up_irq_save();
|
||||
sched_lock();
|
||||
|
||||
oldstate = pm_querystate(domain);
|
||||
newstate = pm_checkstate(domain);
|
||||
|
||||
ret = pm_changestate(domain, newstate);
|
||||
if (ret < 0)
|
||||
{
|
||||
newstate = PM_NORMAL;
|
||||
}
|
||||
|
||||
if (oldstate != newstate)
|
||||
{
|
||||
pm_stay(PM_IDLE_DOMAIN, newstate);
|
||||
if (CPU_ISSET(cpu, &g_pm_idle.firstdone))
|
||||
{
|
||||
pm_relax(PM_IDLE_DOMAIN, oldstate);
|
||||
}
|
||||
else
|
||||
{
|
||||
spin_lock(&g_pm_idle.lock);
|
||||
CPU_SET(cpu, &g_pm_idle.firstdone);
|
||||
spin_unlock(&g_pm_idle.lock);
|
||||
}
|
||||
}
|
||||
|
||||
spin_lock(&g_pm_idle.lock);
|
||||
CPU_CLR(cpu, &g_pm_idle.running);
|
||||
last = (g_pm_idle.running == 0);
|
||||
if (last)
|
||||
{
|
||||
systemstate = pm_checkstate(PM_IDLE_DOMAIN);
|
||||
ret = pm_changestate(PM_IDLE_DOMAIN, systemstate);
|
||||
if (ret < 0)
|
||||
{
|
||||
systemstate = PM_NORMAL;
|
||||
}
|
||||
}
|
||||
|
||||
first = handler(cpu, newstate, systemstate);
|
||||
|
||||
if (first)
|
||||
{
|
||||
pm_changestate(PM_IDLE_DOMAIN, PM_RESTORE);
|
||||
}
|
||||
|
||||
spin_unlock(&g_pm_idle.lock);
|
||||
pm_changestate(domain, PM_RESTORE);
|
||||
|
||||
/* If there is pending irq, enable irq make handlers finish all
|
||||
* execution will be better decrease scheduler context switch times.
|
||||
*/
|
||||
|
||||
up_irq_restore(flags);
|
||||
sched_unlock();
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
/****************************************************************************
|
||||
* Name: pm_idle
|
||||
*
|
||||
* Description:
|
||||
* Standard pm idle work flow for up_idle.
|
||||
* pm_idle_handler_t will be different prototype when SMP.
|
||||
*
|
||||
* Input Parameters:
|
||||
* handler - The execution after cpu and system domain state changed.
|
||||
*
|
||||
* Returned Value:
|
||||
* None.
|
||||
|
@ -75,3 +254,5 @@ void pm_idle(pm_idle_handler_t handler)
|
|||
up_irq_restore(flags);
|
||||
sched_unlock();
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
|
@ -145,7 +145,63 @@ enum pm_state_e
|
|||
PM_COUNT,
|
||||
};
|
||||
|
||||
typedef void (*pm_idle_handler_t)(enum pm_state_e);
|
||||
#ifdef CONFIG_SMP
|
||||
|
||||
/****************************************************************************
|
||||
* Name: pm_idle_handler_t
|
||||
*
|
||||
* Description:
|
||||
* Handle the pm low power operations and lock actions.
|
||||
* As there is WFI inside handler, must manually call
|
||||
* pm_idle_unlock before go into WFI.
|
||||
* Also must call pm_idle_lock when woken from WFI at once.
|
||||
*
|
||||
* Input Parameters:
|
||||
* cpu - The current working cpu.
|
||||
* cpustate - The current cpu power state.
|
||||
* systemstate - The current system power state. If not the lastcore
|
||||
* enter idle, systemstate always PM_RESTORE. If not
|
||||
* PM_RESTORE, handler should cover system pm operations.
|
||||
*
|
||||
* Returned Value:
|
||||
* Should pass the parameter get from pm_idle_lock.
|
||||
* true - Is the first core already wake up from WFI.
|
||||
* false - Not the first core who woken up from WFI.
|
||||
*
|
||||
* Assumptions:
|
||||
* The action between pm_idle_unlock and pm_idle_lock must be
|
||||
* no cross cpu and no system pm operation related.
|
||||
* Always call handler with locked, should do this action chain inside
|
||||
* handle. enter_ops->unlock->wfi->lock->leave_ops->return.
|
||||
* Wait-like kernel API not allowed here.
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
typedef bool (*pm_idle_handler_t)(int cpu,
|
||||
enum pm_state_e cpustate,
|
||||
enum pm_state_e systemstate);
|
||||
#else
|
||||
|
||||
/****************************************************************************
|
||||
* Name: pm_idle_handler_t
|
||||
*
|
||||
* Description:
|
||||
* Handle the pm low power action and execution for not SMP case.
|
||||
* Possible execution for long time because of WFI inside.
|
||||
*
|
||||
* Input Parameters:
|
||||
* systemstate - The new system power state.
|
||||
*
|
||||
* Returned Value:
|
||||
* None
|
||||
*
|
||||
* Assumptions:
|
||||
* Wait-like kernel API not allowed here.
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
typedef void (*pm_idle_handler_t)(enum pm_state_e systemstate);
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_PM_PROCFS
|
||||
struct pm_preparefail_s
|
||||
|
@ -823,10 +879,11 @@ void pm_auto_updatestate(int domain);
|
|||
* Name: pm_idle
|
||||
*
|
||||
* Description:
|
||||
* Standard pm idle work flow for up_idle, for not smp case.
|
||||
* Standard pm idle work flow for up_idle.
|
||||
* pm_idle_handler_t will be different prototype when SMP.
|
||||
*
|
||||
* Input Parameters:
|
||||
* handler - The execution after PM_IDLE_DOMAIN state changed.
|
||||
* handler - The execution after cpu and system domain state changed.
|
||||
*
|
||||
* Returned Value:
|
||||
* None.
|
||||
|
@ -835,6 +892,53 @@ void pm_auto_updatestate(int domain);
|
|||
|
||||
void pm_idle(pm_idle_handler_t handler);
|
||||
|
||||
/****************************************************************************
|
||||
* Name: pm_idle_unlock
|
||||
*
|
||||
* Description:
|
||||
* Release SMP idle cpus lock, allow other cpu continue idle process.
|
||||
*
|
||||
* Input Parameters:
|
||||
* None.
|
||||
*
|
||||
* Returned Value:
|
||||
* None.
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
#if CONFIG_SMP
|
||||
void pm_idle_unlock(void);
|
||||
#else
|
||||
# define pm_idle_unlock()
|
||||
#endif
|
||||
|
||||
/****************************************************************************
|
||||
* Name: pm_idle_lock
|
||||
*
|
||||
* Description:
|
||||
* Claim SMP idle cpus lock, other cpu have to wait until released.
|
||||
*
|
||||
* Input Parameters:
|
||||
* cpu - The current CPU, used to update cpu_set_t
|
||||
*
|
||||
* Returned Value:
|
||||
* true - Current CPU is the first one woken from sleep, should handle
|
||||
* system domain restore process also.
|
||||
* false - Current CPU is not the first one woken from sleep, should only
|
||||
* handle cpu domain restore process.
|
||||
*
|
||||
* Assumptions:
|
||||
* Restore operation pm_changestate(, PM_RESTORE) will done inside pm_idle.
|
||||
* Handler don't have to care about it.
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
#if CONFIG_SMP
|
||||
bool pm_idle_lock(int cpu);
|
||||
#else
|
||||
# define pm_idle_lock(cpu) (0)
|
||||
#endif
|
||||
|
||||
#undef EXTERN
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
|
@ -874,6 +978,9 @@ void pm_idle(pm_idle_handler_t handler);
|
|||
# define pm_changestate(domain,state) (0)
|
||||
# define pm_querystate(domain) (0)
|
||||
# define pm_auto_updatestate(domain)
|
||||
# define pm_idle(handler)
|
||||
# define pm_idle_unlock()
|
||||
# define pm_idle_lock(cpu) (0)
|
||||
|
||||
#endif /* CONFIG_PM */
|
||||
#endif /* __INCLUDE_NUTTX_POWER_PM_H */
|
||||
|
|
Loading…
Reference in New Issue