1
1
/*
2
2
* Copyright (c) 2018, Piotr Mienkowski
3
+ * Copyright (c) 2023, Antmicro <www.antmicro.com>
3
4
*
4
5
* SPDX-License-Identifier: Apache-2.0
5
6
*/
6
7
#include <zephyr/kernel.h>
8
+ #include <zephyr/logging/log.h>
7
9
#include <zephyr/pm/pm.h>
8
10
#include <em_emu.h>
9
11
10
- #include <zephyr/logging/log.h>
12
+ #include <zephyr/device.h>
13
+ #include <zephyr/drivers/counter.h>
14
+
11
15
LOG_MODULE_DECLARE (soc , CONFIG_SOC_LOG_LEVEL );
12
16
17
+ #ifdef CONFIG_SOC_GECKO_DEV_INIT
18
+ #include <sl_power_manager.h>
19
+
20
+ static const struct device * const counter_dev = DEVICE_DT_GET (DT_NODELABEL (stimer0 ));
21
+ static struct counter_alarm_cfg wakeup_cfg = {
22
+ .callback = NULL
23
+ };
24
+ #endif
25
+
13
26
/*
14
27
* Power state map:
15
28
* PM_STATE_RUNTIME_IDLE: EM1 Sleep
@@ -24,6 +37,52 @@ __weak void pm_state_set(enum pm_state state, uint8_t substate_id)
24
37
25
38
LOG_DBG ("SoC entering power state %d" , state );
26
39
40
+ #ifdef CONFIG_SOC_GECKO_DEV_INIT
41
+ sl_power_manager_em_t energy_mode ;
42
+
43
+ /* Save RTCC IRQ priority */
44
+ uint32_t rtcc_prio = NVIC_GetPriority (RTCC_IRQn );
45
+ /*
46
+ * When this function is entered the Kernel has disabled handling interrupts
47
+ * with priority other than 0. The Interrupt for the timer used to wake up
48
+ * the cpu has priority equal to 1. Manually set this priority to 0 so that
49
+ * cpu could exit sleep state.
50
+ *
51
+ * Note on priority value: set priority to -1 because z_arm_irq_priority_set
52
+ * offsets the priorities by 1.
53
+ * This function call will effectively set the priority to 0.
54
+ */
55
+ z_arm_irq_priority_set (RTCC_IRQn , -1 , 0 );
56
+
57
+ switch (state ) {
58
+
59
+ case PM_STATE_STANDBY :
60
+ energy_mode = SL_POWER_MANAGER_EM3 ;
61
+
62
+ /* Limit sleep level to given state */
63
+ sl_power_manager_add_em_requirement (energy_mode );
64
+
65
+ counter_start (counter_dev );
66
+ counter_set_channel_alarm (counter_dev , 0 , & wakeup_cfg );
67
+ sl_power_manager_sleep ();
68
+ k_cpu_idle ();
69
+ counter_stop (counter_dev );
70
+
71
+ /* Remove sleep level limit */
72
+ sl_power_manager_remove_em_requirement (energy_mode );
73
+ break ;
74
+
75
+ default :
76
+ LOG_DBG ("Unsupported power state %u" , state );
77
+ break ;
78
+ }
79
+
80
+ LOG_DBG ("SoC leaving power state %d" , state );
81
+
82
+ /* Restore RTCC IRQ priority */
83
+ z_arm_irq_priority_set (RTCC_IRQn , (rtcc_prio - 1 ), 0 );
84
+ #else
85
+
27
86
/* FIXME: When this function is entered the Kernel has disabled
28
87
* interrupts using BASEPRI register. This is incorrect as it prevents
29
88
* waking up from any interrupt which priority is not 0. Work around the
@@ -55,6 +114,8 @@ __weak void pm_state_set(enum pm_state state, uint8_t substate_id)
55
114
56
115
/* Clear PRIMASK */
57
116
__enable_irq ();
117
+
118
+ #endif
58
119
}
59
120
60
121
/* Handle SOC specific activity after Low Power Mode Exit */
@@ -63,3 +124,37 @@ __weak void pm_state_exit_post_ops(enum pm_state state, uint8_t substate_id)
63
124
ARG_UNUSED (state );
64
125
ARG_UNUSED (substate_id );
65
126
}
127
+
128
+ #if defined(CONFIG_SOC_GECKO_DEV_INIT ) && defined(CONFIG_PM_POLICY_CUSTOM )
129
+ /* CONFIG_PM_POLICY_CUSTOM allows us to set the next alarm to a specific number
130
+ * of ticks in the future. This is needed for the Gecko SleepTimer to wake up
131
+ * the device properly.
132
+ */
133
+ static const struct pm_state_info pm_min_residency [] =
134
+ PM_STATE_INFO_LIST_FROM_DT_CPU (DT_NODELABEL (cpu0 ));
135
+ struct pm_state_info pm_state_active = {PM_STATE_ACTIVE , 0 , 0 , 0 };
136
+
137
+ struct pm_state_info * pm_policy_next_state (uint8_t cpu , int32_t ticks )
138
+ {
139
+ int i ;
140
+
141
+ for (i = ARRAY_SIZE (pm_min_residency ) - 1 ; i >= 0 ; i -- ) {
142
+
143
+ if ((ticks == K_TICKS_FOREVER ) ||
144
+ (ticks >= k_us_to_ticks_ceil32 (
145
+ pm_min_residency [i ].min_residency_us ))) {
146
+ LOG_DBG ("Selected power state %d "
147
+ "(ticks: %d, min_residency: %u)" ,
148
+ pm_min_residency [i ].state , ticks ,
149
+ pm_min_residency [i ].min_residency_us );
150
+
151
+ /* Busy waiting for 1 tick to flush UART buffer */
152
+ k_busy_wait (k_ticks_to_us_floor32 (1 ));
153
+ wakeup_cfg .ticks = ticks ;
154
+
155
+ return ((struct pm_state_info * ) & pm_min_residency [i ]);
156
+ }
157
+ }
158
+ return (struct pm_state_info * )(& pm_state_active );
159
+ }
160
+ #endif
0 commit comments