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