1 /*
2  * Copyright (c) 2024, Texas Instruments Incorporated
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  */
6 
7 
8 #include <zephyr/kernel.h>
9 #include <zephyr/sys/__assert.h>
10 #include <kernel/zephyr/dpl/dpl.h>
11 #include <ti/drivers/dpl/ClockP.h>
12 #include <ti/drivers/dpl/HwiP.h>
13 
14 /* Driverlib includes*/
15 #include <ti/devices/DeviceFamily.h>
16 #include DeviceFamily_constructPath(inc/hw_types.h)
17 #include DeviceFamily_constructPath(inc/hw_memmap.h)
18 #include DeviceFamily_constructPath(inc/hw_systim.h)
19 
20 /** Max number of ClockP ticks into the future supported by this ClockP
21  * implementation.
22  *
23  * Under the hood, ClockP uses the SysTimer whose events trigger immediately if
24  * the compare value is less than 2^22 systimer ticks in the past
25  * (4.194sec at 1us resolution). Therefore, the max number of SysTimer ticks you
26  * can schedule into the future is 2^32 - 2^22 - 1 ticks (~= 4290 sec at 1us
27  * resolution). */
28 #define ClockP_PERIOD_MAX     (0xFFBFFFFFU / ClockP_TICK_PERIOD)
29 /** Max number of seconds into the future supported by this ClockP
30  * implementation.
31  *
32  * This limit affects ClockP_sleep() */
33 #define ClockP_PERIOD_MAX_SEC 4290U
34 
35 /* Get the current ClockP tick value */
36 #define getClockPTick() (HWREG(SYSTIM_BASE + SYSTIM_O_TIME1U) / ClockP_TICK_PERIOD)
37 
38 /*
39  * ClockP_STRUCT_SIZE in ClockP.h must be updated to match the size of this
40  * struct
41  */
42 typedef struct _ClockP_Obj
43 {
44     struct k_timer timer;
45     ClockP_Fxn clock_fxn;
46     uintptr_t arg;
47     uint32_t timeout;
48     uint32_t period;
49     bool active;
50 } ClockP_Obj;
51 
52 static ClockP_Params ClockP_defaultParams = {
53     .startFlag = false,
54     .period    = 0,
55     .arg       = 0,
56 };
57 
expiry_fxn(struct k_timer * timer_id)58 static void expiry_fxn(struct k_timer *timer_id)
59 {
60     ClockP_Obj *obj = (ClockP_Obj *)k_timer_user_data_get(timer_id);
61 
62     obj->clock_fxn(obj->arg);
63 }
64 
65 #ifdef CONFIG_DYNAMIC_DPL_OBJECTS
66 
67 /* We can't easily dynamically allocate kernel objects so we use memory slabs */
68 #define DPL_MAX_CLOCKS 5
69 K_MEM_SLAB_DEFINE(clock_slab, sizeof(ClockP_Obj), DPL_MAX_CLOCKS,\
70           MEM_ALIGN);
71 
dpl_clock_pool_alloc()72 static ClockP_Obj *dpl_clock_pool_alloc()
73 {
74     ClockP_Obj  *clock_ptr = NULL;
75 
76     if (k_mem_slab_alloc(&clock_slab, (void **)&clock_ptr, K_NO_WAIT) < 0) {
77 
78          __ASSERT(0, "Increase size of DPL clock pool");
79     }
80     return clock_ptr;
81 }
82 
dpl_clock_pool_free(ClockP_Obj * clock)83 static void dpl_clock_pool_free(ClockP_Obj *clock)
84 {
85     k_mem_slab_free(&clock_slab, (void *)&clock);
86 
87     return;
88 }
89 
90 /*
91  *  ======== ClockP_create ========
92  */
ClockP_create(ClockP_Fxn clkFxn,uint32_t timeout,ClockP_Params * params)93 ClockP_Handle ClockP_create(ClockP_Fxn clkFxn, uint32_t timeout, ClockP_Params *params)
94 {
95     ClockP_Handle handle;
96 
97     handle = (ClockP_Handle)dpl_clock_pool_alloc();
98 
99     /* ClockP_construct will check handle for NULL, no need here */
100     handle = ClockP_construct((ClockP_Struct *)handle, clkFxn, timeout, params);
101 
102     return (handle);
103 }
104 
105 /*
106  *  ======== ClockP_delete ========
107  */
ClockP_delete(ClockP_Handle handle)108 void ClockP_delete(ClockP_Handle handle)
109 {
110     ClockP_destruct((ClockP_Struct *)handle);
111 
112     dpl_clock_pool_free((ClockP_Obj*) handle);
113 }
114 
115 #endif /* CONFIG_DYNAMIC_DPL_OBJECTS */
116 
117 /*
118  *  ======== ClockP_construct ========
119  */
ClockP_construct(ClockP_Struct * handle,ClockP_Fxn clockFxn,uint32_t timeout,ClockP_Params * params)120 ClockP_Handle ClockP_construct(ClockP_Struct *handle, ClockP_Fxn clockFxn, uint32_t timeout, ClockP_Params *params)
121 {
122     ClockP_Obj *obj = (ClockP_Obj *)handle;
123 
124     if (handle == NULL)
125     {
126         return NULL;
127     }
128 
129     if (params == NULL)
130     {
131         params = &ClockP_defaultParams;
132     }
133 
134     obj->clock_fxn = clockFxn;
135     obj->arg       = params->arg;
136     obj->period    = params->period * ClockP_getSystemTickPeriod() / USEC_PER_MSEC;
137     obj->timeout   = timeout;
138     obj->active    = false;
139 
140     k_timer_init(&obj->timer, expiry_fxn, NULL);
141     k_timer_user_data_set(&obj->timer, obj);
142 
143     if (params->startFlag)
144     {
145         ClockP_start(obj);
146     }
147 
148     return ((ClockP_Handle)handle);
149 }
150 
151 /*
152  *  ======== ClockP_getSystemTickPeriod ========
153  */
154 uint32_t ClockP_tickPeriod = (USEC_PER_SEC / CONFIG_SYS_CLOCK_TICKS_PER_SEC);
ClockP_getSystemTickPeriod()155 uint32_t ClockP_getSystemTickPeriod()
156 {
157     return ClockP_tickPeriod;
158 }
159 
ClockP_getSystemTicks()160 uint32_t ClockP_getSystemTicks()
161 {
162     return (uint32_t)k_ms_to_ticks_ceil32(k_uptime_get_32());
163 }
164 
165 /*
166  *  ======== ClockP_Params_init ========
167  */
ClockP_Params_init(ClockP_Params * params)168 void ClockP_Params_init(ClockP_Params *params)
169 {
170     params->arg       = 0;
171     params->startFlag = false;
172     params->period    = 0;
173 }
174 
175 /*
176  *  ======== ClockP_setTimeout ========
177  */
ClockP_setTimeout(ClockP_Handle handle,uint32_t timeout)178 void ClockP_setTimeout(ClockP_Handle handle, uint32_t timeout)
179 {
180     ClockP_Obj *obj = (ClockP_Obj *)handle;
181 
182     obj->timeout = timeout;
183 }
184 
185 /*
186  *  ======== ClockP_start ========
187  */
ClockP_start(ClockP_Handle handle)188 void ClockP_start(ClockP_Handle handle)
189 {
190     ClockP_Obj *obj = (ClockP_Obj *)handle;
191     int32_t timeout;
192     int32_t period;
193 
194     __ASSERT_NO_MSG(obj->timeout / CONFIG_SYS_CLOCK_TICKS_PER_SEC <= UINT32_MAX / USEC_PER_MSEC);
195     __ASSERT_NO_MSG(obj->period / CONFIG_SYS_CLOCK_TICKS_PER_SEC <= UINT32_MAX / USEC_PER_MSEC);
196 
197     /* Avoid overflow */
198     if (obj->timeout > UINT32_MAX / USEC_PER_MSEC)
199     {
200         timeout = obj->timeout / CONFIG_SYS_CLOCK_TICKS_PER_SEC * USEC_PER_MSEC;
201     }
202     else if ((obj->timeout != 0) && (obj->timeout < CONFIG_SYS_CLOCK_TICKS_PER_SEC / USEC_PER_MSEC))
203     {
204         /* For small timeouts we use 1 msec */
205         timeout = 1;
206     }
207     else
208     {
209         timeout = obj->timeout * USEC_PER_MSEC / CONFIG_SYS_CLOCK_TICKS_PER_SEC;
210     }
211 
212     if (obj->period > UINT32_MAX / USEC_PER_MSEC)
213     {
214         period = obj->period / CONFIG_SYS_CLOCK_TICKS_PER_SEC * USEC_PER_MSEC;
215     }
216     else if ((obj->period != 0) && (obj->period < CONFIG_SYS_CLOCK_TICKS_PER_SEC / USEC_PER_MSEC))
217     {
218         period = 1;
219     }
220     else
221     {
222         period = obj->period * USEC_PER_MSEC / CONFIG_SYS_CLOCK_TICKS_PER_SEC;
223     }
224 
225     k_timer_start(&obj->timer, K_MSEC(timeout), K_MSEC(period));
226 
227     obj->active = true;
228 }
229 
230 /*
231  *  ======== ClockP_stop ========
232  */
ClockP_stop(ClockP_Handle handle)233 void ClockP_stop(ClockP_Handle handle)
234 {
235     ClockP_Obj *obj = (ClockP_Obj *)handle;
236 
237     k_timer_stop(&obj->timer);
238     obj->active = false;
239 }
240 
241 /*
242  *  ======== ClockP_setFunc ========
243  */
ClockP_setFunc(ClockP_Handle handle,ClockP_Fxn clockFxn,uintptr_t arg)244 void ClockP_setFunc(ClockP_Handle handle, ClockP_Fxn clockFxn, uintptr_t arg)
245 {
246     ClockP_Obj *obj = (ClockP_Obj *)handle;
247 
248     uintptr_t key = HwiP_disable();
249 
250     obj->clock_fxn = clockFxn;
251     obj->arg = arg;
252 
253     HwiP_restore(key);
254 }
255 
256 /*
257  *  ======== ClockP_sleep ========
258  */
ClockP_sleep(uint32_t sec)259 void ClockP_sleep(uint32_t sec)
260 {
261     uint32_t ticksToSleep;
262 
263     if (sec > ClockP_PERIOD_MAX_SEC)
264     {
265         sec = ClockP_PERIOD_MAX_SEC;
266     }
267     /* Convert from seconds to number of ticks */
268     ticksToSleep = (sec * USEC_PER_SEC) / ClockP_TICK_PERIOD;
269     k_sleep(K_TICKS(ticksToSleep));
270 }
271 
272 /*
273  *  ======== ClockP_usleep ========
274  */
ClockP_usleep(uint32_t usec)275 void ClockP_usleep(uint32_t usec)
276 {
277     k_sleep(K_USEC(usec));
278 }
279 
280 /*
281  *  ======== ClockP_getTimeout ========
282  */
ClockP_getTimeout(ClockP_Handle handle)283 uint32_t ClockP_getTimeout(ClockP_Handle handle)
284 {
285     ClockP_Obj *obj = (ClockP_Obj *)handle;
286     return k_timer_remaining_get(&obj->timer) * CONFIG_SYS_CLOCK_TICKS_PER_SEC / USEC_PER_MSEC;
287 }
288 
289 /*
290  *  ======== ClockP_isActive ========
291  */
ClockP_isActive(ClockP_Handle handle)292 bool ClockP_isActive(ClockP_Handle handle)
293 {
294     ClockP_Obj *obj = (ClockP_Obj *)handle;
295     return obj->active;
296 }
297 
298 /*
299  *  ======== ClockP_getCpuFreq ========
300  */
ClockP_getCpuFreq(ClockP_FreqHz * freq)301 void ClockP_getCpuFreq(ClockP_FreqHz *freq)
302 {
303     freq->lo = (uint32_t)CONFIG_SYS_CLOCK_HW_CYCLES_PER_SEC;
304     freq->hi = 0;
305 }
306 
ClockP_destruct(ClockP_Struct * clockP)307 void ClockP_destruct(ClockP_Struct *clockP)
308 {
309     ClockP_Obj *obj = (ClockP_Obj *)clockP->data;
310 
311     obj->clock_fxn = NULL;
312     obj->arg       = 0;
313     obj->period    = 0;
314     obj->timeout   = 0;
315     obj->active    = false;
316 
317     k_timer_stop(&obj->timer);
318 }
319