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)40 static 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)87 static 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)105 void 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)129 void 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