1
0
Fork 0
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:
Matias Nitsche 2019-11-09 09:09:33 -06:00 committed by Gregory Nutt
parent 2ab4d635b4
commit e118d99bf5
16 changed files with 1372 additions and 779 deletions

View file

@ -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)

View file

@ -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

View file

@ -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

View 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 */

View 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

View 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;
}

View 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 */

View file

@ -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)

View file

@ -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);
}
}

View file

@ -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 */

View file

@ -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 */

View file

@ -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 */

View file

@ -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 */

View file

@ -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)

View file

@ -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 */

View file

@ -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;
}