Loading...
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 | /* * Copyright (c) 2019 Microchip Technology Inc. * Copyright (c) 2016 Intel Corporation. * * SPDX-License-Identifier: Apache-2.0 */ #include <zephyr.h> #include <sys/sys_io.h> #include <sys/__assert.h> #include <pm/pm.h> #include <soc.h> #include "device_power.h" /* * Deep Sleep * Pros: * Lower power dissipation, 48MHz PLL is off * Cons: * Longer wake latency. CPU start running on ring oscillator * between 16 to 25 MHz. Minimum 3ms until PLL reaches lock * frequency of 48MHz. * * Implementation Notes: * We touch the Cortex-M's primary mask and base priority registers * because we do not want to enter an ISR immediately upon wake. * We must restore any hardware state that was modified upon sleep * entry before allowing interrupts to be serviced. Zephyr arch level * does not provide API's to manipulate both primary mask and base priority. * * DEBUG NOTES: * If a JTAG/SWD debug probe is connected driving TRST# high and * possibly polling the DUT then MEC1501 will not shut off its 48MHz * PLL. Firmware should not disable JTAG/SWD in the EC subsystem * while a probe is using the interface. This can leave the JTAG/SWD * TAP controller in a state of requesting clocks preventing the PLL * from being shut off. */ static void z_power_soc_deep_sleep(void) { /* Mask all exceptions and interrupts except NMI and HardFault */ __set_PRIMASK(1); soc_deep_sleep_periph_save(); soc_deep_sleep_enable(); soc_deep_sleep_wait_clk_idle(); soc_deep_sleep_non_wake_en(); /* * Unmask all interrupts in BASEPRI. PRIMASK is used above to * prevent entering an ISR after unmasking in BASEPRI. */ __set_BASEPRI(0); __DSB(); __WFI(); /* triggers sleep hardware */ __NOP(); __NOP(); soc_deep_sleep_disable(); soc_deep_sleep_non_wake_dis(); /* Wait for PLL to lock */ while ((PCR_REGS->OSC_ID & MCHP_PCR_OSC_ID_PLL_LOCK) == 0) { } soc_deep_sleep_periph_restore(); /* * pm_power_state_exit_post_ops() is not being called * after exiting deep sleep, so need to unmask exceptions * and interrupts here. */ __set_PRIMASK(0); } /* * Light Sleep * Pros: * Fast wake response: * Cons: * Higher power dissipation, 48MHz PLL remains on. */ static void z_power_soc_sleep(void) { __set_PRIMASK(1); soc_lite_sleep_enable(); __set_BASEPRI(0); /* Make sure wake interrupts are not masked! */ __DSB(); __WFI(); /* triggers sleep hardware */ __NOP(); __NOP(); } /* * Called from pm_system_suspend(int32_t ticks) in subsys/power.c * For deep sleep pm_system_suspend has executed all the driver * power management call backs. */ __weak void pm_power_state_set(struct pm_state_info info) { switch (info.state) { case PM_STATE_SUSPEND_TO_IDLE: z_power_soc_sleep(); break; case PM_STATE_SUSPEND_TO_RAM: z_power_soc_deep_sleep(); break; default: break; } } /* * Zephyr PM code expects us to enabled interrupts at post op exit. Zephyr used * arch_irq_lock() which sets BASEPRI to a non-zero value masking all interrupts * preventing wake. MCHP z_power_soc_(deep)_sleep sets PRIMASK=1 and BASEPRI=0 * allowing wake from any enabled interrupt and prevents the CPU from entering * an ISR on wake except for faults. We re-enable interrupts by setting PRIMASK * to 0. */ __weak void pm_power_state_exit_post_ops(struct pm_state_info info) { switch (info.state) { case PM_STATE_SUSPEND_TO_IDLE: case PM_STATE_SUSPEND_TO_RAM: __set_PRIMASK(0); __ISB(); break; default: irq_unlock(0); break; } } |