mirror of
https://github.com/apache/nuttx.git
synced 2026-09-21 05:14:27 +08:00
drivers/power: PM: Add timer to decrease PM level automatically
This commit is contained in:
+7
-1
@@ -48,6 +48,7 @@
|
||||
#include <nuttx/semaphore.h>
|
||||
#include <nuttx/clock.h>
|
||||
#include <nuttx/power/pm.h>
|
||||
#include <nuttx/wdog.h>
|
||||
|
||||
#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)
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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 */
|
||||
|
||||
@@ -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 */
|
||||
|
||||
+126
-114
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user