diff --git a/arch/sim/src/sim/up_head.c b/arch/sim/src/sim/up_head.c index 050ae21c5b6..49b0b893393 100644 --- a/arch/sim/src/sim/up_head.c +++ b/arch/sim/src/sim/up_head.c @@ -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) diff --git a/drivers/power/Kconfig b/drivers/power/Kconfig index 32897de2100..73744919ccb 100644 --- a/drivers/power/Kconfig +++ b/drivers/power/Kconfig @@ -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 diff --git a/drivers/power/Make.defs b/drivers/power/Make.defs index 9d047fe86eb..89bbdee6dec 100644 --- a/drivers/power/Make.defs +++ b/drivers/power/Make.defs @@ -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 diff --git a/drivers/power/activity_governor.c b/drivers/power/activity_governor.c new file mode 100644 index 00000000000..121a6ece97b --- /dev/null +++ b/drivers/power/activity_governor.c @@ -0,0 +1,623 @@ +/**************************************************************************** + * activity_governor.c + * + * Copyright (C) 2011-2012, 2016 Gregory Nutt. All rights reserved. + * Author: Gregory Nutt + * Author: Matias Nitsche + * + * 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 +#include +#include +#include +#include + +#include +#include +#include + +#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 */ diff --git a/drivers/power/activity_governor.h b/drivers/power/activity_governor.h new file mode 100644 index 00000000000..aeaf231b3c1 --- /dev/null +++ b/drivers/power/activity_governor.h @@ -0,0 +1,210 @@ +/**************************************************************************** + * activity_governor.h + * + * Copyright (C) 2011-2012, 2016 Gregory Nutt. All rights reserved. + * Author: Gregory Nutt + * Author: Matias Nitsche + * + * 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 +#include + +/**************************************************************************** + * 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 diff --git a/drivers/power/greedy_governor.c b/drivers/power/greedy_governor.c new file mode 100644 index 00000000000..b613f6bf9ba --- /dev/null +++ b/drivers/power/greedy_governor.c @@ -0,0 +1,173 @@ +/**************************************************************************** + * greedy_governor/greedy_governor.c + * + * Copyright (C) 2019 Matias Nitsche. All rights reserved. + * Author: Matias Nitsche + * + * 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 + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include + +#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; +} diff --git a/drivers/power/greedy_governor.h b/drivers/power/greedy_governor.h new file mode 100644 index 00000000000..09c21ab9aec --- /dev/null +++ b/drivers/power/greedy_governor.h @@ -0,0 +1,90 @@ +/**************************************************************************** + * user_governor/user_governor.c + * + * Copyright (C) 2019 Matias Nitsche. All rights reserved. + * Author: Matias Nitsche + * + * 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 +#include +#include + +/**************************************************************************** + * 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 */ + diff --git a/drivers/power/pm.h b/drivers/power/pm.h index 074f58e0a8a..99543bd4384 100644 --- a/drivers/power/pm.h +++ b/drivers/power/pm.h @@ -3,6 +3,7 @@ * * Copyright (C) 2011-2012, 2016, 2018 Gregory Nutt. All rights reserved. * Author: Gregory Nutt + * Author: Matias Nitsche * * 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) diff --git a/drivers/power/pm_activity.c b/drivers/power/pm_activity.c index 64be1f597b6..a4a79569bd7 100644 --- a/drivers/power/pm_activity.c +++ b/drivers/power/pm_activity.c @@ -3,6 +3,7 @@ * * Copyright (C) 2011-2012, 2016 Gregory Nutt. All rights reserved. * Author: Gregory Nutt + * Author: Matias Nitsche * * 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); } } diff --git a/drivers/power/pm_changestate.c b/drivers/power/pm_changestate.c index e5ab3d1d825..92da5a21f09 100644 --- a/drivers/power/pm_changestate.c +++ b/drivers/power/pm_changestate.c @@ -3,6 +3,7 @@ * * Copyright (C) 2011-2012, 2016, 2018 Gregory Nutt. All rights reserved. * Author: Gregory Nutt + * Author: Matias Nitsche * * 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 */ diff --git a/drivers/power/pm_checkstate.c b/drivers/power/pm_checkstate.c index 7750043f90e..eba7b2ec111 100644 --- a/drivers/power/pm_checkstate.c +++ b/drivers/power/pm_checkstate.c @@ -3,6 +3,7 @@ * * Copyright (C) 2011-2012, 2016 Gregory Nutt. All rights reserved. * Author: Gregory Nutt + * Author: Matias Nitsche * * 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 */ diff --git a/drivers/power/pm_initialize.c b/drivers/power/pm_initialize.c index cba81b7b3ad..ba068fd77d3 100644 --- a/drivers/power/pm_initialize.c +++ b/drivers/power/pm_initialize.c @@ -3,6 +3,7 @@ * * Copyright (C) 2011-2012, 2017 Gregory Nutt. All rights reserved. * Author: Gregory Nutt + * Author: Matias Nitsche * * 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 */ - diff --git a/drivers/power/pm_update.c b/drivers/power/pm_update.c deleted file mode 100644 index b5e35d4c45c..00000000000 --- a/drivers/power/pm_update.c +++ /dev/null @@ -1,296 +0,0 @@ -/**************************************************************************** - * drivers/power/pm_update.c - * - * Copyright (C) 2011-2012, 2016, 2018 Gregory Nutt. All rights reserved. - * Author: Gregory Nutt - * - * 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 - -#include -#include - -#include -#include - -#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 */ diff --git a/include/nuttx/power/pm.h b/include/nuttx/power/pm.h index 16b6390fb29..21cfd6156f1 100644 --- a/include/nuttx/power/pm.h +++ b/include/nuttx/power/pm.h @@ -4,6 +4,7 @@ * * Copyright (C) 2011-2012, 2015-2016 Gregory Nutt. All rights reserved. * Author: Gregory Nutt + * Author: Matias Nitsche * * 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) diff --git a/include/nuttx/power/power_ioctl.h b/include/nuttx/power/power_ioctl.h index eddfeca5f0c..b95f17ba24d 100644 --- a/include/nuttx/power/power_ioctl.h +++ b/include/nuttx/power/power_ioctl.h @@ -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 * * 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 */ diff --git a/tools/nxstyle.c b/tools/nxstyle.c index 29c19a838a6..821ca810e1d 100644 --- a/tools/nxstyle.c +++ b/tools/nxstyle.c @@ -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; }