1 /******************************************************************************
2  *
3  * Copyright (C) 2022-2023 Maxim Integrated Products, Inc. (now owned by
4  * Analog Devices, Inc.),
5  * Copyright (C) 2023-2024 Analog Devices, Inc.
6  *
7  * Licensed under the Apache License, Version 2.0 (the "License");
8  * you may not use this file except in compliance with the License.
9  * You may obtain a copy of the License at
10  *
11  *     http://www.apache.org/licenses/LICENSE-2.0
12  *
13  * Unless required by applicable law or agreed to in writing, software
14  * distributed under the License is distributed on an "AS IS" BASIS,
15  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16  * See the License for the specific language governing permissions and
17  * limitations under the License.
18  *
19  ******************************************************************************/
20 
21 /* **** Includes **** */
22 #include <stdint.h>
23 #include <stddef.h>
24 #include "mxc_device.h"
25 #include "mxc_delay.h"
26 #include "mxc_sys.h"
27 
28 #ifdef __riscv
29 
MXC_Delay(uint32_t us)30 int MXC_Delay(uint32_t us)
31 {
32     // Check if there is nothing to do
33     if (us == 0) {
34         return E_NO_ERROR;
35     }
36 
37     // Calculate number of cycles needed.
38     uint32_t ticks = (MXC_SYS_RiscVClockRate() / 1000000) * us;
39 
40     CSR_SetPCMR(0); // Turn off counter
41     CSR_SetPCCR(0); // Clear counter register
42     CSR_SetPCER(1); // Enable counting of cycles
43     CSR_SetPCMR(3); // Turn on counter
44 
45     while (CSR_GetPCCR() < ticks) {
46         // Wait for counter to reach the tick count.
47     }
48     return E_NO_ERROR;
49 }
50 
MXC_DelayAsync(uint32_t us,mxc_delay_complete_t callback)51 int MXC_DelayAsync(uint32_t us, mxc_delay_complete_t callback)
52 {
53     return E_NOT_SUPPORTED;
54 }
55 
MXC_DelayCheck(void)56 int MXC_DelayCheck(void)
57 {
58     return E_NOT_SUPPORTED;
59 }
60 
MXC_DelayAbort(void)61 void MXC_DelayAbort(void) {}
62 
63 #else
64 
65 /* **** File Scope Variables **** */
66 static volatile int overflows = -1;
67 static uint32_t endtick;
68 static uint32_t ctrl_save;
69 static mxc_delay_complete_t cbFunc;
70 
71 static void MXC_DelayInit(uint32_t us);
72 extern void SysTick_Handler(void);
73 
74 /* ************************************************************************** */
SysTick_Handler(void)75 __weak void SysTick_Handler(void)
76 {
77     MXC_DelayHandler();
78 }
79 
80 /* ************************************************************************** */
MXC_DelayHandler(void)81 void MXC_DelayHandler(void)
82 {
83     // Check and clear overflow flag
84     if (SysTick->CTRL & SysTick_CTRL_COUNTFLAG_Msk) {
85         // Decrement overflow flag if delay is still ongoing
86         if (overflows > 0) {
87             overflows--;
88         } else {
89             MXC_DelayAbort();
90 
91             if (cbFunc != NULL) {
92                 cbFunc(E_NO_ERROR);
93                 cbFunc = NULL;
94             }
95         }
96     }
97 }
98 
99 /* ************************************************************************** */
MXC_DelayInit(uint32_t us)100 static void MXC_DelayInit(uint32_t us)
101 {
102     uint32_t starttick, reload, ticks, lastticks;
103 
104     // Record the current tick value and clear the overflow flag
105     starttick = SysTick->VAL;
106 
107     // Save the state of control register (and clear the overflow flag)
108     ctrl_save = SysTick->CTRL & ~SysTick_CTRL_COUNTFLAG_Msk;
109 
110     // If the SysTick is not running, configure and start it
111     if (!(SysTick->CTRL & SysTick_CTRL_ENABLE_Msk)) {
112         SysTick->LOAD = SysTick_LOAD_RELOAD_Msk;
113         SysTick->VAL = SysTick_VAL_CURRENT_Msk;
114         SysTick->CTRL = SysTick_CTRL_ENABLE_Msk | SysTick_CTRL_CLKSOURCE_Msk;
115         starttick = SysTick_VAL_CURRENT_Msk;
116         reload = SysTick_LOAD_RELOAD_Msk + 1;
117     } else {
118         reload = SysTick->LOAD + 1; // get the current reload value
119     }
120 
121     // Calculate the total number of ticks to delay
122     ticks = (uint32_t)(((uint64_t)us * (uint64_t)SystemCoreClock) / 1000000);
123 
124     // How many overflows of the SysTick will occur
125     overflows = ticks / reload;
126 
127     // How many remaining ticks after the last overflow
128     lastticks = ticks % reload;
129 
130     // Check if there will be another overflow due to the current value of the SysTick
131     if (lastticks >= starttick) {
132         overflows++;
133         endtick = reload - (lastticks - starttick);
134     } else {
135         endtick = starttick - lastticks;
136     }
137 }
138 
139 /* ************************************************************************** */
MXC_DelayAsync(uint32_t us,mxc_delay_complete_t callback)140 int MXC_DelayAsync(uint32_t us, mxc_delay_complete_t callback)
141 {
142     cbFunc = callback;
143 
144     // Check if timeout currently ongoing
145     if (overflows > 0) {
146         return E_BUSY;
147     }
148 
149     // Check if there is nothing to do
150     if (us == 0) {
151         return E_NO_ERROR;
152     }
153 
154     // Calculate the necessary delay and start the timer
155     MXC_DelayInit(us);
156 
157     // Enable SysTick interrupt if necessary
158     if (overflows > 0) {
159         SysTick->CTRL |= SysTick_CTRL_TICKINT_Msk;
160     }
161 
162     return E_NO_ERROR;
163 }
164 
165 /* ************************************************************************** */
MXC_DelayCheck(void)166 int MXC_DelayCheck(void)
167 {
168     // Check if timeout currently ongoing
169     if (overflows < 0) {
170         if (cbFunc != NULL) {
171             cbFunc(E_NO_ERROR);
172             cbFunc = NULL;
173         }
174 
175         return E_NO_ERROR;
176     }
177 
178     // Check the global values
179     if ((overflows == 0) && (SysTick->VAL <= endtick)) {
180         MXC_DelayAbort();
181 
182         if (cbFunc != NULL) {
183             cbFunc(E_NO_ERROR);
184             cbFunc = NULL;
185         }
186 
187         return E_NO_ERROR;
188     }
189 
190     return E_BUSY;
191 }
192 
193 /* ************************************************************************** */
MXC_DelayAbort(void)194 void MXC_DelayAbort(void)
195 {
196     if (cbFunc != NULL) {
197         cbFunc(E_ABORT);
198         cbFunc = NULL;
199     }
200 
201     SysTick->CTRL = ctrl_save;
202     overflows = -1;
203 }
204 
205 /* ************************************************************************** */
MXC_Delay(uint32_t us)206 int MXC_Delay(uint32_t us)
207 {
208     // Check if timeout currently ongoing
209     if (overflows > 0) {
210         return E_BUSY;
211     }
212 
213     // Check if there is nothing to do
214     if (us == 0) {
215         return E_NO_ERROR;
216     }
217 
218     // Calculate the necessary delay and start the timer
219     MXC_DelayInit(us);
220 
221     // Wait for the number of overflows
222     while (overflows > 0) {
223         // If SysTick interrupts are enabled, COUNTFLAG will never be set here and
224         // overflows will be decremented in the ISR. If SysTick interrupts are
225         // disabled, overflows is decremented here.
226         if (SysTick->CTRL & SysTick_CTRL_COUNTFLAG_Msk) {
227             overflows--;
228         }
229     }
230 
231     // Wait for the counter value
232     while (SysTick->VAL > endtick) {}
233 
234     MXC_DelayAbort();
235     return E_NO_ERROR;
236 }
237 
238 #endif // __riscv
239