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