diff -r 89d6a7a84779 -r 25a17d01db0c Symbian3/PDK/Source/GUID-34D1D0BF-20DE-5677-A067-8FF9DD72E703.dita --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Symbian3/PDK/Source/GUID-34D1D0BF-20DE-5677-A067-8FF9DD72E703.dita Fri Jan 22 18:26:19 2010 +0000 @@ -0,0 +1,372 @@ + + + + + +Power +Controller DPowerController Implementation TutorialThis topic describes how to implement the DPowerController class +to provide the main power controller functionality in a base port. +

DPowerController is defined as:

+class DPowerController : public DBase + { +public: + IMPORT_C DPowerController(); + IMPORT_C void Register(); + IMPORT_C void WakeupEvent(); + +#ifndef __X86__ + IMPORT_C TInt RegisterResourceController(DBase* aController, TInt aClientId); +private: + struct SResourceControllerData + { + DBase* iResourceController; + TInt iClientId; + } iResourceControllerData; +#endif + +public: + volatile TPowerState iTargetState; +public: + virtual void CpuIdle() = 0; + virtual void EnableWakeupEvents() = 0; + virtual void DisableWakeupEvents() = 0; + virtual void AbsoluteTimerExpired() = 0; + virtual void PowerDown(TTimeK aWakeupTime) = 0; + }; + +

The first three functions are exported from the kernel, EKERN.EXE, +while the other five pure virtual functions are implemented by your base port. +You do not need to export any other functions.

+ +
Initialising +the power controller

Typically, you implement your derived class +in a power management kernel extension, which has the opportunity to perform +initialisation before the system itself is fully initialised. This is done +via the extension's DLL entry point. You use the DECLARE_STANDARD_EXTENSION() macro +as a wrapper around your initialisation code. There are at least three things +to do here:

    +
  • create an instance of +your power controller object

  • +
  • register your power +controller object with the Symbian OS power manager, by calling DPowerController::Register(). +Your power controller cannot be used by the Symbian OS power manager until +it has been registered.

  • +
  • if you intend to use +a battery monitor, then you would create it and register it as the HAL handler +to deal with THalFunctionGroup::EHALGroupPower calls. See Battery Monitor Implementation +Tutorialc and Power +HAL Handler Tutorial for more detail.

  • +

For example:

DECLARE_STANDARD_EXTENSION() + { + TInt r=KErrNoMemory; + + // Create the power controller object, and register it. + DPowerController* pC = new DYourPowerController; + if !(pC) + { + return (r); + } + pC->Register(); + + // Create the battery monitor. + DBatteryMonitor* pM = new DYourBatteryMonitor; + if !(pM) + { + return(r); + } + + r = KErrNone; + return(r); + }
+
DPowerController::RegisterResourceController()

The +function DPowerController::RegisterResourceController() allows +the Resource Controller to register itself with the Power Controller. See registering +with the resource controller in the PSL implementation document.

To +support idle power management the Power Controller's PSL must override RegisterResourceController() to +include the registration of a list of resources whose states are of interest +to idle power management.

Idle +power management

The Power Controller can assemble a list of resources +whose state it is interested in from a NULL thread in an array of SIdleResourceInfo structures. +Each entry in the list contains the resource ID that the power controller +must enter before registering with the PRM. The resource list should be created +in the kernel heap or data section.

The example below creates a buffer +to capture the state of all static resources. The PRM captures the information +using DPowerResourceController::RegisterResourcesForIdle().

... +//Allocate buffer for Idle resource management + NKern::ThreadEnterCS(); + pBuf = HBuf::New(numResources * sizeof(SIdleResourceInfo)); + NKern::ThreadLeaveCS(); + if(!pBuf) + { + return KErrNoMemory; + } + SIdleResourceInfo* pI = (SIdleResourceInfo*)pBuf->Ptr(); + +//Update resource ID + for(TUint c = 0; c < numResources; c++) + { + pI[c].iResourceId = c+1; + } + r = (iResourceControllerData.iResourceController)->RegisterResourcesForIdle(iResourceControllerData.iClientId, numResources, (TPtr*)pI); + ...

The first parameter passed to RegisterResourcesForIdle() is +the client Id generated for the power controller this is the client ID passed +by the PRM when calling RegisterResourceController.

Note: RegisterResourcesForIdle() is +not exported and is only available for use by the Power Controller to register +resources for idle power management. Note: RegisterResourcesForIdle() can +only be called by the Power Controller once.

+
DPowerController::EnableWakeupEvents() virtual void EnableWakeupEvents() = 0;

The +Symbian Power Model defines 3 generic system-wide power +states: Active, Standby and Off, as defined in +the Symbian OS power +model.

Each of the system-wide low power states: Standby and Off, +has a defined set of wake-up events. If a wake-up event occurs while the system +is preparing to transition into one of these low power states, or when the +system is in the Standby state, then this can result in the system +moving back to the Active power state. Wake-up events may be different +for different target power states. Wake-up events are platform-specific hardware +events, and it is your base port that decides which events count as wake-up +events.

When +is it called?

DPowerController::EnableWakeupEvents() is +called called by the power +manager as a result of a user side call to Power::EnableWakeupEvents(). +The class Power is the user side interface to the power +manager, and is used by the user side entity that is responsible for moving +the device into a low power state.

Context

When the user side entity decides to move the device to a low power +state, it sends a notification to all of the user side power domains registered +with it, giving their applications a chance to save their data and state. +However, before doing this, it calls Power::EnableWakeupEvents(). +It also calls Power::RequestWakeupEventNotification() so +that it can be informed of a wake-up event. If it is notified of a wake-up +event, it can halt, and reverse the user side response to the power transition.

+ +

Before calling DPowerController::EnableWakeupEvents(), +the power manager sets the iTargetState member of DPowerController to +the applicable TPowerState. This means that you can enable +the appropriate set of wake-up events.

Once the user side transition +is complete, it calls Power::PowerDown() in the user side +interface to the power +manager, which results in a call to DPowerController::PowerDown() in +the power controller. This starts the transition of all power handlers to a low power state, a potentially long process. This +means that the power controller needs the ability to detect wake-up events +so that it can halt, and reverse the power transition.

Implementation issues

There are three possibilities in dealing +with wake-up events:

    +
  • if wake-up events are +not handled by specific peripheral drivers, you could set up the hardware +so that wake-up events are recorded in a location accessible by the software. +Prior to completing the system power transition, the Power controller would +check the relevant hardware to see whether a wakeup event had occurred. On +detecting a wake-up event it would tell the power manager by calling DPowerController::WakeupEvent().

  • +
  • if wake-up events are +not handled by specific peripheral drivers, you could arrange for wake-up +events to interrupt the CPU. The code that services those interrupts would +tell the power manager by calling DPowerController::WakepEvent().

  • +
  • if wakeup events are +intercepted, serviced and cleared by peripheral drivers, then the following +outline solution could be adopted:

      +
    • The power controller +would need to publish a list of wake-up events, preferably as a bit mask, +and export an API which peripheral drivers could use to notify the power controller +that a wake-up event has occurred. The following interface class could be +used for this purpose:

      class TPowerController + { +public: + inline static void SetPowerControllerPointer(DPowerController* apPowerController) + { iPowerControllerPointer = apPowerController; } + IMPORT_C static void WakeupEventOccurred(TUint aWakeupEvent); + … // other methods if required +private: + DPowerController* iPowerControllerPointer; + }; +

      The class would be implemented as part +of the power management kernel extension and SetPowerControllerPointer() would +be called when initialising +the power controller, but after creation of the power controller object.

      Those +peripheral drivers that intercept wake-up events would need to link to the +power controller DLL (i.e. the power management kernel extension).

      On +the occurrence of a wake-up event, the peripheral driver software servicing +that event would notify the power controller by calling WakeupEventOccurred(), +specifying the appropriate wake-up event bit(s).

      You might implement WakeupEventOccurred() as +follows:

      EXPORT_C void TPowerController::WakeupEventOccurred(TUint aWakeupEvent) + { + if(iPowerControllerPointer->iWakeupEvents & aWakeupEvent) + { + iPowerControllerPointer->WakeupEvent(); + } + }

      where iWakeupEvents is a data member +defined in your DPowerController -derived class that contains +the bitmask representing the wake-up events. The bitmask groups wakeup events +according to the target power state.

    • +
  • +

When the peripheral driver powers down, it should leave the hardware +that generates the wake-up event powered and enabled for detection. Thus, +if a wake-up event of the type handled by this peripheral driver occurs, it +will not be serviced in the normal way, but will be left pending until the +power controller services it.

When the power controller’s DPowerController::PowerDown() function +is called, the power controller must check the peripheral hardware directly +to see whether the wakeup event has happened, or is happening right now, prior +to transitioning the device to the target system-wide low power state. If +the wake-up event is found to have happened then DPowerController::PowerDown() would +return immediately, instead of initiating the power +handlers power down of the peripheral drivers.

+
DPowerController::AbsoluteTimerExpired() virtual void AbsoluteTimerExpired() = 0;

When is it called?

DPowerController::AbsoluteTimerExpired() is +called by the power +manager whenever an absolute timer expires. An absolute timer is one +that is set to complete at a specific time, as queued by a call to RTimer::At().

Implementation issues

If absolute timer expiration is one +of your wake-up events, then your implementation of DPowerController::AbsoluteTimerExpired() should +call DPowerController::WakeupEvent() to notify the power +manager that a wake-up event has happened.

It is recommended that +you track absolute timers.

+
DPowerController::PowerDown() virtual void PowerDown(TTimeK aWakeupTime) = 0;

When is it called?

DPowerController::PowerDown() is +called by the power +manager to move the device into one of the system-wide low power states: Standby or Off.

Typically, +this is a result of the user side entity that is responsible for moving the +device into a low power state calling Power::PowerDown() in +the user side interface to the power +manager.

If physical +RAM defragmentation is implemented you may wish to include calls to TRamDefragRequest::DefragRam() within +this function to enable defragmentation of RAM zones that can then be powered +down.

Context

The +target state is defined by the value of the iTargetState member +of DPowerController, as set by the power manager.

Implementation issues

Implementation depends on the target +state:

    +
  • if the target state +is Standby, as implied by a value of TPowerState::EPwStandby in DPowerController::iTargetState, +then the power controller should put the hardware into a state corresponding +to the Standby state.

    If at least one wake-up event has been +detected and recorded since the last call to DPowerController::EnableWakeupEvents(), +then PowerDown() should return immediately; otherwise, it +should continue with the process of putting the device into Standby; +execution of the function will halt, and can only continue, and return, when +a wake-up event occurs.

  • +
  • if the target state +is Off, as implied by a value of TPowerState::EPwOff in DPowerController::iTargetState, +then PowerDown() must never return. Typically, the +power controller turns the device off, but can choose to perform other device-specific +action such as a system reboot.

  • +

The TTimeK parameter passed to the function is +a system time value. If non-zero, it specifies the time when the system should +wakeup. The kernel calculates it as the time that the next absolute timer +expires. Typically, your implementation will use the Real Time Clock module +to generate an event at the specified time, and this will cause a return to +the Active state. This implies that the base port should enable Real +Time Clock event detection during Standby.

The Symbian definition +of the Standby state usually translates into the hardware manufacturer’s +CPU “Standby” or “Sleep” modes of operation. Typically, the internal clocks +associated with the core and some of the core peripherals, or their power +supply, are suppressed, and their internal state is not preserved. In this +case, the state of the core and core peripherals should be saved before going +into Standby so that they can be restored when the system wakes-up. +Note the following:

    +
  • for the core state, +save the current mode, the banked registers for each mode, and the stack pointer +for both the current mode and user mode

  • +
  • for the core peripherals, +save the state of the interrupt controller, the pin function controller, the +bus state controller, and the clock controller

  • +
  • for the MMU state, save +the control register, the translation table base address, and the domain access +control, if this is supported

  • +
  • flush the data cache +and drain the write buffer.

  • +

If all of this data is saved to a DRAM device, then this should be +put into self refresh mode.

Peripherals modules involved in the detection +of wake-up events should be left powered.

Tick timer events should +be disabled, and the current count of this and any other system timers should +be saved; relevant wake-up events should be left unmasked, and any others +should be disabled.

+
DPowerController::CpuIdle() virtual void CpuIdle()=0;

When is it called?

DPowerController::CpuIdle() is +called whenever the Null thread is scheduled to run. This can happen as soon +as the power model has been installed. It is the mechanism through which your +base port can increase power savings when the system becomes inactive by moving +the CPU or the system into a low power mode.

If physical +RAM defragmentation is implemented you may wish to include calls to TRamDefragRequest::DefragRam() within +this function to enable defragmentation while the device is idle.

Implementation issues

The implementation can call the Variant +or ASSP implementation of Asic::Idle().

The idle +state is usually implemented via a Wait-For-Interrupt type instruction that +will usually suspend execution of the CPU, possibly triggering other ASIC +power saving actions, and will resume execution when any unmasked interrupt +occurs.

Suppressing +the system tick interrupt

To further increase power savings during +CPU Idle, a base port can choose to suppress the system tick interrupt until +the next nanokernel Timer (as implemented by NTimer) is +due to expire. Nanokernel timers are the basic timing service and all Symbian +OS tick-based timers and time-of-day functions are derived from nanokernel +timers.

In EKA2, timing services rely on a hardware timer, which is +programmed by the base port Variant code, to generate the system tick. We +refer to this as the system timer.

Typically, the platform-specific +ASSP or Variant object has a pointer to the nanokernel timer queue, an NTimerQ object. +The number of ticks before the next NTimer is due to expire +can be found by calling NTimerQ::IdleTime().

Before +going into Idle mode, CpuIdle() disables the hardware timer +used to generate the system tick (i.e. the system timer) for the number of +ticks to be suppressed; i.e. the system timer is reset to go off and generate +an interrupt only when the NTimer expires. Note that the +clock supply to the hardware timer must be left enabled.

On returning +from Idle mode, the software must examine the system timer and decide whether +the NTimer expiration was responsible for waking up the +processor, or whether it was due to some other interrupt.

If waking +up was due to the NTimer, the system timer must be reset +to generate the system tick at the frequency desired, and the tick count, NTimerQ::iMsCount, +must be adjusted with the NTimer expiration time. As the +expiration time is always an integer multiple of the number of ticks, then +the NTimerQ::Advance() function can be used for this purpose.

If +waking up was due to another interrupt, then the software must read the system +timer and calculate the time elapsed since going into Idle mode, and adjust +the system tick count with the elapsed time (which could be a fractional number +of ticks) and reprogram the system timer to continue generating the tick after +the correct interval, as above.

If the hardware timer that is used +to generate the system ticks does not use a Compare-Match function, then some +care has to be taken not to introduce skewing when manipulating the timer +value directly. If the timer needs to be reloaded with the new value to give +the next tick, then the software usually “spins”, waiting for the hardware +timer to change, and then reloads it. This way the timer will always be reloaded +on an edge of its internal clock.

Waking +up from “Sleep” modes with long wakeup latencies

Often, to further +enhance power savings, the CPU and some peripherals are moved into a hardware +“sleep” mode when CpuIdle() is called. This could be of long +latency, and waking up could take longer than the system Tick period. There +are two situations:

    +
  • if the wakeup time can +be determined precisely, then CpuIdle() programs the system +timer to bring the CPU back from the “Sleep“ mode at a time corresponding +to the next NTimer expiration (as a number of Ticks to +suppress) minus the number of ticks it takes to wake up from that mode. +On waking up on the timer, the System tick count should be adjusted with the +total number of ticks suppressed, i.e. the count corresponding to the time +of the NTimer expiration.

  • +
  • If the wakeup time cannot +be known deterministically, then the above scheme must be combined with another +system to allow adjusting the system timer from the hardware Real Time Clock +(RTC).

    Typically, the hardware RTC is clocked with a 1Hz clock, and +can be programmed to interrupt the CPU on multiples of one second intervals. +The clock supply to the RTC must be left enabled on going into "sleep" mode.

  • +

To guarantee that timed events occur when they are due, the CPU should +only be allowed to go to into the long latency hardware “sleep” mode if the +RTC can be guaranteed to complete a second tick before the CPU is due to wakeup.

Note +that if waking up from hardware “Sleep” mode takes a non-negligible amount +of time, extreme care should be taken when deciding to move the platform into +that mode. For example, if a receive request is pending on a data input device and the +CPU reaches the Idle mode and the platform is transitioned into a “sleep” +state and data arrives while it is still in that state, then there +is a possibility that the system will not wake up on time to service the incoming +data.

+
Validation

The e32test programs t_power, +and t_timer will put the system into standby and resume +off a timer.

+
+Power Management + +
\ No newline at end of file