Updated to v0.95
[hackover2013-badge-firmware.git] / core / pmu / pmu.c
1 /**************************************************************************/
2 /*!
3 @file pmu.c
4 @author K. Townsend (microBuilder.eu)
5 @date 22 March 2010
6 @version 0.10
7
8 @section DESCRIPTION
9
10 Controls the power management features of the LPC1343, allowing you
11 to enter sleep/deep-sleep or deep power-down mode.
12
13 For examples of how to enter either mode, see the comments for the
14 functions pmuSleep(), pmuDeepSleep() and pmuPowerDown().
15
16 @section LICENSE
17
18 Software License Agreement (BSD License)
19
20 Copyright (c) 2010, microBuilder SARL
21 All rights reserved.
22
23 Redistribution and use in source and binary forms, with or without
24 modification, are permitted provided that the following conditions are met:
25 1. Redistributions of source code must retain the above copyright
26 notice, this list of conditions and the following disclaimer.
27 2. Redistributions in binary form must reproduce the above copyright
28 notice, this list of conditions and the following disclaimer in the
29 documentation and/or other materials provided with the distribution.
30 3. Neither the name of the copyright holders nor the
31 names of its contributors may be used to endorse or promote products
32 derived from this software without specific prior written permission.
33
34 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ''AS IS'' AND ANY
35 EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
36 WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
37 DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY
38 DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
39 (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
40 LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
41 ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
42 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
43 SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
44 */
45 /**************************************************************************/
46
47 #include "core/gpio/gpio.h"
48 #include "core/cpu/cpu.h"
49 #include "core/timer32/timer32.h"
50 #include "pmu.h"
51
52 #ifdef CFG_CHIBI
53 #include "drivers/chibi/chb_drvr.h"
54 #endif
55 #define PMU_WDTCLOCKSPEED_HZ 7812
56
57 void pmuSetupHW(void);
58 void pmuRestoreHW(void);
59
60
61 /**************************************************************************/
62 /*!
63 Wakeup interrupt handler
64 */
65 /**************************************************************************/
66 void WAKEUP_IRQHandler(void)
67 {
68 uint32_t regVal;
69
70 // Reconfigure system clock/PLL
71 cpuPllSetup(CPU_MULTIPLIER_6);
72
73 // Clear match bit on timer
74 TMR_TMR32B0EMR = 0;
75
76 // Clear pending bits
77 SCB_STARTRSRP0CLR = SCB_STARTRSRP0CLR_MASK;
78
79 // Clear SLEEPDEEP bit
80 SCB_SCR &= ~SCB_SCR_SLEEPDEEP;
81
82 // Disable the deep sleep timer
83 TMR_TMR32B0TCR = TMR_TMR32B0TCR_COUNTERENABLE_DISABLED;
84
85 /* This handler takes care of all the port pins if they
86 are configured as wakeup source. */
87 regVal = SCB_STARTSRP0;
88 if (regVal != 0)
89 {
90 SCB_STARTRSRP0CLR = regVal;
91 }
92
93 // Reconfigure CT32B0
94 timer32Init(0, TIMER32_DEFAULTINTERVAL);
95 timer32Enable(0);
96
97 // Perform peripheral specific and custom wakeup tasks
98 pmuRestoreHW();
99
100 /* See tracker for bug report. */
101 __asm volatile ("NOP");
102
103 return;
104 }
105
106 /**************************************************************************/
107 /*!
108 Setup the clock for the watchdog timer. The default is 7.8125kHz.
109 */
110 /**************************************************************************/
111 static void pmuWDTClockInit (void)
112 {
113 /* Enable WDT clock */
114 SCB_PDRUNCFG &= ~(SCB_PDRUNCFG_WDTOSC);
115
116 /* Configure watchdog clock */
117 /* Freq. = 0.5MHz, div = 64: WDT_OSC = 7.8125kHz */
118 SCB_WDTOSCCTRL = SCB_WDTOSCCTRL_FREQSEL_0_5MHZ |
119 SCB_WDTOSCCTRL_DIVSEL_DIV64;
120
121 // Switch main clock to WDT output
122 SCB_MAINCLKSEL = SCB_MAINCLKSEL_SOURCE_WDTOSC;
123 SCB_MAINCLKUEN = SCB_MAINCLKUEN_UPDATE; // Update clock source
124 SCB_MAINCLKUEN = SCB_MAINCLKUEN_DISABLE; // Toggle update register once
125 SCB_MAINCLKUEN = SCB_MAINCLKUEN_UPDATE;
126
127 // Wait until the clock is updated
128 while (!(SCB_MAINCLKUEN & SCB_MAINCLKUEN_UPDATE));
129 }
130
131 /**************************************************************************/
132 /*!
133 @brief Initialises the power management unit
134 */
135 /**************************************************************************/
136 void pmuInit( void )
137 {
138 /* Enable all clocks, even those turned off at power up. */
139 SCB_PDRUNCFG &= ~(SCB_PDRUNCFG_WDTOSC_MASK |
140 SCB_PDRUNCFG_SYSOSC_MASK |
141 SCB_PDRUNCFG_ADC_MASK);
142
143 return;
144 }
145
146 /**************************************************************************/
147 /*!
148 @brief Puts select peripherals in sleep mode.
149
150 This function will put the device into sleep mode. Most gpio pins
151 can be used to wake the device up, but the pins must first be
152 configured for this in pmuInit.
153
154 @section Example
155
156 @code
157 // Configure wakeup sources before going into sleep/deep-sleep.
158 // By default, pin 0.1 is configured as wakeup source (falling edge)
159 pmuInit();
160
161 // Enter sleep mode
162 pmuSleep();
163 @endcode
164 */
165 /**************************************************************************/
166 void pmuSleep()
167 {
168 SCB_PDAWAKECFG = SCB_PDRUNCFG;
169 __asm volatile ("WFI");
170 return;
171 }
172
173 /**************************************************************************/
174 /*!
175 @brief Turns off select peripherals and puts the device in deep-sleep
176 mode.
177
178 The device can be configured to wakeup from deep-sleep mode after a
179 specified delay by supplying a non-zero value to the wakeupSeconds
180 parameter. This will configure CT32B0 to toggle pin 0.1 (CT32B0_MAT2)
181 after x seconds, waking the device up. The timer will be configured
182 to run off the WDT OSC while in deep-sleep mode, meaning that WDTOSC
183 should not be powered off (using the sleepCtrl parameter) when a
184 wakeup delay is specified.
185
186 The sleepCtrl parameter is used to indicate which peripherals should
187 be put in sleep mode (see the SCB_PDSLEEPCFG register for details).
188
189 @param[in] sleepCtrl
190 The bits to set in the SCB_PDSLEEPCFG register. This
191 controls which peripherals will be put in sleep mode.
192 @param[in] wakeupSeconds
193 The number of seconds to wait until the device will
194 wakeup. If you do not wish to wakeup after a specific
195 delay, enter a value of 0.
196
197 @code
198 uint32_t pmuRegVal;
199
200 // Initialise power management unit
201 pmuInit();
202
203 // Put peripherals into sleep mode
204 pmuRegVal = SCB_PDSLEEPCFG_IRCOUT_PD |
205 SCB_PDSLEEPCFG_IRC_PD |
206 SCB_PDSLEEPCFG_FLASH_PD |
207 SCB_PDSLEEPCFG_USBPLL_PD |
208 SCB_PDSLEEPCFG_SYSPLL_PD |
209 SCB_PDSLEEPCFG_SYSOSC_PD |
210 SCB_PDSLEEPCFG_ADC_PD |
211 SCB_PDSLEEPCFG_BOD_PD;
212
213 // Enter deep sleep mode (wakeup after 5 seconds)
214 // By default, pin 0.1 is configured as wakeup source
215 pmuDeepSleep(pmuRegVal, 5);
216 @endcode
217 */
218 /**************************************************************************/
219 void pmuDeepSleep(uint32_t sleepCtrl, uint32_t wakeupSeconds)
220 {
221 // Setup the board for deep sleep mode, shutting down certain
222 // peripherals and remapping pins for lower power
223 pmuSetupHW();
224 SCB_PDAWAKECFG = SCB_PDRUNCFG;
225 sleepCtrl |= (1 << 9) | (1 << 11);
226 SCB_PDSLEEPCFG = sleepCtrl;
227 SCB_SCR |= SCB_SCR_SLEEPDEEP;
228
229 /* Configure system to run from WDT and set TMR32B0 for wakeup */
230 if (wakeupSeconds > 0)
231 {
232 // Make sure WDTOSC isn't disabled in PDSLEEPCFG
233 SCB_PDSLEEPCFG &= ~(SCB_PDSLEEPCFG_WDTOSC_PD);
234
235 // Disable 32-bit timer 0 if currently in use
236 TMR_TMR32B0TCR = TMR_TMR32B0TCR_COUNTERENABLE_DISABLED;
237
238 // Disable internal pullup on 0.1
239 gpioSetPullup(&IOCON_PIO0_1, gpioPullupMode_Inactive);
240
241 /* Enable the clock for CT32B0 */
242 SCB_SYSAHBCLKCTRL |= (SCB_SYSAHBCLKCTRL_CT32B0);
243
244 /* Configure 0.1 as Timer0_32 MAT2 */
245 IOCON_PIO0_1 &= ~IOCON_PIO0_1_FUNC_MASK;
246 IOCON_PIO0_1 |= IOCON_PIO0_1_FUNC_CT32B0_MAT2;
247
248 /* Set appropriate timer delay */
249 TMR_TMR32B0MR0 = PMU_WDTCLOCKSPEED_HZ * wakeupSeconds;
250
251 /* Configure match control register to raise an interrupt and reset on MR0 */
252 TMR_TMR32B0MCR |= (TMR_TMR32B0MCR_MR0_INT_ENABLED | TMR_TMR32B0MCR_MR0_RESET_ENABLED);
253
254 /* Configure external match register to set 0.1 high on match */
255 TMR_TMR32B0EMR &= ~(0xFF<<4); // Clear EMR config bits
256 TMR_TMR32B0EMR |= TMR_TMR32B0EMR_EMC2_HIGH; // Set MAT2 (0.1) high on match
257
258 /* Enable wakeup interrupts (any I/O pin can be used as a wakeup source) */
259 //NVIC_EnableIRQ(WAKEUP0_IRQn); // P0.0
260 NVIC_EnableIRQ(WAKEUP1_IRQn); // P0.1 (CT32B0_MAT2)
261 //NVIC_EnableIRQ(WAKEUP2_IRQn); // P0.2
262 //NVIC_EnableIRQ(WAKEUP3_IRQn); // P0.3
263 //NVIC_EnableIRQ(WAKEUP4_IRQn); // P0.4
264 //NVIC_EnableIRQ(WAKEUP5_IRQn); // P0.5
265 //NVIC_EnableIRQ(WAKEUP6_IRQn); // P0.6
266 //NVIC_EnableIRQ(WAKEUP7_IRQn); // P0.7
267 //NVIC_EnableIRQ(WAKEUP8_IRQn); // P0.8
268 //NVIC_EnableIRQ(WAKEUP9_IRQn); // P0.9
269 //NVIC_EnableIRQ(WAKEUP10_IRQn); // P0.10
270 //NVIC_EnableIRQ(WAKEUP11_IRQn); // P0.11
271 //NVIC_EnableIRQ(WAKEUP12_IRQn); // P1.0
272 //NVIC_EnableIRQ(WAKEUP13_IRQn); // P1.1
273 //NVIC_EnableIRQ(WAKEUP14_IRQn); // P1.2
274 //NVIC_EnableIRQ(WAKEUP15_IRQn); // P1.3
275 //NVIC_EnableIRQ(WAKEUP16_IRQn); // P1.4
276 //NVIC_EnableIRQ(WAKEUP17_IRQn); // P1.5
277 //NVIC_EnableIRQ(WAKEUP18_IRQn); // P1.6
278 //NVIC_EnableIRQ(WAKEUP19_IRQn); // P1.7
279 //NVIC_EnableIRQ(WAKEUP20_IRQn); // P1.8
280 //NVIC_EnableIRQ(WAKEUP21_IRQn); // P1.9
281 //NVIC_EnableIRQ(WAKEUP22_IRQn); // P1.10
282 //NVIC_EnableIRQ(WAKEUP23_IRQn); // P1.11
283 //NVIC_EnableIRQ(WAKEUP24_IRQn); // P2.0
284 //NVIC_EnableIRQ(WAKEUP25_IRQn); // P2.1
285 //NVIC_EnableIRQ(WAKEUP26_IRQn); // P2.2
286 //NVIC_EnableIRQ(WAKEUP27_IRQn); // P2.3
287 //NVIC_EnableIRQ(WAKEUP28_IRQn); // P2.4
288 //NVIC_EnableIRQ(WAKEUP29_IRQn); // P2.5
289 //NVIC_EnableIRQ(WAKEUP30_IRQn); // P2.6
290 //NVIC_EnableIRQ(WAKEUP31_IRQn); // P2.7
291 //NVIC_EnableIRQ(WAKEUP32_IRQn); // P2.8
292 //NVIC_EnableIRQ(WAKEUP33_IRQn); // P2.9
293 //NVIC_EnableIRQ(WAKEUP34_IRQn); // P2.10
294 //NVIC_EnableIRQ(WAKEUP35_IRQn); // P2.11
295 //NVIC_EnableIRQ(WAKEUP36_IRQn); // P3.0
296 //NVIC_EnableIRQ(WAKEUP37_IRQn); // P3.1
297 //NVIC_EnableIRQ(WAKEUP38_IRQn); // P3.2
298 //NVIC_EnableIRQ(WAKEUP39_IRQn); // P3.3
299
300 /* Use RISING EDGE for wakeup detection. */
301 SCB_STARTAPRP0 |= SCB_STARTAPRP0_APRPIO0_1;
302
303 /* Clear all wakeup sources */
304 SCB_STARTRSRP0CLR = SCB_STARTRSRP0CLR_MASK;
305
306 /* Enable Port 0.1 as wakeup source. */
307 SCB_STARTERP0 |= SCB_STARTERP0_ERPIO0_1;
308
309 // Reconfigure clock to run from WDTOSC
310 pmuWDTClockInit();
311
312 /* Start the timer */
313 TMR_TMR32B0TCR = TMR_TMR32B0TCR_COUNTERENABLE_ENABLED;
314 }
315
316 __asm volatile ("WFI");
317 return;
318 }
319
320 /**************************************************************************/
321 /*!
322 @brief Puts the device in deep power-down mode.
323
324 This function will configure the PMU control register and enter
325 deep power-down mode. Pre-determined values are stored in the four
326 general-purpose registers (PMU_GPREG0..3), which can be used to persist
327 any essential system settings while the device is in deep power-down
328 mode, so long as 3.3V is still available.
329
330 @warning The only way to wake a device up from deep power-down mode
331 is to set a low-level on P1.4. If 3.3V power is lost, the
332 values stored in the four general-purpose registers will
333 also be lost.
334
335 @section Example
336
337 @code
338 #include "core/cpu/cpu.h"
339 #include "core/pmu/pmu.h"
340
341 int main(void)
342 {
343 cpuInit();
344 pmuInit();
345
346 // Enter power-down mode
347 pmuPowerDown();
348
349 while(1)
350 {
351 // Device was woken up by WAKEUP pin
352 }
353 }
354 @endcode
355 */
356 /**************************************************************************/
357 void pmuPowerDown( void )
358 {
359 uint32_t regVal;
360
361 // Make sure HW and external devices are in low power mode
362 pmuSetupHW();
363
364 if ( (PMU_PMUCTRL & ((0x1<<8) | (PMU_PMUCTRL_DPDFLAG))) != 0x0 )
365 {
366 /* Check sleep and deep power down bits. If sleep and/or
367 deep power down mode are entered, clear the PCON bits. */
368 regVal = PMU_PMUCTRL;
369 regVal |= ((0x1<<8) |
370 (PMU_PMUCTRL_DPDEN_SLEEP) |
371 (PMU_PMUCTRL_DPDFLAG));
372 PMU_PMUCTRL = regVal;
373
374 if ( (PMU_GPREG0 != 0x12345678)||(PMU_GPREG1 != 0x87654321)
375 ||(PMU_GPREG2 != 0x56781234)||(PMU_GPREG3 != 0x43218765) )
376 {
377 while (1);
378 }
379 }
380 else
381 {
382 /* If in neither sleep nor deep-sleep mode, enter deep power down mode. */
383 PMU_GPREG0 = 0x12345678;
384 PMU_GPREG1 = 0x87654321;
385 PMU_GPREG2 = 0x56781234;
386 PMU_GPREG3 = 0x43218765;
387 SCB_SCR |= SCB_SCR_SLEEPDEEP;
388 PMU_PMUCTRL = PMU_PMUCTRL_DPDEN_DEEPPOWERDOWN;
389 __asm volatile ("WFI");
390 }
391 return;
392 }
393
394 /**************************************************************************/
395 /*!
396 @brief Configures parts and system peripherals to use lower power
397 before entering sleep mode
398 */
399 /**************************************************************************/
400 void pmuSetupHW(void)
401 {
402 #ifdef CFG_CHIBI
403 chb_sleep(TRUE);
404 #endif
405 }
406
407 /**************************************************************************/
408 /*!
409 @brief Restores parts and system peripherals to an appropriate
410 state after waking up from deep-sleep mode
411 */
412 /**************************************************************************/
413 void pmuRestoreHW(void)
414 {
415 #ifdef CFG_CHIBI
416 // Wakeup Chibi/Transceiver
417 chb_sleep(FALSE);
418 #endif
419 }
This page took 0.08046 seconds and 5 git commands to generate.