diff --git a/drivers/power/pm.h b/drivers/power/pm.h index e8d90261374..b5b2fedaa81 100644 --- a/drivers/power/pm.h +++ b/drivers/power/pm.h @@ -68,7 +68,6 @@ #define TIME_SLICE_TICKS ((CONFIG_PM_SLICEMS * CLOCKS_PER_SEC) / 1000) -/* Function-like macros *****************************************************/ /**************************************************************************** * Name: pm_lock * @@ -93,6 +92,7 @@ /**************************************************************************** * Public Types ****************************************************************************/ + /* This describes the activity and state for one domain */ struct pm_domain_s @@ -112,13 +112,11 @@ struct pm_domain_s uint8_t mndx; uint8_t mcnt; - /* accum - The accumulated counts in this time interval - * thrcnt - The number of below threshold counts seen. - */ + /* accum - The accumulated counts in this time interval */ int16_t accum; - uint16_t thrcnt; +#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 @@ -128,7 +126,6 @@ struct pm_domain_s * CONFIG_PM_COEFn provides weight for each sample. Default: 1 */ -#if CONFIG_PM_MEMORY > 1 int16_t memory[CONFIG_PM_MEMORY-1]; #endif @@ -136,6 +133,10 @@ struct pm_domain_s 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]; @@ -199,7 +200,6 @@ EXTERN struct pm_global_s g_pmglobals; * domain - The domain associated with the accumulator. * accum - The value of the activity accumulator at the end of the time * slice. - * elapsed - The elapsed time from last called pm_update, unit ms * * Returned Value: * None. @@ -211,7 +211,7 @@ EXTERN struct pm_global_s g_pmglobals; * ****************************************************************************/ -void pm_update(int domain, int16_t accum, clock_t elapsed); +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 be78efe2508..806be273820 100644 --- a/drivers/power/pm_activity.c +++ b/drivers/power/pm_activity.c @@ -138,7 +138,7 @@ void pm_activity(int domain, int priority) pdom->stime = now; pdom->accum = 0; - (void)pm_update(domain, tmp, elapsed); + (void)pm_update(domain, tmp); } leave_critical_section(flags); diff --git a/drivers/power/pm_changestate.c b/drivers/power/pm_changestate.c index 38e8b53f04e..79d42e2a539 100644 --- a/drivers/power/pm_changestate.c +++ b/drivers/power/pm_changestate.c @@ -1,7 +1,7 @@ /**************************************************************************** * drivers/power/pm_changestate.c * - * Copyright (C) 2011-2012, 2016 Gregory Nutt. All rights reserved. + * 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 @@ -41,6 +41,7 @@ #include #include +#include #include #include @@ -49,6 +50,12 @@ #ifdef CONFIG_PM +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +#define PM_TIMER_GAP (TIME_SLICE_TICKS * 2) + /**************************************************************************** * Private Functions ****************************************************************************/ @@ -78,24 +85,28 @@ static void pm_timer_cb(int argc, wdparm_t arg1, ...) static void pm_timer(int domain) { FAR struct pm_domain_s *pdom = &g_pmglobals.domain[domain]; - uint32_t delay; + 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) + if (pdom->state < PM_SLEEP && !pdom->stay[pdom->state] && + pmtick[pdom->state]) { - const uint16_t g_pmcount[3] = - { - CONFIG_PM_IDLEENTER_COUNT, - CONFIG_PM_STANDBYENTER_COUNT, - CONFIG_PM_SLEEPENTER_COUNT - }; + int delay = pmtick[pdom->state] + pdom->btime - clock_systimer(); + int left = wd_gettime(pdom->wdog); - delay = (g_pmcount[pdom->state] - pdom->thrcnt) * CONFIG_PM_SLICEMS; - wd_start(pdom->wdog, MSEC2TICK(delay), pm_timer_cb, 0); + if (!WDOG_ISACTIVE(pdom->wdog) || abs(delay - left) > PM_TIMER_GAP) + { + wd_start(pdom->wdog, delay, pm_timer_cb, 0); + } } else { diff --git a/drivers/power/pm_checkstate.c b/drivers/power/pm_checkstate.c index 4f043036336..7750043f90e 100644 --- a/drivers/power/pm_checkstate.c +++ b/drivers/power/pm_checkstate.c @@ -126,7 +126,7 @@ enum pm_state_e pm_checkstate(int domain) pdom->stime = now; pdom->accum = 0; - (void)pm_update(domain, accum, elapsed); + (void)pm_update(domain, accum); } /* Consider the possible power state lock here */ diff --git a/drivers/power/pm_initialize.c b/drivers/power/pm_initialize.c index a1d7a5df81e..cba81b7b3ad 100644 --- a/drivers/power/pm_initialize.c +++ b/drivers/power/pm_initialize.c @@ -98,6 +98,7 @@ void pm_initialize(void) { pdom = &g_pmglobals.domain[i]; pdom->stime = clock_systimer(); + pdom->btime = clock_systimer(); } } diff --git a/drivers/power/pm_update.c b/drivers/power/pm_update.c index 4c2bf41517e..f0162aa48a8 100644 --- a/drivers/power/pm_update.c +++ b/drivers/power/pm_update.c @@ -1,7 +1,7 @@ /**************************************************************************** * drivers/power/pm_update.c * - * Copyright (C) 2011-2012, 2016 Gregory Nutt. All rights reserved. + * 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 @@ -52,8 +52,9 @@ /**************************************************************************** * Private Data ****************************************************************************/ + /* CONFIG_PM_MEMORY is the total number of time slices (including the current - * time slice. The histor or previous values is then CONFIG_PM_MEMORY-1. + * time slice). The history of previous values is then CONFIG_PM_MEMORY-1. */ #if CONFIG_PM_MEMORY > 1 @@ -124,9 +125,8 @@ static const uint16_t g_pmcount[3] = * * Input Parameters: * domain - The PM domain associated with the accumulator - * accum - The value of the activity accumulator at the end of the time - * slice. - * elapsed - The elapsed time from last called pm_update, unit ms + * accum - The value of the activity accumulator at the end of the time + * slice. * * Returned Value: * None. @@ -138,10 +138,9 @@ static const uint16_t g_pmcount[3] = * ****************************************************************************/ -void pm_update(int domain, int16_t accum_, clock_t elapsed) +void pm_update(int domain, int16_t accum) { FAR struct pm_domain_s *pdom; - int16_t accum = 0; int32_t Y; int index; #if CONFIG_PM_MEMORY > 1 @@ -155,153 +154,142 @@ void pm_update(int domain, int16_t accum_, clock_t elapsed) DEBUGASSERT(domain >= 0 && domain < CONFIG_PM_NDOMAINS); pdom = &g_pmglobals.domain[domain]; - while (elapsed >= TIME_SLICE_TICKS) - { - if (elapsed - TIME_SLICE_TICKS < TIME_SLICE_TICKS) - { - accum = accum_; - } - #if CONFIG_PM_MEMORY > 1 - /* We won't bother to do anything until we have accumulated - * CONFIG_PM_MEMORY-1 samples. - */ + /* We won't bother to do anything until we have accumulated + * CONFIG_PM_MEMORY-1 samples. + */ - if (pdom->mcnt < CONFIG_PM_MEMORY-1) + 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) { - index = pdom->mcnt++; - pdom->memory[index] = accum; - continue; + j = 0; } - /* 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, calclate Y = An*X - */ + Y += g_pmcoeffs[i] * pdom->memory[j]; + denom += g_pmcoeffs[i]; + } - Y = CONFIG_PM_COEFN * accum; - denom = CONFIG_PM_COEFN; + /* Compute and save the new activity value */ - /* 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; - } + Y /= denom; + index = pdom->mndx++; + pdom->memory[index] = Y; + if (pdom->mndx >= CONFIG_PM_MEMORY-1) + { + pdom->mndx = 0; + } #else - /* No smoothing */ - - Y = accum; + /* 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. + /* 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) */ - if (pdom->state > PM_NORMAL) + index = pdom->state - 1; + + /* Has the threshold to return to normal power consumption state been + * exceeded? + */ + + if (Y > g_pmexitthresh[index]) { - /* Get the table index for the current state (which will be the - * current state minus one) - */ + /* Yes... reset the count and recommend the normal state. */ - index = pdom->state - 1; + pdom->btime = clock_systimer(); + pdom->recommended = PM_NORMAL; + return; + } + } - /* Has the threshold to return to normal power consumption state been - * exceeded? - */ + /* 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 (Y > g_pmexitthresh[index]) - { - /* Yes... reset the count and recommend the normal state. */ + if (pdom->state < PM_SLEEP) + { + unsigned int nextstate; - pdom->thrcnt = 0; - pdom->recommended = PM_NORMAL; - return; - } + /* 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; } - /* 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!). - */ + /* Yes.. have we already recommended this state? If so, do nothing */ - if (pdom->state < PM_SLEEP) + else if (pdom->recommended < nextstate) { - unsigned int nextstate; - - /* Get the next state and the table index for the next state (which will - * be the current state) + /* No.. calculate the count. Has it passed the count required + * for a state transition? */ - 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]) + if (clock_systimer() - pdom->btime >= + g_pmcount[index] * TIME_SLICE_TICKS) { - /* No... reset the count and recommend the current state */ - - pdom->thrcnt = 0; - pdom->recommended = pdom->state; - } - - /* Yes.. have we already recommended this state? If so, do nothing */ - - else if (pdom->recommended < nextstate) - { - /* No.. increment the count. Has it passed the count required - * for a state transition? + /* Yes, recommend the new state and set up for the next + * transition. */ - if (++pdom->thrcnt >= g_pmcount[index]) - { - /* Yes, recommend the new state and set up for the next - * transition. - */ - - pdom->thrcnt = 0; - pdom->recommended = nextstate; - } + pdom->btime = clock_systimer(); + pdom->recommended = nextstate; } } - - elapsed -= TIME_SLICE_TICKS; } }