diff -r 578be2adaf3e -r 307f4279f433 Adaptation/GUID-34D1D0BF-20DE-5677-A067-8FF9DD72E703.dita --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Adaptation/GUID-34D1D0BF-20DE-5677-A067-8FF9DD72E703.dita Fri Oct 15 14:32:18 2010 +0100 @@ -0,0 +1,331 @@ + + + + + +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 platform power manager, by +calling DPowerController::Register(). Your power +controller cannot be used by the Symbian platform 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 platform +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 platform 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