rel_1.6.0 init

This commit is contained in:
guocheng.kgc 2020-06-18 20:06:52 +08:00 committed by shengdong.dsd
commit 27b3e2883d
19359 changed files with 8093121 additions and 0 deletions

View file

@ -0,0 +1,24 @@
menuconfig AOS_COMP_PWRMGMT
bool "pwrmgmt"
default n
help
if AOS_COMP_PWRMGMT
# Configurations for component pwrmgmt
config PWRMGMT_CONFIG_SHOW
int "PWRMGMT_CONFIG_SHOW"
range 0 1
default 0
help
set to 1 if you want to call state show function:enable cpu_pwr_c_support_print,
cpu_pwr_info_show,cpu_pwr_state_show.
default 0
config PWRMGMT_CONFIG_DEBUG
int "PWRMGMT_CONFIG_DEBUG"
range 0 1
default 0
help
set to 1 if you want to show debug infomation.
default 0
endif

View file

@ -0,0 +1,20 @@
## Contents
## Introduction
**pwrmgmt** power management module of AliOS Things
### Features
- power management feature
### Directories
```sh
pwrmgmt
├─ cpu_pwr_lib.c # API implementation of power management module
├─ cpu_tickless.c # tickless strategy
├─ cpu_pwr_hal_lib.c # hal layer of power management module
├─ cpu_pwr_show.c # used for infomation show
```
### Dependencies
## Reference

View file

@ -0,0 +1,935 @@
/*
* Copyright (C) 2018 Alibaba Group Holding Limited
*/
/*
This file provides Hardware Abstraction Layer of CPU power management support.
*/
#include <stdlib.h>
#include "cpu_pwr_lib.h"
#include "cpu_pwr_hal_lib.h"
#include "pwrmgmt_api.h"
#include "pwrmgmt_debug.h"
#define CPU_TREE_MUX_LOCK() \
(void)krhino_mutex_lock(&cpu_pwr_hal_mux, RHINO_WAIT_FOREVER)
#define CPU_TREE_MUX_UNLOCK() (void)krhino_mutex_unlock(&cpu_pwr_hal_mux)
/* the index of this p_cpu_node_array implies the cpu logic id */
cpu_pwr_t *p_cpu_node_array[RHINO_CONFIG_CPU_NUM];
static kmutex_t cpu_pwr_hal_mux;
static kspinlock_t cpu_pwr_lock;
/*
board_cpu_pwr_init() must be provided by board or platform,
different board/platform may has different cpu topology.
*/
extern pwr_status_t board_cpu_pwr_init(void);
static int num_of_bit_one_get(uint32_t n);
cpu_pwr_t *cpu_pwr_node_find_by_name(char *p_name, uint32_t index)
{
int i = 0;
for (i = 0; i < RHINO_CONFIG_CPU_NUM; i++) {
if (p_cpu_node_array[i]->name == NULL) {
continue;
}
if ((!strncmp(p_cpu_node_array[i]->name, p_name, CPU_PWR_NODE_NAME_LEN)) &&
(p_cpu_node_array[i]->unit == index)) {
/* find it, and return */
return p_cpu_node_array[i];
}
}
return NULL;
}
pwr_status_t cpu_pwr_node_init_(char *name, uint32_t unit, cpu_pwr_t *p_cpu_node)
{
if (name == NULL || p_cpu_node == NULL) {
return PWR_ERR;
}
memset(p_cpu_node, 0, sizeof(cpu_pwr_t));
p_cpu_node->name = name;
p_cpu_node->unit = unit;
p_cpu_node->current_c_state = CPU_CSTATE_NONE;
p_cpu_node->support_bitset_c = 0;
PWR_DBG(DBG_INFO, "init %s%d done\n", name, unit);
return PWR_OK;
}
/**
* cpu_pwr_node_init_static will create and initialize a cpu power management
* node according the given level number, node name and node unit.
*
* This routine is normally used to create cpu power management topology tree by
* boards/platform.
*
* For example, cpu_pwr_node_init_static(2, "core", 0, p_cpu_node) will init a
* cpu power management node named "core0" in level2 of the cpu power management
* topology tree, and return the struction point address, but it do not belong
* to any other node and do not has any child at this time,
* cpu_pwr_child_add(p_parent, p_child) could be used to create relationship
* with other node in the topology tree.
*
* the space of p_cpu_node is provided by caller.
*/
pwr_status_t cpu_pwr_node_init_static(char *name, uint32_t unit, cpu_pwr_t *p_cpu_node)
{
if (name == NULL || p_cpu_node == NULL) {
return PWR_ERR;
}
return cpu_pwr_node_init_(name, unit, p_cpu_node);
}
/**
* cpu_pwr_node_init_dyn() is same as cpu_pwr_node_init_static(),
* the difference is the space of pp_cpu_node is mallocced in
* running time.
*/
pwr_status_t cpu_pwr_node_init_dyn(char *name, uint32_t unit, cpu_pwr_t **pp_cpu_node)
{
cpu_pwr_t *p_cpu_node;
if (name == NULL || pp_cpu_node == NULL) {
return PWR_ERR;
}
p_cpu_node = (cpu_pwr_t *)malloc(sizeof(cpu_pwr_t));
if (p_cpu_node == NULL) {
PWR_DBG(DBG_ERR, "malloc failed\n");
return PWR_ERR;
}
*pp_cpu_node = p_cpu_node;
return cpu_pwr_node_init_(name, unit, *pp_cpu_node);
}
/**
*
* cpu_pwr_node_record - record the cpu power management node.
* The node information is setup by this way:
* a) a array named p_cpu_node_array[32] with 32 point(type is cpu_pwr_t *) is
* created in cpu_pwr_hal_lib_init();
* b) all the 32 point is set to NULL in cpu_pwr_hal_lib_init();
* c) when BSP/platform init the cpu pwr, it knows the cpu index information of
* that cpu, BSP/platform should call cpu_pwr_node_record(p_cpu_node, cpu_idx)
* to save the cpu_pwr_t * information of cpu indicated by cpu_idx into
* p_cpu_node_array[cpu_idx];
*
* When there is requirement to find the cpu_pwr_t * according the cpu_idx,
* just return the p_cpu_node_array[cpu_idx] is fine and save much time.
*
* @return PWR_OK or PWR_ERR when failed.
*/
pwr_status_t cpu_pwr_node_record(cpu_pwr_t *p_cpu_node, uint32_t cpu_idx)
{
if (p_cpu_node == NULL) {
PWR_DBG(DBG_ERR, "p_cpu_node == NULL\n");
return PWR_ERR;
}
if (cpu_idx >= RHINO_CONFIG_CPU_NUM) {
PWR_DBG(DBG_ERR, "cpu_idx(%d) error, it should be 0 ~ %d\n", cpu_idx,
RHINO_CONFIG_CPU_NUM - 1);
return PWR_ERR;
}
if (p_cpu_node_array[cpu_idx] != NULL) {
PWR_DBG(DBG_ERR, "p_cpu_node_array[%d] != NULL\n", cpu_idx);
return PWR_ERR;
}
/* set the flag to a default value */
p_cpu_node->cstate_updating = FALSE;
p_cpu_node_array[cpu_idx] = p_cpu_node;
PWR_DBG(DBG_INFO, "%s%d connect to logic id %d done\n", p_cpu_node->name,
p_cpu_node->unit, cpu_idx);
return PWR_OK;
}
/**
* cpu_pwr_c_method_set() set the method of controlling C state,
*normally it will be called by platform in board_cpu_pwr_init().
*
* @renturn PWR_OK or PWR_ERR when failed.
*/
pwr_status_t cpu_pwr_c_method_set(uint32_t cpu_idx, cpu_cstate_set_t cpu_cstate_set_func)
{
cpu_pwr_t *p_cpu_node;
pwr_status_t ret = PWR_OK;
if (cpu_idx >= RHINO_CONFIG_CPU_NUM) {
PWR_DBG(DBG_ERR, "cpu_idx(%d) error, it should be 0 ~ %d\n", cpu_idx,
RHINO_CONFIG_CPU_NUM - 1);
return PWR_ERR;
}
/* fetch the p_cpu_node directly from p_cpu_node_array according cpu_idx */
p_cpu_node = p_cpu_node_array[cpu_idx];
if (p_cpu_node == NULL) {
PWR_DBG(DBG_ERR, "did not find p_cpu_node in p_cpu_node_array "
"with cpu_idx[%d]\n", cpu_idx);
return PWR_ERR;
}
CPU_TREE_MUX_LOCK();
/* bind P state set/get method into this node */
p_cpu_node->cpu_cstate_set_func = cpu_cstate_set_func;
/* set C state to C0 by default, is here the right place ?*/
p_cpu_node->current_c_state = CPU_CSTATE_C0;
PWR_DBG(DBG_INFO, "set CPU(%s%d) C-state-set(function) to 0x%08x\n",
p_cpu_node->name, p_cpu_node->unit, cpu_cstate_set_func);
PWR_DBG(DBG_INFO, "set CPU(%s%d) current_c_state to C%d\n",
p_cpu_node->name, p_cpu_node->unit, p_cpu_node->current_c_state);
CPU_TREE_MUX_UNLOCK();
return ret;
}
static pwr_status_t cpu_pwr_c_state_set_(cpu_pwr_t *p_cpu_node,
cpu_cstate_t cpu_c_state, int master,
int all_cores_need_sync)
{
pwr_status_t ret = PWR_OK;
uint32_t state_c_match = 0;
if (p_cpu_node == NULL) {
PWR_DBG(DBG_ERR, "PWR_ERR: p_cpu_node == NULL\n");
return PWR_ERR;
}
state_c_match = (CPU_STATE_BIT(cpu_c_state) & p_cpu_node->support_bitset_c);
if ((p_cpu_node->cpu_cstate_set_func != NULL) && (state_c_match == 0)) {
PWR_DBG(DBG_INFO,
"%s%d(with supportCBitset[0x%08x]) "
"do not support C state P%d\n",
p_cpu_node->name, p_cpu_node->unit,
p_cpu_node->support_bitset_c, cpu_c_state);
}
/*
* If current p_cpu_node support cpu_cstate_set_func() and match the C state
* which it support, use it directly and update C state of all the children
* at same time.
*/
if ((p_cpu_node->cpu_cstate_set_func != NULL) && (state_c_match != 0)) {
/*
* The new C state should be recorded before calling the real C
* control method, the reason is when C0->C1~Cn, the CPU will stop
* to execute the instruction.
*/
/* update the current C state */
p_cpu_node->current_c_state = cpu_c_state;
/* hold input C state into desire_c_state */
p_cpu_node->desire_c_state = cpu_c_state;
/*
* cpu_c_state == CPU_CSTATE_C1, C1 state must a independent
* state that each core support, each core could enter/exit
* C1 state independently, so each core could update it's C1
* state independently;
*
* C2/C3 or higher is dependent on platfrom/arch, some platform
* support C2/C3 per-core, but some platform do not support.
* all_cores_need_sync is assert here to check this information,
* 1) all_cores_need_sync == TRUE, means that this Cx state is not
* per-core independent, it need all cores enter/exit this Cx
* state at same time.
* 2) all_cores_need_sync == FALSE, means that this Cx state is
* pre-core independent, it could enter/exit this Cx state
* independently.
*
* When all_cores_need_sync is TRUE, we leave the responsibility
* to master(means the last core enter idle mode) to update
* all cores C state of Cx(C2/C3 or higher).
*/
/* call the real C idle method */
ret = p_cpu_node->cpu_cstate_set_func(cpu_c_state, master);
if (ret != PWR_OK) {
PWR_DBG(DBG_ERR, "%s%d -> cpu_cstate_set_func(P%d) failed\n",
p_cpu_node->name, p_cpu_node->unit, cpu_c_state);
return PWR_ERR;
}
return PWR_OK;
}
/*
* if we
* 1) - do not support cpu_cstate_set_func()
* or
* 2) - the request C state is not support
*/
PWR_DBG(DBG_INFO, "p_cpu_node(%s%d) do not support cpu_cstate_set_func or the request C state is not support\n",
p_cpu_node->name, p_cpu_node->unit);
return PWR_OK;
}
/**
* cpu_pwr_c_state_get - get C state of CPU indicated by cpu_idx
*
* This routine will get C state of CPU indicated by cpu_idx, the C state will
* return in pCpuCStatus.
*
* @return PWR_OK or PWR_ERR when failed.
*/
static pwr_status_t _cpu_pwr_c_state_get(uint32_t cpu_idx,
cpu_cstate_t *p_cpu_c_state)
{
cpu_pwr_t *p_cpu_node;
if (cpu_idx >= RHINO_CONFIG_CPU_NUM) {
PWR_DBG(DBG_ERR, "cpu_idx(%d) error, it should be 0 ~ %d\n", cpu_idx,
RHINO_CONFIG_CPU_NUM - 1);
return PWR_ERR;
}
/* fetch the p_cpu_node directly from p_cpu_node_array according cpu_idx */
p_cpu_node = p_cpu_node_array[cpu_idx];
if (p_cpu_node == NULL) {
PWR_DBG(DBG_ERR, "did not find p_cpu_node in p_cpu_node_array "
"with cpu_idx[%d]\n", cpu_idx);
return PWR_ERR;
}
*p_cpu_c_state = p_cpu_node->current_c_state;
return PWR_OK;
}
pwr_status_t cpu_pwr_c_state_get(uint32_t cpu_idx, cpu_cstate_t *p_cpu_c_state)
{
pwr_status_t ret;
krhino_spin_lock_irq_save(&cpu_pwr_lock);
ret = _cpu_pwr_c_state_get(cpu_idx, p_cpu_c_state);
krhino_spin_unlock_irq_restore(&cpu_pwr_lock);
return ret;
}
/**
*
* cpu_pwr_c_state_set - put CPU into given C state
*
* This routine will put CPU into given C state, under SMP environment,
* cpu_idx is fetched locally from cpu_index_get which implies that
* this function is always applied to the current core.
* This function has to be called on the CPU for which the C state is being set.
* the caller has to ensure that the code will be executed on the same core
* (such as called by task with CPU affinity or from ISR context).
*
* @return PWR_OK or PWR_ERR when failed.
*/
static pwr_status_t _cpu_pwr_c_state_set(cpu_cstate_t target_c_state)
{
uint32_t cpu_idx;
cpu_pwr_t * p_cpu_node;
pwr_status_t ret = PWR_OK;
int master;
cpu_idx = 0;
/* fetch the p_cpu_node directly from p_cpu_node_array according cpu_idx */
p_cpu_node = p_cpu_node_array[cpu_idx];
if (p_cpu_node == NULL) {
PWR_DBG(DBG_ERR, "did not find p_cpu_node in p_cpu_node_array with cpu_idx[%d]\n",
cpu_idx);
return PWR_ERR;
}
if (p_cpu_node->current_c_state == target_c_state) {
return PWR_OK;
}
/*
* at any time for one CPU, only one C state updating process is allowed,
* when a C state updating is going on, it set cstate_updating to 1,
* if the other thread(a task or interrupt) try to start other C state
* updating, it will encount cstate_updating == 1, and should
* return immediately.
*
* a exception should be consider, idle task()->cpu_pwr_down()->
* cpu_pwr_c_state_set(CPU_CSTATE_C1(C2,C3,...)) will make
* CPU stopping execute instruction and never return, which will
* leave cstate_updating as TRUE and never be cleaned to FALSE.
*
* when cpu_pwr_c_state_set(CPU_CSTATE_C0) is called again
* to make C state changed from C1(C2,C3,...) to C0, we check
* the value of cstate_updating, if it is TURE, which implies that
* cstate_updating is not cleaned in previous state change(from C0
* to C1,C2,C3,...), cstate_updating will be forced to FALSE.
*/
if (target_c_state == CPU_CSTATE_C0 && p_cpu_node->current_c_state != CPU_CSTATE_C0
&& p_cpu_node->cstate_updating == TRUE) {
p_cpu_node->cstate_updating = FALSE;
}
if (p_cpu_node->cstate_updating == FALSE) {
p_cpu_node->cstate_updating = TRUE;
if (target_c_state == CPU_CSTATE_C0) {
/*
* cpu_c_state == CPU_CSTATE_C0, which means current call
* stack is:
* krhino_intrpt_enter() -> cpu_pwr_up() -> cpu_pwr_c_state_set(C0)
* what we need to do here is set current_c_state/desire_c_state
* to C0.
* For the first core which wakeup there maybe need some
* prepare work need to do, so the master(the first one
* open door) should be distinguished here.
*/
/*
* for UP, we are always the master, because there is
* only one core in system.
*/
master = TRUE;
/*
* master == TRUE, means we are the first one wakeup,
* a liite more work need to do than other cores here
* is update all the parent to C0 state. And send IPI
* to other cores to update it's self Cx state when
* wake up from C2 or high Cx state(C3/C4...).
*/
if (master == TRUE) {
/* call the real C control method */
if (p_cpu_node->cpu_cstate_set_func != NULL) {
/*
* Note, the master value is TRUE here,
* cpu_cstate_set_func() could take proper action
* based on this value, because we are the first
* core wake up.
*/
ret = p_cpu_node->cpu_cstate_set_func(target_c_state, master);
if (ret != PWR_OK) {
PWR_DBG(DBG_ERR, "%s%d -> cpu_cstate_set_func(P%d) failed\n",
p_cpu_node->name, p_cpu_node->unit, target_c_state);
ret = PWR_ERR;
}
}
/* update C state to C0 */
p_cpu_node->current_c_state = CPU_CSTATE_C0;
p_cpu_node->desire_c_state = CPU_CSTATE_C0;
} else {
/*
* If come here, we must be subsequent core wake up
* after first core, what we need to do is updating
* self state to C0 and call function if need,
* normmally cpu_cstate_set_func() will do nothing here.
*/
if (p_cpu_node->cpu_cstate_set_func != NULL) {
ret = p_cpu_node->cpu_cstate_set_func(target_c_state, master);
if (ret != PWR_OK) {
PWR_DBG(DBG_ERR, "%s%d -> cpu_cstate_set_func(P%d) failed\n",
p_cpu_node->name, p_cpu_node->unit, target_c_state);
ret = PWR_ERR;
}
}
p_cpu_node->current_c_state = CPU_CSTATE_C0;
p_cpu_node->desire_c_state = CPU_CSTATE_C0;
}
} else {
/*
* if come here, cpu_c_state must is not CPU_CSTATE_C0,
* it must be C1 or high C2/C3..., it must be going
* to idle Cx mode.
*/
/*
* for UP, we are always the master, because there is
* only one core in system.
*/
master = TRUE;
ret = cpu_pwr_c_state_set_(p_cpu_node, target_c_state, master, FALSE);
}
/*
* after C state change completed, it should set cstate_updating
* to 0 to allow C state updating again.
*
* a exception should be consider, idletask()->cpu_pwr_down()->
* cpu_pwr_c_state_set(CPU_CSTATE_C1(C2,C3,...)) will make
* CPU stopping execute instruction and never return, which will
* lead this line never be executed and leave cstate_updating as TRUE.
*
* when cpu_pwr_c_state_set(CPU_CSTATE_C0) is called again
* to make C state changed from C1(C2,C3,...) to C0, we check
* the value of cstate_updating, if it is TURE, which implies that
* cstate_updating is not cleaned in previous state change(from C0
* to C1,C2,C3,...), cstate_updating will be forced to FALSE.
*/
p_cpu_node->cstate_updating = FALSE;
}
return ret;
}
pwr_status_t cpu_pwr_c_state_set(cpu_cstate_t target_c_state)
{
pwr_status_t ret;
krhino_spin_lock_irq_save(&cpu_pwr_lock);
ret = _cpu_pwr_c_state_set(target_c_state);
krhino_spin_unlock_irq_restore(&cpu_pwr_lock);
return ret;
}
static pwr_status_t cpu_pwr_c_state_capability_set_(cpu_pwr_t *p_cpu_node,
int cpu_idx, int32_t support_bitset_c)
{
if (p_cpu_node == NULL) {
PWR_DBG(DBG_ERR, "p_cpu_node == NULL\n");
return PWR_ERR;
}
/* if this level support setting the P state, it implies that it
maintain the support_bitset_p value. */
if (p_cpu_node->cpu_cstate_set_func != NULL) {
p_cpu_node->support_bitset_c = support_bitset_c;
PWR_DBG(DBG_INFO, "p_cpu_node(%s%d) support_bitset_c = 0x%016lx\n",
p_cpu_node->name, p_cpu_node->unit, p_cpu_node->support_bitset_c);
return PWR_OK;
}
PWR_DBG(DBG_ERR, "PWR_ERR: p_cpu_node(%s%d) support_bitset_c = 0x%016lx\n",
p_cpu_node->name, p_cpu_node->unit, p_cpu_node->support_bitset_c);
return PWR_ERR;
}
static pwr_status_t cpu_pwr_c_state_capability_get_(cpu_pwr_t *p_cpu_node, int cpu_idx,
uint32_t *p_support_bitset_c)
{
if (p_cpu_node == NULL) {
PWR_DBG(DBG_ERR, "p_cpu_node == NULL\n");
return PWR_ERR;
}
/*
* if this level support setting the C state, it implies that it
* maintain the support_bitset_c value.
*/
if (p_cpu_node->cpu_cstate_set_func != NULL) {
*p_support_bitset_c |= p_cpu_node->support_bitset_c;
PWR_DBG(DBG_INFO, "p_cpu_node(%s%d) support_bitset_c = 0x%016lx\n",
p_cpu_node->name, p_cpu_node->unit, p_cpu_node->support_bitset_c);
/* do not return, continue check if any support C state from parent */
}
return PWR_OK;
}
/**
* cpu_pwr_c_state_capability_set() will set the C state supported of given CPU.
* There is two way to set the C state of CPU which is supplied by this library,
* one is according cpu index, other is according cpu level.
*
* If all the cpu node in same level support same C state, user can use
* cpu_pwr_c_state_capability_set_by_level(level, support_bitset_c) to set all
* nodes in same level at one time.
* If different CPU support different C state, user can use this routine to set
* support C state for each cpu individually.
*
* @return PWR_OK or PWR_ERR when failed.
*/
pwr_status_t cpu_pwr_c_state_capability_set(uint32_t cpu_idx, uint32_t support_bitset_c)
{
cpu_pwr_t *p_cpu_node;
pwr_status_t ret = PWR_OK;
if (cpu_idx >= RHINO_CONFIG_CPU_NUM) {
PWR_DBG(DBG_ERR, "cpu_idx(%d) error, it should be 0 ~ %d\n", cpu_idx,
RHINO_CONFIG_CPU_NUM - 1);
return PWR_ERR;
}
/* fetch the p_cpu_node directly from p_cpu_node_array according cpu_idx */
p_cpu_node = p_cpu_node_array[cpu_idx];
if (p_cpu_node == NULL) {
PWR_DBG(DBG_ERR, "did not find p_cpu_node in p_cpu_node_array with cpu_idx[%d]\n",
cpu_idx);
return PWR_ERR;
}
CPU_TREE_MUX_LOCK();
ret = cpu_pwr_c_state_capability_set_(p_cpu_node, cpu_idx, support_bitset_c);
CPU_TREE_MUX_UNLOCK();
return ret;
}
/**
* cpu_pwr_c_state_capability_get() will return the all C state supported of
* given CPU.
*
* @return PWR_OK or PWR_ERR when failed.
*/
pwr_status_t cpu_pwr_c_state_capability_get(uint32_t cpu_idx,
uint32_t *p_support_bitset_c)
{
cpu_pwr_t *p_cpu_node;
pwr_status_t ret = PWR_OK;
if (cpu_idx >= RHINO_CONFIG_CPU_NUM) {
PWR_DBG(DBG_ERR, "cpu_idx(%d) error, it should be 0 ~ %d\n", cpu_idx,
RHINO_CONFIG_CPU_NUM - 1);
return PWR_ERR;
}
/* fetch the p_cpu_node directly from p_cpu_node_array according cpu_idx */
p_cpu_node = p_cpu_node_array[cpu_idx];
if (p_cpu_node == NULL) {
PWR_DBG(DBG_ERR, "did not find p_cpu_node in p_cpu_node_array with cpu_idx[%d]\n",
cpu_idx);
return PWR_ERR;
}
/* bzero return value first */
*p_support_bitset_c = 0;
CPU_TREE_MUX_LOCK();
ret = cpu_pwr_c_state_capability_get_(p_cpu_node, cpu_idx, p_support_bitset_c);
CPU_TREE_MUX_UNLOCK();
PWR_DBG(DBG_INFO, "%s%d C support bit set 0x%08x\n", p_cpu_node->name,
p_cpu_node->unit, *p_support_bitset_c);
return ret;
}
static pwr_status_t cpu_pwr_c_state_latency_save_(cpu_pwr_t *p_cpu_node,
cpu_cstate_t cpu_c_state,
uint32_t latency)
{
int i;
size_t spaceSize = 0;
if (p_cpu_node == NULL) {
PWR_DBG(DBG_ERR, "PWR_ERR: p_cpu_node == NULL\n");
return PWR_ERR;
}
/*
* Note that comparing enums like this is technically
* not valid. There is some ambiguity because while enums are
* intended to be represented by integers, it's up to the
* compiler to decide whether they should be signed or unsigned.
* In this case, we happen to know that the cpu_cstate_t is defined
* to only use unsigned integer values, so we can temporarily
* get away with using a cast here to silence the compiler
* warning, but this test should probably be removed.
*/
for (i = 0; i <= CPU_CSTATE_MAX; i++) {
if (cpu_c_state == CPU_CSTATE_C0 + i) {
break;
}
}
if (i > CPU_CSTATE_MAX) {
PWR_DBG(DBG_ERR, "PWR_ERR: cpu_c_state(%d) > CPU_CSTATE_MAX(%d)\n",
cpu_c_state, CPU_PSTATE_MAX);
return PWR_ERR;
}
if (p_cpu_node->cpu_cstate_set_func != NULL) {
/* how many C state is supportted */
if (p_cpu_node->cstate_nums == 0) {
p_cpu_node->cstate_nums = num_of_bit_one_get(p_cpu_node->support_bitset_c);
if (p_cpu_node->cstate_nums == 0) {
PWR_DBG(DBG_ERR, "%s%d->support_bitset_c(%d), cstate_nums() = 0\n",
p_cpu_node->name, p_cpu_node->unit, p_cpu_node->support_bitset_c);
return PWR_ERR;
}
}
/* malloc the space to save frequency info */
if (p_cpu_node->p_pair_latency == NULL) {
spaceSize = sizeof(state_val_pair_t) *p_cpu_node->cstate_nums;
p_cpu_node->p_pair_latency = (state_val_pair_t *)malloc(spaceSize);
if (p_cpu_node->p_pair_latency == NULL) {
PWR_DBG(DBG_ERR, "zalloc failed\n");
return PWR_ERR;
}
/* clean space */
memset(p_cpu_node->p_pair_latency, 0, spaceSize);
/* init value to init value CPU_LATENCY_UNKNOW */
for (i = 0; i < p_cpu_node->cstate_nums; i++) {
p_cpu_node->p_pair_latency[i].value =
(uint32_t)CPU_LATENCY_UNKNOW;
}
}
/* if we support this C state, save the corresponding latency info */
if (CPU_STATE_BIT(cpu_c_state) & p_cpu_node->support_bitset_c) {
/* check if info has been saved before */
for (i = 0; i < p_cpu_node->cstate_nums; i++) {
if ((p_cpu_node->p_pair_latency[i].state == cpu_c_state) &&
(p_cpu_node->p_pair_latency[i].value == latency)) {
return PWR_OK;
}
}
for (i = 0; i < p_cpu_node->cstate_nums; i++) {
/* search a free space on p_pair_latency to save info */
if (p_cpu_node->p_pair_latency[i].value == (uint32_t)CPU_LATENCY_UNKNOW) {
p_cpu_node->p_pair_latency[i].state = cpu_c_state;
p_cpu_node->p_pair_latency[i].value = latency;
return PWR_OK;
}
}
/* if search free space failed */
if (i == p_cpu_node->cstate_nums) {
PWR_DBG(DBG_ERR, "p_cpu_node(%s%d) search free space failed\n",
p_cpu_node->name, p_cpu_node->unit);
return PWR_ERR;
}
}
}
/*
* if come here, there is two possible case:
* 1) this node do not support cpu_cstate_set_func
* or
* 2) this node support cpu_cstate_set_func, but the given C state
* cpu_c_state is not supported
*/
PWR_DBG(DBG_INFO, "p_cpu_node(%s%d) do not support cpu_cstate_set_func "
"or the given C state cpu_c_state is not supported\n",
p_cpu_node->name, p_cpu_node->unit);
return PWR_OK;
}
/**
* cpu_pwr_c_state_latency_save() save the cpu latency information of C state,
* it will let cpu topology system to know how long when the CPU wake up from
* C state, normally it will be called by platform in
* board_cpu_pwr_topo_create().
*
* @return PWR_OK or PWR_ERR when failed.
*/
pwr_status_t cpu_pwr_c_state_latency_save(uint32_t cpu_idx, cpu_cstate_t cpu_c_state,
uint32_t latency)
{
cpu_pwr_t *p_cpu_node;
pwr_status_t ret = PWR_OK;
if (cpu_idx >= RHINO_CONFIG_CPU_NUM) {
PWR_DBG(DBG_ERR, "cpu_idx(%d) error, it should be 0 ~ %d\n", cpu_idx,
RHINO_CONFIG_CPU_NUM - 1);
return PWR_ERR;
}
/* fetch the p_cpu_node directly from p_cpu_node_array according cpu_idx */
p_cpu_node = p_cpu_node_array[cpu_idx];
if (p_cpu_node == NULL) {
PWR_DBG(DBG_ERR, "did not find p_cpu_node in p_cpu_node_array with cpu_idx[%d]\n",
cpu_idx);
return PWR_ERR;
}
CPU_TREE_MUX_LOCK();
ret = cpu_pwr_c_state_latency_save_(p_cpu_node, cpu_c_state, latency);
CPU_TREE_MUX_UNLOCK();
return ret;
}
static uint32_t cpu_pwr_c_state_latency_get_(cpu_pwr_t *p_cpu_node,
cpu_cstate_t cpu_c_state)
{
int i;
if (p_cpu_node == NULL) {
PWR_DBG(DBG_ERR, "p_cpu_node is NULL\n", p_cpu_node);
return (uint32_t)CPU_LATENCY_UNKNOW;
}
/* if we support cpu_cstate_set_func, then we should save the CPU latency
in initialization stage already. */
if (p_cpu_node->cpu_cstate_set_func != NULL) {
/*
* if the query C state is supported, then we must has the CPU
* latency info, or it is a bug which is something such as
* p_pair_latency is is not initialized in the boot stage.
*/
if (CPU_STATE_BIT(cpu_c_state) & p_cpu_node->support_bitset_c) {
if (p_cpu_node->p_pair_latency != NULL) {
for (i = 0; i < p_cpu_node->cstate_nums; i++) {
if (cpu_c_state == p_cpu_node->p_pair_latency[i].state) {
return p_cpu_node->p_pair_latency[i].value;
}
}
} else {
PWR_DBG(DBG_ERR, "p_cpu_node(%s%d)->pPStateAttrArray is not initialized\n",
p_cpu_node->name, p_cpu_node->unit);
return (uint32_t)CPU_LATENCY_UNKNOW;
}
}
}
return (uint32_t)CPU_LATENCY_UNKNOW;
}
/**
* This routine gets the Cstate latency of a logical CPU for the specified
* Cstate. Cstate latency is the time when CPU wake up from idle/sleep state,
* normally this time is a fixed value which is decided by hardware design and
* electrical characteristics. The deeper idle/sleep state CPU enter, the longer
* latency time is need when CPU wakeup.
*
* Tickless module will use latency info to evaluate which C state should enter,
* for example, if latency C1 is 5 ticks, latency C2 is 10 ticks, than if there
* is nothing to do for the system in the feature 10 ticks, system would enter
* C2 state and wake up after 10 ticks.
*
* @return Cstate latency(microsecond) of a logical CPU for specified Cstate
* or CPU_LATENCY_UNKNOW when failed.
*/
uint32_t cpu_pwr_c_state_latency_get(uint32_t cpu_idx, cpu_cstate_t cpu_c_state)
{
cpu_pwr_t *p_cpu_node;
if (cpu_idx >= RHINO_CONFIG_CPU_NUM) {
PWR_DBG(DBG_ERR, "cpu_idx(%d) error, it should be 0 ~ %d\n", cpu_idx,
RHINO_CONFIG_CPU_NUM - 1);
return (uint32_t)CPU_LATENCY_UNKNOW;
}
p_cpu_node = p_cpu_node_array[cpu_idx];
return cpu_pwr_c_state_latency_get_(p_cpu_node, cpu_c_state);
}
/*
* cpu_pwr_hal_lib_init() is called to initialize the cpu hardware
* abstraction layer infrastructure, such as constructing the semphore.
*
* In the end of this routine, it will execute a function named
* board_cpu_pwr_init() which is provided by board/platform
*/
void cpu_pwr_hal_lib_init(void)
{
/* clean the space */
memset(p_cpu_node_array, 0, sizeof(cpu_pwr_t *) *RHINO_CONFIG_CPU_NUM);
krhino_spin_lock_init(&cpu_pwr_lock);
if (krhino_mutex_create(&cpu_pwr_hal_mux, "cpu_pwr_hal_mux") != PWR_OK) {
PWR_DBG(DBG_ERR, "krhino_mutex_create(cpu_pwr_hal_mux) failed!\n");
return;
}
/* call board function to create CPU power management topology tree */
board_cpu_pwr_init();
/*
* set CPU default idle mode to sleep mode,
* if tickless configuration is enabled,
* the idle mode will be updated to tickless mode by tickless module.
*/
(void)cpu_pwr_idle_mode_set(CPU_IDLE_MODE_SLEEP);
PWR_DBG(DBG_INFO, "cpu_pwr_hal_lib_init() done!\n");
}
/**
* num_of_bit_one_get() return the number of 1 for the given binary value.
*/
static int num_of_bit_one_get(uint32_t n)
{
int count = 0;
while (n != 0) {
n = n & (n - 1);
count++;
}
return count;
}

View file

@ -0,0 +1,122 @@
/*
* Copyright (C) 2018 Alibaba Group Holding Limited
*/
#include "pwrmgmt_api.h"
#include "cpu_pwr_hal_lib.h"
#include "cpu_pwr_lib.h"
#include "cpu_tickless.h"
static CPU_IDLE_MODE cpu_pwr_idle_mode = CPU_IDLE_MODE_RUN;
static int cpu_pwr_init_flag = 0;
static kspinlock_t cpu_pwr_spin;
void (*_func_cpu_tickless_down)(void);
void (*_func_cpu_tickless_up)(void);
/**
* cpu_pwrmgmt_init() is the entry of whole cpu power manamgement system.
*/
int cpu_pwrmgmt_init(void)
{
/* avoid reentry */
if (cpu_pwr_init_flag) {
return -1;
}
krhino_spin_lock_init(&cpu_pwr_spin);
/* create cpu power management framework and cpu topology structure */
cpu_pwr_hal_lib_init();
/* init cpu tickless framework */
tickless_init();
/* save the job complete flag */
cpu_pwr_init_flag = 1;
return 0;
}
/**
* cpu_pwr_down() is invoked from idle task when it detects that the kernel
* can go idle. cpu_pwr_down() checks the current cpu idle mode and put
* cpu into lower power mode according different idle mode.
*/
void cpu_pwr_down(void)
{
if (cpu_pwr_init_flag == 0) {
return;
}
if ((cpu_pwr_idle_mode == CPU_IDLE_MODE_TICKLESS) &&
(_func_cpu_tickless_down != NULL)) {
_func_cpu_tickless_down();
} else if (cpu_pwr_idle_mode == CPU_IDLE_MODE_SLEEP) {
(void)cpu_pwr_c_state_set(CPU_CSTATE_C1);
} else {
/* CPU_IDLE_MODE_RUN */
return;
}
}
/**
* cpu_pwr_up() is invoked from all interrupt and exception stubs to wakeup
* the CPU in case it was in lower power mode. This is necessary so that
* the kernel can adjust its tick count to reflect the length of time that
* it was in lower power mode.
*/
void cpu_pwr_up(void)
{
if (cpu_pwr_init_flag == 0) {
return;
}
(void)cpu_pwr_c_state_set(CPU_CSTATE_C0);
/* execute tickless up hook if existed */
if ((cpu_pwr_idle_mode == CPU_IDLE_MODE_TICKLESS) &&
(_func_cpu_tickless_up != NULL)) {
_func_cpu_tickless_up();
}
}
/**
* cpu_pwr_idle_mode_set() sets the power mode used when CPUs are idle. The
* following <mode> settings are supported: CPU_IDLE_MODE_RUN - CPUs are
* continuously running when idle CPU_IDLE_MODE_SLEEP - system clock interupts
* occur at every tick. CPU_IDLE_MODE_TICKLESS- CPUs sleep for multiple tick
* durations, system tick interupts occur only when kernel event expirations
* occur (timout, delays, etc).
* @return PWR_OK, or PWR_ERR in case of failure
*/
pwr_status_t cpu_pwr_idle_mode_set(CPU_IDLE_MODE request_mode)
{
if (request_mode > CPU_IDLE_MODE_TICKLESS) {
return (PWR_ERR);
}
if ((request_mode == CPU_IDLE_MODE_TICKLESS) &&
(_func_cpu_tickless_down == NULL)) {
return (PWR_ERR);
}
if (request_mode == cpu_pwr_idle_mode) {
return (PWR_OK);
}
cpu_pwr_idle_mode = request_mode;
return PWR_OK;
}
/**
* cpu_pwr_idle_mode_get() returns the current idle CPU power mode
*
* @return current CPU idle mode
*/
CPU_IDLE_MODE cpu_pwr_idle_mode_get(void)
{
return cpu_pwr_idle_mode;
}

View file

@ -0,0 +1,113 @@
/*
* Copyright (C) 2018 Alibaba Group Holding Limited
*/
/*
* This file provides cpu_pwr_topology_show()/cpu_pwr_state_show() to show
* information for CPU power management.
*/
#include <stdio.h>
#include <string.h>
#include "pwrmgmt_api.h"
#include "cpu_pwr_lib.h"
#include "cpu_pwr_hal_lib.h"
#include "pwrmgmt_debug.h"
#if (PWRMGMT_CONFIG_SHOW > 0)
extern cpu_pwr_t *p_cpu_pwr_root_node;
extern cpu_pwr_t *p_cpu_node_array[];
static void cpu_pwr_c_support_print(cpu_pwr_t *p_cpu_node, char *prefix)
{
uint32_t state_set;
uint32_t latency = 0;
int state;
int ix;
uint32_t tmp;
(void)printf("%s C-States:\n", prefix);
state_set = p_cpu_node->support_bitset_c;
while (state_set != 0) {
/* find first bit set */
if (state_set == 0) {
state = 0;
} else {
tmp = state_set & (-state_set);
state = 31 - krhino_find_first_bit(&tmp);
}
for (ix = 0; ix < p_cpu_node->cstate_nums; ix++) {
if (state == p_cpu_node->p_pair_latency[ix].state) {
latency = p_cpu_node->p_pair_latency[ix].value;
break;
}
}
(void)printf("%s C%d: %4d us latency\n", prefix, state, latency);
state_set &= ~(1 << state);
}
}
/**
* cpu_pwr_info_show() will show information for all nodes of the cpu power
* management. For each node the C-state and P-state capability
* is provided, as applicable, depending on hardware capabilities and
* configuration.
*
* For P-state capability the CPU core frequency and voltage setting is
* displayed. For C-state capabilities the latency information (wake-up time)
* is displayed.
*/
void cpu_pwr_info_show(void)
{
cpu_pwr_t *p_cpu_node;
int cpu_idx;
(void)printf("\n CPU NODE CAPABILITIES\n"
"--------------- ---------------\n");
for (cpu_idx = 0; cpu_idx < RHINO_CONFIG_CPU_NUM; cpu_idx++) {
p_cpu_node = p_cpu_node_array[cpu_idx];
(void)printf("%s%u (CPU%d)\n", p_cpu_node->name, p_cpu_node->unit, cpu_idx);
cpu_pwr_c_support_print(p_cpu_node, " ");
}
}
/**
* cpu_pwr_state_show() display the CPU power/performance states.
*/
void cpu_pwr_state_show(void)
{
uint32_t cpu_idx;
cpu_cstate_t cState;
printf("\n CPU C-STATE\n"
"---------- ----------\n");
for (cpu_idx = 0; cpu_idx < RHINO_CONFIG_CPU_NUM; cpu_idx++) {
cpu_pwr_c_state_get(cpu_idx, &cState);
printf("CPU%-5u C%-2u\n", cpu_idx, cState);
}
printf("\n");
}
#endif /* RHINO_CONFIG_CPU_PWR_SHOW */
#if (PWRMGMT_CONFIG_DEBUG > 0)
void pwr_debug(pwr_debug_level_t debug_level, const char *fmt_str, ...)
{
va_list param;
if (debug_level >= PWR_DEBUG_LEVEL) {
va_start(param,fmt_str);
vprintf(fmt_str,param);
va_end(param);
}
}
#endif /* RHINO_CONFIG_CPU_PWR_DEBUG > 0 */

View file

@ -0,0 +1,438 @@
/*
* Copyright (C) 2018 Alibaba Group Holding Limited
*/
/*
This file provides support for cpu tickless-idle.
Tickless-idle eliminates unnecessary timer interrupts when the processor is
otherwise idle. When the processor exits the idle state, it advances time by
the number of ticks that elapsed during the idle period. For SMP, the tickless
idle is entered when all CPUs are ready.
*/
#include <stdlib.h>
#include "pwrmgmt_api.h"
#include "cpu_pwr_lib.h"
#include "cpu_pwr_hal_lib.h"
#include "pwrmgmt_debug.h"
#include "cpu_tickless.h"
/* 100 * 365 * 24 * 3600 * 1000 * 1000 = 0xB342EB7C38000 */
#define TIME_100_YEARS_IN_US 0xB342EB7C38000ULL
static uint32_t tickless_ctate_mask; /* C-states support set */
static cpu_cstate_t c_state_entered;
static int is_current_tickless = FALSE;
static uint32_t cStateConfig[RHINO_CONFIG_CPU_NUM];
static uint32_t cStateLatency[RHINO_CONFIG_CPU_NUM][CPU_CSTATE_MAX + 1];
static one_shot_timer_t *cStateOneShotTimer[CPU_CSTATE_MAX + 1];
/*
* tickless_live_cpu_set, after tickless module is initialized,
* all the cores will set bit in tickless_live_cpu_set, each core
* will clear the relative bit in tickless_live_cpu_set before
* it is going to enter tickless idle mode, when all the cores
* clears the bit in tickless_live_cpu_set which means all cores
* want to enter tickless idle mode, the last core will close
* the system tick interrupt and start a one-shot plan, and
* then enter tickless idle mode.
*
* On a big-little system, there are some big cores(higher
* frequency) and some little cores(lower frequency), normally
* all big cores belong same cluster and all little cores belong
* same cluster, and these two clusters could has different
* Cx state, say big cluster could in C2 while little cluster
* could in C0. For tickless mode, only when all cores want
* to enter tickless idle mode, the system could enter tickless
* mode, all cores here means include both big cores and little
* cores.
*
* In the struct cpu_pwr_t, it also has a value named child_live_cpu_set,
* that is not same thing as tickless_live_cpu_set, different cluster
* could has different Cx state in one CPU package, child_live_cpu_set
* in cpu_pwr_t is used to track idle state of each cores under same
* cluster in this example.
*/
static unsigned int tickless_live_cpu_set = 0;
static kspinlock_t ticklessSpin;
/* import */
extern void systick_suspend(void);
extern void systick_resume(void);
/* forward declare */
static pwr_status_t tickless_timer_init(void);
static void tickless_enter(void);
static void tickless_exit(void);
static void tickless_enter_check(uint32_t cpu_idx, uint32_t cStatesCfg,
uint64_t *p_sleeptime, cpu_cstate_t *p_cstate_to_set);
static pwr_status_t tickless_one_shot_start(uint64_t sleep_time, cpu_cstate_t c_state_to_enter);
static tick_t tickless_one_shot_stop(cpu_cstate_t c_state_current);
static void tickless_announce_n(tick_t n_ticks);
static pwr_status_t tickless_c_state_latency_init(uint32_t cpu_idx, uint32_t *p_latency);
/**
* This routine is supplied by this module for board/platform,
* it is responsibility of board/platform to provide information
* that which C state is supported with tickless mode.
*/
void tickless_c_states_add(uint32_t c_state_set)
{
tickless_ctate_mask |= c_state_set;
}
/**
* This routine installs the static routines tickless_enter(),
* tickless_exit(), into cpu power management facility.
*/
pwr_status_t tickless_init(void)
{
uint32_t cpu_idx;
if (tickless_ctate_mask == 0) {
return PWR_ERR;
}
/* initialize the spinlock */
krhino_spin_lock_init(&ticklessSpin);
/* initialize the one-shot timers */
if (tickless_timer_init() != PWR_OK) {
return PWR_ERR;
}
/* set cpu idle mode to run until initialization completes */
cpu_pwr_idle_mode_set(CPU_IDLE_MODE_RUN);
for (cpu_idx = 0; cpu_idx < RHINO_CONFIG_CPU_NUM; cpu_idx++) {
if (cpu_pwr_c_state_capability_get(cpu_idx, &cStateConfig[cpu_idx]) != PWR_OK) {
return PWR_ERR;
}
cStateConfig[cpu_idx] &= tickless_ctate_mask;
tickless_c_state_latency_init(cpu_idx, cStateLatency[cpu_idx]);
/* set live flag for all cores */
tickless_live_cpu_set |= 1 << cpu_idx;
}
_func_cpu_tickless_down = tickless_enter;
_func_cpu_tickless_up = tickless_exit;
/*set cpu idle mode to tickless mode now */
cpu_pwr_idle_mode_set(CPU_IDLE_MODE_TICKLESS);
return (PWR_OK);
}
static pwr_status_t tickless_timer_init(void)
{
uint32_t idx;
for (idx = 0; idx < CPU_CSTATE_MAX; idx++) {
if (cStateOneShotTimer[idx] != NULL) {
if (cStateOneShotTimer[idx]->one_shot_init() != PWR_OK) {
return PWR_ERR;
}
}
}
return PWR_OK;
}
static pwr_status_t tickless_one_shot_start(uint64_t sleep_time, cpu_cstate_t c_state_to_enter)
{
if (cStateOneShotTimer[c_state_to_enter]->one_shot_start(sleep_time) != PWR_OK) {
PWR_DBG(DBG_INFO, "start one shot(%lld ms) fail\n", sleep_time);
return (PWR_ERR);
}
return (PWR_OK);
}
static tick_t tickless_one_shot_stop(cpu_cstate_t c_state_current)
{
tick_t n_ticks;
uint64_t passed_micro_sec;
uint32_t us_per_tick = 1000000 / RHINO_CONFIG_TICKS_PER_SECOND;
static uint64_t us_remain = 0;
if (cStateOneShotTimer[c_state_current]->one_shot_stop(&passed_micro_sec) !=
PWR_OK) {
PWR_DBG(DBG_ERR, "timerOneShotCancel fail\n");
return 0;
}
n_ticks = (tick_t)((passed_micro_sec * RHINO_CONFIG_TICKS_PER_SECOND) / (1000000));
/* fixs ticks drift issue */
us_remain += passed_micro_sec % us_per_tick;
if (us_remain >= us_per_tick) {
n_ticks++;
us_remain -= us_per_tick;
}
return n_ticks;
}
/**
* tickless_c_state_latency_init() sets the latency times for entering and
* exiting the various C states for idle power management. Latency table
* is only used when CPU Power Manager is present.
* @param[in] cpu_idx CPU index
* @param[in] p_latency[] Latency array
* @return PWR_OK or PWR_ERR when failed.
*/
static pwr_status_t tickless_c_state_latency_init(uint32_t cpu_idx, uint32_t p_latency[])
{
cpu_cstate_t cstate;
uint32_t cstate_all;
uint32_t latency;
if (cpu_pwr_c_state_capability_get(cpu_idx, &cstate_all) != PWR_OK) {
return (PWR_ERR);
}
for (cstate = CPU_CSTATE_C0; cstate <= CPU_CSTATE_MAX; cstate++) {
p_latency[cstate] = 0;
if (cstate_all & (1 << cstate)) {
latency = cpu_pwr_c_state_latency_get(cpu_idx, cstate);
if (latency == (uint32_t)CPU_LATENCY_UNKNOW) {
p_latency[cstate] = 0;
} else {
p_latency[cstate] = latency;
}
}
}
return (PWR_OK);
}
/**
* tickless_enter_check() calculates the amount of time until the next kernel
* time event, adjusts it based on configuration and CPU capabilities.
* It also deteremines the CPU idle state (C-state) to enter.
* @param[in] cpu_idx current CPU
* @param[in] cstate_cfg, enabled C states
* @param[out] p_sleeptime, time to next event in us
* @param[out] p_cstate_to_set C state to set
* @return N/A
*/
static void tickless_enter_check(uint32_t cpu_idx, uint32_t cstate_cfg,
uint64_t *p_sleeptime, cpu_cstate_t *p_cstate_to_set)
{
uint32_t cpu_c_state;
uint64_t sleep_time_us; /* sleep time in microseconds */
uint64_t one_shot_max_us_support; /* max one shot time support */
tick_t n_ticks;
n_ticks = krhino_next_sleep_ticks_get();
if (n_ticks == RHINO_WAIT_FOREVER) {
sleep_time_us = TIME_100_YEARS_IN_US;
} else {
if (n_ticks > 1) {
n_ticks = n_ticks -1;
}
sleep_time_us = 1000000ull * n_ticks / RHINO_CONFIG_TICKS_PER_SECOND;
}
/* get max valid Cx from cstate_cfg */
if (cstate_cfg == 0) {
cpu_c_state = 0;
} else {
cpu_c_state = 31 - krhino_find_first_bit(&cstate_cfg);
}
while (cpu_c_state > 0) {
/* check if latency is less than idle time */
if (cStateLatency[cpu_idx][cpu_c_state] < sleep_time_us) {
/* hit! save cpu_c_state as the target C state */
*p_cstate_to_set = (cpu_cstate_t)cpu_c_state;
/* find the target, break here */
break;
}
/* clear the current Cx from cstate_cfg */
cstate_cfg &= ~(1 << cpu_c_state);
/* get max valid Cx from cstate_cfg */
if (cstate_cfg == 0) {
cpu_c_state = 0;
} else {
cpu_c_state = 31 - krhino_find_first_bit(&cstate_cfg);
}
}
one_shot_max_us_support = 0;
if ((cStateOneShotTimer[cpu_c_state] != NULL) &&
(cStateOneShotTimer[cpu_c_state]->one_shot_seconds_max != NULL)) {
one_shot_max_us_support =
1000000 * (uint64_t)cStateOneShotTimer[cpu_c_state]->one_shot_seconds_max();
}
/* if request sleeptime is longer than support, cut it down
to max support value. */
if (sleep_time_us > one_shot_max_us_support) {
sleep_time_us = one_shot_max_us_support;
}
/* real sleep time should be reduced by latency time. */
sleep_time_us -= cStateLatency[cpu_idx][cpu_c_state];
/* save the real sleep time into p_sleeptime and return. */
*p_sleeptime = sleep_time_us;
}
/**
* tickless_enter() is called when a CPU is going to enter idle state, a one
* shot interrupt is planned at sametime which is used to wake up CPU.
* @return N/A
*/
static void tickless_enter(void)
{
uint64_t sleep_time;
cpu_cstate_t cstate_to_enter = CPU_CSTATE_C1;
uint32_t cpu_idx = 0;
tick_t n_ticks = 0;
CPSR_ALLOC();
krhino_spin_lock_irq_save(&ticklessSpin);
/* Check if tickless can be entered now */
tickless_enter_check(cpu_idx, cStateConfig[cpu_idx], &sleep_time,
&cstate_to_enter);
if ((sleep_time > 0) && (is_current_tickless == FALSE)) {
/*
* Enable a one shot timer to wake up the system from an idle power
* management state. This one shot timer will wakeup the system
* unless another asynchronous event has woken up the CPU already.
*/
if (tickless_one_shot_start(sleep_time, cstate_to_enter) == PWR_OK) {
is_current_tickless = TRUE;
}
}
c_state_entered = cstate_to_enter;
if (is_current_tickless == TRUE) {
/* suspend system tick interrupt */
systick_suspend();
/*
* take CPU into relative C idle state which is decided by
* tickless_enter_check().
*
* NOTE, the CPU interrupt is still locked here, so CPU will be put
* into Cx state with cpu interrupt locked, but it is safe to wake
* up CPU throught interrupt.
*
* For ARM cortex-M, the processor ignores the value of PRIMASK in
* determining whether an asynchronous exception is a WFI wakeup event.
*
* For ARM cortex-A, when processor is in WFI state, it will be waked
* up by:
* a physical IRQ interrupt regardless of the value of the CPSR.I bit,
* a physical FIQ interrupt regardless of the value of the CPSR.F bit.
*
* For Xtensa, WAITI sets the interrupt level in PS.INTLEVEL to imm4
* and then, on some Xtensa ISA implementations, suspends processor
* operation until an interrupt occurs. The combination of setting
* the interrupt level and suspending operation avoids a race condition
* where an interrupt between the interrupt level setting and the
* suspension of operation would be ignored until a second interrupt
* occurred.
*/
(void)cpu_pwr_c_state_set(cstate_to_enter);
}
#if 1
if (is_current_tickless == TRUE) {
cpu_pwr_c_state_set(CPU_CSTATE_C0);
n_ticks = tickless_one_shot_stop(c_state_entered);
/* set is_current_tickless to FALSE */
is_current_tickless = FALSE;
if (n_ticks > 0) {
/* announces elapsed ticks to the kernel */
tickless_announce_n(n_ticks);
}
/* resume system tick interrupt */
systick_resume();
}
#endif
RHINO_CRITICAL_ENTER();
RHINO_CRITICAL_EXIT_SCHED();
krhino_spin_unlock_irq_restore(&ticklessSpin);
}
/**
* tickless_exit() is called when a CPU gets interrupted. If it determeines
* that the system is waking up from tickless idle, it re-enables tick
* interrupts and stop the one shot interrupt plan, also announces elapsed
* ticks to the kernel.
*
* @return N/A
*/
static void tickless_exit(void)
{
tick_t n_ticks = 0;
krhino_spin_lock_irq_save(&ticklessSpin);
if (!is_current_tickless) {
krhino_spin_unlock_irq_restore(&ticklessSpin);
return;
}
n_ticks = tickless_one_shot_stop(c_state_entered);
/* set is_current_tickless to FALSE */
is_current_tickless = FALSE;
krhino_spin_unlock_irq_restore(&ticklessSpin);
if (n_ticks > 0) {
/* announces elapsed ticks to the kernel */
tickless_announce_n(n_ticks);
}
/* resume system tick interrupt */
systick_resume();
}
/**
* tickless_announce_n() is called to announces elapsed ticks to the kernel.
*/
static void tickless_announce_n(tick_t n_ticks)
{
tick_list_update((tick_i_t)n_ticks);
}
/**
* tickless_one_shot_timer_save() is used to build connection between
* one shot timer and c state, different c state could has different
* wake up timer.
*/
void tickless_one_shot_timer_save(cpu_cstate_t cstate, one_shot_timer_t *p_timer)
{
cStateOneShotTimer[cstate] = p_timer;
}

View file

@ -0,0 +1,62 @@
/*
* Copyright (C) 2018 Alibaba Group Holding Limited
*/
#ifndef CPU_PWR_HAL_H
#define CPU_PWR_HAL_H
#ifdef __cplusplus
extern "C"
{
#endif
#include <k_err.h>
#include "pwrmgmt_state.h"
#define CPU_PWR_NODE_NAME_LEN 64
#define CPU_STATE_BIT(n) (1 << (n))
#define CPU_FREQ_UNKNOW (-1)
#define CPU_VOLT_UNKNOW (-1)
#define CPU_LATENCY_UNKNOW (-1)
typedef struct {
uint32_t state;
uint32_t value;
} state_val_pair_t;
struct cpu_pwr;
typedef pwr_status_t (*cpu_pstate_set_t)(struct cpu_pwr *p_cpu_node, uint32_t cpu_state);
typedef pwr_status_t (*cpu_cstate_set_t)(uint32_t cpu_state, int master);
typedef pwr_status_t (*cpu_cstate_get_t)(struct cpu_pwr *p_cpu_node, uint32_t *pCpuState);
typedef struct cpu_pwr {
uint32_t support_bitset_c;
int cstate_nums;
state_val_pair_t *p_pair_latency;
unsigned int cstate_updating;
cpu_cstate_t current_c_state;
cpu_cstate_t desire_c_state;
cpu_cstate_set_t cpu_cstate_set_func;
char * name;
uint32_t unit;
} cpu_pwr_t;
extern void cpu_pwr_hal_lib_init(void);
extern pwr_status_t cpu_pwr_node_init_static(char *, uint32_t, cpu_pwr_t *);
extern pwr_status_t cpu_pwr_node_init_dyn(char *, uint32_t, cpu_pwr_t **);
extern cpu_pwr_t * cpu_pwr_node_find_by_name(char *pName, uint32_t index);
extern pwr_status_t cpu_pwr_node_record(cpu_pwr_t *p_cpu_node, uint32_t cpuIndex);
extern pwr_status_t cpu_pwr_c_state_capability_set(uint32_t cpuIndex, uint32_t supportBitsetC);
extern pwr_status_t cpu_pwr_c_state_latency_save(uint32_t cpuIndex, cpu_cstate_t cpu_state,
uint32_t latency);
extern uint32_t cpu_pwr_c_state_latency_get(uint32_t cpuIndex, cpu_cstate_t CState);
extern pwr_status_t cpu_pwr_c_method_set(uint32_t cpu_idx, cpu_cstate_set_t cpu_cstate_set_func);
#ifdef __cplusplus
}
#endif
#endif /* CPU_PWR_HAL_H */

View file

@ -0,0 +1,33 @@
/*
* Copyright (C) 2018 Alibaba Group Holding Limited
*/
#ifndef CPU_PWR_LIB_H
#define CPU_PWR_LIB_H
#ifdef __cplusplus
extern "C"
{
#endif
#include <k_api.h>
#include "pwrmgmt_state.h"
#ifndef container_of
#define container_of(ptr, type, member) \
((type *)((char *)(ptr) - offsetof(type, member)))
#endif /* container_of */
int cpu_pwrmgmt_init(void);
void cpu_pwr_down(void);
void cpu_pwr_up(void);
extern void (*_func_cpu_tickless_up)(void);
extern void (*_func_cpu_tickless_down)(void);
#ifdef __cplusplus
}
#endif
#endif /* CPU_PWR_LIB_H */

View file

@ -0,0 +1,37 @@
/*
* Copyright (C) 2018 Alibaba Group Holding Limited
*/
#ifndef CPU_TICKLSEE_H
#define CPU_TICKLSEE_H
#ifdef __cplusplus
extern "C"
{
#endif
#include <k_api.h>
#include "pwrmgmt_state.h"
typedef pwr_status_t (*one_shot_init_t)(void);
typedef uint32_t (*one_shot_seconds_max_t)(void);
typedef pwr_status_t (*one_shot_start_t)(uint64_t planUse);
typedef pwr_status_t (*one_shot_stop_t)(uint64_t *pPassedUs);
typedef struct {
one_shot_init_t one_shot_init;
one_shot_seconds_max_t one_shot_seconds_max;
one_shot_start_t one_shot_start;
one_shot_stop_t one_shot_stop;
} one_shot_timer_t;
extern pwr_status_t tickless_init(void);
extern void tickless_c_states_add(uint32_t c_state_set);
extern void tickless_one_shot_timer_save(cpu_cstate_t c_state, one_shot_timer_t *p_timer);
#ifdef __cplusplus
}
#endif
#endif /* CPU_TICKLSEE_H */

View file

@ -0,0 +1,68 @@
/*
* Copyright (C) 2018 Alibaba Group Holding Limited
*/
/*
DESCRIPTION
This file provides APIs for management CPU power.
*/
#ifndef PWRMGMT_API_H
#define PWRMGMT_API_H
#ifdef __cplusplus
extern "C"
{
#endif
#include <k_api.h>
#include "pwrmgmt_default_config.h"
#include "cpu_pwr_lib.h"
#include "cpu_pwr_hal_lib.h"
#include "pwrmgmt_state.h"
/*
* CPU power management operates in one of three possible modes:
*
* CPU_IDLE_MODE_RUN:
* In this CPU mode of operation, idle CPU power management is disabled.
*
* CPU_IDLE_MODE_SLEEP:
* the CPUs sleep when idle, but system clock interupts wakes up the CPU
* at every tick.
*
* CPU_IDLE_MODE_TICKLESS: CPUs sleep when idle, potentially for multiple
* ticks once enter idle mode.
*/
typedef enum cpu_idle_mode {
CPU_IDLE_MODE_RUN = 0,
CPU_IDLE_MODE_SLEEP = 1,
CPU_IDLE_MODE_TICKLESS = 2
} CPU_IDLE_MODE;
extern pwr_status_t cpu_pwr_c_state_set(cpu_cstate_t target_c_state);
extern pwr_status_t cpu_pwr_c_state_get(uint32_t cpu_idx, cpu_cstate_t *p_cstate);
extern pwr_status_t cpu_pwr_c_state_capability_get(uint32_t cpu_idx, uint32_t *p_support_bitset_c);
extern pwr_status_t cpu_pwr_idle_mode_set(CPU_IDLE_MODE mode);
extern CPU_IDLE_MODE cpu_pwr_idle_mode_get(void);
int pwrmgmt_suspend_lowpower();
int pwrmgmt_resume_lowpower();
#if (PWRMGMT_CONFIG_SHOW > 0)
extern void cpu_pwr_state_show(void);
extern void cpu_pwr_info_show(void);
#endif /* PWRMGMT_CONFIG_SHOW */
#if (WIFI_CONFIG_SUPPORT_LOWPOWER > 0)
extern int wifi_enter_powersave();
extern int wifi_exit_powersave();
#endif
#ifdef __cplusplus
}
#endif
#endif /* PWRMGMT_API_H */

View file

@ -0,0 +1,42 @@
/*
* Copyright (C) 2018 Alibaba Group Holding Limited
*/
#ifndef PWRMGMT_DEBUG_H
#define PWRMGMT_DEBUG_H
#ifdef __cplusplus
extern "C"
{
#endif
#include <stdio.h>
#include <stdarg.h>
#include "pwrmgmt_default_config.h"
/*
* when use debug, do not close uart when turn into pwr down state
*/
#define PWR_DEBUG_LEVEL DBG_INFO
typedef enum {
DBG_OFF = 0x00000000,
DBG_INFO = 0x00000001,
DBG_WARN = 0x00000002,
DBG_ERR = 0x00000003,
} pwr_debug_level_t;
void pwr_debug(pwr_debug_level_t debug_level, const char *fmt_str, ...);
#if (PWRMGMT_CONFIG_DEBUG > 0)
#define PWR_DBG pwr_debug
#else
#define PWR_DBG(lvl, ...)
#endif /* PWRMGMT_CONFIG_DEBUG > DBG_OFF */
#ifdef __cplusplus
}
#endif
#endif /* PWRMGMT_DEBUG_H */

View file

@ -0,0 +1,31 @@
/*
* Copyright (C) 2018 Alibaba Group Holding Limited
*/
#ifndef PWRMGMT_DEFAULT_CONFIG_H
#define PWRMGMT_DEFAULT_CONFIG_H
#ifdef __cplusplus
extern "C"
{
#endif
#include "pwrmgmt_api.h"
#ifndef PWRMGMT_CONFIG_SHOW
#define PWRMGMT_CONFIG_SHOW 0
#endif
#ifndef PWRMGMT_CONFIG_DEBUG
#define PWRMGMT_CONFIG_DEBUG 0
#endif
#ifndef PWRMGMT_CONFIG_LOG_ENTERSLEEP
#define PWRMGMT_CONFIG_LOG_ENTERSLEEP 0
#endif
#ifdef __cplusplus
}
#endif /* __cplusplus */
#endif /* PWRMGMT_DEFAULT_CONFIG_H */

View file

@ -0,0 +1,111 @@
/*
* Copyright (C) 2018 Alibaba Group Holding Limited
*/
/*
DESCRIPTION
This file provides base type define power state.
*/
#ifndef PWRMGMT_STATE_H
#define PWRMGMT_STATE_H
#ifdef __cplusplus
extern "C"
{
#endif /* __cplusplus */
#ifndef TRUE
#define TRUE 1
#endif
#ifndef FALSE
#define FALSE 0
#endif
typedef enum {
PWR_ERR = (-1),
PWR_OK = (0)
} pwr_status_t;
/* CPU states */
typedef enum {
CPU_CSTATE_C0 = 0, /* Operating */
CPU_CSTATE_C1 = 1, /* Halt -- not executing put powered on */
CPU_CSTATE_C2 = 2, /* not execuing with lower power than C1 */
CPU_CSTATE_C3 = 3, /* not execuing with lower power than C2 */
CPU_CSTATE_C4 = 4, /* not execuing with lower power than C3 */
CPU_CSTATE_C5 = 5,
CPU_CSTATE_C6 = 6,
CPU_CSTATE_C7 = 7,
CPU_CSTATE_C8 = 8,
CPU_CSTATE_C9 = 9,
CPU_CSTATE_C10 = 10,
CPU_CSTATE_C11 = 11,
CPU_CSTATE_C12 = 12,
CPU_CSTATE_C13 = 13,
CPU_CSTATE_C14 = 14,
CPU_CSTATE_C15 = 15,
CPU_CSTATE_C16 = 16,
CPU_CSTATE_C17 = 17,
CPU_CSTATE_C18 = 18,
CPU_CSTATE_C19 = 19,
CPU_CSTATE_C20 = 20,
CPU_CSTATE_C21 = 21,
CPU_CSTATE_C22 = 22,
CPU_CSTATE_C23 = 23,
CPU_CSTATE_C24 = 24,
CPU_CSTATE_C25 = 25,
CPU_CSTATE_C26 = 26,
CPU_CSTATE_C27 = 27,
CPU_CSTATE_C28 = 28,
CPU_CSTATE_C29 = 29,
CPU_CSTATE_C30 = 30,
CPU_CSTATE_C31 = 31,
CPU_CSTATE_MAX = 31,
CPU_CSTATE_NONE = 0xFF
} cpu_cstate_t;
/* Performance states */
typedef enum {
CPU_PSTATE_P0 = 0, /* Max power and clock */
CPU_PSTATE_P1 = 1,
CPU_PSTATE_P2 = 2,
CPU_PSTATE_P3 = 3,
CPU_PSTATE_P4 = 4,
CPU_PSTATE_P5 = 5,
CPU_PSTATE_P6 = 6,
CPU_PSTATE_P7 = 7,
CPU_PSTATE_P8 = 8,
CPU_PSTATE_P9 = 9,
CPU_PSTATE_P10 = 10,
CPU_PSTATE_P11 = 11,
CPU_PSTATE_P12 = 12,
CPU_PSTATE_P13 = 13,
CPU_PSTATE_P14 = 14,
CPU_PSTATE_P15 = 15,
CPU_PSTATE_P16 = 16,
CPU_PSTATE_P17 = 17,
CPU_PSTATE_P18 = 18,
CPU_PSTATE_P19 = 19,
CPU_PSTATE_P20 = 20,
CPU_PSTATE_P21 = 21,
CPU_PSTATE_P22 = 22,
CPU_PSTATE_P23 = 23,
CPU_PSTATE_P24 = 24,
CPU_PSTATE_P25 = 25,
CPU_PSTATE_P26 = 26,
CPU_PSTATE_P27 = 27,
CPU_PSTATE_P28 = 28,
CPU_PSTATE_P29 = 29,
CPU_PSTATE_P30 = 30,
CPU_PSTATE_P31 = 31,
CPU_PSTATE_MAX = 32,
CPU_PSTATE_NONE = 0xFF
} cpu_pstate_t;
#ifdef __cplusplus
}
#endif
#endif /* PWRMGMT_STATE_H */

View file

@ -0,0 +1,29 @@
NAME := pwrmgmt
$(NAME)_MBINS_TYPE := kernel
$(NAME)_VERSION := 1.0.0
$(NAME)_SUMMARY :=
$(NAME)_SOURCES := cpu_pwr_lib.c
$(NAME)_SOURCES += cpu_pwr_hal_lib.c
$(NAME)_SOURCES += cpu_pwr_show.c
$(NAME)_SOURCES += cpu_tickless.c
$(NAME)_SOURCES += wifi_pwr_lib.c
ifeq ($(HOST_ARCH),linux)
$(NAME)_DEFINES += IO_NEED_TRAP
endif
#default gcc
ifeq ($(COMPILER),)
$(NAME)_CFLAGS += -Wall -Werror
else ifeq ($(COMPILER),gcc)
$(NAME)_CFLAGS += -Wall -Werror
else ifeq ($(COMPILER),armcc)
GLOBAL_DEFINES += __BSD_VISIBLE
endif
GLOBAL_INCLUDES += include
GLOBAL_DEFINES += AOS_COMP_PWRMGMT
GLOBAL_DEFINES += RHINO_CONFIG_CPU_PWR_MGMT=1
AOS_COMP_PWRMGMT := y

View file

@ -0,0 +1,10 @@
src = Split('''
cpu_pwr_hal_lib.c
cpu_pwr_lib.c
cpu_pwr_show.c
cpu_tickless.c
''')
component = aos_component('pwrmgmt', src)
component.add_global_includes('include')
component.add_global_macros('AOS_COMP_PWRMGMT')

View file

@ -0,0 +1,24 @@
/*
* Copyright (C) 2018 Alibaba Group Holding Limited
*/
#include "pwrmgmt_api.h"
#include <hal/hal.h>
#if (WIFI_CONFIG_SUPPORT_LOWPOWER > 0)
int wifi_enter_powersave()
{
hal_wifi_module_t *module;
module = hal_wifi_get_default_module();
return hal_wifi_enter_powersave(module, WIFI_CONFIG_RECEIVE_DTIM);
}
int wifi_exit_powersave()
{
hal_wifi_module_t *module;
module = hal_wifi_get_default_module();
return hal_wifi_exit_powersave(module);
}
#endif