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 follwoing 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 <arch/arm/aarch32/cortex_m/cmsis.h>
49 #include <zephyr.h>
50 #include <drivers/espi.h>
51 #include <pm/pm.h>
52 #include <soc.h>
53
54 #include "soc_host.h"
55 #include "soc_power.h"
56
57 #include <logging/log.h>
58 LOG_MODULE_DECLARE(soc, CONFIG_SOC_LOG_LEVEL);
59
60 /* The steps that npcx ec enters sleep/deep mode and leaves it. */
61 #define NPCX_ENTER_SYSTEM_SLEEP() ({ \
62 __asm__ volatile ( \
63 "push {r0-r5}\n" /* Save the registers used for delay */ \
64 "wfi\n" /* Enter sleep mode after receiving wfi */ \
65 "ldm %0, {r0-r5}\n" /* Add a delay before instructions fetching */ \
66 "pop {r0-r5}\n" /* Restore the registers used for delay */ \
67 "isb\n" /* Flush the cpu pipelines */ \
68 :: "r" (CONFIG_SRAM_BASE_ADDRESS)); /* A valid addr used for delay */ \
69 })
70
71 /* Variables for tracing */
72 static uint32_t cnt_sleep0;
73 static uint32_t cnt_sleep1;
74
75 /* Supported sleep mode in npcx series */
76 enum {
77 NPCX_SLEEP,
78 NPCX_DEEP_SLEEP,
79 };
80
81 /* Supported wake-up mode in npcx series */
82 enum {
83 NPCX_INSTANT_WAKE_UP,
84 NPCX_STANDARD_WAKE_UP,
85 };
86
87 #ifdef CONFIG_UART_CONSOLE_INPUT_EXPIRED
88 static int64_t expired_timeout = CONFIG_UART_CONSOLE_INPUT_EXPIRED_TIMEOUT;
89 static int64_t console_expired_time = CONFIG_UART_CONSOLE_INPUT_EXPIRED_TIMEOUT;
90
91 /* Platform specific power control functions */
npcx_power_console_is_in_use(void)92 bool npcx_power_console_is_in_use(void)
93 {
94 return (k_uptime_get() < console_expired_time);
95 }
96
npcx_power_console_is_in_use_refresh(void)97 void npcx_power_console_is_in_use_refresh(void)
98 {
99 console_expired_time = k_uptime_get() + expired_timeout;
100 }
101
npcx_power_set_console_in_use_timeout(int64_t timeout)102 void npcx_power_set_console_in_use_timeout(int64_t timeout)
103 {
104 expired_timeout = timeout;
105 }
106 #endif
107
npcx_power_enter_system_sleep(int slp_mode,int wk_mode)108 static void npcx_power_enter_system_sleep(int slp_mode, int wk_mode)
109 {
110 /* Disable interrupts */
111 __disable_irq();
112
113 /*
114 * Disable priority mask temporarily to make sure that wake-up events
115 * are visible to the WFI instruction.
116 */
117 __set_BASEPRI(0);
118
119 /* Configure sleep/deep sleep settings in clock control module. */
120 npcx_clock_control_turn_on_system_sleep(slp_mode == NPCX_DEEP_SLEEP,
121 wk_mode == NPCX_INSTANT_WAKE_UP);
122
123 /* A bypass in npcx7 series to prevent leakage in low-voltage pads */
124 if (IS_ENABLED(CONFIG_SOC_SERIES_NPCX7)) {
125 npcx_lvol_suspend_io_pads();
126 }
127
128 /* Turn on eSPI/LPC host access wake-up interrupt. */
129 if (IS_ENABLED(CONFIG_ESPI_NPCX)) {
130 npcx_host_enable_access_interrupt();
131 }
132
133 /* Turn on UART RX wake-up interrupt. */
134 if (IS_ENABLED(CONFIG_UART_NPCX)) {
135 npcx_uart_enable_access_interrupt();
136 }
137
138 /*
139 * Capture the reading of low-freq timer for compensation before ec
140 * enters system sleep mode.
141 */
142 npcx_clock_capture_low_freq_timer();
143
144 /* Enter system sleep mode */
145 NPCX_ENTER_SYSTEM_SLEEP();
146
147 /*
148 * Compensate system timer by the elasped time of low-freq timer during
149 * system sleep mode.
150 */
151 npcx_clock_compensate_system_timer();
152
153 /* Turn off eSPI/LPC host access wake-up interrupt. */
154 if (IS_ENABLED(CONFIG_ESPI_NPCX)) {
155 npcx_host_disable_access_interrupt();
156 }
157
158 /* A bypass in npcx7 series to prevent leakage in low-voltage pads */
159 if (IS_ENABLED(CONFIG_SOC_SERIES_NPCX7)) {
160 npcx_lvol_restore_io_pads();
161 }
162
163 /* Turn off system sleep mode. */
164 npcx_clock_control_turn_off_system_sleep();
165 }
166
167 /* Invoke when enter "Suspend/Low Power" mode. */
pm_power_state_set(struct pm_state_info info)168 __weak void pm_power_state_set(struct pm_state_info info)
169 {
170 if (info.state != PM_STATE_SUSPEND_TO_IDLE) {
171 LOG_DBG("Unsupported power state %u", info.state);
172 } else {
173 switch (info.substate_id) {
174 case 0: /* Sub-state 0: Deep sleep with instant wake-up */
175 npcx_power_enter_system_sleep(NPCX_DEEP_SLEEP,
176 NPCX_INSTANT_WAKE_UP);
177 if (IS_ENABLED(CONFIG_SOC_POWER_MANAGEMENT_TRACE)) {
178 cnt_sleep0++;
179 }
180 break;
181 case 1: /* Sub-state 1: Deep sleep with standard wake-up */
182 npcx_power_enter_system_sleep(NPCX_DEEP_SLEEP,
183 NPCX_STANDARD_WAKE_UP);
184 if (IS_ENABLED(CONFIG_SOC_POWER_MANAGEMENT_TRACE)) {
185 cnt_sleep1++;
186 }
187 break;
188 default:
189 LOG_DBG("Unsupported power substate-id %u",
190 info.substate_id);
191 break;
192 }
193 }
194 }
195
196 /* Handle soc specific activity after exiting "Suspend/Low Power" mode. */
pm_power_state_exit_post_ops(struct pm_state_info info)197 __weak void pm_power_state_exit_post_ops(struct pm_state_info info)
198 {
199 if (info.state != PM_STATE_SUSPEND_TO_IDLE) {
200 LOG_DBG("Unsupported power state %u", info.state);
201 } else {
202 switch (info.substate_id) {
203 case 0: /* Sub-state 0: Deep sleep with instant wake-up */
204 /* Restore interrupts */
205 __enable_irq();
206 break;
207 case 1: /* Sub-state 1: Deep sleep with standard wake-up */
208 /* Restore interrupts */
209 __enable_irq();
210 break;
211 default:
212 LOG_DBG("Unsupported power substate-id %u",
213 info.substate_id);
214 break;
215 }
216 }
217
218 if (IS_ENABLED(CONFIG_SOC_POWER_MANAGEMENT_TRACE)) {
219 LOG_DBG("sleep: %d, deep sleep: %d", cnt_sleep0, cnt_sleep1);
220 LOG_INF("total ticks in sleep: %lld",
221 npcx_clock_get_sleep_ticks());
222 }
223 }
224