Implement the controllable power resources

This document describes how to write and use power resources.

Purpose

Implement your power resource according to the type of resource you wish to support.

Introduction

Power resources controlled by the PRM can be turned on, off and varied with software.

See the Power resources section for detailed information about resources. There are five main types of resource that can be implemented:

  • static resources - derived from the DStaticPowerResource base class,

  • static resource that support dependancy - derived from the DStaticpowerResourceD base class,

  • dynamic resources - derived from the DDynamicPowerResource base class. Create dynamic resources in the kernel data section or in the heap,

  • dynamic resource that support dependancy - derived from the DDynamicPowerResourceD.

  • custom sense resources - when shared the power level of this resource may be increased or decreased freely by some privileged clients but not by others, which are bound by the requirement of the privileged clients.

Note: dynamic resources and resource dependancies are only supported in the extended version of the PRM. See setup and configuration requirements.

Implementing power resources

The following tasks are covered in this tutorial:

Override the pure virtual functions

The pure virtual functions of DStaticPowerResource or DDynamicPowerResource must be implemented.

Constructor

Information about the resource based on the resources category must be set in the iFlags bit mask. Static variables that define a resources classification can be found within 32\include\drivers\resource.h.

Each resource is classified based on:

  • usage,

  • operating levels,

  • latency,

  • resource sense.

Below is the example implementation of derived class constructor

// multilevel, shared, long latency, read and write, positive resource. 
DMLSHLGLSPResource::DMLSHLGLSPResource() : DStaticPowerResource(KDBSHLGLSPResource, E_ON), 
                     iMinLevel(E_OFF), iMaxLevel(E_ON), iCurrentLevel(E_ON), iPolled(ETrue)
    {
    iFlags = EMultilevel | KShared | KLongLatencySet | KLongLatencyGet;
    . . .   
    }

GetInfo()

The PIL uses GetInfo() to get resource information. The default implementation of this function provides generic information about the resource by updating the passed descriptor that contains an information structure TPowerResourceInfoV01. Derived implementations must fill in all the PSL specific fields and call the base class implementation. Below is an example implementation.

TInt DH4SndFclkResource::GetInfo(TDes8* info) const
    {
    __KTRACE_OPT(KRESMANAGER, Kern::Printf(">DH4SndFclkResource::GetInfo\n"));

    /** Call base class implementation to fill generic details */
    DStaticPowerResource::GetInfo((TDes8*) info);
    TPowerResourceInfoV01 *buf1 = (TPowerResourceInfoV01*) info;
    buf1->iMinLevel = iMinLevel;
    buf1->iMaxLevel = iMaxLevel;
    __KTRACE_OPT(KRESMANAGER, Kern::Printf("<DH4SndFclkResource::GetInfo\n"));
    return KErrNone;
    }

The member variables of the TPowerResourceInfoV01 structure are reserved for PSL information. These can be used to return resource specific information to clients.

DoRequest()

DoRequest() is used by the PIL to control resources. For example, to change and read the state of resources. DoRequest() takes a TPowerRequest object that contains all the request information. Note: This function needs to be implemented for all resources.

Below is an example DoRequest() function implementation for a binary, instantaneous resource.

TInt DH4MmcCtrllerPwrResource::DoRequest(TPowerRequest& aRequest)
    {
    TInt retVal = KErrNone;
    TUint16 config;
    /** Enable/disable the MMC controller by programming the MMC controller 'CON' register- 'POW' bit */
    if (aRequest.ReqType()==TPowerRequest::EGet) 
        {
        PRM_PSL_RESOURCE_GET_STATE_START_TRACE
        config = OMAPMMC_GET_REG(KHoMMC_Con_Reg);
        if(config & KHtMMC_PowerUp)
            {
            aRequest.Level() = 1;
            iCurLevel = 1;
            }
        else
            {
            aRequest.Level() = 0;
            iCurLevel = 0;
            }
        PRM_PSL_RESOURCE_GET_STATE_END_TRACE
        }
    else if(aRequest.ReqType()==TPowerRequest::ESetDefaultLevel)
        {
        PRM_PSL_RESOURCE_CHANGE_STATE_START_TRACE
        OMAPMMC_MOD_REG(KHoMMC_Con_Reg, KHtMMC_PowerUp, KClear32);
        iCurLevel = iDefaultLevel;
        aRequest.Level() = iDefaultLevel;
        PRM_PSL_RESOURCE_CHANGE_STATE_END_TRACE
        }
    else 
        {
        PRM_PSL_RESOURCE_CHANGE_STATE_START_TRACE
        if(aRequest.Level() == 1)
            {
            OMAPMMC_MOD_REG(KHoMMC_Con_Reg, KClear32, KHtMMC_PowerUp);
            }
        else
            {
            OMAPMMC_MOD_REG(KHoMMC_Con_Reg, KHtMMC_PowerUp, KClear32);
            }
        iCurLevel = aRequest.Level();
        PRM_PSL_RESOURCE_CHANGE_STATE_END_TRACE
        }
    return retVal;
    }

The DoRequest function implementation should take care of blocking the Resource Controller thread as operations on a long latency resource take a significant amount of time to complete (in hardware). Usually the request completion is notified either through an ISR (interrupt driven) or through register setting (polling wait) by hardware.

Below is an example DoRequest() implementation for a long latency resource.

 TInt DH4MmcPowerResource::DoRequest(TPowerRequest& aRequest)
    {
    TUint8 iMenelausConf;
    TInt retVal = KErrNone;

    /** Access to Menelaus registers is over I2C bus. This is a long latency synchronous operation.
    Create a Menelaus Request structure and send it to Menelaus chip, Wait until a response is received */

    if (aRequest.ReqType()==TPowerRequest::EGet)
        {
        // Frame the request
        ... 
        }
				// Default level is off.
    else if (aRequest.ReqType() == TPowerRequest::ESetDefaultLevel)
        {
        // Frame request to move the resource to default state
        ... 
        }
				// EChange
    else 
       {
       // Frame request to set the requested resource state
       ...     
       }
    
    // Request the operation on the hardware
    retVal = Menelaus::AccessRegister(iMenelausReq);
    
    // Wait for the request to complete  
    __KTRACE_OPT(KPBUS1, Kern::Printf("Waiting for menelaus req to signal sem\n"));
    NKern::FSSetOwner(&iSemaphore,NULL);
    iSemaphore.iCount = 0;	

    // This will be signalled in Menelaus access functions callback below
    NKern::FSWait(&(iSemaphore)); 
	
    // Menelaus chip has responded
    if(iMenelausAccessErr == KErrNone)
        {
        switch(iMenelausState)
            {
            case EMenelausRead:
                // Return 0/1 depending on the value set
                iMenelausConf = (*(iRdBuffer.Ptr())) & KHoMenelausVmmcModeMask;		                    
                if(iMenelausConf == 3)
                    {
                    iCurLevel=aRequest.Level()=1; 
                    }
                else 
                    {
                    iCurLevel=aRequest.Level()=0;
                    }
                PRM_PSL_RESOURCE_GET_STATE_END_TRACE
                break;

            case EMenelausSingleWrite:
                iCurLevel = aRequest.Level();
                PRM_PSL_RESOURCE_CHANGE_STATE_END_TRACE
                break;
            }
        }
        return iMenelausAccessErr;
    }

/** Below is the Menelaus access functions callback where the semaphore is signalled. */
void DH4MmcPowerResource::MenelausAccessDone(TAny* aPtr, TInt aResult)
    {
    DH4MmcPowerResource* ptr = reinterpret_cast<DH4MmcPowerResource*>(aPtr);
    ptr->iMenelausAccessErr = aResult;
    if(aResult != KErrNone)
        {
        __KTRACE_OPT(KRESMANAGER, 
        Kern::Printf("Menelaus::MenelausAccessRegister ERR(%d)\n", aResult));
        }
				// Signal the waiting thread
    NKern::FSSignal(&(ptr->iSemaphore)); 
    return;
    }

Create resource dependencies

A resource has a dependency if the state of the resource depends on the state of one or more resources. For example, a clock whose frequency is derived from another clock or voltage is a dependent resource. The PSL usually handles resource dependencies transparently. However, if the resources have public users (clients of the PRM), then these resources should be registered with the PRM as Resource dependencies that are linked.

Each dependency must be assigned a priority value. The priority is used by the PIL when propagating the change and propagating the request for notifications. Each link stemming from a resource must have a unique priority value.

Note: Resource dependencies are only supported in the extended version of the PRM. Only long latency resources are allowed to have dependents and no closed loop dependencies are allowed.

Register resource dependencies for dynamic and static resources

Static resources that support dependencies are created and registered with the PRM during resource controller creation time and are derived from DStaticPowerResourceD:

DXXXStaticPowerResourceD : public DStaticPowerResourceD
    {
public:
    DXXXStaticPowerResourceD();
    TInt DoRequest(TPowerRequest &req);
    TInt GetInfo(TDes8* aInfo) const;
    TChangePropagationStatus TranslateDependentState(TInt aDepId, TInt aDepState, TInt& aResState);
private:
    TInt iMinLevel;
    TInt iMaxLevel;
    TInt iCurrentLevel;
    ...
    };

Dependencies between static resources should be established by the PSL before registering these resources with the PIL. Use the DStaticPowerResourceD::AddNode() function provided in the base class to establish a dependency link between static resources. See Creating dependencies between static resources.

Dynamic resources that support dependency are derived from DDynamicPowerResourceD. These can be registered and deregistered from the PRM at any time after the PRM is fully initialised. Dependencies between resources are established by the client using PowerResourceManager::RegisterResourceDependency().

DXXXDynamicPowerResourceD : public DDynamicPowerResourceD
    {
public:
    DXXXDynamicPowerResourceD();
    TInt DoRequest(TPowerRequest &req);
    TInt GetInfo(TDes8* aInfo) const;
    TChangePropagationStatus TranslateDependentState(TInt aDepId, TInt aDepState, TInt& aResState);
private:
    TInt iMinLevel;
    TInt iMaxLevel;
    TInt iCurrentLevel;
    ...
    };

Dependencies can be established between a dynamic resource and a static (dependency enabled) resource using the same API. A client can deregister dependencies between a pair of dynamic resource (or between a dynamic and static resource) using PowerResourceManager::DeRegisterResourceDependency(). When a dynamic resource that supports dependency deregisters from the PRM, dependencies are removed automatically by the PIL.

Change the state of dependent resources

When a state change is requested by a client on any resource in a dependency tree before proceeding with the change the PIL must check if the change is allowed by its dependents and the ones it depends on. The PIL does this by calling the TranslateDependentState() function on each resource. This is a pure virtual function in the base class and must be implemented in the derived class.

This function is called by the PIL prior to a resource change on any of its dependents. The function returns one of these values:

  • EChange - If the resource accepts the state change of the dependent resource and, as a result of the dependent resources state change this resource needs to change its own state. The new state of this resource is updated in aResState,

  • ENoChange - if the resource accepts the dependent resources state change and this resource does not need to change its state,

  • ENotAllowed - if the resource does not accept the dependent resources state change.

TChangePropagationStatus DXXDependResource::TranslateDependentState(TInt /*aDepId*/, TInt aDepState,
   TInt& aResState)
    {
    /* Switch ON if the dependent resource is ON */
    if((aDepState == 1) && (iCurrentLevel == 0)
        {
        aResState = iMaxLevel;
           return EChange;
           }

    /* Don’t allow dependent to OFF, if this is still ON */
    else if (aDepState == 0) && (iCurrentLevel == 1)
        {
        return ENotAllowed;
        }

    return ENoChange;
    }

Create custom resources

Clients on a shared resource may have different requirements on the state of a shared resource. The resource sense is used by the PIL to determine whose requirement prevails:

  • positive sense resources - when shared can have their value increased without prejudice to their clients,

  • negative sense resources - when shared can have their value decreased without prejudice to their clients,

  • custom sense resources - when shared may be increased or decreased freely by some privileged clients but not by others, which are bound by the requirement of the privileged clients.

A custom function must be supplied for every Power resources resource. This function is called by the PIL every time a change request is issued to determine if the change requested by the client is allowed as a decision to allow the resource change is determined solely by the PSL. The resource object contains a pointer that must be set at construction for custom sense resources by calling this function:

inline void SetCustomFunction(TCustomFunction aCustomFunction)

This function sets the custom function for the resource. If a custom function is not supplied for a custom sense resource the PIL panics during a resource state change. An example of a custom sense resource is a shared domain clock when a domain client uses it as a bit clock.

TCustomFunction is a function pointer:

typedef TBool (*TCustomFunction) (TInt& /*aClientId*/,
                                  TUint /*aResourceId*/,
                                  TInt& /*aLevel*/,
                                  TAny* /*aLevelList*/);

The values held in TCustomFunction are:

  • the Id of the client requesting the change,

  • the Id of the resource on which the change is requested (this allows a single function to handle multiple resources),

  • the level the client is requesting,

  • a pointer to the list of the resource’s client levels.

Custom functions can be set at any time before a resource state change is requested on a resource. Ideally a custom function is set at resource creation time in the resource's constructor.

DBSHLGLSCResource::DBSHLGLSCResource() : DStaticPowerResource(KDBSHLGLSCResource, E_ON), iMinLevel(E_OFF), iMaxLevel(E_ON), iCurrentLevel(E_ON), iPolled(EFalse)
    {
    iFlags = KMultiLevel | KShared | KLongLatencySet | KSenseCustom;
    SetCustomFunction(CustomFunction);
 ...
    }

The decision to allow the requested resource state change is specified in the custom function return value. This is ETrue if the change is allowed and EFalse if the change is not allowed.

The function can change the state of the resource to a level other than the one requested and can choose a client other than the passed client to hold the Caching the prevailing level of the resource.

Supporting dependencies

If the custom sense resource supports dependencies then use the function pointer TDependencyCustomFunction. This takes an additional parameter that specifies the resource Client level objects.