sl@0: // Copyright (c) 1994-2009 Nokia Corporation and/or its subsidiary(-ies). sl@0: // All rights reserved. sl@0: // This component and the accompanying materials are made available sl@0: // under the terms of the License "Eclipse Public License v1.0" sl@0: // which accompanies this distribution, and is available sl@0: // at the URL "http://www.eclipse.org/legal/epl-v10.html". sl@0: // sl@0: // Initial Contributors: sl@0: // Nokia Corporation - initial contribution. sl@0: // sl@0: // Contributors: sl@0: // sl@0: // Description: sl@0: // template\template_variant\specific\power.cpp sl@0: // Template Power Management sl@0: // (see also variant.cpp for a discussion on Sleep modes and xyin.cpp for example sl@0: // of usage of Resource Manager and Peripheral self power down and interaction sl@0: // with Power Controller for Wakeup Events) sl@0: // sl@0: // sl@0: sl@0: #include "template_power.h" sl@0: sl@0: static TemplateResourceManager TheResourceManager; sl@0: sl@0: DTemplatePowerController* TTemplatePowerController::iPowerController; sl@0: sl@0: sl@0: //-/-/-/-/-/-/-/-/-/ class DTemplatePowerController /-/-/-/-/-/-/-/-/-/ sl@0: sl@0: DTemplatePowerController::DTemplatePowerController() sl@0: { sl@0: Register(); // register Power Controller with Power Manager sl@0: TTemplatePowerController::RegisterPowerController(this); sl@0: } sl@0: sl@0: void DTemplatePowerController::CpuIdle() sl@0: { sl@0: Arch::TheAsic()->Idle(); sl@0: } sl@0: sl@0: void DTemplatePowerController::EnableWakeupEvents() sl@0: { sl@0: // sl@0: // TO DO: (mandatory) sl@0: // sl@0: // Enable tracking of wake-up events directly in hardware. If the hardware is controlled by a Driver sl@0: // or Extension, may need to disable interrupts and preemption around the code that accesses the hardware sl@0: // and set up a flag which the Driver/Extension code need to read before modifying the state of that piece sl@0: // of hardware. Note in that case the Driver/Extension may need to link to this Library. sl@0: // sl@0: sl@0: // sl@0: // EXAMPLE ONLY sl@0: // In this example we simply assume that the driver will call the Power Controller every time a sl@0: // wakeup event occurr. It is up to the Power Controller to know if it is tracking them or not. sl@0: // We also assume that if a wakeup event occurrs when the CPU is in Standby, this will automatically sl@0: // bring it back from that state. sl@0: iWakeupEventsOn = ETrue; // start tracking wakeup events sl@0: } sl@0: sl@0: void DTemplatePowerController::DisableWakeupEvents() sl@0: { sl@0: // sl@0: // TO DO: (mandatory) sl@0: // sl@0: // Disable tracking of wake-up events directly in hardware or if the hardware is controlled by a Driver or sl@0: // Extension need to set up a flag which the Driver/Extension reads whenever the event occurs, in order to sl@0: // find out if it needs to deliver notification to the Power Controller sl@0: // sl@0: iWakeupEventsOn = EFalse; // stop tracking wakeup events sl@0: } sl@0: sl@0: void DTemplatePowerController::AbsoluteTimerExpired() sl@0: { sl@0: if (iTargetState == EPwStandby && iWakeupEventsOn) sl@0: { sl@0: iWakeupEventsOn = EFalse; // one occurred, no longer track wakeup events sl@0: WakeupEvent(); sl@0: } sl@0: } sl@0: sl@0: void DTemplatePowerController::PowerDown(TTimeK aWakeupST) sl@0: { sl@0: if (iTargetState == EPwStandby) sl@0: { sl@0: // sl@0: // TO DO: (mandatory) sl@0: // sl@0: // Converts between the Wakeup time in System Time units as passed in to this function and a Wakeup sl@0: // time in RTC units. The following code is given as an example how to convert between System time units sl@0: // RTC time units on a system with a 32 bit RTC timer and which is incremented on a second interval: sl@0: // sl@0: TUint32 wakeupRTC; sl@0: if (aWakeupST) sl@0: { sl@0: TUint32 nowRTC = TTemplate::RtcData(); sl@0: TTimeK nowST = Kern::SystemTime(); sl@0: __KTRACE_OPT(KPOWER,Kern::Printf("system time: now = 0x%lx(us) wakeup = 0x%lx(us)", nowST, aWakeupST)); sl@0: if (aWakeupST < nowST) sl@0: return; sl@0: Int64 deltaSecs = (aWakeupST - nowST) / 1000000; sl@0: if (deltaSecs <= 0) sl@0: return; sl@0: if (deltaSecs + (Int64)nowRTC > (Int64)(KMaxTInt - 2)) sl@0: wakeupRTC = (KMaxTInt - 2); // RTC can't wrap around during standby sl@0: else sl@0: wakeupRTC = nowRTC + deltaSecs; sl@0: __KTRACE_OPT(KPOWER,Kern::Printf("RTC: now = %d(s) wakeup = %d(s)", nowRTC, wakeupRTC)); sl@0: } sl@0: else sl@0: wakeupRTC = 0; sl@0: // sl@0: // TO DO: (optional) sl@0: // sl@0: // It then uses the calculated value to program the RTC to wakeup the System at the Wakeup sl@0: // time ans sets the CPU and remaining hardware to go to the correponding low power mode. When the sl@0: // state of the Core and Core Peripherals is not preserved in this mode the following is usually sl@0: // required: sl@0: // - save current Core state (current Mode, banked registers for each Mode and Stack Pointer for sl@0: // both current and User Modes sl@0: // - save MMU state: Control Register, TTB and Domain Access Control sl@0: // - Flush Dta Cache and drain Write Buffer sl@0: // - save Core Peripherals state: Interrupt Controller, Pin Function, Bus State and Clock settings sl@0: // SDRAM should be put in self refresh mode. Peripheral devices involved in detection of Wakeup events sl@0: // should be left powered. sl@0: // The Tick timer should be disabled and the current count of this and other System timers shall be sl@0: // saved. sl@0: // On wakeing up the state should be restored from the save state as above. SDRAM shall be brought back sl@0: // under CPU control, The Tick count shall be restored and timers re-enabled. sl@0: sl@0: // We assume that if a wakeup event occurrs when the CPU is in Standby, this will automatically sl@0: // bring it back from that state. Therefore we stop tracking wakeup events as the Power Manager will sl@0: // complete any pending notifications anyway. When the driver delivers its notification, we just ignore sl@0: // it. sl@0: iWakeupEventsOn = EFalse; // tracking of wakeup events is now done in hardware sl@0: } sl@0: else sl@0: { sl@0: Kern::Restart(0x80000000); sl@0: } sl@0: } sl@0: sl@0: //-/-/-/-/-/-/-/-/-/ class TTemplatePowerController /-/-/-/-/-/-/-/-/-/ sl@0: sl@0: EXPORT_C TemplateResourceManager* TTemplatePowerController::ResourceManager() sl@0: { sl@0: return &TheResourceManager; sl@0: } sl@0: sl@0: sl@0: EXPORT_C void TTemplatePowerController::WakeupEvent() sl@0: { sl@0: if(!iPowerController) sl@0: __PM_PANIC("Power Controller not present"); sl@0: else if(iPowerController->iWakeupEventsOn) sl@0: { sl@0: iPowerController->iWakeupEventsOn=EFalse; // one occurred, no longer track wakeup events sl@0: iPowerController->WakeupEvent(); sl@0: } sl@0: } sl@0: sl@0: //-/-/-/-/-/-/-/-/-/ class TemplateResourceManager /-/-/-/-/-/-/-/-/-/ sl@0: sl@0: void TemplateResourceManager::InitResources() sl@0: { sl@0: // sl@0: // TO DO: (optional) sl@0: // sl@0: // Initialise any power resources required by the platform and not initialised in the Bootstrap sl@0: // sl@0: } sl@0: sl@0: //-/-/-/-/-/-/-/-/-/ interface for shared resources /-/-/-/-/-/-/-/-/-/ sl@0: sl@0: void SharedBinaryResource1::Use() sl@0: { sl@0: NKern::Lock(); // lock Kernel as shared resource is likely to be modified from different threads sl@0: if (iCount++ == 0) sl@0: { sl@0: // sl@0: // TO DO: (optional) sl@0: // sl@0: // Modify hardware register bit or bits to switch the resource On. If the resource sl@0: // can be accessed from an ISR need to disable/enable interrupts around it. sl@0: // sl@0: NKern::Unlock(); sl@0: // sl@0: // TO DO: (optional) sl@0: // sl@0: // If the resource is asynchronous may need to sleep or block the thread until the change is complete sl@0: // sl@0: } sl@0: else sl@0: NKern::Unlock(); sl@0: } sl@0: sl@0: void SharedBinaryResource1::Release() sl@0: { sl@0: NKern::Lock(); sl@0: __PM_ASSERT(iCount); sl@0: if (--iCount == 0) sl@0: { sl@0: // sl@0: // TO DO: (optional) sl@0: // sl@0: // Modify hardware register bit or bits to switch the resource Off. If the resource sl@0: // can be accessed from an ISR need to disable/enable interrupts around it. sl@0: // sl@0: NKern::Unlock(); sl@0: // sl@0: // TO DO: (optional) sl@0: // sl@0: // If the resource is asynchronous may need to sleep or block the thread until the change is complete sl@0: // sl@0: } sl@0: else sl@0: NKern::Unlock(); sl@0: } sl@0: sl@0: TUint SharedBinaryResource1::GetCount() sl@0: { sl@0: return iCount; sl@0: } sl@0: sl@0: SharedMultilevelResource1::SharedMultilevelResource1() sl@0: // sl@0: // TO DO: (optional) sl@0: // sl@0: // May need to initialise current level and the Id of its owner if these have been initialised in the Bootstrap sl@0: // sl@0: // : iCurrentLevel(/* a level for this resource as initialised in the Bootstrap */), sl@0: // iCurrentLevelOwnerId(/* the Id of the requester of this resource that requires the initial value */) sl@0: { sl@0: } sl@0: sl@0: void SharedMultilevelResource1::IncreaseToLevel(TUint aLevel, TInt aRequester) sl@0: { sl@0: // sl@0: // Drivers should use this API if they wish to request a level higher than the previous level they required sl@0: // Drivers should keep track of the level they require and be disciplined sl@0: // sl@0: NKern::Lock(); sl@0: __PM_ASSERT(aLevel iCurrentLevel) // need to increase the level sl@0: { sl@0: // if(aLevel <= MAXLEVEL) sl@0: // aLevel = MAXLEVEL; sl@0: iCurrentLevel = aLevel; sl@0: iCurrentLevelOwnerId = aRequester; sl@0: // sl@0: // TO DO: (optional) sl@0: // sl@0: // Modify hardware register bits to set the level of the resource to aLevel sl@0: NKern::Unlock(); sl@0: // sl@0: // TO DO: (optional) sl@0: // sl@0: // If the resource is asynchronous may need to sleep or block the thread until the change is complete sl@0: // sl@0: } sl@0: else sl@0: NKern::Unlock(); sl@0: } sl@0: sl@0: void SharedMultilevelResource1::ReduceToLevel(TUint aLevel, TInt aRequester) sl@0: { sl@0: // sl@0: // Drivers should use this API if they wish to request a level higher than the previous level they required sl@0: // sl@0: NKern::Lock(); sl@0: __PM_ASSERT(aLevel>Levels[aRequester]); sl@0: sl@0: Levels[aRequester]=aLevel; sl@0: if(aLevel < iCurrentLevel && aRequester == iCurrentLevelOwnerId) // the holder of the current level as lowered its request sl@0: { sl@0: FindMaxLevel(&iCurrentLevel, &iCurrentLevelOwnerId); // find max level required and the ID of its holder sl@0: // sl@0: // TO DO: (optional) sl@0: // sl@0: // Modify hardware register bits to set the level of the resource to iCurrentLevel sl@0: NKern::Unlock(); sl@0: // sl@0: // TO DO: (optional) sl@0: // sl@0: // If the resource is asynchronous may need to sleep or block the thread until the change is complete sl@0: // sl@0: } sl@0: else sl@0: NKern::Unlock(); sl@0: } sl@0: sl@0: TUint SharedMultilevelResource1::GetResourceLevel() sl@0: { sl@0: return iCurrentLevel; sl@0: } sl@0: sl@0: void SharedMultilevelResource1::FindMaxLevel(TUint* aLevel, TInt* aId) sl@0: { sl@0: // sl@0: // TO DO: (optional) sl@0: // sl@0: // Place your clever array search algorithm here... sl@0: // return max level and id of owner sl@0: } sl@0: sl@0: TInt BinaryPowerInit(); // the Symbian example Battery Monitor and Power HAL handling sl@0: sl@0: GLDEF_C TInt KernelModuleEntry(TInt aReason) sl@0: { sl@0: if(aReason==KModuleEntryReasonVariantInit0) sl@0: { sl@0: // sl@0: // TO DO: (optional) sl@0: // sl@0: // Start the Resource Manager earlier so that Variant and other extension could make use of Power Resources sl@0: // sl@0: __KTRACE_OPT(KPOWER, Kern::Printf("Starting Template Resource controller")); sl@0: new(&TheResourceManager)TemplateResourceManager; sl@0: TheResourceManager.InitResources(); sl@0: return KErrNone; sl@0: } sl@0: else if(aReason==KModuleEntryReasonExtensionInit0) sl@0: { sl@0: __KTRACE_OPT(KPOWER, Kern::Printf("Starting Template power controller")); sl@0: // sl@0: // TO DO: (optional) sl@0: // sl@0: // Start the Kernel-side Battery Monitor and hook a Power HAL handling function. sl@0: // Symbian provides example code for both of the above in \e32\include\driver\binpower.h sl@0: // You may want to write your own versions. sl@0: // The call below starts the example Battery Monitor and hooks the example Power HAL handling function sl@0: // At the end we return an error to make sure that the entry point is not called again with sl@0: // KModuleEntryReasonExtensionInit1 (which would call the constructor of TheResourceManager again) sl@0: // sl@0: TInt r = BinaryPowerInit(); sl@0: if (r!= KErrNone) sl@0: __PM_PANIC("Can't initialise Binary Power model"); sl@0: DTemplatePowerController* c = new DTemplatePowerController(); sl@0: if(c) sl@0: return KErrGeneral; sl@0: else sl@0: __PM_PANIC("Can't create Power Controller"); sl@0: } sl@0: else if(aReason==KModuleEntryReasonExtensionInit1) sl@0: { sl@0: // does not get called... sl@0: } sl@0: return KErrArgument; sl@0: }