Controllable Power Resources

This topic explains how power resources were controlled in older releases of Symbian platform. For later releases see the Power Resource Manager documentation.

This documentation is retained for reference for those working with hardware or software that is based on older versions of Symbian platform version (in particular old release 9.5). For later releases see the Porting the Power Resource Manager documentation.

Controllable power resources are defined as being any device resource, such as voltages on power-lines or clocks, which can be independently enabled, disabled or modified under software control.

The following hardware block diagram is an example of the way power resources might be arranged:

Figure 1. Example power resource arrangement

There is no default support or default implementation for controllable power resources in the generic layers of Symbian platform. They must be managed by the base port.

We suggest that the management of controllable power resources (the resource manager) be implemented in the Variant DLL or the ASSP DLL. This allows the resource manager to be up and running by the time peripheral drivers, which might also be kernel extensions, are loaded. See the ASSP/Variant Architecture and the ASSP/Variant Tutorials.

A suggested implementation has the instance of the Asic derived class owning a pointer to the resource manager object. The interface class for the ASSP or Variant could then have an exported or an inline member function to return a pointer to this resource manager object. Peripheral drivers are expected to link against either the ASSP DLL, or the Variant DLL, or both.

For example, for a device that has a Variant and an ASSP:

class MyVariant : public MyASSP
    // Other mandatory Asic-derived member functions
    ResourceManager* myResMgrPtr;

where class MyASSP is derived from class Asic.

GLREF_D MyVariant TheVariant;
class TMyVariant
    inline static ResourceManager* ResourceManager()
    // other exported methods

where TMyvariant is the Variant interface class, and ResourceManager is the resource manager class.

The resource manager can be declared as a global object at the Variant (or ASSP) scope and can therefore be created when this component is first loaded. In this case, the Asic::Init3() function would initialise the ResourceManager object, and set up myResMgrPtr. Alternatively, if the Variant (or ASSP) were to need the resource manager up and running, then you would initialise ResourceManager in Asic::Init1().

In this implementation, the Variant would export a header file(s) publishing a list of controllable power resources.

Shared controllable power resources

Some controllable power resources can be shared between groups of peripherals and therefore may require usage tracking.

We recommend that shared power resources be represented by a class derived from MPowerInput. This class offers the Use() and Release() interface that encapsulates resource counting behaviour. This mechanism should turn the resource on when the count becomes non-zero, and turn the resource off when the count becomes zero.

We suggest that your MPowerInput derived class defines and implements a GetCount() function that returns the usage count on the shared resource.

A suggested implementation of a resource manager

While we have implied that a resource manager is a single class, which we have called ResourceManager, the resource manager is a notional concept for controlling power resources. Symbian neither mandates a format for a ResourceManager class, nor even its existence. In practice, it may be more useful to embed resource manager functions and data in the Variant interface class (or the ASSP interface class, if appropriate).

However, for the purpose of explaining what a resource manager can do, it is easier to assume that this behaviour is handled by a resource manager class. This is the outline definition of such a class.

class ResourceManager
    void InitResources();                            // called by your Asic::Init3()/Asic::Init1()
    void Modify(TUint aSetMask, TUint aClrMask);    // only 32 simple Resources can be managed
    TBool GetResourceState(TUint aResBitMask);    // only 1 bit set on this mask, returns On/Off
    (MPowerInput-derived) iSharedResource1;        // 1 per shared resource
    (MPowerInput-derived) iSharedResource2;

The InitResources() function would do at least two things:

  • enable an appropriate set of simple resources at boot time

  • initialise the shared power resource tracking objects, i.e. the instances of your MPowerInput derived class(es).

The function would be called by the Variant or ASSP implementation of Asic::Init3() or Asic:Init1().

The Modify() function is an example of how to deal with power resources that are either on or off. The function would take bit masks, where a bit represents a separate controllable power resource. The first bit mask would represent the set of power resources to be turned on, while the second would represent the set of power resources to be turned off. This assumes that the function could behave synchronously. Drivers for peripherals that use a set of these controllable power resources, would call Modify() to turn the relevant controllable power resources on or off.

As an example, the most common method of switching clock resources on or off is by setting and clearing bits in a hardware register. A base port could choose to implement common code for setting bits in registers.

A shared power resource is further represented by a MPowerInput derived class. The implementations of MPowerInput::Use() and MPowerInput::Release() would call Modify() to turn on the shared resource when the usage count becomes non zero, or turn it off when the usage count reaches zero. Peripheral drivers would have access to shared resources, and also to the member functions of the ResourceManager class, through the ASSP or Variant Interface class function that returns a pointer to it.

The resource manager, or its functions and members, could be offered as part of a power controller interface class, the TPowerController class suggested when we discussed the implementation of the Power Controller's EnableWakeupEvents() function. The class would provide an exported function that would give access to the resource manager. The resource manager class would be made part of the power controller kernel extension. Peripheral drivers needing access to shared power resources would then link to the power controller DLL. If the Variant or ASSP were also to need access to the resource manager, then the resource manager would have to be initialised much earlier in the kernel start-up sequence. In this case, we would suggest that the standard power controller kernel extension entry point defined by DECLARE_STANDARD_EXTENSION() be replaced with a custom entry point that might look like this:

GLDEF_C TInt KernelModuleEntry(TInt aReason)
        ResourceManager* c = new ResourceManager ();    // create Resource Manager
        return c ? KErrNone : KErrNoMemory;    
    else if(aReason==KModuleEntryReasonExtensionInit0)
        (create DPowerController and Battery Monitor and Power HAL handler)
    return KErrNone;         // gets here from calling with KModuleEntryReasonExtensionInit1

The Variant or ASSP would also need to link to the power controller DLL.

The following base port software architecture could be applied to the power supply arrangement as illustrated in the hardware block diagram above (see the beginning of this section at Controllable Power Resources):

Figure 2. Base port software architecture

Multi-level, composite and asynchronous power resources

Some power resources may have more than one operational level. For example, it is possible that an input clock to a peripheral can be operated at a nominal value corresponding to the fully active state, and may be lowered to another level corresponding to the power saving state.

The same could apply to the input voltages that the peripheral can operate on. The control model suggested above for simple resources may not be appropriate. A suggested alternative would have these multi-Level resources implementing an interface that offers a public enumeration to cover all possible usage levels at which the resource can be used, and APIs such as:

class MultiLevelResource
    enum TResourceLevel
        . . .
        . . .
    void UseToLevel();
    void ReleaseToLevel();
    TResourceLevel GetCurrentLevel();
    . . . 

More importantly, it is possible that resources may have to be operated in combination with other resources. For example, some peripherals may have more than one input clock, and on transition between power states, these clocks may have to be changed simultaneously.

If the resources to be actuated simultaneously are simple resources, then the model above with a Modify() call that takes a bit mask and allows more than one resource to be changed at a time may be appropriate. If the resources to be actuated are shared or multi-level, or have to be represented individually, then another interface will have to be defined to allow modifying more than one of them simultaneously.

Modifying some resources may not be an instantaneous operation. For example, it may take a non-negligible amount of time to stabilise an input clock that has just been modified.

The nature of EKA2 strongly discourages actively waiting (or “spinning”) inside drivers; instead it is suggested that the multithreading capabilities of EKA2 kernel side code and the synchronisation mechanisms available be used to solve the problem of having the driver code having to wait for a resource to stabilise before proceeding with its use.

As an example, let us assume that the multi-level resource mentioned above has a non-negligible delay associated with any level changes:

  • If the length of time to wait for a power resource to stabilise is known, and is the order of a few milliseconds or less, or the driver executes on its unique kernel thread (not used by any other kernel-side code), then the best solution would be to sleep the driver thread, by calling NKern::Sleep(aTime) for the specified length of time after issuing a call to UseToLevel() (or ReleaseToLevel()).

  • When the length of time to wait for a power resource to stabilise increases, then sleeping the driver thread may become a problem if the driver executes in a DFC queue used by other drivers, which is often the case. This is because blocking that thread means that no other drivers can run. Here, the best solution is for the driver to create another thread and DFC queue, by calling Kern::DfcQInit(), and whenever it needs to change the resource, queue a DFC on that queue, which will issue the calls to UseToLevel() (or ReleaseToLevel()) and then sleep the thread.

  • If the length of time to change a power resource cannot be known in advance, but the hardware provides an indication of completion of the change, for example, in the form of an interrupt, then the solution may involve the driver code waiting on a Fast Mutex, which is signalled by the completion event (in code belonging to the MultiLevelResource derived object).