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