1 /*
2  * Copyright (c) 2021 Nuvoton Technology Corporation.
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  */
6 
7 /**
8  * @file
9  * @brief Nuvoton NPCX power management driver
10  *
11  * This file contains the drivers of NPCX Power Manager Modules that improves
12  * the efficiency of ec operation by adjusting the chip’s power consumption to
13  * the level of activity required by the application. The following table
14  * summarizes the main properties of the various power states and shows the
15  * activity levels of the various clocks while in these power states.
16  *
17  * +--------------------------------------------------------------------------+
18  * | Power State | LFCLK | HFCLK | APB/AHB |  Core  | RAM/Regs   | VCC | VSBY |
19  * |--------------------------------------------------------------------------|
20  * | Active      | On    | On    | On      | Active | Active     | On  | On   |
21  * | Idle (wfi)  | On    | On    | On      | Wait   | Active     | On  | On   |
22  * | Sleep       | On    | On    | Stop    | Stop   | Preserved  | On  | On   |
23  * | Deep Sleep  | On    | Stop  | Stop    | Stop   | Power Down | On  | On   |
24  * | Stand-By    | Off   | Off   | Off     | Off    | Off        | Off | On   |
25  * +--------------------------------------------------------------------------+
26  *
27  * LFCLK - Low-Frequency Clock. Its frequency is fixed to 32kHz.
28  * HFCLK - High-Frequency (PLL) Clock. Its frequency is configured to OFMCLK.
29  *
30  * Based on the following criteria:
31  *
32  * - A delay of 'Instant' wake-up from 'Deep Sleep' is 20 us.
33  * - A delay of 'Standard' wake-up from 'Deep Sleep' is 3.43 ms.
34  * - Max residency time in Deep Sleep for 'Instant' wake-up is 200 ms
35  * - Min Residency time in Deep Sleep for 'Instant' wake-up is 61 us
36  * - The unit to determine power state residency policy is tick.
37  *
38  * this driver implements one power state, PM_STATE_SUSPEND_TO_IDLE, with
39  * two sub-states for power management system.
40  * Sub-state 0 - "Deep Sleep" mode with “Instant” wake-up if residency time
41  *               is greater or equal to 1 ms
42  * Sub-state 1 - "Deep Sleep" mode with "Standard" wake-up if residency time
43  *               is greater or equal to 201 ms
44  *
45  * INCLUDE FILES: soc_clock.h
46  */
47 
48 #include <cmsis_core.h>
49 #include <zephyr/kernel.h>
50 #include <zephyr/drivers/gpio.h>
51 #include <zephyr/drivers/espi.h>
52 #include <zephyr/pm/pm.h>
53 #include <soc.h>
54 
55 #include "soc_gpio.h"
56 #include "soc_host.h"
57 #include "soc_power.h"
58 
59 #include <zephyr/logging/log.h>
60 LOG_MODULE_DECLARE(soc, CONFIG_SOC_LOG_LEVEL);
61 
62 /* The steps that npcx ec enters sleep/deep mode and leaves it. */
63 #define NPCX_ENTER_SYSTEM_SLEEP() ({                                           \
64 	__asm__ volatile (                                                     \
65 	"push {r0-r5}\n"     /* Save the registers used for delay */           \
66 	"wfi\n"              /* Enter sleep mode after receiving wfi */        \
67 	"ldm %0, {r0-r5}\n"  /* Add a delay before instructions fetching */    \
68 	"pop {r0-r5}\n"      /* Restore the registers used for delay */        \
69 	"isb\n"              /* Flush the cpu pipelines */                     \
70 	:: "r" (CONFIG_SRAM_BASE_ADDRESS)); /* A valid addr used for delay */  \
71 	})
72 
73 /* Variables for tracing */
74 static uint32_t cnt_sleep0;
75 static uint32_t cnt_sleep1;
76 
77 /* Supported sleep mode in npcx series */
78 enum {
79 	NPCX_SLEEP,
80 	NPCX_DEEP_SLEEP,
81 };
82 
83 /* Supported wake-up mode in npcx series */
84 enum {
85 	NPCX_INSTANT_WAKE_UP,
86 	NPCX_STANDARD_WAKE_UP,
87 };
88 
89 #define NODE_LEAKAGE_IO DT_INST(0, nuvoton_npcx_leakage_io)
90 #if DT_NODE_HAS_PROP(NODE_LEAKAGE_IO, leak_gpios)
91 struct npcx_leak_gpio {
92 	const struct device *gpio;
93 	gpio_pin_t pin;
94 };
95 
96 #define NPCX_POWER_LEAKAGE_IO_INIT(node_id, prop, idx)	{		\
97 	.gpio = DEVICE_DT_GET(DT_GPIO_CTLR_BY_IDX(node_id, prop, idx)),	\
98 	.pin = DT_GPIO_PIN_BY_IDX(node_id, prop, idx),			\
99 },
100 
101 /*
102  * Get io array which have leakage current from 'leak-gpios' property of
103  * 'power_leakage_io' DT node. User can overwrite this prop. at board DT file to
104  * save power consumption when ec enter deep sleep.
105  *
106  * &power_leakage_io {
107  *       leak-gpios = <&gpio0 0 0
108  *                    &gpiob 1 0>;
109  * };
110  */
111 static struct npcx_leak_gpio leak_gpios[] = {
112 	DT_FOREACH_PROP_ELEM(NODE_LEAKAGE_IO, leak_gpios, NPCX_POWER_LEAKAGE_IO_INIT)
113 };
114 
npcx_power_suspend_leak_io_pads(void)115 static void npcx_power_suspend_leak_io_pads(void)
116 {
117 	for (int i = 0; i < ARRAY_SIZE(leak_gpios); i++) {
118 		npcx_gpio_disable_io_pads(leak_gpios[i].gpio, leak_gpios[i].pin);
119 	}
120 }
121 
npcx_power_restore_leak_io_pads(void)122 static void npcx_power_restore_leak_io_pads(void)
123 {
124 	for (int i = 0; i < ARRAY_SIZE(leak_gpios); i++) {
125 		npcx_gpio_enable_io_pads(leak_gpios[i].gpio, leak_gpios[i].pin);
126 	}
127 }
128 #else
npcx_power_suspend_leak_io_pads(void)129 void npcx_power_suspend_leak_io_pads(void)
130 {
131 	/* do nothing */
132 }
133 
npcx_power_restore_leak_io_pads(void)134 void npcx_power_restore_leak_io_pads(void)
135 {
136 	/* do nothing */
137 }
138 #endif /* DT_NODE_HAS_PROP(NODE_LEAKAGE_IO, leak_gpios) */
139 
npcx_power_enter_system_sleep(int slp_mode,int wk_mode)140 static void npcx_power_enter_system_sleep(int slp_mode, int wk_mode)
141 {
142 	/* Disable interrupts */
143 	__disable_irq();
144 
145 	/*
146 	 * Disable priority mask temporarily to make sure that wake-up events
147 	 * are visible to the WFI instruction.
148 	 */
149 	__set_BASEPRI(0);
150 
151 	/* Configure sleep/deep sleep settings in clock control module. */
152 	npcx_clock_control_turn_on_system_sleep(slp_mode == NPCX_DEEP_SLEEP,
153 					wk_mode == NPCX_INSTANT_WAKE_UP);
154 
155 	/*
156 	 * Disable the connection between io pads that have leakage current and
157 	 * input buffer to save power consumption.
158 	 */
159 	npcx_power_suspend_leak_io_pads();
160 
161 	/* Turn on eSPI/LPC host access wake-up interrupt. */
162 	if (IS_ENABLED(CONFIG_ESPI_NPCX)) {
163 		npcx_host_enable_access_interrupt();
164 	}
165 
166 	/* Turn on UART RX wake-up interrupt. */
167 	if (IS_ENABLED(CONFIG_UART_NPCX)) {
168 		npcx_uart_enable_access_interrupt();
169 	}
170 
171 	/*
172 	 * Capture the reading of low-freq timer for compensation before ec
173 	 * enters system sleep mode.
174 	 */
175 	npcx_clock_capture_low_freq_timer();
176 
177 	/* Enter system sleep mode */
178 	NPCX_ENTER_SYSTEM_SLEEP();
179 
180 	/*
181 	 * Compensate system timer by the elapsed time of low-freq timer during
182 	 * system sleep mode.
183 	 */
184 	npcx_clock_compensate_system_timer();
185 
186 	/* Turn off eSPI/LPC host access wake-up interrupt. */
187 	if (IS_ENABLED(CONFIG_ESPI_NPCX)) {
188 		npcx_host_disable_access_interrupt();
189 	}
190 
191 	/*
192 	 * Restore the connection between io pads that have leakage current and
193 	 * input buffer.
194 	 */
195 	npcx_power_restore_leak_io_pads();
196 
197 	/* Turn off system sleep mode. */
198 	npcx_clock_control_turn_off_system_sleep();
199 }
200 
201 /* Invoke when enter "Suspend/Low Power" mode. */
pm_state_set(enum pm_state state,uint8_t substate_id)202 void pm_state_set(enum pm_state state, uint8_t substate_id)
203 {
204 	if (state != PM_STATE_SUSPEND_TO_IDLE) {
205 		LOG_DBG("Unsupported power state %u", state);
206 	} else {
207 		switch (substate_id) {
208 		case 0:	/* Sub-state 0: Deep sleep with instant wake-up */
209 			npcx_power_enter_system_sleep(NPCX_DEEP_SLEEP,
210 							NPCX_INSTANT_WAKE_UP);
211 			if (IS_ENABLED(CONFIG_NPCX_PM_TRACE)) {
212 				cnt_sleep0++;
213 			}
214 			break;
215 		case 1:	/* Sub-state 1: Deep sleep with standard wake-up */
216 			npcx_power_enter_system_sleep(NPCX_DEEP_SLEEP,
217 							NPCX_STANDARD_WAKE_UP);
218 			if (IS_ENABLED(CONFIG_NPCX_PM_TRACE)) {
219 				cnt_sleep1++;
220 			}
221 			break;
222 		default:
223 			LOG_DBG("Unsupported power substate-id %u",
224 				substate_id);
225 			break;
226 		}
227 	}
228 }
229 
230 /* Handle soc specific activity after exiting "Suspend/Low Power" mode. */
pm_state_exit_post_ops(enum pm_state state,uint8_t substate_id)231 void pm_state_exit_post_ops(enum pm_state state, uint8_t substate_id)
232 {
233 	if (state != PM_STATE_SUSPEND_TO_IDLE) {
234 		LOG_DBG("Unsupported power state %u", state);
235 	} else {
236 		switch (substate_id) {
237 		case 0:	/* Sub-state 0: Deep sleep with instant wake-up */
238 			/* Restore interrupts */
239 			__enable_irq();
240 			break;
241 		case 1:	/* Sub-state 1: Deep sleep with standard wake-up */
242 			/* Restore interrupts */
243 			__enable_irq();
244 			break;
245 		default:
246 			LOG_DBG("Unsupported power substate-id %u",
247 				substate_id);
248 			break;
249 		}
250 	}
251 
252 	if (IS_ENABLED(CONFIG_NPCX_PM_TRACE)) {
253 		LOG_DBG("sleep: %d, deep sleep: %d", cnt_sleep0, cnt_sleep1);
254 		LOG_INF("total ticks in sleep: %lld",
255 			npcx_clock_get_sleep_ticks());
256 	}
257 }
258