|
1 // Copyright (c) 1994-2009 Nokia Corporation and/or its subsidiary(-ies). |
|
2 // All rights reserved. |
|
3 // This component and the accompanying materials are made available |
|
4 // under the terms of the License "Eclipse Public License v1.0" |
|
5 // which accompanies this distribution, and is available |
|
6 // at the URL "http://www.eclipse.org/legal/epl-v10.html". |
|
7 // |
|
8 // Initial Contributors: |
|
9 // Nokia Corporation - initial contribution. |
|
10 // |
|
11 // Contributors: |
|
12 // |
|
13 // Description: |
|
14 // template\template_variant\specific\power.cpp |
|
15 // Template Power Management |
|
16 // (see also variant.cpp for a discussion on Sleep modes and xyin.cpp for example |
|
17 // of usage of Resource Manager and Peripheral self power down and interaction |
|
18 // with Power Controller for Wakeup Events) |
|
19 // |
|
20 // |
|
21 |
|
22 #include "template_power.h" |
|
23 |
|
24 static TemplateResourceManager TheResourceManager; |
|
25 |
|
26 DTemplatePowerController* TTemplatePowerController::iPowerController; |
|
27 |
|
28 |
|
29 //-/-/-/-/-/-/-/-/-/ class DTemplatePowerController /-/-/-/-/-/-/-/-/-/ |
|
30 |
|
31 DTemplatePowerController::DTemplatePowerController() |
|
32 { |
|
33 Register(); // register Power Controller with Power Manager |
|
34 TTemplatePowerController::RegisterPowerController(this); |
|
35 } |
|
36 |
|
37 void DTemplatePowerController::CpuIdle() |
|
38 { |
|
39 Arch::TheAsic()->Idle(); |
|
40 } |
|
41 |
|
42 void DTemplatePowerController::EnableWakeupEvents() |
|
43 { |
|
44 // |
|
45 // TO DO: (mandatory) |
|
46 // |
|
47 // Enable tracking of wake-up events directly in hardware. If the hardware is controlled by a Driver |
|
48 // or Extension, may need to disable interrupts and preemption around the code that accesses the hardware |
|
49 // and set up a flag which the Driver/Extension code need to read before modifying the state of that piece |
|
50 // of hardware. Note in that case the Driver/Extension may need to link to this Library. |
|
51 // |
|
52 |
|
53 // |
|
54 // EXAMPLE ONLY |
|
55 // In this example we simply assume that the driver will call the Power Controller every time a |
|
56 // wakeup event occurr. It is up to the Power Controller to know if it is tracking them or not. |
|
57 // We also assume that if a wakeup event occurrs when the CPU is in Standby, this will automatically |
|
58 // bring it back from that state. |
|
59 iWakeupEventsOn = ETrue; // start tracking wakeup events |
|
60 } |
|
61 |
|
62 void DTemplatePowerController::DisableWakeupEvents() |
|
63 { |
|
64 // |
|
65 // TO DO: (mandatory) |
|
66 // |
|
67 // Disable tracking of wake-up events directly in hardware or if the hardware is controlled by a Driver or |
|
68 // Extension need to set up a flag which the Driver/Extension reads whenever the event occurs, in order to |
|
69 // find out if it needs to deliver notification to the Power Controller |
|
70 // |
|
71 iWakeupEventsOn = EFalse; // stop tracking wakeup events |
|
72 } |
|
73 |
|
74 void DTemplatePowerController::AbsoluteTimerExpired() |
|
75 { |
|
76 if (iTargetState == EPwStandby && iWakeupEventsOn) |
|
77 { |
|
78 iWakeupEventsOn = EFalse; // one occurred, no longer track wakeup events |
|
79 WakeupEvent(); |
|
80 } |
|
81 } |
|
82 |
|
83 void DTemplatePowerController::PowerDown(TTimeK aWakeupST) |
|
84 { |
|
85 if (iTargetState == EPwStandby) |
|
86 { |
|
87 // |
|
88 // TO DO: (mandatory) |
|
89 // |
|
90 // Converts between the Wakeup time in System Time units as passed in to this function and a Wakeup |
|
91 // time in RTC units. The following code is given as an example how to convert between System time units |
|
92 // RTC time units on a system with a 32 bit RTC timer and which is incremented on a second interval: |
|
93 // |
|
94 TUint32 wakeupRTC; |
|
95 if (aWakeupST) |
|
96 { |
|
97 TUint32 nowRTC = TTemplate::RtcData(); |
|
98 TTimeK nowST = Kern::SystemTime(); |
|
99 __KTRACE_OPT(KPOWER,Kern::Printf("system time: now = 0x%lx(us) wakeup = 0x%lx(us)", nowST, aWakeupST)); |
|
100 if (aWakeupST < nowST) |
|
101 return; |
|
102 Int64 deltaSecs = (aWakeupST - nowST) / 1000000; |
|
103 if (deltaSecs <= 0) |
|
104 return; |
|
105 if (deltaSecs + (Int64)nowRTC > (Int64)(KMaxTInt - 2)) |
|
106 wakeupRTC = (KMaxTInt - 2); // RTC can't wrap around during standby |
|
107 else |
|
108 wakeupRTC = nowRTC + deltaSecs; |
|
109 __KTRACE_OPT(KPOWER,Kern::Printf("RTC: now = %d(s) wakeup = %d(s)", nowRTC, wakeupRTC)); |
|
110 } |
|
111 else |
|
112 wakeupRTC = 0; |
|
113 // |
|
114 // TO DO: (optional) |
|
115 // |
|
116 // It then uses the calculated value to program the RTC to wakeup the System at the Wakeup |
|
117 // time ans sets the CPU and remaining hardware to go to the correponding low power mode. When the |
|
118 // state of the Core and Core Peripherals is not preserved in this mode the following is usually |
|
119 // required: |
|
120 // - save current Core state (current Mode, banked registers for each Mode and Stack Pointer for |
|
121 // both current and User Modes |
|
122 // - save MMU state: Control Register, TTB and Domain Access Control |
|
123 // - Flush Dta Cache and drain Write Buffer |
|
124 // - save Core Peripherals state: Interrupt Controller, Pin Function, Bus State and Clock settings |
|
125 // SDRAM should be put in self refresh mode. Peripheral devices involved in detection of Wakeup events |
|
126 // should be left powered. |
|
127 // The Tick timer should be disabled and the current count of this and other System timers shall be |
|
128 // saved. |
|
129 // On wakeing up the state should be restored from the save state as above. SDRAM shall be brought back |
|
130 // under CPU control, The Tick count shall be restored and timers re-enabled. |
|
131 |
|
132 // We assume that if a wakeup event occurrs when the CPU is in Standby, this will automatically |
|
133 // bring it back from that state. Therefore we stop tracking wakeup events as the Power Manager will |
|
134 // complete any pending notifications anyway. When the driver delivers its notification, we just ignore |
|
135 // it. |
|
136 iWakeupEventsOn = EFalse; // tracking of wakeup events is now done in hardware |
|
137 } |
|
138 else |
|
139 { |
|
140 Kern::Restart(0x80000000); |
|
141 } |
|
142 } |
|
143 |
|
144 //-/-/-/-/-/-/-/-/-/ class TTemplatePowerController /-/-/-/-/-/-/-/-/-/ |
|
145 |
|
146 EXPORT_C TemplateResourceManager* TTemplatePowerController::ResourceManager() |
|
147 { |
|
148 return &TheResourceManager; |
|
149 } |
|
150 |
|
151 |
|
152 EXPORT_C void TTemplatePowerController::WakeupEvent() |
|
153 { |
|
154 if(!iPowerController) |
|
155 __PM_PANIC("Power Controller not present"); |
|
156 else if(iPowerController->iWakeupEventsOn) |
|
157 { |
|
158 iPowerController->iWakeupEventsOn=EFalse; // one occurred, no longer track wakeup events |
|
159 iPowerController->WakeupEvent(); |
|
160 } |
|
161 } |
|
162 |
|
163 //-/-/-/-/-/-/-/-/-/ class TemplateResourceManager /-/-/-/-/-/-/-/-/-/ |
|
164 |
|
165 void TemplateResourceManager::InitResources() |
|
166 { |
|
167 // |
|
168 // TO DO: (optional) |
|
169 // |
|
170 // Initialise any power resources required by the platform and not initialised in the Bootstrap |
|
171 // |
|
172 } |
|
173 |
|
174 //-/-/-/-/-/-/-/-/-/ interface for shared resources /-/-/-/-/-/-/-/-/-/ |
|
175 |
|
176 void SharedBinaryResource1::Use() |
|
177 { |
|
178 NKern::Lock(); // lock Kernel as shared resource is likely to be modified from different threads |
|
179 if (iCount++ == 0) |
|
180 { |
|
181 // |
|
182 // TO DO: (optional) |
|
183 // |
|
184 // Modify hardware register bit or bits to switch the resource On. If the resource |
|
185 // can be accessed from an ISR need to disable/enable interrupts around it. |
|
186 // |
|
187 NKern::Unlock(); |
|
188 // |
|
189 // TO DO: (optional) |
|
190 // |
|
191 // If the resource is asynchronous may need to sleep or block the thread until the change is complete |
|
192 // |
|
193 } |
|
194 else |
|
195 NKern::Unlock(); |
|
196 } |
|
197 |
|
198 void SharedBinaryResource1::Release() |
|
199 { |
|
200 NKern::Lock(); |
|
201 __PM_ASSERT(iCount); |
|
202 if (--iCount == 0) |
|
203 { |
|
204 // |
|
205 // TO DO: (optional) |
|
206 // |
|
207 // Modify hardware register bit or bits to switch the resource Off. If the resource |
|
208 // can be accessed from an ISR need to disable/enable interrupts around it. |
|
209 // |
|
210 NKern::Unlock(); |
|
211 // |
|
212 // TO DO: (optional) |
|
213 // |
|
214 // If the resource is asynchronous may need to sleep or block the thread until the change is complete |
|
215 // |
|
216 } |
|
217 else |
|
218 NKern::Unlock(); |
|
219 } |
|
220 |
|
221 TUint SharedBinaryResource1::GetCount() |
|
222 { |
|
223 return iCount; |
|
224 } |
|
225 |
|
226 SharedMultilevelResource1::SharedMultilevelResource1() |
|
227 // |
|
228 // TO DO: (optional) |
|
229 // |
|
230 // May need to initialise current level and the Id of its owner if these have been initialised in the Bootstrap |
|
231 // |
|
232 // : iCurrentLevel(/* a level for this resource as initialised in the Bootstrap */), |
|
233 // iCurrentLevelOwnerId(/* the Id of the requester of this resource that requires the initial value */) |
|
234 { |
|
235 } |
|
236 |
|
237 void SharedMultilevelResource1::IncreaseToLevel(TUint aLevel, TInt aRequester) |
|
238 { |
|
239 // |
|
240 // Drivers should use this API if they wish to request a level higher than the previous level they required |
|
241 // Drivers should keep track of the level they require and be disciplined |
|
242 // |
|
243 NKern::Lock(); |
|
244 __PM_ASSERT(aLevel<Levels[aRequester]); |
|
245 Levels[aRequester]=aLevel; |
|
246 if(aLevel > iCurrentLevel) // need to increase the level |
|
247 { |
|
248 // if(aLevel <= MAXLEVEL) |
|
249 // aLevel = MAXLEVEL; |
|
250 iCurrentLevel = aLevel; |
|
251 iCurrentLevelOwnerId = aRequester; |
|
252 // |
|
253 // TO DO: (optional) |
|
254 // |
|
255 // Modify hardware register bits to set the level of the resource to aLevel |
|
256 NKern::Unlock(); |
|
257 // |
|
258 // TO DO: (optional) |
|
259 // |
|
260 // If the resource is asynchronous may need to sleep or block the thread until the change is complete |
|
261 // |
|
262 } |
|
263 else |
|
264 NKern::Unlock(); |
|
265 } |
|
266 |
|
267 void SharedMultilevelResource1::ReduceToLevel(TUint aLevel, TInt aRequester) |
|
268 { |
|
269 // |
|
270 // Drivers should use this API if they wish to request a level higher than the previous level they required |
|
271 // |
|
272 NKern::Lock(); |
|
273 __PM_ASSERT(aLevel>Levels[aRequester]); |
|
274 |
|
275 Levels[aRequester]=aLevel; |
|
276 if(aLevel < iCurrentLevel && aRequester == iCurrentLevelOwnerId) // the holder of the current level as lowered its request |
|
277 { |
|
278 FindMaxLevel(&iCurrentLevel, &iCurrentLevelOwnerId); // find max level required and the ID of its holder |
|
279 // |
|
280 // TO DO: (optional) |
|
281 // |
|
282 // Modify hardware register bits to set the level of the resource to iCurrentLevel |
|
283 NKern::Unlock(); |
|
284 // |
|
285 // TO DO: (optional) |
|
286 // |
|
287 // If the resource is asynchronous may need to sleep or block the thread until the change is complete |
|
288 // |
|
289 } |
|
290 else |
|
291 NKern::Unlock(); |
|
292 } |
|
293 |
|
294 TUint SharedMultilevelResource1::GetResourceLevel() |
|
295 { |
|
296 return iCurrentLevel; |
|
297 } |
|
298 |
|
299 void SharedMultilevelResource1::FindMaxLevel(TUint* aLevel, TInt* aId) |
|
300 { |
|
301 // |
|
302 // TO DO: (optional) |
|
303 // |
|
304 // Place your clever array search algorithm here... |
|
305 // return max level and id of owner |
|
306 } |
|
307 |
|
308 TInt BinaryPowerInit(); // the Symbian example Battery Monitor and Power HAL handling |
|
309 |
|
310 GLDEF_C TInt KernelModuleEntry(TInt aReason) |
|
311 { |
|
312 if(aReason==KModuleEntryReasonVariantInit0) |
|
313 { |
|
314 // |
|
315 // TO DO: (optional) |
|
316 // |
|
317 // Start the Resource Manager earlier so that Variant and other extension could make use of Power Resources |
|
318 // |
|
319 __KTRACE_OPT(KPOWER, Kern::Printf("Starting Template Resource controller")); |
|
320 new(&TheResourceManager)TemplateResourceManager; |
|
321 TheResourceManager.InitResources(); |
|
322 return KErrNone; |
|
323 } |
|
324 else if(aReason==KModuleEntryReasonExtensionInit0) |
|
325 { |
|
326 __KTRACE_OPT(KPOWER, Kern::Printf("Starting Template power controller")); |
|
327 // |
|
328 // TO DO: (optional) |
|
329 // |
|
330 // Start the Kernel-side Battery Monitor and hook a Power HAL handling function. |
|
331 // Symbian provides example code for both of the above in \e32\include\driver\binpower.h |
|
332 // You may want to write your own versions. |
|
333 // The call below starts the example Battery Monitor and hooks the example Power HAL handling function |
|
334 // At the end we return an error to make sure that the entry point is not called again with |
|
335 // KModuleEntryReasonExtensionInit1 (which would call the constructor of TheResourceManager again) |
|
336 // |
|
337 TInt r = BinaryPowerInit(); |
|
338 if (r!= KErrNone) |
|
339 __PM_PANIC("Can't initialise Binary Power model"); |
|
340 DTemplatePowerController* c = new DTemplatePowerController(); |
|
341 if(c) |
|
342 return KErrGeneral; |
|
343 else |
|
344 __PM_PANIC("Can't create Power Controller"); |
|
345 } |
|
346 else if(aReason==KModuleEntryReasonExtensionInit1) |
|
347 { |
|
348 // does not get called... |
|
349 } |
|
350 return KErrArgument; |
|
351 } |