1 /******************************************************************************
2 * Copyright (c) 2022 Telink Semiconductor (Shanghai) Co., Ltd. ("TELINK")
3 * All rights reserved.
4 *
5 * Licensed under the Apache License, Version 2.0 (the "License");
6 * you may not use this file except in compliance with the License.
7 * You may obtain a copy of the License at
8 *
9 * http://www.apache.org/licenses/LICENSE-2.0
10 *
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
16 *
17 *****************************************************************************/
18
19 #include <target_config.h>
20
21 #include <los_arch_timer.h>
22 #include <los_compiler.h>
23 #include <los_pm.h>
24 #include <los_reg.h>
25 #include <los_task.h>
26 #include <los_timer.h>
27
28 #include <B91/clock.h>
29 #include <B91/stimer.h>
30 #include <B91/sys.h>
31
32 #include <B91/ext_driver/ext_pm.h>
33
34 #include <stack/ble/ble.h>
35
36 #include <inttypes.h>
37
38 #define SYSTICKS_MAX_SLEEP (0xFFFFFFFF >> 2)
39 #define MTICKS_MIN_SLEEP (80)
40 #define MTICKS_CORRECTION_TIME (0)
41 #define MTICKS_RESERVE_TIME (1 + MTICKS_CORRECTION_TIME)
42
43 bool B91_system_suspend(UINT32 wake_stimer_tick);
44
MticksToSysticks(UINT64 mticks)45 static inline UINT64 MticksToSysticks(UINT64 mticks)
46 {
47 return (mticks * SYSTEM_TIMER_TICK_1S) / OS_SYS_CLOCK;
48 }
49
SysticksToMticks(UINT32 sticks)50 static inline UINT32 SysticksToMticks(UINT32 sticks)
51 {
52 return (UINT32)((UINT64)sticks * OS_SYS_CLOCK / SYSTEM_TIMER_TICK_1S);
53 }
54
55 static UINT32 B91Suspend(VOID);
56
57 static LosPmSysctrl g_sysctrl = {
58 .normalSuspend = B91Suspend,
59 };
60
SetMtime(UINT64 time)61 static inline void SetMtime(UINT64 time)
62 {
63 HalIrqDisable(RISCV_MACH_TIMER_IRQ);
64 WRITE_UINT32(U32_MAX, MTIMER + MTIMER_HI_OFFSET);
65 WRITE_UINT32((UINT32)time, MTIMER);
66 WRITE_UINT32((UINT32)(time >> SHIFT_32_BIT), MTIMER + MTIMER_HI_OFFSET);
67 HalIrqEnable(RISCV_MACH_TIMER_IRQ);
68 }
69
GetMtimeCompare(void)70 static inline UINT64 GetMtimeCompare(void)
71 {
72 return *(volatile UINT64 *)(MTIMERCMP);
73 }
74
GetMtime(void)75 static inline UINT64 GetMtime(void)
76 {
77 const volatile UINT32 *const rl = (const volatile UINT32 *const)MTIMER;
78 const volatile UINT32 *const rh = (const volatile UINT32 *const)(MTIMER + sizeof(UINT32));
79 UINT32 mtimeL, mtimeH;
80 do {
81 mtimeH = *rh;
82 mtimeL = *rl;
83 } while (mtimeH != *rh);
84 return (((UINT64)mtimeH) << SHIFT_32_BIT) | mtimeL;
85 }
86
87 /**
88 * @brief This function is used instead of the default sleep function
89 * ArchEnterSleep()
90 * @param[in] none.
91 * @return none.
92 */
B91Suspend(VOID)93 _attribute_ram_code_ static UINT32 B91Suspend(VOID)
94 {
95 UINT64 mcompare = GetMtimeCompare();
96 UINT64 mtick = GetMtime();
97 if ((mcompare - mtick) < (MTICKS_MIN_SLEEP + MTICKS_RESERVE_TIME)) {
98 return 0;
99 }
100
101 while (uart_tx_is_busy(UART0) || uart_tx_is_busy(UART1)) {
102 LOS_Msleep(1);
103 }
104
105 UINT32 intSave = LOS_IntLock();
106 mcompare = GetMtimeCompare();
107 mtick = GetMtime();
108 if ((mcompare - mtick) < MTICKS_MIN_SLEEP + MTICKS_RESERVE_TIME) {
109 LOS_IntRestore(intSave);
110 return 0;
111 }
112
113 UINT64 systicksSleepTimeout = MticksToSysticks(mcompare - mtick);
114 if (systicksSleepTimeout > SYSTICKS_MAX_SLEEP) {
115 systicksSleepTimeout = SYSTICKS_MAX_SLEEP;
116 }
117 blc_pm_setWakeupSource(PM_WAKEUP_PAD);
118
119 UINT32 sleepTick = stimer_get_tick();
120 if (B91_system_suspend(sleepTick + systicksSleepTimeout - MTICKS_RESERVE_TIME)) {
121 UINT32 span = SysticksToMticks(stimer_get_tick() - sleepTick) + MTICKS_CORRECTION_TIME;
122 mtick += span;
123 SetMtime(mtick);
124 uart_clr_tx_index(UART0);
125 uart_clr_tx_index(UART1);
126 uart_clr_rx_index(UART0);
127 uart_clr_rx_index(UART1);
128 }
129 LOS_IntRestore(intSave);
130 return 0;
131 }
132
B91SuspendSleepInit(VOID)133 VOID B91SuspendSleepInit(VOID)
134 {
135 UINT32 ret = LOS_PmRegister(LOS_PM_TYPE_SYSCTRL, &g_sysctrl);
136 if (ret != LOS_OK) {
137 printf("Ret of PMRegister = %#x\r\n", ret);
138 } else {
139 printf("\r\n B91_SLEEP_init\r\n");
140 }
141 }
142
B91_system_suspend(UINT32 wake_stimer_tick)143 _attribute_ram_code_ bool B91_system_suspend(UINT32 wake_stimer_tick)
144 {
145 bool result = false;
146
147 extern bool blc_ll_isBleTaskIdle(void);
148 if (!blc_ll_isBleTaskIdle()) {
149 blc_pm_setAppWakeupLowPower(wake_stimer_tick, 1);
150 if (!blc_pm_handler()) {
151 result = true;
152 }
153 blc_pm_setAppWakeupLowPower(0, 0);
154 } else {
155 cpu_sleep_wakeup_32k_rc(SUSPEND_MODE, PM_WAKEUP_TIMER | PM_WAKEUP_PAD, wake_stimer_tick);
156 result = true;
157 }
158 return result;
159 }