forked from nuttx/nuttx-update
drivers/power/pm: Historically, the NuttX PM subsystem has consisted of two functional components: (1) an "Upper" part that detects state changes based on a random walk driven by activity levels, and (2) and "lower" part that implementst the state changes.
This change decouples that upper activity-based logic from the lower random walk logic and allows use of other upper state detection logic (such as a custom, application-specific state machine).
This commit is contained in:
parent
2ab4d635b4
commit
e118d99bf5
16 changed files with 1372 additions and 779 deletions
|
@ -80,14 +80,6 @@ int main(int argc, char **argv, char **envp)
|
|||
sim_cpu0_initialize();
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
/* Power management should be initialized early in the (simulated) boot
|
||||
* sequence.
|
||||
*/
|
||||
|
||||
pm_initialize();
|
||||
#endif
|
||||
|
||||
/* Then start NuttX */
|
||||
|
||||
if (setjmp(g_simabort) == 0)
|
||||
|
|
|
@ -4,17 +4,79 @@
|
|||
#
|
||||
|
||||
menuconfig PM
|
||||
bool "Power management (PM) driver interfaces"
|
||||
bool "Power management (PM) system"
|
||||
default n
|
||||
---help---
|
||||
Power management (PM) driver interfaces. These interfaces are used
|
||||
to manage power usage of a platform by monitoring driver activity
|
||||
and by placing drivers into reduce power usage modes when the
|
||||
drivers are not active.
|
||||
Power management (PM) system. It consists of an OS interface
|
||||
to board logic which can be used to obtain a recommended
|
||||
power level according to a power management policy set by the
|
||||
currently chosen PM governor. It is also used by drivers which
|
||||
can allow or not a power level to be changed or not.
|
||||
|
||||
if PM
|
||||
|
||||
config PM_SLICEMS
|
||||
config PM_NDOMAINS
|
||||
int "Number of PM activity domains"
|
||||
default 1
|
||||
---help---
|
||||
Defines the number of "domains" that the PM system can control.
|
||||
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.
|
||||
|
||||
choice
|
||||
prompt "PM system governor"
|
||||
default PM_GOVERNOR_ACTIVITY
|
||||
---help---
|
||||
A PM governor applies a policy to control the change in power
|
||||
states.
|
||||
|
||||
config PM_GOVERNOR_GREEDY
|
||||
bool "Greedy governor"
|
||||
---help---
|
||||
This governor simply suggests the lowest-possible power state,
|
||||
considering any states locked by calls to pm_stay() (accessible
|
||||
via BOARDIOC_PM_STAY boardctl calls).
|
||||
|
||||
config PM_GOVERNOR_ACTIVITY
|
||||
bool "Activity based"
|
||||
---help---
|
||||
The activity based governor receives activity reports from drivers
|
||||
in units which are accumulated during a certain time slice interval.
|
||||
The governor will then switch between power states given a set of
|
||||
activity thresholds for each state.
|
||||
|
||||
config PM_GOVERNOR_CUSTOM
|
||||
bool "Custom governor"
|
||||
---help---
|
||||
By selecting this option, a custom governor can be supplied from
|
||||
board-logic.
|
||||
|
||||
endchoice
|
||||
|
||||
menu "Governor options"
|
||||
|
||||
if PM_GOVERNOR_GREEDY
|
||||
|
||||
config PM_GOVERNOR_EXPLICIT_RELAX
|
||||
bool "Stay initially at PM_NORMAL"
|
||||
---help---
|
||||
If you boot into NSH, when using the greedy PM governor, since NuttX will
|
||||
almost immediately go idle (when waiting for a prompt), he lowest possible
|
||||
run-level will be selected, which may not be desireable.
|
||||
This is not a problem if you directly run you application at boot, which
|
||||
can hold off power levels using pm_stay() (via boardctl).
|
||||
|
||||
This option will initialize all run levels as if pm_stay() were to be
|
||||
called once for each, so that your application needs to call pm_relax()
|
||||
(via boardctl()) for every run-level you wish to allow to enter.
|
||||
|
||||
endif
|
||||
|
||||
|
||||
if PM_GOVERNOR_ACTIVITY
|
||||
|
||||
config PM_GOVERNOR_SLICEMS
|
||||
int "PM time slice (msec)"
|
||||
default 100
|
||||
---help---
|
||||
|
@ -23,19 +85,10 @@ config PM_SLICEMS
|
|||
interval is applied to an averaging algorithm to determine the
|
||||
activity level.
|
||||
|
||||
CONFIG_PM_SLICEMS provides the duration of that time slice in
|
||||
CONFIG_GOVERNOR_PM_SLICEMS provides the duration of that time slice in
|
||||
milliseconds. Default: 100 Milliseconds
|
||||
|
||||
config PM_NDOMAINS
|
||||
int "Number of PM activity domains"
|
||||
default 1
|
||||
---help---
|
||||
Defines the number of "domains" that activity may be monitored on.
|
||||
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.
|
||||
|
||||
config PM_MEMORY
|
||||
config PM_GOVERNOR_MEMORY
|
||||
int "PM memory (msec)"
|
||||
default 2
|
||||
range 1 6
|
||||
|
@ -46,24 +99,24 @@ config PM_MEMORY
|
|||
activity. These weights may be negative and a limited to the
|
||||
range of int16_t.
|
||||
|
||||
CONFIG_PM_MEMORY provides the memory for the algorithm. Default: 2
|
||||
CONFIG_PM_COEFn provides weight for each sample. Default: 1
|
||||
CONFIG_PM_MEMORY provides the memory for the algorithm. Default: 2
|
||||
CONFIG_PM_COEFn provides weight for each sample. Default: 1
|
||||
|
||||
Setting CONFIG_PM_MEMORY=1 disables all smoothing.
|
||||
|
||||
config PM_COEFN
|
||||
config PM_GOVERNOR_COEFN
|
||||
int "PM coefficient"
|
||||
default 1
|
||||
---help---
|
||||
See help associated with CONFIG_PM_MEMORY.
|
||||
|
||||
config PM_COEF1
|
||||
config PM_GOVERNOR_COEF1
|
||||
int "PM coefficient 1"
|
||||
default 1
|
||||
---help---
|
||||
See help associated with CONFIG_PM_MEMORY.
|
||||
|
||||
config PM_COEF2
|
||||
config PM_GOVERNOR_COEF2
|
||||
int "PM coefficient 2"
|
||||
default 1
|
||||
---help---
|
||||
|
@ -71,7 +124,7 @@ config PM_COEF2
|
|||
|
||||
Ignored if CONFIG_PM_MEMORY <= 2
|
||||
|
||||
config PM_COEF3
|
||||
config PM_GOVERNOR_COEF3
|
||||
int "PM coefficient 3"
|
||||
default 1
|
||||
---help---
|
||||
|
@ -79,7 +132,7 @@ config PM_COEF3
|
|||
|
||||
Ignored if CONFIG_PM_MEMORY <= 3
|
||||
|
||||
config PM_COEF4
|
||||
config PM_GOVERNOR_COEF4
|
||||
int "PM coefficient 4"
|
||||
default 1
|
||||
---help---
|
||||
|
@ -87,7 +140,7 @@ config PM_COEF4
|
|||
|
||||
Ignored if CONFIG_PM_MEMORY <= 4
|
||||
|
||||
config PM_COEF5
|
||||
config PM_GOVERNOR_COEF5
|
||||
int "PM coefficient 5"
|
||||
default 1
|
||||
---help---
|
||||
|
@ -95,141 +148,145 @@ config PM_COEF5
|
|||
|
||||
Ignored if CONFIG_PM_MEMORY <= 5
|
||||
|
||||
config PM_IDLEENTER_THRESH
|
||||
config PM_GOVERNOR_IDLEENTER_THRESH
|
||||
int "PM IDLE enter threshold"
|
||||
default 1
|
||||
---help---
|
||||
State changes then occur when the weight activity account crosses
|
||||
threshold values for certain periods of time (time slice count).
|
||||
|
||||
CONFIG_PM_xxxENTER_THRESH is the threshold value for entering state xxx.
|
||||
CONFIG_PM_xxxENTER_COUNT is the count for entering state xxx.
|
||||
CONFIG_PM_xxxENTER_THRESH is the threshold value for entering state xxx.
|
||||
CONFIG_PM_xxxENTER_COUNT is the count for entering state xxx.
|
||||
|
||||
Resuming to normal state, on the other hand, is usually immediate and
|
||||
controlled by wakeup conditions established by the platform. The PM
|
||||
|
||||
Default: <=1: Essentially no activity
|
||||
|
||||
config PM_IDLEEXIT_THRESH
|
||||
config PM_GOVERNOR_IDLEEXIT_THRESH
|
||||
int "PM IDLE exit threshold"
|
||||
default 2
|
||||
---help---
|
||||
State changes then occur when the weight activity account crosses
|
||||
threshold values for certain periods of time (time slice count).
|
||||
|
||||
CONFIG_PM_xxxENTER_THRESH is the threshold value for entering state xxx.
|
||||
CONFIG_PM_xxxENTER_COUNT is the count for entering state xxx.
|
||||
CONFIG_PM_xxxENTER_THRESH is the threshold value for entering state xxx.
|
||||
CONFIG_PM_xxxENTER_COUNT is the count for entering state xxx.
|
||||
|
||||
Resuming to normal state, on the other hand, is usually immediate and
|
||||
controlled by wakeup conditions established by the platform. The PM
|
||||
|
||||
Default: >=2: Active
|
||||
|
||||
config PM_IDLEENTER_COUNT
|
||||
config PM_GOVERNOR_IDLEENTER_COUNT
|
||||
int "PM IDLE enter count"
|
||||
default 30
|
||||
---help---
|
||||
State changes then occur when the weight activity account crosses
|
||||
threshold values for certain periods of time (time slice count).
|
||||
|
||||
CONFIG_PM_xxxENTER_THRESH is the threshold value for entering state xxx.
|
||||
CONFIG_PM_xxxENTER_COUNT is the count for entering state xxx.
|
||||
CONFIG_PM_xxxENTER_THRESH is the threshold value for entering state xxx.
|
||||
CONFIG_PM_xxxENTER_COUNT is the count for entering state xxx.
|
||||
|
||||
Resuming to normal state, on the other hand, is usually immediate and
|
||||
controlled by wakeup conditions established by the platform. The PM
|
||||
|
||||
Default: Thirty IDLE slices to enter IDLE mode from normal
|
||||
|
||||
config PM_STANDBYENTER_THRESH
|
||||
config PM_GOVERNOR_STANDBYENTER_THRESH
|
||||
int "PM STANDBY enter threshold"
|
||||
default 1
|
||||
---help---
|
||||
State changes then occur when the weight activity account crosses
|
||||
threshold values for certain periods of time (time slice count).
|
||||
|
||||
CONFIG_PM_xxxENTER_THRESH is the threshold value for entering state xxx.
|
||||
CONFIG_PM_xxxENTER_COUNT is the count for entering state xxx.
|
||||
CONFIG_PM_xxxENTER_THRESH is the threshold value for entering state xxx.
|
||||
CONFIG_PM_xxxENTER_COUNT is the count for entering state xxx.
|
||||
|
||||
Resuming to normal state, on the other hand, is usually immediate and
|
||||
controlled by wakeup conditions established by the platform. The PM
|
||||
|
||||
Default: <=1: Essentially no activity
|
||||
|
||||
config PM_STANDBYEXIT_THRESH
|
||||
config PM_GOVERNOR_STANDBYEXIT_THRESH
|
||||
int "PM STANDBY exit threshold"
|
||||
default 2
|
||||
---help---
|
||||
State changes then occur when the weight activity account crosses
|
||||
threshold values for certain periods of time (time slice count).
|
||||
|
||||
CONFIG_PM_xxxENTER_THRESH is the threshold value for entering state xxx.
|
||||
CONFIG_PM_xxxENTER_COUNT is the count for entering state xxx.
|
||||
CONFIG_PM_xxxENTER_THRESH is the threshold value for entering state xxx.
|
||||
CONFIG_PM_xxxENTER_COUNT is the count for entering state xxx.
|
||||
|
||||
Resuming to normal state, on the other hand, is usually immediate and
|
||||
controlled by wakeup conditions established by the platform. The PM
|
||||
|
||||
Default: >=2: Active
|
||||
|
||||
config PM_STANDBYENTER_COUNT
|
||||
config PM_GOVERNOR_STANDBYENTER_COUNT
|
||||
int "PM STANDBY enter count"
|
||||
default 50
|
||||
---help---
|
||||
State changes then occur when the weight activity account crosses
|
||||
threshold values for certain periods of time (time slice count).
|
||||
|
||||
CONFIG_PM_xxxENTER_THRESH is the threshold value for entering state xxx.
|
||||
CONFIG_PM_xxxENTER_COUNT is the count for entering state xxx.
|
||||
CONFIG_PM_xxxENTER_THRESH is the threshold value for entering state xxx.
|
||||
CONFIG_PM_xxxENTER_COUNT is the count for entering state xxx.
|
||||
|
||||
Resuming to normal state, on the other hand, is usually immediate and
|
||||
controlled by wakeup conditions established by the platform. The PM
|
||||
|
||||
Default: Fifty IDLE slices to enter STANDBY mode from IDLE
|
||||
|
||||
config PM_SLEEPENTER_THRESH
|
||||
config PM_GOVERNOR_SLEEPENTER_THRESH
|
||||
int "PM SLEEP enter threshold"
|
||||
default 1
|
||||
---help---
|
||||
State changes then occur when the weight activity account crosses
|
||||
threshold values for certain periods of time (time slice count).
|
||||
|
||||
CONFIG_PM_xxxENTER_THRESH is the threshold value for entering state xxx.
|
||||
CONFIG_PM_xxxENTER_COUNT is the count for entering state xxx.
|
||||
CONFIG_PM_xxxENTER_THRESH is the threshold value for entering state xxx.
|
||||
CONFIG_PM_xxxENTER_COUNT is the count for entering state xxx.
|
||||
|
||||
Resuming to normal state, on the other hand, is usually immediate and
|
||||
controlled by wakeup conditions established by the platform. The PM
|
||||
|
||||
Default: <=1: Essentially no activity
|
||||
|
||||
config PM_SLEEPEXIT_THRESH
|
||||
config PM_GOVERNOR_SLEEPEXIT_THRESH
|
||||
int "PM SLEEP exit threshold"
|
||||
default 2
|
||||
---help---
|
||||
State changes then occur when the weight activity account crosses
|
||||
threshold values for certain periods of time (time slice count).
|
||||
|
||||
CONFIG_PM_xxxENTER_THRESH is the threshold value for entering state xxx.
|
||||
CONFIG_PM_xxxENTER_COUNT is the count for entering state xxx.
|
||||
CONFIG_PM_xxxENTER_THRESH is the threshold value for entering state xxx.
|
||||
CONFIG_PM_xxxENTER_COUNT is the count for entering state xxx.
|
||||
|
||||
Resuming to normal state, on the other hand, is usually immediate and
|
||||
controlled by wakeup conditions established by the platform. The PM
|
||||
|
||||
Default: >=2: Active
|
||||
|
||||
config PM_SLEEPENTER_COUNT
|
||||
config PM_GOVERNOR_SLEEPENTER_COUNT
|
||||
int "PM SLEEP enter count"
|
||||
default 70
|
||||
---help---
|
||||
State changes then occur when the weight activity account crosses
|
||||
threshold values for certain periods of time (time slice count).
|
||||
|
||||
CONFIG_PM_xxxENTER_THRESH is the threshold value for entering state xxx.
|
||||
CONFIG_PM_xxxENTER_COUNT is the count for entering state xxx.
|
||||
CONFIG_PM_xxxENTER_THRESH is the threshold value for entering state xxx.
|
||||
CONFIG_PM_xxxENTER_COUNT is the count for entering state xxx.
|
||||
|
||||
Resuming to normal state, on the other hand, is usually immediate and
|
||||
controlled by wakeup conditions established by the platform. The PM
|
||||
|
||||
Default: Fifty IDLE slices to enter SLEEP mode from STANDBY
|
||||
|
||||
endif # PM_GOVERNOR_ACTIVITY
|
||||
|
||||
endmenu
|
||||
|
||||
endif # PM
|
||||
|
||||
config DRIVERS_POWERLED
|
||||
|
|
|
@ -41,8 +41,22 @@ POWER_CFLAGS =
|
|||
|
||||
ifeq ($(CONFIG_PM),y)
|
||||
|
||||
CSRCS += pm_activity.c pm_changestate.c pm_checkstate.c pm_initialize.c
|
||||
CSRCS += pm_register.c pm_unregister.c pm_update.c
|
||||
CSRCS += pm_initialize.c pm_activity.c pm_changestate.c pm_checkstate.c
|
||||
CSRCS += pm_register.c pm_unregister.c
|
||||
|
||||
# Governor implementations
|
||||
|
||||
ifeq ($(CONFIG_PM_GOVERNOR_ACTIVITY),y)
|
||||
|
||||
CSRCS += activity_governor.c
|
||||
|
||||
endif
|
||||
|
||||
ifeq ($(CONFIG_PM_GOVERNOR_GREEDY),y)
|
||||
|
||||
CSRCS += greedy_governor.c
|
||||
|
||||
endif
|
||||
|
||||
# Include power management in the build
|
||||
|
||||
|
|
623
drivers/power/activity_governor.c
Normal file
623
drivers/power/activity_governor.c
Normal file
|
@ -0,0 +1,623 @@
|
|||
/****************************************************************************
|
||||
* activity_governor.c
|
||||
*
|
||||
* Copyright (C) 2011-2012, 2016 Gregory Nutt. All rights reserved.
|
||||
* Author: Gregory Nutt <gnutt@nuttx.org>
|
||||
* Author: Matias Nitsche <mnitsche@dc.uba.ar>
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in
|
||||
* the documentation and/or other materials provided with the
|
||||
* distribution.
|
||||
* 3. Neither the name NuttX nor the names of its contributors may be
|
||||
* used to endorse or promote products derived from this software
|
||||
* without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
|
||||
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
|
||||
* COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
|
||||
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
|
||||
* OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
|
||||
* AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
|
||||
* ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
/****************************************************************************
|
||||
* Included Files
|
||||
****************************************************************************/
|
||||
|
||||
#include <nuttx/config.h>
|
||||
#include <stdint.h>
|
||||
#include <assert.h>
|
||||
#include <sys/types.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include <nuttx/power/pm.h>
|
||||
#include <nuttx/wqueue.h>
|
||||
#include <nuttx/irq.h>
|
||||
|
||||
#include "pm.h"
|
||||
|
||||
#ifdef CONFIG_PM_GOVERNOR_ACTIVITY
|
||||
|
||||
/****************************************************************************
|
||||
* Pre-processor Definitions
|
||||
****************************************************************************/
|
||||
|
||||
#define PM_TIMER_GAP (TIME_SLICE_TICKS * 2)
|
||||
|
||||
/* Convert the time slice interval into system clock ticks.
|
||||
*
|
||||
* CONFIG_PM_SLICEMS provides the duration of one time slice in milliseconds.
|
||||
* CLOCKS_PER_SEC provides the number of timer ticks in one second.
|
||||
*
|
||||
* slice ticks = (CONFIG_PM_SLICEMS msec / 1000 msec/sec) /
|
||||
* (CLOCKS_PER_SEC ticks/sec)
|
||||
*/
|
||||
|
||||
#define TIME_SLICE_TICKS ((CONFIG_PM_GOVERNOR_SLICEMS * CLOCKS_PER_SEC) / 1000)
|
||||
|
||||
/****************************************************************************
|
||||
* Private Type Declarations
|
||||
****************************************************************************/
|
||||
|
||||
struct pm_domain_state_s
|
||||
{
|
||||
/* recommended - The recommended state based on the governor policy
|
||||
* mndex - The index to the next slot in the memory[] array to use.
|
||||
* mcnt - A tiny counter used only at start up. The actual
|
||||
* algorithm cannot be applied until CONFIG_PM_MEMORY
|
||||
* samples have been collected.
|
||||
*/
|
||||
|
||||
uint8_t recommended;
|
||||
uint8_t mndx;
|
||||
uint8_t mcnt;
|
||||
|
||||
/* accum - The accumulated counts in this time interval */
|
||||
|
||||
int16_t accum;
|
||||
|
||||
#if CONFIG_PM_GOVERNOR_MEMORY > 1
|
||||
/* This is the averaging "memory." The averaging algorithm is simply:
|
||||
* Y = (An*X + SUM(Ai*Yi))/SUM(Aj), where i = 1..n-1 and j= 1..n, n is the
|
||||
* length of the "memory", Ai is the weight applied to each value, and X is
|
||||
* the current activity.
|
||||
*
|
||||
* CONFIG_PM_MEMORY provides the memory for the algorithm. Default: 2
|
||||
* CONFIG_PM_COEFn provides weight for each sample. Default: 1
|
||||
*/
|
||||
|
||||
int16_t memory[CONFIG_PM_GOVERNOR_MEMORY - 1];
|
||||
#endif
|
||||
|
||||
/* stime - The time (in ticks) at the start of the current time slice */
|
||||
|
||||
clock_t stime;
|
||||
|
||||
/* btime - The time (in ticks) at the start of the current state */
|
||||
|
||||
clock_t btime;
|
||||
|
||||
/* Timer to decrease state */
|
||||
|
||||
WDOG_ID wdog;
|
||||
};
|
||||
|
||||
struct pm_activity_governor_s
|
||||
{
|
||||
/* Threshold time slice count to enter the next low power consdumption
|
||||
* state. Indexing is next state 0:IDLE, 1: STANDBY, 2: SLEEP.
|
||||
*/
|
||||
|
||||
const uint32_t pmcount[3];
|
||||
|
||||
/* Threshold activity values to enter into the next lower power consumption
|
||||
* state. Indexing is next state 0:IDLE, 1:STANDBY, 2:SLEEP.
|
||||
*/
|
||||
|
||||
const int32_t pmenterthresh[3];
|
||||
|
||||
/* Threshold activity values to leave the current low power consdumption
|
||||
* state. Indexing is current state 0:IDLE, 1: STANDBY, 2: SLEEP.
|
||||
*/
|
||||
|
||||
const int32_t pmexitthresh[3];
|
||||
|
||||
/* CONFIG_PM_MEMORY is the total number of time slices (including the
|
||||
* current time slice). The history of previous values is then
|
||||
* CONFIG_PM_MEMORY-1.
|
||||
*/
|
||||
|
||||
#if CONFIG_PM_GOVERNOR_MEMORY > 1
|
||||
const int16_t pmcoeffs[CONFIG_PM_GOVERNOR_MEMORY - 1];
|
||||
#endif
|
||||
|
||||
struct pm_domain_state_s domain_states[CONFIG_PM_NDOMAINS];
|
||||
};
|
||||
|
||||
/****************************************************************************
|
||||
* Private Function Prototypes
|
||||
****************************************************************************/
|
||||
|
||||
static void governor_initialize(void);
|
||||
static void governor_statechanged(int domain, enum pm_state_e newstate);
|
||||
static enum pm_state_e governor_checkstate(int domain);
|
||||
static void governor_activity(int domain, int count);
|
||||
static void governor_timer(int domain);
|
||||
static void governor_update(int domain, int16_t accum);
|
||||
|
||||
/****************************************************************************
|
||||
* Private Data
|
||||
****************************************************************************/
|
||||
|
||||
struct pm_activity_governor_s g_pm_activity_governor =
|
||||
{
|
||||
.pmcount =
|
||||
{
|
||||
CONFIG_PM_GOVERNOR_IDLEENTER_COUNT,
|
||||
CONFIG_PM_GOVERNOR_STANDBYENTER_COUNT,
|
||||
CONFIG_PM_GOVERNOR_SLEEPENTER_COUNT
|
||||
},
|
||||
.pmenterthresh =
|
||||
{
|
||||
CONFIG_PM_GOVERNOR_IDLEENTER_THRESH,
|
||||
CONFIG_PM_GOVERNOR_STANDBYENTER_THRESH,
|
||||
CONFIG_PM_GOVERNOR_SLEEPENTER_THRESH
|
||||
},
|
||||
.pmexitthresh =
|
||||
{
|
||||
CONFIG_PM_GOVERNOR_IDLEEXIT_THRESH,
|
||||
CONFIG_PM_GOVERNOR_STANDBYEXIT_THRESH,
|
||||
CONFIG_PM_GOVERNOR_SLEEPEXIT_THRESH
|
||||
},
|
||||
|
||||
#if CONFIG_PM_GOVERNOR_MEMORY > 1
|
||||
.pmcoeffs =
|
||||
{
|
||||
CONFIG_PM_GOVERNOR_COEF1
|
||||
#if CONFIG_PM_GOVERNOR_MEMORY > 2
|
||||
, CONFIG_PM_GOVERNOR_COEF2
|
||||
#endif
|
||||
#if CONFIG_PM_GOVERNOR_MEMORY > 3
|
||||
, CONFIG_PM_GOVERNOR_COEF3
|
||||
#endif
|
||||
#if CONFIG_PM_GOVERNOR_MEMORY > 4
|
||||
, CONFIG_PM_GOVERNOR_COEF4
|
||||
#endif
|
||||
#if CONFIG_PM_GOVERNOR_MEMORY > 5
|
||||
, CONFIG_PM_GOVERNOR_COEF5
|
||||
#endif
|
||||
#if CONFIG_PM_GOVERNOR_MEMORY > 6
|
||||
# warning "This logic needs to be extended"
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
};
|
||||
|
||||
struct pm_governor_s g_pmgovernor =
|
||||
{
|
||||
.initialize = governor_initialize,
|
||||
.checkstate = governor_checkstate,
|
||||
.statechanged = governor_statechanged,
|
||||
.activity = governor_activity
|
||||
};
|
||||
|
||||
/****************************************************************************
|
||||
* Private Functions
|
||||
****************************************************************************/
|
||||
|
||||
static void governor_initialize(void)
|
||||
{
|
||||
FAR struct pm_domain_state_s *pdomstate;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < CONFIG_PM_NDOMAINS; i++)
|
||||
{
|
||||
pdomstate = &g_pm_activity_governor.domain_states[i];
|
||||
pdomstate->stime = clock_systimer();
|
||||
pdomstate->btime = clock_systimer();
|
||||
}
|
||||
}
|
||||
|
||||
static void governor_activity(int domain, int count)
|
||||
{
|
||||
FAR struct pm_domain_state_s *pdomstate;
|
||||
clock_t now, elapsed;
|
||||
uint32_t accum;
|
||||
irqstate_t flags;
|
||||
|
||||
/* Get a convenience pointer to minimize all of the indexing */
|
||||
|
||||
DEBUGASSERT(domain >= 0 && domain < CONFIG_PM_NDOMAINS);
|
||||
pdomstate = &g_pm_activity_governor.domain_states[domain];
|
||||
|
||||
/* Just increment the activity count in the current time slice. The priority
|
||||
* is simply the number of counts that are added.
|
||||
*/
|
||||
|
||||
if (count > 0)
|
||||
{
|
||||
/* Add the activity count to the accumulated counts in a critical section. */
|
||||
|
||||
flags = enter_critical_section();
|
||||
accum = (uint32_t)pdomstate->accum + count;
|
||||
|
||||
/* Make sure that we do not overflow the underlying uint16_t representation */
|
||||
|
||||
if (accum > INT16_MAX)
|
||||
{
|
||||
accum = INT16_MAX;
|
||||
}
|
||||
|
||||
/* Save the updated count */
|
||||
|
||||
pdomstate->accum = (int16_t)accum;
|
||||
|
||||
/* Check the elapsed time. In periods of low activity, time slicing is
|
||||
* controlled by IDLE loop polling; in periods of higher activity, time
|
||||
* slicing is controlled by driver activity. In either case, the
|
||||
* duration of the time slice is only approximate; during times of heavy
|
||||
* activity, time slices may be become longer and the activity level may
|
||||
* be over-estimated.
|
||||
*/
|
||||
|
||||
now = clock_systimer();
|
||||
elapsed = now - pdomstate->stime;
|
||||
if (elapsed >= TIME_SLICE_TICKS)
|
||||
{
|
||||
int16_t tmp;
|
||||
|
||||
/* Sample the count, reset the time and count, and assess the PM
|
||||
* state. This is an atomic operation because interrupts are
|
||||
* still disabled.
|
||||
*/
|
||||
|
||||
tmp = pdomstate->accum;
|
||||
pdomstate->stime = now;
|
||||
pdomstate->accum = 0;
|
||||
|
||||
(void)governor_update(domain, tmp);
|
||||
}
|
||||
|
||||
leave_critical_section(flags);
|
||||
}
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
* Name: governor_update
|
||||
*
|
||||
* Description:
|
||||
* This internal function is called at the end of a time slice in order to
|
||||
* update driver activity metrics and recommended states.
|
||||
*
|
||||
* Input Parameters:
|
||||
* domain - The PM domain associated with the accumulator
|
||||
* accum - The value of the activity accumulator at the end of the time
|
||||
* slice.
|
||||
*
|
||||
* Returned Value:
|
||||
* None.
|
||||
*
|
||||
* Assumptions:
|
||||
* This function may be called from a driver, perhaps even at the interrupt
|
||||
* level. It may also be called from the IDLE loop at the lowest possible
|
||||
* priority level.
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
static void governor_update(int domain, int16_t accum)
|
||||
{
|
||||
FAR struct pm_domain_state_s *pdomstate;
|
||||
uint8_t state;
|
||||
int32_t y;
|
||||
int index;
|
||||
#if CONFIG_PM_GOVERNOR_MEMORY > 1
|
||||
int32_t denom;
|
||||
int i;
|
||||
int j;
|
||||
#endif
|
||||
|
||||
/* Get a convenience pointer to minimize all of the indexing */
|
||||
|
||||
DEBUGASSERT(domain >= 0 && domain < CONFIG_PM_NDOMAINS);
|
||||
pdomstate = &g_pm_activity_governor.domain_states[domain];
|
||||
state = g_pmglobals.domain[domain].state;
|
||||
|
||||
#if CONFIG_PM_GOVERNOR_MEMORY > 1
|
||||
/* We won't bother to do anything until we have accumulated
|
||||
* CONFIG_PM_GOVERNOR_MEMORY-1 samples.
|
||||
*/
|
||||
|
||||
if (pdomstate->mcnt < CONFIG_PM_GOVERNOR_MEMORY - 1)
|
||||
{
|
||||
index = pdomstate->mcnt++;
|
||||
pdomstate->memory[index] = accum;
|
||||
return;
|
||||
}
|
||||
|
||||
/* The averaging algorithm is simply: Y = (An*X + SUM(Ai*Yi))/SUM(Aj), where
|
||||
* i = 1..n-1 and j= 1..n, n is the length of the "memory", Ai is the
|
||||
* weight applied to each value, and X is the current activity.
|
||||
*
|
||||
* CONFIG_PM_GOVERNOR_MEMORY:
|
||||
* provides the memory for the algorithm. Default: 2
|
||||
* CONFIG_PM_GOVERNOR_COEFn:
|
||||
* provides weight for each sample. Default: 1
|
||||
*
|
||||
* First, calculate Y = An*X
|
||||
*/
|
||||
|
||||
y = CONFIG_PM_GOVERNOR_COEFN * accum;
|
||||
denom = CONFIG_PM_GOVERNOR_COEFN;
|
||||
|
||||
/* Then calculate Y += SUM(Ai*Yi), i = 1..n-1. The oldest sample will
|
||||
* reside at the domain's mndx (and this is the value that we will overwrite
|
||||
* with the new value).
|
||||
*/
|
||||
|
||||
for (i = 0, j = pdomstate->mndx; i < CONFIG_PM_GOVERNOR_MEMORY - 1; i++, j++)
|
||||
{
|
||||
if (j >= CONFIG_PM_GOVERNOR_MEMORY - 1)
|
||||
{
|
||||
j = 0;
|
||||
}
|
||||
|
||||
y += g_pm_activity_governor.pmcoeffs[i] * pdomstate->memory[j];
|
||||
denom += g_pm_activity_governor.pmcoeffs[i];
|
||||
}
|
||||
|
||||
/* Compute and save the new activity value */
|
||||
|
||||
y /= denom;
|
||||
|
||||
index = pdomstate->mndx++;
|
||||
pdomstate->memory[index] = y;
|
||||
if (pdomstate->mndx >= CONFIG_PM_GOVERNOR_MEMORY - 1)
|
||||
{
|
||||
pdomstate->mndx = 0;
|
||||
}
|
||||
#else
|
||||
|
||||
/* No smoothing */
|
||||
|
||||
Y = accum;
|
||||
#endif
|
||||
|
||||
/* First check if increased activity should cause us to return to the
|
||||
* normal operating state. This would be unlikely for the lowest power
|
||||
* consumption states because the CPU is probably asleep. However this
|
||||
* probably does apply for the IDLE state.
|
||||
*/
|
||||
|
||||
if (state > PM_NORMAL)
|
||||
{
|
||||
/* Get the table index for the current state (which will be the
|
||||
* current state minus one)
|
||||
*/
|
||||
|
||||
index = state - 1;
|
||||
|
||||
/* Has the threshold to return to normal power consumption state been
|
||||
* exceeded?
|
||||
*/
|
||||
|
||||
if (y > g_pm_activity_governor.pmexitthresh[index])
|
||||
{
|
||||
/* Yes... reset the count and recommend the normal state. */
|
||||
|
||||
pdomstate->btime = clock_systimer();
|
||||
pdomstate->recommended = PM_NORMAL;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
/* Now, compare this new activity level to the thresholds and counts for
|
||||
* the next lower power consumption state. If we are already in the SLEEP
|
||||
* state, then there is nothing more to be done (in fact, I would be
|
||||
* surprised to be executing!).
|
||||
*/
|
||||
|
||||
if (state < PM_SLEEP)
|
||||
{
|
||||
unsigned int nextstate;
|
||||
|
||||
/* Get the next state and the table index for the next state (which will
|
||||
* be the current state)
|
||||
*/
|
||||
|
||||
index = state;
|
||||
nextstate = state + 1;
|
||||
|
||||
/* Has the threshold to enter the next lower power consumption state
|
||||
* been exceeded?
|
||||
*/
|
||||
|
||||
if (y > g_pm_activity_governor.pmenterthresh[index])
|
||||
{
|
||||
/* No... reset the count and recommend the current state */
|
||||
|
||||
pdomstate->btime = clock_systimer();
|
||||
pdomstate->recommended = state;
|
||||
}
|
||||
|
||||
/* Yes.. have we already recommended this state? If so, do nothing */
|
||||
|
||||
else if (pdomstate->recommended < nextstate)
|
||||
{
|
||||
/* No.. calculate the count. Has it passed the count required
|
||||
* for a state transition?
|
||||
*/
|
||||
|
||||
if (clock_systimer() - pdomstate->btime >=
|
||||
g_pm_activity_governor.pmcount[index] * TIME_SLICE_TICKS)
|
||||
{
|
||||
/* Yes, recommend the new state and set up for the next
|
||||
* transition.
|
||||
*/
|
||||
|
||||
pdomstate->btime = clock_systimer();
|
||||
pdomstate->recommended = nextstate;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static enum pm_state_e governor_checkstate(int domain)
|
||||
{
|
||||
FAR struct pm_domain_state_s *pdomstate;
|
||||
FAR struct pm_domain_s *pdom;
|
||||
clock_t now, elapsed;
|
||||
irqstate_t flags;
|
||||
int index;
|
||||
|
||||
/* Get a convenience pointer to minimize all of the indexing */
|
||||
|
||||
pdomstate = &g_pm_activity_governor.domain_states[domain];
|
||||
pdom = &g_pmglobals.domain[domain];
|
||||
|
||||
/* Check for the end of the current time slice. This must be performed
|
||||
* with interrupts disabled so that it does not conflict with the similar
|
||||
* logic in governor_activity().
|
||||
*/
|
||||
|
||||
flags = enter_critical_section();
|
||||
|
||||
/* Check the elapsed time. In periods of low activity, time slicing is
|
||||
* controlled by IDLE loop polling; in periods of higher activity, time
|
||||
* slicing is controlled by driver activity. In either case, the duration
|
||||
* of the time slice is only approximate; during times of heavy activity,
|
||||
* time slices may be become longer and the activity level may be over-
|
||||
* estimated.
|
||||
*/
|
||||
|
||||
now = clock_systimer();
|
||||
elapsed = now - pdomstate->stime;
|
||||
if (elapsed >= TIME_SLICE_TICKS)
|
||||
{
|
||||
int16_t accum;
|
||||
|
||||
/* Sample the count, reset the time and count, and assess the PM
|
||||
* state. This is an atomic operation because interrupts are
|
||||
* still disabled.
|
||||
*/
|
||||
|
||||
accum = pdomstate->accum;
|
||||
pdomstate->stime = now;
|
||||
pdomstate->accum = 0;
|
||||
|
||||
(void)governor_update(domain, accum);
|
||||
}
|
||||
|
||||
/* Consider the possible power state lock here */
|
||||
|
||||
for (index = 0; index < pdomstate->recommended; index++)
|
||||
{
|
||||
if (pdom->stay[index] != 0)
|
||||
{
|
||||
pdomstate->recommended = index;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
leave_critical_section(flags);
|
||||
|
||||
return pdomstate->recommended;
|
||||
}
|
||||
|
||||
static void governor_statechanged(int domain, enum pm_state_e newstate)
|
||||
{
|
||||
if (newstate != PM_RESTORE)
|
||||
{
|
||||
/* Start PM timer to decrease PM state */
|
||||
|
||||
governor_timer(domain);
|
||||
}
|
||||
}
|
||||
|
||||
static void governor_timer_cb(int argc, wdparm_t arg1, ...)
|
||||
{
|
||||
/* Do nothing here, cause we only need TIMER ISR to wake up PM,
|
||||
* for deceasing PM state.
|
||||
*/
|
||||
|
||||
UNUSED(argc);
|
||||
UNUSED(arg1);
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
* Name: governor_timer
|
||||
*
|
||||
* Description:
|
||||
* This internal function is called to start one timer to decrease power
|
||||
* state level.
|
||||
*
|
||||
* Input Parameters:
|
||||
* domain - The PM domain associated with the accumulator
|
||||
*
|
||||
* Returned Value:
|
||||
* None.
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
static void governor_timer(int domain)
|
||||
{
|
||||
FAR struct pm_domain_state_s *pdomstate;
|
||||
FAR struct pm_domain_s *pdom;
|
||||
uint8_t state;
|
||||
|
||||
static const int pmtick[3] =
|
||||
{
|
||||
TIME_SLICE_TICKS * CONFIG_PM_GOVERNOR_IDLEENTER_COUNT,
|
||||
TIME_SLICE_TICKS * CONFIG_PM_GOVERNOR_STANDBYENTER_COUNT,
|
||||
TIME_SLICE_TICKS * CONFIG_PM_GOVERNOR_SLEEPENTER_COUNT
|
||||
};
|
||||
|
||||
pdom = &g_pmglobals.domain[domain];
|
||||
pdomstate = &g_pm_activity_governor.domain_states[domain];
|
||||
state = pdom->state;
|
||||
|
||||
if (!pdomstate->wdog)
|
||||
{
|
||||
pdomstate->wdog = wd_create();
|
||||
}
|
||||
|
||||
if (state < PM_SLEEP && !pdom->stay[pdom->state] &&
|
||||
pmtick[state])
|
||||
{
|
||||
int delay = pmtick[state] + pdomstate->btime - clock_systimer();
|
||||
int left = wd_gettime(pdomstate->wdog);
|
||||
|
||||
if (!WDOG_ISACTIVE(pdomstate->wdog) || abs(delay - left) > PM_TIMER_GAP)
|
||||
{
|
||||
wd_start(pdomstate->wdog, delay, governor_timer_cb, 0);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
wd_cancel(pdomstate->wdog);
|
||||
}
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
* Public Functions
|
||||
****************************************************************************/
|
||||
|
||||
FAR struct pm_governor_s *pm_activity_governor_initialize(void)
|
||||
{
|
||||
return &g_pmgovernor;
|
||||
}
|
||||
|
||||
#endif /* CONFIG_PM_GOVERNOR_ACTIVITY */
|
210
drivers/power/activity_governor.h
Normal file
210
drivers/power/activity_governor.h
Normal file
|
@ -0,0 +1,210 @@
|
|||
/****************************************************************************
|
||||
* activity_governor.h
|
||||
*
|
||||
* Copyright (C) 2011-2012, 2016 Gregory Nutt. All rights reserved.
|
||||
* Author: Gregory Nutt <gnutt@nuttx.org>
|
||||
* Author: Matias Nitsche <mnitsche@dc.uba.ar>
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in
|
||||
* the documentation and/or other materials provided with the
|
||||
* distribution.
|
||||
* 3. Neither the name NuttX nor the names of its contributors may be
|
||||
* used to endorse or promote products derived from this software
|
||||
* without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
|
||||
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
|
||||
* COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
|
||||
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
|
||||
* OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
|
||||
* AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
|
||||
* ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
#ifndef __DRIVERS_POWER_ACTIVITY_GOVERNER_H
|
||||
#define __DRIVERS_POWER_ACTIVITY_GOVERNER_H
|
||||
|
||||
/****************************************************************************
|
||||
* Included Files
|
||||
****************************************************************************/
|
||||
|
||||
#include <nuttx/config.h>
|
||||
#include <nuttx/power/pm.h>
|
||||
|
||||
/****************************************************************************
|
||||
* Pre-processor Definitions
|
||||
****************************************************************************/
|
||||
|
||||
/* CONFIG_PM_GOVERNOR_SLICEMS. The activity based governor collects activity
|
||||
* counts in time slices. At the end of the time slice, the count accumulated
|
||||
* during that interval is applied to an averaging algorithm to determine
|
||||
* the activity level.
|
||||
*
|
||||
* CONFIG_PM_GOVERNOR_SLICEMS provides the duration of that time slice in
|
||||
* milliseconds. Default: 100 Milliseconds
|
||||
*/
|
||||
|
||||
#ifndef CONFIG_PM_GOVERNOR_SLICEMS
|
||||
# define CONFIG_PM_GOVERNOR_SLICEMS 100 /* Default is 100 msec */
|
||||
#endif
|
||||
|
||||
#if CONFIG_PM_GOVERNOR_SLICEMS < 1
|
||||
# error CONFIG_PM_GOVERNOR_SLICEMS invalid
|
||||
#endif
|
||||
|
||||
/* The averaging algorithm is simply: Y = (An*X + SUM(Ai*Yi))/SUM(Aj), where
|
||||
* i = 1..n-1 and j= 1..n, n is the length of the "memory", Ai is the
|
||||
* weight applied to each value, and X is the current activity. These weights
|
||||
* may be negative and a limited to the range of int16_t.
|
||||
*
|
||||
* CONFIG_PM_GOVERNOR_MEMORY:
|
||||
* provides the memory for the algorithm. Default: 2
|
||||
* CONFIG_PM_GOVERNOR_COEFn:
|
||||
* provides weight for each sample. Default: 1
|
||||
*
|
||||
* Setting CONFIG_PM_GOVERNOR_MEMORY=1 disables all smoothing.
|
||||
*/
|
||||
|
||||
#ifndef CONFIG_PM_GOVERNOR_MEMORY
|
||||
# define CONFIG_PM_GOVERNOR_MEMORY 2
|
||||
#endif
|
||||
|
||||
#if CONFIG_PM_GOVERNOR_MEMORY < 1
|
||||
# error "CONFIG_PM_GOVERNOR_MEMORY must be >= 1"
|
||||
#endif
|
||||
|
||||
#ifndef CONFIG_PM_GOVERNOR_COEFN
|
||||
# define CONFIG_PM_GOVERNOR_COEFN 1
|
||||
#endif
|
||||
|
||||
#if CONFIG_PM_GOVERNOR_MEMORY > 1 && !defined(CONFIG_PM_GOVERNOR_COEF1)
|
||||
# define CONFIG_PM_GOVERNOR_COEF1 1
|
||||
#endif
|
||||
|
||||
#if CONFIG_PM_GOVERNOR_MEMORY > 2 && !defined(CONFIG_PM_GOVERNOR_COEF2)
|
||||
# define CONFIG_PM_GOVERNOR_COEF2 1
|
||||
#endif
|
||||
|
||||
#if CONFIG_PM_GOVERNOR_MEMORY > 3 && !defined(CONFIG_PM_GOVERNOR_COEF3)
|
||||
# define CONFIG_PM_GOVERNOR_COEF3 1
|
||||
#endif
|
||||
|
||||
#if CONFIG_PM_GOVERNOR_MEMORY > 4 && !defined(CONFIG_PM_GOVERNOR_COEF4)
|
||||
# define CONFIG_PM_GOVERNOR_COEF4 1
|
||||
#endif
|
||||
|
||||
#if CONFIG_PM_GOVERNOR_MEMORY > 5 && !defined(CONFIG_PM_GOVERNOR_COEF5)
|
||||
# define CONFIG_PM_GOVERNOR_COEF5 1
|
||||
#endif
|
||||
|
||||
#if CONFIG_PM_GOVERNOR_MEMORY > 6
|
||||
# warning "This logic needs to be extended"
|
||||
#endif
|
||||
|
||||
/* State changes then occur when the weight activity account crosses
|
||||
* threshold values for certain periods of time (time slice count).
|
||||
*
|
||||
* CONFIG_PM_GOVERNOR_xxxENTER_THRESH is the threshold value for entering
|
||||
* state xxx.
|
||||
* CONFIG_PM_GOVERNOR_xxxENTER_COUNT is the count for entering state xxx.
|
||||
*
|
||||
* Resuming to normal state, on the other hand, is usually immediate and
|
||||
* controlled by wakeup conditions established by the platform. The PM
|
||||
* module only recommends reduced power states.
|
||||
*/
|
||||
|
||||
#ifndef CONFIG_PM_GOVERNOR_IDLEENTER_THRESH
|
||||
# define CONFIG_PM_GOVERNOR_IDLEENTER_THRESH 1 /* <=1: Essentially no activity */
|
||||
#endif
|
||||
|
||||
#ifndef CONFIG_PM_GOVERNOR_IDLEEXIT_THRESH
|
||||
# define CONFIG_PM_GOVERNOR_IDLEEXIT_THRESH 2 /* >=2: Active */
|
||||
#endif
|
||||
|
||||
#if CONFIG_PM_GOVERNOR_IDLEENTER_THRESH >= CONFIG_PM_GOVERNOR_IDLEEXIT_THRESH
|
||||
# error "Must have CONFIG_PM_GOVERNOR_IDLEENTER_THRESH < CONFIG_PM_GOVERNOR_IDLEEXIT_THRESH"
|
||||
#endif
|
||||
|
||||
#ifndef CONFIG_PM_GOVERNOR_IDLEENTER_COUNT
|
||||
# define CONFIG_PM_GOVERNOR_IDLEENTER_COUNT 30 /* Thirty IDLE slices to enter
|
||||
* IDLE mode from normal
|
||||
*/
|
||||
#endif
|
||||
|
||||
#ifndef CONFIG_PM_GOVERNOR_STANDBYENTER_THRESH
|
||||
# define CONFIG_PM_GOVERNOR_STANDBYENTER_THRESH 1 /* <=1: Essentially no activity */
|
||||
#endif
|
||||
|
||||
#ifndef CONFIG_PM_GOVERNOR_STANDBYEXIT_THRESH
|
||||
# define CONFIG_PM_GOVERNOR_STANDBYEXIT_THRESH 2 /* >=2: Active */
|
||||
#endif
|
||||
|
||||
#if CONFIG_PM_GOVERNOR_STANDBYENTER_THRESH >= CONFIG_PM_GOVERNOR_STANDBYEXIT_THRESH
|
||||
# error "Must have CONFIG_PM_GOVERNOR_STANDBYENTER_THRESH < CONFIG_PM_GOVERNOR_STANDBYEXIT_THRESH"
|
||||
#endif
|
||||
|
||||
#ifndef CONFIG_PM_GOVERNOR_STANDBYENTER_COUNT
|
||||
# define CONFIG_PM_GOVERNOR_STANDBYENTER_COUNT 50 /* Fifty IDLE slices to enter
|
||||
* STANDBY mode from IDLE
|
||||
*/
|
||||
#endif
|
||||
|
||||
#ifndef CONFIG_PM_GOVERNOR_SLEEPENTER_THRESH
|
||||
# define CONFIG_PM_GOVERNOR_SLEEPENTER_THRESH 1 /* <=1: Essentially no activity */
|
||||
#endif
|
||||
|
||||
#ifndef CONFIG_PM_GOVERNOR_SLEEPEXIT_THRESH
|
||||
# define CONFIG_PM_GOVERNOR_SLEEPEXIT_THRESH 2 /* >=2: Active */
|
||||
#endif
|
||||
|
||||
#if CONFIG_PM_GOVERNOR_SLEEPENTER_THRESH >= CONFIG_PM_GOVERNOR_SLEEPEXIT_THRESH
|
||||
# error "Must have CONFIG_PM_GOVERNOR_SLEEPENTER_THRESH < CONFIG_PM_GOVERNOR_SLEEPEXIT_THRESH"
|
||||
#endif
|
||||
|
||||
#ifndef CONFIG_PM_GOVERNOR_SLEEPENTER_COUNT
|
||||
# define CONFIG_PM_GOVERNOR_SLEEPENTER_COUNT 70 /* 70 IDLE slices to enter SLEEP
|
||||
* mode from STANDBY
|
||||
*/
|
||||
#endif
|
||||
|
||||
/****************************************************************************
|
||||
* Type Definitions
|
||||
****************************************************************************/
|
||||
|
||||
/****************************************************************************
|
||||
* Public Data
|
||||
****************************************************************************/
|
||||
|
||||
#ifdef __cplusplus
|
||||
#define EXTERN extern "C"
|
||||
extern "C"
|
||||
{
|
||||
#else
|
||||
#define EXTERN extern
|
||||
#endif
|
||||
|
||||
/****************************************************************************
|
||||
* Public Function Prototypes
|
||||
****************************************************************************/
|
||||
|
||||
FAR struct pm_governor_s *pm_activity_governor_initialize(void);
|
||||
|
||||
#undef EXTERN
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif // __DRIVERS_POWER_ACTIVITY_GOVERNER_H
|
173
drivers/power/greedy_governor.c
Normal file
173
drivers/power/greedy_governor.c
Normal file
|
@ -0,0 +1,173 @@
|
|||
/****************************************************************************
|
||||
* greedy_governor/greedy_governor.c
|
||||
*
|
||||
* Copyright (C) 2019 Matias Nitsche. All rights reserved.
|
||||
* Author: Matias Nitsche <mnitsche@dc.uba.ar>
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in
|
||||
* the documentation and/or other materials provided with the
|
||||
* distribution.
|
||||
* 3. Neither the name NuttX nor the names of its contributors may be
|
||||
* used to endorse or promote products derived from this software
|
||||
* without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
|
||||
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
|
||||
* COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
|
||||
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
|
||||
* OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
|
||||
* AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
|
||||
* ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
/****************************************************************************
|
||||
* Included Files
|
||||
****************************************************************************/
|
||||
|
||||
#include <nuttx/config.h>
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <stdbool.h>
|
||||
#include <string.h>
|
||||
#include <poll.h>
|
||||
#include <errno.h>
|
||||
#include <debug.h>
|
||||
#include <assert.h>
|
||||
|
||||
#include <nuttx/kmalloc.h>
|
||||
#include <nuttx/signal.h>
|
||||
#include <nuttx/fs/fs.h>
|
||||
#include <nuttx/power/pm.h>
|
||||
#include <nuttx/power/power_ioctl.h>
|
||||
|
||||
#include <nuttx/irq.h>
|
||||
|
||||
#include "greedy_governor.h"
|
||||
#include "pm.h"
|
||||
|
||||
/****************************************************************************
|
||||
* Private Types
|
||||
****************************************************************************/
|
||||
|
||||
/****************************************************************************
|
||||
* Private Function Prototypes
|
||||
****************************************************************************/
|
||||
|
||||
/* PM governor methods */
|
||||
|
||||
static void greedy_governor_initialize(void);
|
||||
static void greedy_governor_statechanged(int domain,
|
||||
enum pm_state_e newstate);
|
||||
static enum pm_state_e greedy_governor_checkstate(int domain);
|
||||
|
||||
/****************************************************************************
|
||||
* Private Data
|
||||
****************************************************************************/
|
||||
|
||||
static struct pm_governor_s g_greedy_governor_ops =
|
||||
{
|
||||
.initialize = greedy_governor_initialize, /* initialize */
|
||||
.statechanged = greedy_governor_statechanged, /* statechanged */
|
||||
.checkstate = greedy_governor_checkstate, /* checkstate */
|
||||
.activity = NULL, /* activity */
|
||||
};
|
||||
|
||||
/****************************************************************************
|
||||
* Private Functions
|
||||
****************************************************************************/
|
||||
|
||||
/****************************************************************************
|
||||
* Name: greedy_governor_initialize
|
||||
****************************************************************************/
|
||||
|
||||
static void greedy_governor_initialize(void)
|
||||
{
|
||||
#ifdef CONFIG_PM_GOVERNOR_EXPLICIT_RELAX
|
||||
for (int dom = 0; dom < CONFIG_PM_NDOMAINS; dom++)
|
||||
{
|
||||
for (int state = 0; state < PM_COUNT; state++)
|
||||
{
|
||||
pm_stay(dom, state);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
* Name: greedy_governor_statechanged
|
||||
****************************************************************************/
|
||||
|
||||
static void greedy_governor_statechanged(int domain, enum pm_state_e newstate)
|
||||
{
|
||||
/* no need to react to state changes */
|
||||
|
||||
UNUSED(domain);
|
||||
UNUSED(newstate);
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
* Name: user_governor_checkstate
|
||||
****************************************************************************/
|
||||
|
||||
static enum pm_state_e greedy_governor_checkstate(int domain)
|
||||
{
|
||||
FAR struct pm_domain_s *pdom;
|
||||
int state;
|
||||
irqstate_t flags;
|
||||
|
||||
pdom = &g_pmglobals.domain[domain];
|
||||
state = PM_NORMAL;
|
||||
|
||||
/* We disable interrupts since pm_stay()/pm_relax() could be simultaneously
|
||||
* invoked, which modifies the stay count which we are about to read
|
||||
*/
|
||||
|
||||
flags = enter_critical_section();
|
||||
|
||||
/* Find the lowest power-level which is not locked. */
|
||||
|
||||
while (!pdom->stay[state] && state < (PM_COUNT - 1))
|
||||
{
|
||||
state++;
|
||||
}
|
||||
|
||||
leave_critical_section(flags);
|
||||
|
||||
/* Return the found state */
|
||||
|
||||
return state;
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
* Public Functions
|
||||
****************************************************************************/
|
||||
|
||||
/****************************************************************************
|
||||
* Name: pm_greedy_governor_initialize
|
||||
*
|
||||
* Description:
|
||||
* Register the user_governor driver as the specified device.
|
||||
*
|
||||
* Returned Value:
|
||||
* Zero (OK) is returned on success. Otherwise a negated errno value is
|
||||
* returned to indicate the nature of the failure.
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
FAR const struct pm_governor_s *pm_greedy_governor_initialize(void)
|
||||
{
|
||||
return &g_greedy_governor_ops;
|
||||
}
|
90
drivers/power/greedy_governor.h
Normal file
90
drivers/power/greedy_governor.h
Normal file
|
@ -0,0 +1,90 @@
|
|||
/****************************************************************************
|
||||
* user_governor/user_governor.c
|
||||
*
|
||||
* Copyright (C) 2019 Matias Nitsche. All rights reserved.
|
||||
* Author: Matias Nitsche <mnitsche@dc.uba.ar>
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in
|
||||
* the documentation and/or other materials provided with the
|
||||
* distribution.
|
||||
* 3. Neither the name NuttX nor the names of its contributors may be
|
||||
* used to endorse or promote products derived from this software
|
||||
* without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
|
||||
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
|
||||
* COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
|
||||
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
|
||||
* OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
|
||||
* AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
|
||||
* ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
#ifndef __DRIVERS_POWER_GREEDY_GOVERNER_H
|
||||
#define __DRIVERS_POWER_GREEDY_GOVERNER_H
|
||||
|
||||
/****************************************************************************
|
||||
* Included Files
|
||||
****************************************************************************/
|
||||
|
||||
#include <nuttx/config.h>
|
||||
#include <nuttx/input/ioctl.h>
|
||||
#include <nuttx/power/pm.h>
|
||||
|
||||
/****************************************************************************
|
||||
* Pre-processor Definitions
|
||||
****************************************************************************/
|
||||
|
||||
/****************************************************************************
|
||||
* Type Definitions
|
||||
****************************************************************************/
|
||||
|
||||
/****************************************************************************
|
||||
* Public Data
|
||||
****************************************************************************/
|
||||
|
||||
#ifdef __cplusplus
|
||||
#define EXTERN extern "C"
|
||||
extern "C"
|
||||
{
|
||||
#else
|
||||
#define EXTERN extern
|
||||
#endif
|
||||
|
||||
/****************************************************************************
|
||||
* Public Function Prototypes
|
||||
****************************************************************************/
|
||||
|
||||
/****************************************************************************
|
||||
* Name: pm_greedy_governor_register
|
||||
*
|
||||
* Description:
|
||||
* Return the greedy governor instance.
|
||||
*
|
||||
* Returned Value:
|
||||
* A pointer to the governor struct. Otherwise NULL is returned on error.
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
FAR const struct pm_governor_s *pm_greedy_governor_initialize(void);
|
||||
|
||||
#undef EXTERN
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* __DRIVERS_POWER_GREEDY_GOVERNER_H */
|
||||
|
|
@ -3,6 +3,7 @@
|
|||
*
|
||||
* Copyright (C) 2011-2012, 2016, 2018 Gregory Nutt. All rights reserved.
|
||||
* Author: Gregory Nutt <gnutt@nuttx.org>
|
||||
* Author: Matias Nitsche <mnitsche@dc.uba.ar>
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
|
@ -56,19 +57,6 @@
|
|||
* Pre-processor Definitions
|
||||
****************************************************************************/
|
||||
|
||||
/* Configuration ************************************************************/
|
||||
|
||||
/* Convert the time slice interval into system clock ticks.
|
||||
*
|
||||
* CONFIG_PM_SLICEMS provides the duration of one time slice in milliseconds.
|
||||
* CLOCKS_PER_SEC provides the number of timer ticks in one second.
|
||||
*
|
||||
* slice ticks = (CONFIG_PM_SLICEMS msec / 1000 msec/sec) /
|
||||
* (CLOCKS_PER_SEC ticks/sec)
|
||||
*/
|
||||
|
||||
#define TIME_SLICE_TICKS ((CONFIG_PM_SLICEMS * CLOCKS_PER_SEC) / 1000)
|
||||
|
||||
/****************************************************************************
|
||||
* Name: pm_lock
|
||||
*
|
||||
|
@ -98,56 +86,18 @@
|
|||
|
||||
struct pm_domain_s
|
||||
{
|
||||
/* state - The current state for this PM domain (as determined by an
|
||||
* explicit call to pm_changestate())
|
||||
* recommended - The recommended state based on the PM algorithm in
|
||||
* function pm_update().
|
||||
* mndex - The index to the next slot in the memory[] array to use.
|
||||
* mcnt - A tiny counter used only at start up. The actual
|
||||
* algorithm cannot be applied until CONFIG_PM_MEMORY
|
||||
* samples have been collected.
|
||||
/* The current state for this PM domain (as determined by an
|
||||
* explicit call to pm_changestate())
|
||||
*/
|
||||
|
||||
uint8_t state;
|
||||
uint8_t recommended;
|
||||
uint8_t mndx;
|
||||
uint8_t mcnt;
|
||||
|
||||
/* accum - The accumulated counts in this time interval */
|
||||
|
||||
int16_t accum;
|
||||
|
||||
#if CONFIG_PM_MEMORY > 1
|
||||
/* This is the averaging "memory." The averaging algorithm is simply:
|
||||
* Y = (An*X + SUM(Ai*Yi))/SUM(Aj), where i = 1..n-1 and j= 1..n, n is the
|
||||
* length of the "memory", Ai is the weight applied to each value, and X is
|
||||
* the current activity.
|
||||
*
|
||||
* CONFIG_PM_MEMORY provides the memory for the algorithm. Default: 2
|
||||
* CONFIG_PM_COEFn provides weight for each sample. Default: 1
|
||||
*/
|
||||
|
||||
int16_t memory[CONFIG_PM_MEMORY - 1];
|
||||
#endif
|
||||
|
||||
/* stime - The time (in ticks) at the start of the current time slice */
|
||||
|
||||
clock_t stime;
|
||||
|
||||
/* btime - The time (in ticks) at the start of the current state */
|
||||
|
||||
clock_t btime;
|
||||
|
||||
/* The power state lock count */
|
||||
|
||||
uint16_t stay[PM_COUNT];
|
||||
|
||||
/* Timer to decrease state */
|
||||
|
||||
WDOG_ID wdog;
|
||||
};
|
||||
|
||||
/* This structure encapsulates all of the global data used by the PM module */
|
||||
/* This structure encapsulates all of the global data used by the PM system */
|
||||
|
||||
struct pm_global_s
|
||||
{
|
||||
|
@ -164,9 +114,13 @@ struct pm_global_s
|
|||
|
||||
dq_queue_t registry;
|
||||
|
||||
/* The activity/state information for each PM domain */
|
||||
/* The state information for each PM domain */
|
||||
|
||||
struct pm_domain_s domain[CONFIG_PM_NDOMAINS];
|
||||
|
||||
/* A pointer to the PM governor instance */
|
||||
|
||||
FAR const struct pm_governor_s *governor;
|
||||
};
|
||||
|
||||
/****************************************************************************
|
||||
|
@ -186,33 +140,9 @@ extern "C"
|
|||
|
||||
EXTERN struct pm_global_s g_pmglobals;
|
||||
|
||||
/************************************************************************************
|
||||
/*****************************************************************************
|
||||
* Public Function Prototypes
|
||||
************************************************************************************/
|
||||
|
||||
/****************************************************************************
|
||||
* Name: pm_update
|
||||
*
|
||||
* Description:
|
||||
* This internal function is called at the end of a time slice in order to
|
||||
* update driver activity metrics and recommended states.
|
||||
*
|
||||
* Input Parameters:
|
||||
* domain - The domain associated with the accumulator.
|
||||
* accum - The value of the activity accumulator at the end of the time
|
||||
* slice.
|
||||
*
|
||||
* Returned Value:
|
||||
* None.
|
||||
*
|
||||
* Assumptions:
|
||||
* This function may be called from a driver, perhaps even at the interrupt
|
||||
* level. It may also be called from the IDLE loop at the lowest possible
|
||||
* priority level.
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
void pm_update(int domain, int16_t accum);
|
||||
*****************************************************************************/
|
||||
|
||||
#undef EXTERN
|
||||
#if defined(__cplusplus)
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
*
|
||||
* Copyright (C) 2011-2012, 2016 Gregory Nutt. All rights reserved.
|
||||
* Author: Gregory Nutt <gnutt@nuttx.org>
|
||||
* Author: Matias Nitsche <mnitsche@dc.uba.ar>
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
|
@ -83,65 +84,9 @@
|
|||
|
||||
void pm_activity(int domain, int priority)
|
||||
{
|
||||
FAR struct pm_domain_s *pdom;
|
||||
clock_t now, elapsed;
|
||||
uint32_t accum;
|
||||
irqstate_t flags;
|
||||
|
||||
/* Get a convenience pointer to minimize all of the indexing */
|
||||
|
||||
DEBUGASSERT(domain >= 0 && domain < CONFIG_PM_NDOMAINS);
|
||||
pdom = &g_pmglobals.domain[domain];
|
||||
|
||||
/* Just increment the activity count in the current time slice. The priority
|
||||
* is simply the number of counts that are added.
|
||||
*/
|
||||
|
||||
if (priority > 0)
|
||||
if (g_pmglobals.governor->activity)
|
||||
{
|
||||
/* Add the priority to the accumulated counts in a critical section. */
|
||||
|
||||
flags = enter_critical_section();
|
||||
accum = (uint32_t)pdom->accum + priority;
|
||||
|
||||
/* Make sure that we do not overflow the underlying uint16_t representation */
|
||||
|
||||
if (accum > INT16_MAX)
|
||||
{
|
||||
accum = INT16_MAX;
|
||||
}
|
||||
|
||||
/* Save the updated count */
|
||||
|
||||
pdom->accum = (int16_t)accum;
|
||||
|
||||
/* Check the elapsed time. In periods of low activity, time slicing is
|
||||
* controlled by IDLE loop polling; in periods of higher activity, time
|
||||
* slicing is controlled by driver activity. In either case, the
|
||||
* duration of the time slice is only approximate; during times of
|
||||
* heavy activity, time slices may be become longer and the activity
|
||||
* level may be over-estimated.
|
||||
*/
|
||||
|
||||
now = clock_systimer();
|
||||
elapsed = now - pdom->stime;
|
||||
if (elapsed >= TIME_SLICE_TICKS)
|
||||
{
|
||||
int16_t tmp;
|
||||
|
||||
/* Sample the count, reset the time and count, and assess the PM
|
||||
* state. This is an atomic operation because interrupts are
|
||||
* still disabled.
|
||||
*/
|
||||
|
||||
tmp = pdom->accum;
|
||||
pdom->stime = now;
|
||||
pdom->accum = 0;
|
||||
|
||||
(void)pm_update(domain, tmp);
|
||||
}
|
||||
|
||||
leave_critical_section(flags);
|
||||
g_pmglobals.governor->activity(domain, priority);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
*
|
||||
* Copyright (C) 2011-2012, 2016, 2018 Gregory Nutt. All rights reserved.
|
||||
* Author: Gregory Nutt <gnutt@nuttx.org>
|
||||
* Author: Matias Nitsche <mnitsche@dc.uba.ar>
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
|
@ -50,70 +51,10 @@
|
|||
|
||||
#ifdef CONFIG_PM
|
||||
|
||||
/****************************************************************************
|
||||
* Pre-processor Definitions
|
||||
****************************************************************************/
|
||||
|
||||
#define PM_TIMER_GAP (TIME_SLICE_TICKS * 2)
|
||||
|
||||
/****************************************************************************
|
||||
* Private Functions
|
||||
****************************************************************************/
|
||||
|
||||
static void pm_timer_cb(int argc, wdparm_t arg1, ...)
|
||||
{
|
||||
/* Do nothing here, cause we only need TIMER ISR to wake up PM,
|
||||
* for deceasing PM state.
|
||||
*/
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
* Name: pm_timer
|
||||
*
|
||||
* Description:
|
||||
* This internal function is called to start one timer to decrease power
|
||||
* state level.
|
||||
*
|
||||
* Input Parameters:
|
||||
* domain - The PM domain associated with the accumulator
|
||||
*
|
||||
* Returned Value:
|
||||
* None.
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
static void pm_timer(int domain)
|
||||
{
|
||||
FAR struct pm_domain_s *pdom = &g_pmglobals.domain[domain];
|
||||
static const int pmtick[3] =
|
||||
{
|
||||
TIME_SLICE_TICKS * CONFIG_PM_IDLEENTER_COUNT,
|
||||
TIME_SLICE_TICKS * CONFIG_PM_STANDBYENTER_COUNT,
|
||||
TIME_SLICE_TICKS * CONFIG_PM_SLEEPENTER_COUNT
|
||||
};
|
||||
|
||||
if (!pdom->wdog)
|
||||
{
|
||||
pdom->wdog = wd_create();
|
||||
}
|
||||
|
||||
if (pdom->state < PM_SLEEP && !pdom->stay[pdom->state] &&
|
||||
pmtick[pdom->state])
|
||||
{
|
||||
int delay = pmtick[pdom->state] + pdom->btime - clock_systimer();
|
||||
int left = wd_gettime(pdom->wdog);
|
||||
|
||||
if (!WDOG_ISACTIVE(pdom->wdog) || abs(delay - left) > PM_TIMER_GAP)
|
||||
{
|
||||
wd_start(pdom->wdog, delay, pm_timer_cb, 0);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
wd_cancel(pdom->wdog);
|
||||
}
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
* Name: pm_prepall
|
||||
*
|
||||
|
@ -121,7 +62,7 @@ static void pm_timer(int domain)
|
|||
* Prepare every driver for the state change.
|
||||
*
|
||||
* Input Parameters:
|
||||
* domain - Identifies the domain of the new PM state
|
||||
* domain - Identifies the domain of the new PM state
|
||||
* newstate - Identifies the new PM state
|
||||
*
|
||||
* Returned Value:
|
||||
|
@ -190,7 +131,7 @@ static int pm_prepall(int domain, enum pm_state_e newstate)
|
|||
* Inform all drivers of the state change.
|
||||
*
|
||||
* Input Parameters:
|
||||
* domain - Identifies the domain of the new PM state
|
||||
* domain - Identifies the domain of the new PM state
|
||||
* newstate - Identifies the new PM state
|
||||
*
|
||||
* Returned Value:
|
||||
|
@ -209,7 +150,8 @@ static inline void pm_changeall(int domain, enum pm_state_e newstate)
|
|||
{
|
||||
/* Visit each registered callback structure in normal order. */
|
||||
|
||||
for (entry = dq_peek(&g_pmglobals.registry); entry; entry = dq_next(entry))
|
||||
for (entry = dq_peek(&g_pmglobals.registry);
|
||||
entry; entry = dq_next(entry))
|
||||
{
|
||||
/* Is the notification callback supported? */
|
||||
|
||||
|
@ -226,7 +168,8 @@ static inline void pm_changeall(int domain, enum pm_state_e newstate)
|
|||
{
|
||||
/* Visit each registered callback structure in reverse order. */
|
||||
|
||||
for (entry = dq_tail(&g_pmglobals.registry); entry; entry = dq_prev(entry))
|
||||
for (entry = dq_tail(&g_pmglobals.registry);
|
||||
entry; entry = dq_prev(entry))
|
||||
{
|
||||
/* Is the notification callback supported? */
|
||||
|
||||
|
@ -254,7 +197,7 @@ static inline void pm_changeall(int domain, enum pm_state_e newstate)
|
|||
* drivers that have registered for power management event callbacks.
|
||||
*
|
||||
* Input Parameters:
|
||||
* domain - Identifies the domain of the new PM state
|
||||
* domain - Identifies the domain of the new PM state
|
||||
* newstate - Identifies the new PM state
|
||||
*
|
||||
* Returned Value:
|
||||
|
@ -311,10 +254,13 @@ int pm_changestate(int domain, enum pm_state_e newstate)
|
|||
if (newstate != PM_RESTORE)
|
||||
{
|
||||
g_pmglobals.domain[domain].state = newstate;
|
||||
}
|
||||
|
||||
/* Start PM timer to decrease PM state */
|
||||
/* Notify governor of (possible) state change */
|
||||
|
||||
pm_timer(domain);
|
||||
if (g_pmglobals.governor->statechanged)
|
||||
{
|
||||
g_pmglobals.governor->statechanged(domain, newstate);
|
||||
}
|
||||
|
||||
/* Restore the interrupt state */
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
*
|
||||
* Copyright (C) 2011-2012, 2016 Gregory Nutt. All rights reserved.
|
||||
* Author: Gregory Nutt <gnutt@nuttx.org>
|
||||
* Author: Matias Nitsche <mnitsche@dc.uba.ar>
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
|
@ -59,10 +60,10 @@
|
|||
* Description:
|
||||
* This function is called from the MCU-specific IDLE loop to monitor the
|
||||
* the power management conditions. This function returns the "recommended"
|
||||
* power management state based on the PM configuration and activity
|
||||
* reported in the last sampling periods. The power management state is
|
||||
* not automatically changed, however. The IDLE loop must call
|
||||
* pm_changestate() in order to make the state change.
|
||||
* power management state based on the PM policy applied by the currently
|
||||
* chosen governor. The IDLE loop must call pm_changestate() in order to
|
||||
* make the state change, which will interact with all drivers registered
|
||||
* with the PM system.
|
||||
*
|
||||
* These two steps are separated because the plaform-specific IDLE loop may
|
||||
* have additional situational information that is not available to the
|
||||
|
@ -86,63 +87,10 @@
|
|||
|
||||
enum pm_state_e pm_checkstate(int domain)
|
||||
{
|
||||
FAR struct pm_domain_s *pdom;
|
||||
clock_t now, elapsed;
|
||||
irqstate_t flags;
|
||||
int index;
|
||||
DEBUGASSERT(domain >= 0 && domain < CONFIG_PM_NDOMAINS &&
|
||||
g_pmglobals.governor->checkstate);
|
||||
|
||||
/* Get a convenience pointer to minimize all of the indexing */
|
||||
|
||||
DEBUGASSERT(domain >= 0 && domain < CONFIG_PM_NDOMAINS);
|
||||
pdom = &g_pmglobals.domain[domain];
|
||||
|
||||
/* Check for the end of the current time slice. This must be performed
|
||||
* with interrupts disabled so that it does not conflict with the similar
|
||||
* logic in pm_activity().
|
||||
*/
|
||||
|
||||
flags = enter_critical_section();
|
||||
|
||||
/* Check the elapsed time. In periods of low activity, time slicing is
|
||||
* controlled by IDLE loop polling; in periods of higher activity, time
|
||||
* slicing is controlled by driver activity. In either case, the duration
|
||||
* of the time slice is only approximate; during times of heavy activity,
|
||||
* time slices may be become longer and the activity level may be over-
|
||||
* estimated.
|
||||
*/
|
||||
|
||||
now = clock_systimer();
|
||||
elapsed = now - pdom->stime;
|
||||
if (elapsed >= TIME_SLICE_TICKS)
|
||||
{
|
||||
int16_t accum;
|
||||
|
||||
/* Sample the count, reset the time and count, and assess the PM
|
||||
* state. This is an atomic operation because interrupts are
|
||||
* still disabled.
|
||||
*/
|
||||
|
||||
accum = pdom->accum;
|
||||
pdom->stime = now;
|
||||
pdom->accum = 0;
|
||||
|
||||
(void)pm_update(domain, accum);
|
||||
}
|
||||
|
||||
/* Consider the possible power state lock here */
|
||||
|
||||
for (index = 0; index < pdom->recommended; index++)
|
||||
{
|
||||
if (pdom->stay[index] != 0)
|
||||
{
|
||||
pdom->recommended = index;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
leave_critical_section(flags);
|
||||
|
||||
return pdom->recommended;
|
||||
return g_pmglobals.governor->checkstate(domain);
|
||||
}
|
||||
|
||||
#endif /* CONFIG_PM */
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
*
|
||||
* Copyright (C) 2011-2012, 2017 Gregory Nutt. All rights reserved.
|
||||
* Author: Gregory Nutt <gnutt@nuttx.org>
|
||||
* Author: Matias Nitsche <mnitsche@dc.uba.ar>
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
|
@ -46,6 +47,12 @@
|
|||
|
||||
#include "pm.h"
|
||||
|
||||
#if defined(CONFIG_PM_GOVERNOR_ACTIVITY)
|
||||
# include "activity_governor.h"
|
||||
#elif defined(CONFIG_PM_GOVERNOR_GREEDY)
|
||||
# include "greedy_governor.h"
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
|
||||
/****************************************************************************
|
||||
|
@ -62,7 +69,7 @@
|
|||
|
||||
struct pm_global_s g_pmglobals =
|
||||
{
|
||||
SEM_INITIALIZER(1)
|
||||
.regsem = SEM_INITIALIZER(1)
|
||||
};
|
||||
|
||||
/****************************************************************************
|
||||
|
@ -89,18 +96,23 @@ struct pm_global_s g_pmglobals =
|
|||
|
||||
void pm_initialize(void)
|
||||
{
|
||||
FAR struct pm_domain_s *pdom;
|
||||
int i;
|
||||
/* Select governor */
|
||||
|
||||
/* Init saved time slice */
|
||||
#if defined(CONFIG_PM_GOVERNOR_ACTIVITY)
|
||||
g_pmglobals.governor = pm_activity_governor_initialize();
|
||||
#elif defined(CONFIG_PM_GOVERNOR_GREEDY)
|
||||
g_pmglobals.governor = pm_greedy_governor_initialize();
|
||||
#elif defined(CONFIG_PM_GOVERNOR_CUSTOM)
|
||||
/* TODO: call to board function to retrieve custom governor,
|
||||
* such as board_pm_governor_initialize()
|
||||
*/
|
||||
|
||||
for (i = 0; i < CONFIG_PM_NDOMAINS; i++)
|
||||
{
|
||||
pdom = &g_pmglobals.domain[i];
|
||||
pdom->stime = clock_systimer();
|
||||
pdom->btime = clock_systimer();
|
||||
}
|
||||
# error "Not supported yet"
|
||||
#endif
|
||||
|
||||
/* Initialize selected governor */
|
||||
|
||||
g_pmglobals.governor->initialize();
|
||||
}
|
||||
|
||||
#endif /* CONFIG_PM */
|
||||
|
||||
|
|
|
@ -1,296 +0,0 @@
|
|||
/****************************************************************************
|
||||
* drivers/power/pm_update.c
|
||||
*
|
||||
* Copyright (C) 2011-2012, 2016, 2018 Gregory Nutt. All rights reserved.
|
||||
* Author: Gregory Nutt <gnutt@nuttx.org>
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in
|
||||
* the documentation and/or other materials provided with the
|
||||
* distribution.
|
||||
* 3. Neither the name NuttX nor the names of its contributors may be
|
||||
* used to endorse or promote products derived from this software
|
||||
* without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
|
||||
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
|
||||
* COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
|
||||
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
|
||||
* OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
|
||||
* AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
|
||||
* ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
/****************************************************************************
|
||||
* Included Files
|
||||
****************************************************************************/
|
||||
|
||||
#include <nuttx/config.h>
|
||||
|
||||
#include <stdint.h>
|
||||
#include <assert.h>
|
||||
|
||||
#include <nuttx/power/pm.h>
|
||||
#include <nuttx/wqueue.h>
|
||||
|
||||
#include "pm.h"
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
|
||||
/****************************************************************************
|
||||
* Private Data
|
||||
****************************************************************************/
|
||||
|
||||
/* CONFIG_PM_MEMORY is the total number of time slices (including the current
|
||||
* time slice). The history of previous values is then CONFIG_PM_MEMORY-1.
|
||||
*/
|
||||
|
||||
#if CONFIG_PM_MEMORY > 1
|
||||
static const int16_t g_pmcoeffs[CONFIG_PM_MEMORY - 1] =
|
||||
{
|
||||
CONFIG_PM_COEF1
|
||||
#if CONFIG_PM_MEMORY > 2
|
||||
, CONFIG_PM_COEF2
|
||||
#endif
|
||||
#if CONFIG_PM_MEMORY > 3
|
||||
, CONFIG_PM_COEF3
|
||||
#endif
|
||||
#if CONFIG_PM_MEMORY > 4
|
||||
, CONFIG_PM_COEF4
|
||||
#endif
|
||||
#if CONFIG_PM_MEMORY > 5
|
||||
, CONFIG_PM_COEF5
|
||||
#endif
|
||||
#if CONFIG_PM_MEMORY > 6
|
||||
# warning "This logic needs to be extended"
|
||||
#endif
|
||||
};
|
||||
#endif
|
||||
|
||||
/* Threshold activity values to enter into the next lower power consumption
|
||||
* state. Indexing is next state 0:IDLE, 1:STANDBY, 2:SLEEP.
|
||||
*/
|
||||
|
||||
static const int32_t g_pmenterthresh[3] =
|
||||
{
|
||||
CONFIG_PM_IDLEENTER_THRESH,
|
||||
CONFIG_PM_STANDBYENTER_THRESH,
|
||||
CONFIG_PM_SLEEPENTER_THRESH
|
||||
};
|
||||
|
||||
/* Threshold activity values to leave the current low power consdumption
|
||||
* state. Indexing is current state 0:IDLE, 1: STANDBY, 2: SLEEP.
|
||||
*/
|
||||
|
||||
static const int32_t g_pmexitthresh[3] =
|
||||
{
|
||||
CONFIG_PM_IDLEEXIT_THRESH,
|
||||
CONFIG_PM_STANDBYEXIT_THRESH,
|
||||
CONFIG_PM_SLEEPEXIT_THRESH
|
||||
};
|
||||
|
||||
/* Threshold time slice count to enter the next low power consdumption
|
||||
* state. Indexing is next state 0:IDLE, 1: STANDBY, 2: SLEEP.
|
||||
*/
|
||||
|
||||
static const uint32_t g_pmcount[3] =
|
||||
{
|
||||
CONFIG_PM_IDLEENTER_COUNT,
|
||||
CONFIG_PM_STANDBYENTER_COUNT,
|
||||
CONFIG_PM_SLEEPENTER_COUNT
|
||||
};
|
||||
|
||||
/****************************************************************************
|
||||
* Public Functions
|
||||
****************************************************************************/
|
||||
|
||||
/****************************************************************************
|
||||
* Name: pm_update
|
||||
*
|
||||
* Description:
|
||||
* This internal function is called at the end of a time slice in order to
|
||||
* update driver activity metrics and recommended states.
|
||||
*
|
||||
* Input Parameters:
|
||||
* domain - The PM domain associated with the accumulator
|
||||
* accum - The value of the activity accumulator at the end of the time
|
||||
* slice.
|
||||
*
|
||||
* Returned Value:
|
||||
* None.
|
||||
*
|
||||
* Assumptions:
|
||||
* This function may be called from a driver, perhaps even at the interrupt
|
||||
* level. It may also be called from the IDLE loop at the lowest possible
|
||||
* priority level.
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
void pm_update(int domain, int16_t accum)
|
||||
{
|
||||
FAR struct pm_domain_s *pdom;
|
||||
int32_t Y;
|
||||
int index;
|
||||
#if CONFIG_PM_MEMORY > 1
|
||||
int32_t denom;
|
||||
int i;
|
||||
int j;
|
||||
#endif
|
||||
|
||||
/* Get a convenience pointer to minimize all of the indexing */
|
||||
|
||||
DEBUGASSERT(domain >= 0 && domain < CONFIG_PM_NDOMAINS);
|
||||
pdom = &g_pmglobals.domain[domain];
|
||||
|
||||
#if CONFIG_PM_MEMORY > 1
|
||||
/* We won't bother to do anything until we have accumulated
|
||||
* CONFIG_PM_MEMORY-1 samples.
|
||||
*/
|
||||
|
||||
if (pdom->mcnt < CONFIG_PM_MEMORY - 1)
|
||||
{
|
||||
index = pdom->mcnt++;
|
||||
pdom->memory[index] = accum;
|
||||
return;
|
||||
}
|
||||
|
||||
/* The averaging algorithm is simply: Y = (An*X + SUM(Ai*Yi))/SUM(Aj), where
|
||||
* i = 1..n-1 and j= 1..n, n is the length of the "memory", Ai is the
|
||||
* weight applied to each value, and X is the current activity.
|
||||
*
|
||||
* CONFIG_PM_MEMORY provides the memory for the algorithm. Default: 2
|
||||
* CONFIG_PM_COEFn provides weight for each sample. Default: 1
|
||||
*
|
||||
* First, calculate Y = An*X
|
||||
*/
|
||||
|
||||
Y = CONFIG_PM_COEFN * accum;
|
||||
denom = CONFIG_PM_COEFN;
|
||||
|
||||
/* Then calculate Y += SUM(Ai*Yi), i = 1..n-1. The oldest sample will
|
||||
* reside at the domain's mndx (and this is the value that we will overwrite
|
||||
* with the new value).
|
||||
*/
|
||||
|
||||
for (i = 0, j = pdom->mndx;
|
||||
i < CONFIG_PM_MEMORY - 1;
|
||||
i++, j++)
|
||||
{
|
||||
if (j >= CONFIG_PM_MEMORY - 1)
|
||||
{
|
||||
j = 0;
|
||||
}
|
||||
|
||||
Y += g_pmcoeffs[i] * pdom->memory[j];
|
||||
denom += g_pmcoeffs[i];
|
||||
}
|
||||
|
||||
/* Compute and save the new activity value */
|
||||
|
||||
Y /= denom;
|
||||
|
||||
index = pdom->mndx++;
|
||||
pdom->memory[index] = Y;
|
||||
if (pdom->mndx >= CONFIG_PM_MEMORY - 1)
|
||||
{
|
||||
pdom->mndx = 0;
|
||||
}
|
||||
#else
|
||||
|
||||
/* No smoothing */
|
||||
|
||||
Y = accum;
|
||||
#endif
|
||||
|
||||
/* First check if increased activity should cause us to return to the
|
||||
* normal operating state. This would be unlikely for the lowest power
|
||||
* consumption states because the CPU is probably asleep. However this
|
||||
* probably does apply for the IDLE state.
|
||||
*/
|
||||
|
||||
if (pdom->state > PM_NORMAL)
|
||||
{
|
||||
/* Get the table index for the current state (which will be the
|
||||
* current state minus one)
|
||||
*/
|
||||
|
||||
index = pdom->state - 1;
|
||||
|
||||
/* Has the threshold to return to normal power consumption state been
|
||||
* exceeded?
|
||||
*/
|
||||
|
||||
if (Y > g_pmexitthresh[index])
|
||||
{
|
||||
/* Yes... reset the count and recommend the normal state. */
|
||||
|
||||
pdom->btime = clock_systimer();
|
||||
pdom->recommended = PM_NORMAL;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
/* Now, compare this new activity level to the thresholds and counts for
|
||||
* the next lower power consumption state. If we are already in the SLEEP
|
||||
* state, then there is nothing more to be done (in fact, I would be
|
||||
* surprised to be executing!).
|
||||
*/
|
||||
|
||||
if (pdom->state < PM_SLEEP)
|
||||
{
|
||||
unsigned int nextstate;
|
||||
|
||||
/* Get the next state and the table index for the next state (which will
|
||||
* be the current state)
|
||||
*/
|
||||
|
||||
index = pdom->state;
|
||||
nextstate = pdom->state + 1;
|
||||
|
||||
/* Has the threshold to enter the next lower power consumption state
|
||||
* been exceeded?
|
||||
*/
|
||||
|
||||
if (Y > g_pmenterthresh[index])
|
||||
{
|
||||
/* No... reset the count and recommend the current state */
|
||||
|
||||
pdom->btime = clock_systimer();
|
||||
pdom->recommended = pdom->state;
|
||||
}
|
||||
|
||||
/* Yes.. have we already recommended this state? If so, do nothing */
|
||||
|
||||
else if (pdom->recommended < nextstate)
|
||||
{
|
||||
/* No.. calculate the count. Has it passed the count required
|
||||
* for a state transition?
|
||||
*/
|
||||
|
||||
if (clock_systimer() - pdom->btime >=
|
||||
g_pmcount[index] * TIME_SLICE_TICKS)
|
||||
{
|
||||
/* Yes, recommend the new state and set up for the next
|
||||
* transition.
|
||||
*/
|
||||
|
||||
pdom->btime = clock_systimer();
|
||||
pdom->recommended = nextstate;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif /* CONFIG_PM */
|
|
@ -4,6 +4,7 @@
|
|||
*
|
||||
* Copyright (C) 2011-2012, 2015-2016 Gregory Nutt. All rights reserved.
|
||||
* Author: Gregory Nutt <gnutt@nuttx.org>
|
||||
* Author: Matias Nitsche <mnitsche@dc.uba.ar>
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
|
@ -100,135 +101,6 @@
|
|||
* own, custom idle loop to support board-specific IDLE time power management
|
||||
*/
|
||||
|
||||
/* CONFIG_PM_SLICEMS. The power management module collects activity counts
|
||||
* in time slices. At the end of the time slice, the count accumulated
|
||||
* during that interval is applied to an averaging algorithm to determine
|
||||
* the activity level.
|
||||
*
|
||||
* CONFIG_PM_SLICEMS provides the duration of that time slice in
|
||||
* milliseconds. Default: 100 Milliseconds
|
||||
*/
|
||||
|
||||
#ifndef CONFIG_PM_SLICEMS
|
||||
# define CONFIG_PM_SLICEMS 100 /* Default is 100 msec */
|
||||
#endif
|
||||
|
||||
#if CONFIG_PM_SLICEMS < 1
|
||||
# error CONFIG_PM_SLICEMS invalid
|
||||
#endif
|
||||
|
||||
/* The averaging algorithm is simply: Y = (An*X + SUM(Ai*Yi))/SUM(Aj), where
|
||||
* i = 1..n-1 and j= 1..n, n is the length of the "memory", Ai is the
|
||||
* weight applied to each value, and X is the current activity. These weights
|
||||
* may be negative and a limited to the range of int16_t.
|
||||
*
|
||||
* CONFIG_PM_MEMORY provides the memory for the algorithm. Default: 2
|
||||
* CONFIG_PM_COEFn provides weight for each sample. Default: 1
|
||||
*
|
||||
* Setting CONFIG_PM_MEMORY=1 disables all smoothing.
|
||||
*/
|
||||
|
||||
#ifndef CONFIG_PM_MEMORY
|
||||
# define CONFIG_PM_MEMORY 2
|
||||
#endif
|
||||
|
||||
#if CONFIG_PM_MEMORY < 1
|
||||
# error "CONFIG_PM_MEMORY must be >= 1"
|
||||
#endif
|
||||
|
||||
#ifndef CONFIG_PM_COEFN
|
||||
# define CONFIG_PM_COEFN 1
|
||||
#endif
|
||||
|
||||
#if CONFIG_PM_MEMORY > 1 && !defined(CONFIG_PM_COEF1)
|
||||
# define CONFIG_PM_COEF1 1
|
||||
#endif
|
||||
|
||||
#if CONFIG_PM_MEMORY > 2 && !defined(CONFIG_PM_COEF2)
|
||||
# define CONFIG_PM_COEF2 1
|
||||
#endif
|
||||
|
||||
#if CONFIG_PM_MEMORY > 3 && !defined(CONFIG_PM_COEF3)
|
||||
# define CONFIG_PM_COEF3 1
|
||||
#endif
|
||||
|
||||
#if CONFIG_PM_MEMORY > 4 && !defined(CONFIG_PM_COEF4)
|
||||
# define CONFIG_PM_COEF4 1
|
||||
#endif
|
||||
|
||||
#if CONFIG_PM_MEMORY > 5 && !defined(CONFIG_PM_COEF5)
|
||||
# define CONFIG_PM_COEF5 1
|
||||
#endif
|
||||
|
||||
#if CONFIG_PM_MEMORY > 6
|
||||
# warning "This logic needs to be extended"
|
||||
#endif
|
||||
|
||||
/* State changes then occur when the weight activity account crosses
|
||||
* threshold values for certain periods of time (time slice count).
|
||||
*
|
||||
* CONFIG_PM_xxxENTER_THRESH is the threshold value for entering state xxx.
|
||||
* CONFIG_PM_xxxENTER_COUNT is the count for entering state xxx.
|
||||
*
|
||||
* Resuming to normal state, on the other hand, is usually immediate and
|
||||
* controlled by wakeup conditions established by the platform. The PM
|
||||
* module only recommends reduced power states.
|
||||
*/
|
||||
|
||||
#ifndef CONFIG_PM_IDLEENTER_THRESH
|
||||
# define CONFIG_PM_IDLEENTER_THRESH 1 /* <=1: Essentially no activity */
|
||||
#endif
|
||||
|
||||
#ifndef CONFIG_PM_IDLEEXIT_THRESH
|
||||
# define CONFIG_PM_IDLEEXIT_THRESH 2 /* >=2: Active */
|
||||
#endif
|
||||
|
||||
#if CONFIG_PM_IDLEENTER_THRESH >= CONFIG_PM_IDLEEXIT_THRESH
|
||||
# error "Must have CONFIG_PM_IDLEENTER_THRESH < CONFIG_PM_IDLEEXIT_THRESH"
|
||||
#endif
|
||||
|
||||
#ifndef CONFIG_PM_IDLEENTER_COUNT
|
||||
# define CONFIG_PM_IDLEENTER_COUNT 30 /* Thirty IDLE slices to enter
|
||||
* IDLE mode from normal
|
||||
*/
|
||||
#endif
|
||||
|
||||
#ifndef CONFIG_PM_STANDBYENTER_THRESH
|
||||
# define CONFIG_PM_STANDBYENTER_THRESH 1 /* <=1: Essentially no activity */
|
||||
#endif
|
||||
|
||||
#ifndef CONFIG_PM_STANDBYEXIT_THRESH
|
||||
# define CONFIG_PM_STANDBYEXIT_THRESH 2 /* >=2: Active */
|
||||
#endif
|
||||
|
||||
#if CONFIG_PM_STANDBYENTER_THRESH >= CONFIG_PM_STANDBYEXIT_THRESH
|
||||
# error "Must have CONFIG_PM_STANDBYENTER_THRESH < CONFIG_PM_STANDBYEXIT_THRESH"
|
||||
#endif
|
||||
|
||||
#ifndef CONFIG_PM_STANDBYENTER_COUNT
|
||||
# define CONFIG_PM_STANDBYENTER_COUNT 50 /* Fifty IDLE slices to enter
|
||||
* STANDBY mode from IDLE
|
||||
*/
|
||||
#endif
|
||||
|
||||
#ifndef CONFIG_PM_SLEEPENTER_THRESH
|
||||
# define CONFIG_PM_SLEEPENTER_THRESH 1 /* <=1: Essentially no activity */
|
||||
#endif
|
||||
|
||||
#ifndef CONFIG_PM_SLEEPEXIT_THRESH
|
||||
# define CONFIG_PM_SLEEPEXIT_THRESH 2 /* >=2: Active */
|
||||
#endif
|
||||
|
||||
#if CONFIG_PM_SLEEPENTER_THRESH >= CONFIG_PM_SLEEPEXIT_THRESH
|
||||
# error "Must have CONFIG_PM_SLEEPENTER_THRESH < CONFIG_PM_SLEEPEXIT_THRESH"
|
||||
#endif
|
||||
|
||||
#ifndef CONFIG_PM_SLEEPENTER_COUNT
|
||||
# define CONFIG_PM_SLEEPENTER_COUNT 70 /* 70 IDLE slices to enter SLEEP
|
||||
* mode from STANDBY
|
||||
*/
|
||||
#endif
|
||||
|
||||
/****************************************************************************
|
||||
* Public Types
|
||||
****************************************************************************/
|
||||
|
@ -316,8 +188,8 @@ struct pm_callback_s
|
|||
*
|
||||
**************************************************************************/
|
||||
|
||||
int (*prepare)(FAR struct pm_callback_s *cb, int domain,
|
||||
enum pm_state_e pmstate);
|
||||
CODE int (*prepare)(FAR struct pm_callback_s *cb, int domain,
|
||||
enum pm_state_e pmstate);
|
||||
|
||||
/**************************************************************************
|
||||
* Name: notify
|
||||
|
@ -342,8 +214,81 @@ struct pm_callback_s
|
|||
*
|
||||
**************************************************************************/
|
||||
|
||||
void (*notify)(FAR struct pm_callback_s *cb, int domain,
|
||||
enum pm_state_e pmstate);
|
||||
CODE void (*notify)(FAR struct pm_callback_s *cb, int domain,
|
||||
enum pm_state_e pmstate);
|
||||
};
|
||||
|
||||
/* An instance of a given PM governor */
|
||||
|
||||
struct pm_governor_s
|
||||
{
|
||||
/**************************************************************************
|
||||
* Name: initialize
|
||||
*
|
||||
* Description:
|
||||
* Invoked by the PM system during initialization, to allow the governor
|
||||
* to initialize its internal data. This can be left to NULL if not needed
|
||||
* by the governor.
|
||||
*
|
||||
* NOTE: since this will be called from pm_initialize(), the system
|
||||
* is in very early boot state when this callback is invoked. Thus,
|
||||
* only ver basic initialization should be performed (e.g. no memory
|
||||
* should be allocated).
|
||||
*
|
||||
**************************************************************************/
|
||||
|
||||
CODE void (*initialize)(void);
|
||||
|
||||
/**************************************************************************
|
||||
* Name: statechanged
|
||||
*
|
||||
* Description:
|
||||
* Invoked by the PM system when the power state is changed. This can be
|
||||
* left to NULL if this notification is not needed by the governor.
|
||||
*
|
||||
**************************************************************************/
|
||||
|
||||
CODE void (*statechanged)(int domain, enum pm_state_e newstate);
|
||||
|
||||
/**************************************************************************
|
||||
* Name: checkstate
|
||||
*
|
||||
* Description:
|
||||
* Invoked by the PM system to obtain the governor's suggestion for the
|
||||
* power level to set. Implementing this callback is mandatory for a
|
||||
* governor since recommending power levels is its main responsibility.
|
||||
*
|
||||
* NOTE: the governor should consider the "stay" count in order to hold
|
||||
* off the recommendation of a lower-power level.
|
||||
*
|
||||
**************************************************************************/
|
||||
|
||||
CODE enum pm_state_e (*checkstate)(int domain);
|
||||
|
||||
/**************************************************************************
|
||||
* Name: activity
|
||||
*
|
||||
* Description:
|
||||
* Invoked by the PM system when a driver reports activity via
|
||||
* pm_activity(). This may or may not be useful to the governor to
|
||||
* recommend power levels.
|
||||
* It can be left NULL, in which case it will not be invoked.
|
||||
*
|
||||
**************************************************************************/
|
||||
|
||||
CODE void (*activity)(int domain, int count);
|
||||
|
||||
/* Private data for governor's internal use */
|
||||
|
||||
FAR void *priv;
|
||||
};
|
||||
|
||||
/* To be used for accessing the user governor via ioctl calls */
|
||||
|
||||
struct pm_user_governor_state_s
|
||||
{
|
||||
int domain;
|
||||
enum pm_state_e state;
|
||||
};
|
||||
|
||||
/****************************************************************************
|
||||
|
@ -363,6 +308,7 @@ extern "C"
|
|||
/****************************************************************************
|
||||
* Public Function Prototypes
|
||||
****************************************************************************/
|
||||
|
||||
/****************************************************************************
|
||||
* Name: pm_initialize
|
||||
*
|
||||
|
@ -424,9 +370,8 @@ int pm_unregister(FAR struct pm_callback_s *callbacks);
|
|||
*
|
||||
* Description:
|
||||
* This function is called by a device driver to indicate that it is
|
||||
* performing meaningful activities (non-idle). This increment an activity
|
||||
* count and/or will restart a idle timer and prevent entering reduced
|
||||
* power states.
|
||||
* performing meaningful activities (non-idle). This is reported to
|
||||
* the PM governor, which may be used to suggest power states.
|
||||
*
|
||||
* Input Parameters:
|
||||
* domain - The domain of the PM activity
|
||||
|
@ -454,7 +399,7 @@ void pm_activity(int domain, int priority);
|
|||
* Description:
|
||||
* This function is called by a device driver to indicate that it is
|
||||
* performing meaningful activities (non-idle), needs the power kept at
|
||||
* last the specified level.
|
||||
* least the specified level.
|
||||
*
|
||||
* Input Parameters:
|
||||
* domain - The domain of the PM activity
|
||||
|
@ -615,8 +560,6 @@ enum pm_state_e pm_querystate(int domain);
|
|||
# define pm_register(cb) (0)
|
||||
# define pm_unregister(cb) (0)
|
||||
# define pm_activity(domain,prio)
|
||||
# define pm_stay(domain,state)
|
||||
# define pm_relax(domain,state)
|
||||
# define pm_checkstate(domain) (0)
|
||||
# define pm_changestate(domain,state) (0)
|
||||
# define pm_querystate(domain) (0)
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
* include/nuttx/power/power_ioctl.h
|
||||
* NuttX Power-Related IOCTLs definitions
|
||||
*
|
||||
* Copyright (C) 2017 Gregory Nutt. All rights reserved.
|
||||
* Copyright (C) 2017, 2019 Gregory Nutt. All rights reserved.
|
||||
* Author: Gregory Nutt <gnutt@nuttx.org>
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
|
@ -58,9 +58,10 @@
|
|||
#define PWRIOC_SET_MODE _PWRIOC(3)
|
||||
#define PWRIOC_SET_LIMITS _PWRIOC(4)
|
||||
#define PWRIOC_GET_STATE _PWRIOC(5)
|
||||
#define PWRIOC_GET_FAULT _PWRIOC(6)
|
||||
#define PWRIOC_SET_FAULT _PWRIOC(7)
|
||||
#define PWRIOC_CLEAN_FAULT _PWRIOC(8)
|
||||
#define PWRIOC_SET_PARAMS _PWRIOC(9)
|
||||
#define PWRIOC_SET_STATE _PWRIOC(6)
|
||||
#define PWRIOC_GET_FAULT _PWRIOC(7)
|
||||
#define PWRIOC_SET_FAULT _PWRIOC(8)
|
||||
#define PWRIOC_CLEAN_FAULT _PWRIOC(9)
|
||||
#define PWRIOC_SET_PARAMS _PWRIOC(10)
|
||||
|
||||
#endif /* __INCLUDE_NUTTX_POWER_POWER_IOCTL_H */
|
||||
|
|
|
@ -443,6 +443,11 @@ int main(int argc, char **argv, char **envp)
|
|||
lineno);
|
||||
}
|
||||
|
||||
/* 'comment_lineno 'holds the line number of the last closing
|
||||
* comment. It is used only to verify that the comment is
|
||||
* followed by a blank line.
|
||||
*/
|
||||
|
||||
comment_lineno = lineno;
|
||||
brhcomment = false;
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue