From a02f919bec62c4aba91fbb5b1327a2a933aab492 Mon Sep 17 00:00:00 2001 From: ligd Date: Mon, 27 Aug 2018 13:28:48 -0600 Subject: [PATCH] drivers/power: PM: Add timer to decrease PM level automatically --- drivers/power/pm.h | 8 +- drivers/power/pm_activity.c | 9 +- drivers/power/pm_changestate.c | 54 ++++++++ drivers/power/pm_checkstate.c | 9 +- drivers/power/pm_update.c | 240 +++++++++++++++++---------------- 5 files changed, 197 insertions(+), 123 deletions(-) diff --git a/drivers/power/pm.h b/drivers/power/pm.h index 338238bf620..e8d90261374 100644 --- a/drivers/power/pm.h +++ b/drivers/power/pm.h @@ -48,6 +48,7 @@ #include #include #include +#include #ifdef CONFIG_PM @@ -138,6 +139,10 @@ struct pm_domain_s /* 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 */ @@ -194,6 +199,7 @@ 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. @@ -205,7 +211,7 @@ EXTERN struct pm_global_s g_pmglobals; * ****************************************************************************/ -void pm_update(int domain, int16_t accum); +void pm_update(int domain, int16_t accum, clock_t elapsed); #undef EXTERN #if defined(__cplusplus) diff --git a/drivers/power/pm_activity.c b/drivers/power/pm_activity.c index 559c3cdee79..be78efe2508 100644 --- a/drivers/power/pm_activity.c +++ b/drivers/power/pm_activity.c @@ -84,7 +84,7 @@ void pm_activity(int domain, int priority) { FAR struct pm_domain_s *pdom; - clock_t now; + clock_t now, elapsed; uint32_t accum; irqstate_t flags; @@ -123,8 +123,9 @@ void pm_activity(int domain, int priority) * estimated. */ - now = clock_systimer(); - if (now - pdom->stime >= TIME_SLICE_TICKS) + now = clock_systimer(); + elapsed = now - pdom->stime; + if (elapsed >= TIME_SLICE_TICKS) { int16_t tmp; @@ -137,7 +138,7 @@ void pm_activity(int domain, int priority) pdom->stime = now; pdom->accum = 0; - (void)pm_update(domain, tmp); + (void)pm_update(domain, tmp, elapsed); } leave_critical_section(flags); diff --git a/drivers/power/pm_changestate.c b/drivers/power/pm_changestate.c index be5786ee421..38e8b53f04e 100644 --- a/drivers/power/pm_changestate.c +++ b/drivers/power/pm_changestate.c @@ -53,6 +53,56 @@ * 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]; + uint32_t delay; + + if (!pdom->wdog) + { + pdom->wdog = wd_create(); + } + + if (pdom->state < PM_SLEEP) + { + const uint16_t g_pmcount[3] = + { + CONFIG_PM_IDLEENTER_COUNT, + CONFIG_PM_STANDBYENTER_COUNT, + CONFIG_PM_SLEEPENTER_COUNT + }; + + delay = (g_pmcount[pdom->state] - pdom->thrcnt) * CONFIG_PM_SLICEMS; + wd_start(pdom->wdog, MSEC2TICK(delay), pm_timer_cb, 0); + } + else + { + wd_cancel(pdom->wdog); + } +} + /**************************************************************************** * Name: pm_prepall * @@ -250,6 +300,10 @@ 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 */ + + pm_timer(domain); } /* Restore the interrupt state */ diff --git a/drivers/power/pm_checkstate.c b/drivers/power/pm_checkstate.c index 796814fb305..4f043036336 100644 --- a/drivers/power/pm_checkstate.c +++ b/drivers/power/pm_checkstate.c @@ -87,7 +87,7 @@ enum pm_state_e pm_checkstate(int domain) { FAR struct pm_domain_s *pdom; - clock_t now; + clock_t now, elapsed; irqstate_t flags; int index; @@ -111,8 +111,9 @@ enum pm_state_e pm_checkstate(int domain) * estimated. */ - now = clock_systimer(); - if (now - pdom->stime >= TIME_SLICE_TICKS) + now = clock_systimer(); + elapsed = now - pdom->stime; + if (elapsed >= TIME_SLICE_TICKS) { int16_t accum; @@ -125,7 +126,7 @@ enum pm_state_e pm_checkstate(int domain) pdom->stime = now; pdom->accum = 0; - (void)pm_update(domain, accum); + (void)pm_update(domain, accum, elapsed); } /* Consider the possible power state lock here */ diff --git a/drivers/power/pm_update.c b/drivers/power/pm_update.c index 4e90853e186..4c2bf41517e 100644 --- a/drivers/power/pm_update.c +++ b/drivers/power/pm_update.c @@ -126,6 +126,7 @@ static const uint16_t g_pmcount[3] = * 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 * * Returned Value: * None. @@ -137,9 +138,10 @@ static const uint16_t g_pmcount[3] = * ****************************************************************************/ -void pm_update(int domain, int16_t accum) +void pm_update(int domain, int16_t accum_, clock_t elapsed) { FAR struct pm_domain_s *pdom; + int16_t accum = 0; int32_t Y; int index; #if CONFIG_PM_MEMORY > 1 @@ -153,143 +155,153 @@ void pm_update(int domain, int16_t accum) 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) + while (elapsed >= TIME_SLICE_TICKS) { - 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, calclate 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) + if (elapsed - TIME_SLICE_TICKS < TIME_SLICE_TICKS) { - j = 0; + accum = accum_; } - Y += g_pmcoeffs[i] * pdom->memory[j]; - denom += g_pmcoeffs[i]; - } +#if CONFIG_PM_MEMORY > 1 + /* We won't bother to do anything until we have accumulated + * CONFIG_PM_MEMORY-1 samples. + */ - /* Compute and save the new activity value */ + if (pdom->mcnt < CONFIG_PM_MEMORY-1) + { + index = pdom->mcnt++; + pdom->memory[index] = accum; + continue; + } - Y /= denom; + /* 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 + */ - index = pdom->mndx++; - pdom->memory[index] = Y; - if (pdom->mndx >= CONFIG_PM_MEMORY-1) - { - pdom->mndx = 0; - } + 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 */ + /* No smoothing */ - Y = accum; + 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) + /* 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. */ - index = pdom->state - 1; - - /* Has the threshold to return to normal power consumption state been - * exceeded? - */ - - if (Y > g_pmexitthresh[index]) + if (pdom->state > PM_NORMAL) { - /* Yes... reset the count and recommend the normal state. */ - - pdom->thrcnt = 0; - 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->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? + /* Get the table index for the current state (which will be the + * current state minus one) */ - if (++pdom->thrcnt >= g_pmcount[index]) + index = pdom->state - 1; + + /* Has the threshold to return to normal power consumption state been + * exceeded? + */ + + if (Y > g_pmexitthresh[index]) { - /* Yes, recommend the new state and set up for the next - * transition. - */ + /* Yes... reset the count and recommend the normal state. */ pdom->thrcnt = 0; - pdom->recommended = nextstate; + 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->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? + */ + + if (++pdom->thrcnt >= g_pmcount[index]) + { + /* Yes, recommend the new state and set up for the next + * transition. + */ + + pdom->thrcnt = 0; + pdom->recommended = nextstate; + } + } + } + + elapsed -= TIME_SLICE_TICKS; } }