1 /***************************************************************************//**
2 * \file cyhal_utils_psoc.c
3 *
4 * \brief
5 * Provides utility functions for working with the CAT1/CAT2 HAL implementation.
6 *
7 ********************************************************************************
8 * \copyright
9 * Copyright 2018-2021 Cypress Semiconductor Corporation (an Infineon company) or
10 * an affiliate of Cypress Semiconductor Corporation
11 *
12 * SPDX-License-Identifier: Apache-2.0
13 *
14 * Licensed under the Apache License, Version 2.0 (the "License");
15 * you may not use this file except in compliance with the License.
16 * You may obtain a copy of the License at
17 *
18 *     http://www.apache.org/licenses/LICENSE-2.0
19 *
20 * Unless required by applicable law or agreed to in writing, software
21 * distributed under the License is distributed on an "AS IS" BASIS,
22 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
23 * See the License for the specific language governing permissions and
24 * limitations under the License.
25 *******************************************************************************/
26 
27 #include <stdlib.h>
28 #include <stdarg.h>
29 #include "cyhal_utils.h"
30 #include "cyhal_utils_psoc.h"
31 #include "cyhal_hwmgr.h"
32 #include "cyhal_interconnect.h"
33 #include "cyhal_clock.h"
34 
35 #if defined(__cplusplus)
36 extern "C"
37 {
38 #endif
39 
40 #if !defined(SRSS_NUM_PLL)
41 #define SRSS_NUM_PLL (SRSS_NUM_PLL200M + SRSS_NUM_PLL400M)
42 #endif
43 
44 #if defined(COMPONENT_CAT1B)
45 #define _CYHAL_MXSPERI_PCLK_DIV_CNT(gr) \
46     case CYHAL_CLOCK_BLOCK_PERIPHERAL##gr##_8BIT: return PERI_PERI_PCLK_PCLK_GROUP_NR##gr##_GR_DIV_8_VECT; \
47     case CYHAL_CLOCK_BLOCK_PERIPHERAL##gr##_16BIT: return PERI_PERI_PCLK_PCLK_GROUP_NR##gr##_GR_DIV_16_VECT; \
48     case CYHAL_CLOCK_BLOCK_PERIPHERAL##gr##_16_5BIT: return PERI_PERI_PCLK_PCLK_GROUP_NR##gr##_GR_DIV_16_5_VECT; \
49     case CYHAL_CLOCK_BLOCK_PERIPHERAL##gr##_24_5BIT: return PERI_PERI_PCLK_PCLK_GROUP_NR##gr##_GR_DIV_24_5_VECT;
50 #endif
51 
_cyhal_utils_reserve_and_connect(const cyhal_resource_pin_mapping_t * mapping,uint8_t drive_mode)52 cy_rslt_t _cyhal_utils_reserve_and_connect(const cyhal_resource_pin_mapping_t *mapping, uint8_t drive_mode)
53 {
54     cyhal_resource_inst_t pinRsc = _cyhal_utils_get_gpio_resource(mapping->pin);
55     cy_rslt_t status = cyhal_hwmgr_reserve(&pinRsc);
56     if (CY_RSLT_SUCCESS == status)
57     {
58         status = cyhal_connect_pin(mapping, drive_mode);
59         if (CY_RSLT_SUCCESS != status)
60         {
61             cyhal_hwmgr_free(&pinRsc);
62         }
63     }
64     return status;
65 }
66 
_cyhal_utils_disconnect_and_free(cyhal_gpio_t pin)67 void _cyhal_utils_disconnect_and_free(cyhal_gpio_t pin)
68 {
69     cy_rslt_t rslt = cyhal_disconnect_pin(pin);
70     CY_UNUSED_PARAMETER(rslt); /* CY_ASSERT only processes in DEBUG, ignores for others */
71     CY_ASSERT(CY_RSLT_SUCCESS == rslt);
72     cyhal_resource_inst_t rsc = _cyhal_utils_get_gpio_resource(pin);
73     cyhal_hwmgr_free(&rsc);
74 }
75 
_cyhal_utils_convert_haltopdl_pm_mode(cyhal_syspm_callback_mode_t mode)76 cy_en_syspm_callback_mode_t _cyhal_utils_convert_haltopdl_pm_mode(cyhal_syspm_callback_mode_t mode)
77 {
78     switch (mode)
79     {
80         case CYHAL_SYSPM_CHECK_READY:
81             return CY_SYSPM_CHECK_READY;
82         case CYHAL_SYSPM_CHECK_FAIL:
83             return CY_SYSPM_CHECK_FAIL;
84         case CYHAL_SYSPM_BEFORE_TRANSITION:
85             return CY_SYSPM_BEFORE_TRANSITION;
86         case CYHAL_SYSPM_AFTER_TRANSITION:
87             return CY_SYSPM_AFTER_TRANSITION;
88         default:
89             /* Should not get here */
90             CY_ASSERT(false);
91             return CY_SYSPM_CHECK_READY;
92     }
93 }
94 
_cyhal_utils_convert_pdltohal_pm_mode(cy_en_syspm_callback_mode_t mode)95 cyhal_syspm_callback_mode_t _cyhal_utils_convert_pdltohal_pm_mode(cy_en_syspm_callback_mode_t mode)
96 {
97     switch (mode)
98     {
99         case CY_SYSPM_CHECK_READY:
100             return CYHAL_SYSPM_CHECK_READY;
101         case CY_SYSPM_CHECK_FAIL:
102             return CYHAL_SYSPM_CHECK_FAIL;
103         case CY_SYSPM_BEFORE_TRANSITION:
104             return CYHAL_SYSPM_BEFORE_TRANSITION;
105         case CY_SYSPM_AFTER_TRANSITION:
106             return CYHAL_SYSPM_AFTER_TRANSITION;
107         default:
108             /* Should not get here */
109             CY_ASSERT(false);
110             return CYHAL_SYSPM_CHECK_READY;
111     }
112 }
113 
_cyhal_utils_calculate_tolerance(cyhal_clock_tolerance_unit_t type,uint32_t desired_hz,uint32_t actual_hz)114 int32_t _cyhal_utils_calculate_tolerance(cyhal_clock_tolerance_unit_t type, uint32_t desired_hz, uint32_t actual_hz)
115 {
116     switch (type)
117     {
118         case CYHAL_TOLERANCE_HZ:
119             return (int32_t)(desired_hz - actual_hz);
120         case CYHAL_TOLERANCE_PPM:
121             return (int32_t)(((int64_t)(desired_hz - actual_hz)) * 1000000) / ((int32_t)desired_hz);
122         case CYHAL_TOLERANCE_PERCENT:
123             return (int32_t)((((int64_t)desired_hz - actual_hz) * 100) / desired_hz);
124         default:
125             CY_ASSERT(false);
126             return 0;
127     }
128 }
129 
_cyhal_utils_allocate_peri(cyhal_clock_t * clock,uint8_t peri_group,cyhal_clock_block_t div,bool accept_larger)130 static inline cy_rslt_t _cyhal_utils_allocate_peri(cyhal_clock_t *clock, uint8_t peri_group, cyhal_clock_block_t div, bool accept_larger)
131 {
132     static const cyhal_clock_block_t PERI_DIVIDERS[] =
133     {
134         CYHAL_CLOCK_BLOCK_PERIPHERAL_8BIT,
135         CYHAL_CLOCK_BLOCK_PERIPHERAL_16BIT,
136         CYHAL_CLOCK_BLOCK_PERIPHERAL_16_5BIT,
137         CYHAL_CLOCK_BLOCK_PERIPHERAL_24_5BIT
138     };
139 
140     cy_rslt_t result = CYHAL_HWMGR_RSLT_ERR_NONE_FREE;
141     bool found_minimum = false;
142 #if defined(COMPONENT_CAT1B)
143     bool dividers_exist = false;
144 #endif
145     for(size_t i = 0; i < sizeof(PERI_DIVIDERS) / sizeof(PERI_DIVIDERS[0]); ++i)
146     {
147         if(PERI_DIVIDERS[i] == div)
148         {
149             found_minimum = true;
150         }
151 
152         if(found_minimum)
153         {
154 #if defined(COMPONENT_CAT1A)
155             CY_UNUSED_PARAMETER(peri_group);
156             result = _cyhal_clock_allocate_peri(clock, PERI_DIVIDERS[i]);
157 #elif defined(COMPONENT_CAT1B)
158             cyhal_clock_block_t adjusted_div = (cyhal_clock_block_t)_CYHAL_PERIPHERAL_GROUP_ADJUST(peri_group, PERI_DIVIDERS[i]);
159             dividers_exist |= (_cyhal_utils_get_clock_count(adjusted_div) > 0);
160             result = _cyhal_clock_allocate_peri(clock, adjusted_div);
161 #elif defined(COMPONENT_CAT2)
162             CY_UNUSED_PARAMETER(peri_group);
163             result = cyhal_clock_allocate(clock, PERI_DIVIDERS[i]);
164 #endif
165 
166             if(CY_RSLT_SUCCESS == result || !accept_larger)
167             {
168                 break;
169             }
170         }
171     }
172 
173 #if defined(COMPONENT_CAT1B)
174     // If no dividers exist, try to reserve the hfclk that drives the peri group
175     if(CY_RSLT_SUCCESS != result && false == dividers_exist)
176     {
177         uint8_t hfclk_idx = _cyhal_utils_get_hfclk_for_peri_group(peri_group);
178         result = cyhal_clock_reserve(clock, &CYHAL_CLOCK_HF[hfclk_idx]);
179     }
180 #endif
181     return result;
182 }
183 
184 #if defined(COMPONENT_CAT1B)
_cyhal_utils_get_hfclk_for_peri_group(uint8_t peri_group)185 uint8_t _cyhal_utils_get_hfclk_for_peri_group(uint8_t peri_group)
186 {
187     switch (peri_group)
188     {
189         /* Peripheral groups are device specific. */
190 #if defined(CY_DEVICE_CYW20829)
191         case 0:
192         case 2:
193             return 0;
194         case 1:
195         case 3:
196         case 6:
197             return 1;
198         case 4:
199             return 2;
200         case 5:
201             return 3;
202 #else
203 #warning "Unsupported device"
204 #endif /* defined(CY_DEVICE_CYW20829) */
205         default:
206             CY_ASSERT(false); /* Use APIs provided by the clock driver */
207             break;
208     }
209     return 0;
210 }
_cyhal_utils_get_peri_group(const cyhal_resource_inst_t * clocked_item)211 uint8_t _cyhal_utils_get_peri_group(const cyhal_resource_inst_t *clocked_item)
212 {
213     switch (clocked_item->type)
214     {
215         /* Peripheral groups are device specific. */
216 #if defined(CY_DEVICE_CYW20829)
217         case CYHAL_RSC_CAN:
218         case CYHAL_RSC_LIN:
219         case CYHAL_RSC_SCB:
220         case CYHAL_RSC_TCPWM:
221             return 1;
222         case CYHAL_RSC_SMIF:
223         case CYHAL_RSC_CRYPTO:
224             return 2;
225         case CYHAL_RSC_I2S:
226         case CYHAL_RSC_TDM:
227         case CYHAL_RSC_PDM:
228             return 3;
229         case CYHAL_RSC_BLESS:
230             return 4;
231         case CYHAL_RSC_ADCMIC:
232             return 5;
233 #else
234 #warning "Unsupported device"
235 #endif
236         default:
237             CY_ASSERT(false); /* Use APIs provided by the clock driver */
238             break;
239     }
240     return 0;
241 }
242 #endif
243 
_cyhal_utils_get_clock_count(cyhal_clock_block_t block)244 uint32_t _cyhal_utils_get_clock_count(cyhal_clock_block_t block)
245 {
246     //NOTE: This could potentially reuse the cyhal_hwmgr.c cyhal_block_offsets_clock array
247     switch (block)
248     {
249         #if defined(COMPONENT_CAT1)
250         #if defined(COMPONENT_CAT1A)
251         case CYHAL_CLOCK_BLOCK_PERIPHERAL_8BIT:
252             return PERI_DIV_8_NR;
253         case CYHAL_CLOCK_BLOCK_PERIPHERAL_16BIT:
254             return PERI_DIV_16_NR;
255         case CYHAL_CLOCK_BLOCK_PERIPHERAL_16_5BIT:
256             return PERI_DIV_16_5_NR;
257         case CYHAL_CLOCK_BLOCK_PERIPHERAL_24_5BIT:
258             return PERI_DIV_24_5_NR;
259         case CYHAL_CLOCK_BLOCK_PLL:
260             return SRSS_NUM_PLL;
261         #elif defined(COMPONENT_CAT1B)
262         #if (PERI_PERI_PCLK_PCLK_GROUP_NR > 0)
263         _CYHAL_MXSPERI_PCLK_DIV_CNT(0);
264         #endif
265         #if (PERI_PERI_PCLK_PCLK_GROUP_NR > 1)
266         _CYHAL_MXSPERI_PCLK_DIV_CNT(1);
267         #endif
268         #if (PERI_PERI_PCLK_PCLK_GROUP_NR > 2)
269         _CYHAL_MXSPERI_PCLK_DIV_CNT(2);
270         #endif
271         #if (PERI_PERI_PCLK_PCLK_GROUP_NR > 3)
272         _CYHAL_MXSPERI_PCLK_DIV_CNT(3);
273         #endif
274         #if (PERI_PERI_PCLK_PCLK_GROUP_NR > 4)
275         _CYHAL_MXSPERI_PCLK_DIV_CNT(4);
276         #endif
277         #if (PERI_PERI_PCLK_PCLK_GROUP_NR > 5)
278         _CYHAL_MXSPERI_PCLK_DIV_CNT(5);
279         #endif
280         #if (PERI_PERI_PCLK_PCLK_GROUP_NR > 6)
281         _CYHAL_MXSPERI_PCLK_DIV_CNT(6);
282         #endif
283         #if (PERI_PERI_PCLK_PCLK_GROUP_NR > 7)
284         _CYHAL_MXSPERI_PCLK_DIV_CNT(7);
285         #endif
286         #if (PERI_PERI_PCLK_PCLK_GROUP_NR > 8)
287         _CYHAL_MXSPERI_PCLK_DIV_CNT(8);
288         #endif
289         #if (PERI_PERI_PCLK_PCLK_GROUP_NR > 9)
290         _CYHAL_MXSPERI_PCLK_DIV_CNT(9);
291         #endif
292         #if (PERI_PERI_PCLK_PCLK_GROUP_NR > 10)
293         _CYHAL_MXSPERI_PCLK_DIV_CNT(10);
294         #endif
295         #if (PERI_PERI_PCLK_PCLK_GROUP_NR > 11)
296         _CYHAL_MXSPERI_PCLK_DIV_CNT(11);
297         #endif
298         #if (PERI_PERI_PCLK_PCLK_GROUP_NR > 12)
299         _CYHAL_MXSPERI_PCLK_DIV_CNT(12);
300         #endif
301         #if (PERI_PERI_PCLK_PCLK_GROUP_NR > 13)
302         _CYHAL_MXSPERI_PCLK_DIV_CNT(13);
303         #endif
304         #if (PERI_PERI_PCLK_PCLK_GROUP_NR > 14)
305         _CYHAL_MXSPERI_PCLK_DIV_CNT(14);
306         #endif
307         #if (PERI_PERI_PCLK_PCLK_GROUP_NR > 15)
308         _CYHAL_MXSPERI_PCLK_DIV_CNT(15);
309         #endif
310         case CYHAL_CLOCK_BLOCK_PERI:
311             return PERI_PCLK_GROUP_NR;
312         case CYHAL_CLOCK_BLOCK_PLL200:
313             return SRSS_NUM_PLL200M;
314         case CYHAL_CLOCK_BLOCK_PLL400:
315             return SRSS_NUM_PLL400M;
316         #endif /* defined(COMPONENT_CAT1B) */
317         case CYHAL_CLOCK_BLOCK_PATHMUX:
318             return SRSS_NUM_CLKPATH;
319         case CYHAL_CLOCK_BLOCK_HF:
320             return SRSS_NUM_HFROOT;
321         #elif defined(COMPONENT_CAT2) /* defined(COMPONENT_CAT1) */
322         case CYHAL_CLOCK_BLOCK_PERIPHERAL_8BIT:
323             return PERI_PCLK_DIV_8_NR;
324         case CYHAL_CLOCK_BLOCK_PERIPHERAL_16BIT:
325             return PERI_PCLK_DIV_16_NR;
326         case CYHAL_CLOCK_BLOCK_PERIPHERAL_16_5BIT:
327             return PERI_PCLK_DIV_16_5_NR;
328         case CYHAL_CLOCK_BLOCK_PERIPHERAL_24_5BIT:
329             return PERI_PCLK_DIV_24_5_NR;
330         #endif /* defined(COMPONENT_CAT2) */
331         default:
332             return 1;
333     }
334 }
335 
336 #if defined(COMPONENT_CAT1A)
_cyhal_utils_allocate_clock(cyhal_clock_t * clock,const cyhal_resource_inst_t * clocked_item,cyhal_clock_block_t div,bool accept_larger)337 cy_rslt_t _cyhal_utils_allocate_clock(cyhal_clock_t *clock, const cyhal_resource_inst_t *clocked_item, cyhal_clock_block_t div, bool accept_larger)
338 {
339     CY_ASSERT(NULL != clocked_item);
340 
341     cyhal_clock_t clock_rsc;
342     switch (clocked_item->type)
343     {
344         /* High frequency clock assignments are device specific. */
345 #if defined(CY_DEVICE_PSOC6ABLE2) || defined(CY_DEVICE_PSOC6A2M)
346         case CYHAL_RSC_I2S:
347         case CYHAL_RSC_PDM:
348             clock_rsc = CYHAL_CLOCK_HF[1];
349             break;
350 #endif
351 #if defined(CY_DEVICE_PSOC6ABLE2) || defined(CY_DEVICE_PSOC6A2M) || defined(CY_DEVICE_PSOC6A512K) || defined(CY_DEVICE_PSOC6A256K)
352         case CYHAL_RSC_SMIF:
353             clock_rsc = CYHAL_CLOCK_HF[2];
354             break;
355         case CYHAL_RSC_USB:
356             clock_rsc = CYHAL_CLOCK_HF[3];
357             break;
358 #endif
359 #if defined(CY_DEVICE_PSOC6A2M)
360         case CYHAL_RSC_SDHC:
361             clock_rsc = (clocked_item->block_num == 0)
362                 ? CYHAL_CLOCK_HF[4]
363                 : CYHAL_CLOCK_HF[2];
364             break;
365 #elif defined(CY_DEVICE_PSOC6A512K)
366         case CYHAL_RSC_SDHC:
367             clock_rsc = CYHAL_CLOCK_HF[4];
368             break;
369 #endif
370         case CYHAL_RSC_CLOCK:
371             CY_ASSERT(false); /* Use APIs provided by the clock driver */
372             return CYHAL_CLOCK_RSLT_ERR_NOT_SUPPORTED;
373         default:
374             return _cyhal_utils_allocate_peri(clock, 0, div, accept_larger);
375     }
376     return cyhal_clock_reserve(clock, &clock_rsc);
377 }
378 #elif defined(COMPONENT_CAT1B)
_cyhal_utils_allocate_clock(cyhal_clock_t * clock,const cyhal_resource_inst_t * clocked_item,cyhal_clock_block_t div,bool accept_larger)379 cy_rslt_t _cyhal_utils_allocate_clock(cyhal_clock_t *clock, const cyhal_resource_inst_t *clocked_item, cyhal_clock_block_t div, bool accept_larger)
380 {
381     CY_ASSERT(NULL != clocked_item);
382     uint8_t peri_group = _cyhal_utils_get_peri_group(clocked_item);
383     return _cyhal_utils_allocate_peri(clock, peri_group, div, accept_larger);
384 }
385 #elif defined(COMPONENT_CAT2)
_cyhal_utils_allocate_clock(cyhal_clock_t * clock,const cyhal_resource_inst_t * clocked_item,cyhal_clock_block_t div,bool accept_larger)386 cy_rslt_t _cyhal_utils_allocate_clock(cyhal_clock_t *clock, const cyhal_resource_inst_t *clocked_item, cyhal_clock_block_t div, bool accept_larger)
387 {
388     CY_ASSERT(NULL != clocked_item);
389     CY_UNUSED_PARAMETER(clocked_item);
390     return _cyhal_utils_allocate_peri(clock, 0, div, accept_larger);
391 }
392 #endif
393 
_cyhal_utils_set_clock_frequency(cyhal_clock_t * clock,uint32_t hz,const cyhal_clock_tolerance_t * tolerance)394 cy_rslt_t _cyhal_utils_set_clock_frequency(cyhal_clock_t* clock, uint32_t hz, const cyhal_clock_tolerance_t *tolerance)
395 {
396 #if defined(COMPONENT_CAT1A) || defined(COMPONENT_CAT1B)
397     if(clock->block == CYHAL_CLOCK_BLOCK_HF)
398     {
399         uint32_t divider;
400         cy_en_clkhf_in_sources_t source = Cy_SysClk_ClkHfGetSource(clock->channel);
401         uint32_t source_hz = Cy_SysClk_ClkPathGetFrequency((uint32_t)source);
402         if (CY_RSLT_SUCCESS == _cyhal_utils_find_hf_clk_div(source_hz, hz, tolerance, false, &divider))
403         {
404             return cyhal_clock_set_divider(clock, divider);
405         }
406         return CYHAL_CLOCK_RSLT_ERR_FREQ;
407     }
408     else
409     {
410 #endif
411         // Defer to the clock driver
412         return cyhal_clock_set_frequency(clock, hz, tolerance);
413 #if defined(COMPONENT_CAT1A) || defined(COMPONENT_CAT1B)
414     }
415 #endif
416 }
417 
418 #if defined(COMPONENT_CAT1A) || defined(COMPONENT_CAT1B)
_cyhal_utils_find_hf_clk_div(uint32_t hz_src,uint32_t desired_hz,const cyhal_clock_tolerance_t * tolerance,bool only_below_desired,uint32_t * div)419 cy_rslt_t _cyhal_utils_find_hf_clk_div(uint32_t hz_src, uint32_t desired_hz, const cyhal_clock_tolerance_t *tolerance,
420                         bool only_below_desired, uint32_t *div)
421 {
422     const uint8_t HFCLK_DIVIDERS[] = { 1, 2, 4, 8};
423     cy_rslt_t retval = CYHAL_CLOCK_RSLT_ERR_FREQ;
424     uint32_t tolerance_check_value = (NULL != tolerance) ? tolerance->value : 0xFFFFFFFFU;
425     cyhal_clock_tolerance_unit_t tolerance_type = (NULL != tolerance) ? tolerance->type : CYHAL_TOLERANCE_HZ;
426 
427     for(uint8_t i = 0; i < sizeof(HFCLK_DIVIDERS) / sizeof(HFCLK_DIVIDERS[0]); ++i)
428     {
429         const uint32_t divider = HFCLK_DIVIDERS[i];
430         uint32_t actual_freq = hz_src / divider;
431         if ((actual_freq > desired_hz) && only_below_desired)
432             continue;
433         uint32_t achieved_tolerance = abs(_cyhal_utils_calculate_tolerance(tolerance_type, desired_hz, actual_freq));
434         if (achieved_tolerance < tolerance_check_value)
435         {
436             *div = divider;
437             retval = CY_RSLT_SUCCESS;
438             if ((NULL != tolerance) || (achieved_tolerance == 0))
439                 break;
440             tolerance_check_value = achieved_tolerance;
441         }
442         else if (only_below_desired)
443         {
444             /* We are going from smallest divider, to highest. If we've not achieved better tolerance in
445             *   this iteration, we will no achieve it in futher for sure. */
446             break;
447         }
448     }
449     return retval;
450 }
451 
_cyhal_utils_find_hf_source_n_divider(cyhal_clock_t * clock,uint32_t hz,const cyhal_clock_tolerance_t * tolerance,_cyhal_utils_clk_div_func_t div_find_func,cyhal_clock_t * hf_source,uint32_t * div)452 cy_rslt_t _cyhal_utils_find_hf_source_n_divider(cyhal_clock_t *clock, uint32_t hz, const cyhal_clock_tolerance_t *tolerance,
453         _cyhal_utils_clk_div_func_t div_find_func, cyhal_clock_t *hf_source, uint32_t *div)
454 {
455     CY_ASSERT(NULL != clock);
456     CY_ASSERT(hz != 0);
457 
458     uint32_t count;
459     const cyhal_resource_inst_t ** sources;
460     cy_rslt_t retval = cyhal_clock_get_sources(clock, &sources, &count);
461     if (CY_RSLT_SUCCESS != retval)
462         return retval;
463 
464     uint32_t best_tolerance_hz = 0xFFFFFFFFU;
465     cyhal_clock_t best_clock;
466     uint32_t best_clock_freq = 0;
467     uint32_t best_divider = 1;
468     /* Go through all possible HFCLK clock sources and check what source fits best */
469     for (uint32_t i = 0; i < count; ++i)
470     {
471         cyhal_clock_t temp_clock;
472         if (CY_RSLT_SUCCESS == cyhal_clock_get(&temp_clock, sources[i]))
473         {
474             uint32_t cur_hf_source_freq = cyhal_clock_get_frequency(&temp_clock);
475             /* source frequency is much lower than desired, no reason to continue */
476             if ((0 == cur_hf_source_freq) ||
477                 ((NULL != tolerance) && (_cyhal_utils_calculate_tolerance(tolerance->type, hz, cur_hf_source_freq) > (int32_t)tolerance->value)))
478             {
479                 continue;
480             }
481             /* Covering situation when PATHMUX has enabled FLL / PLL on its way. In that case FLL / PLL frequency
482                 is observed on PATHMUX which is covered in other iterations of the sources loop */
483             if (CYHAL_CLOCK_BLOCK_PATHMUX == temp_clock.block)
484             {
485                 if (((sources[i]->channel_num == 0) && Cy_SysClk_FllIsEnabled()) ||
486                     ((sources[i]->channel_num > 0) && (sources[i]->channel_num <= SRSS_NUM_PLL) &&
487                         Cy_SysClk_PllIsEnabled(sources[i]->channel_num)))
488                 {
489                     continue;
490                 }
491             }
492 
493             uint32_t cur_clock_divider;
494             if (CY_RSLT_SUCCESS == div_find_func(cur_hf_source_freq, hz, NULL, true, &cur_clock_divider))
495             {
496                 uint32_t cur_divided_freq = cur_hf_source_freq / cur_clock_divider;
497                 uint32_t cur_clock_tolerance = abs(_cyhal_utils_calculate_tolerance(CYHAL_TOLERANCE_HZ, hz, cur_divided_freq));
498                 if (cur_clock_tolerance < best_tolerance_hz)
499                 {
500                     best_clock = temp_clock;
501                     best_tolerance_hz = cur_clock_tolerance;
502                     best_clock_freq = cur_divided_freq;
503                     best_divider = cur_clock_divider;
504                     if (cur_divided_freq == hz)
505                         break;
506                 }
507             }
508         }
509     }
510 
511     if (0 == best_clock_freq)
512     {
513         retval = CYHAL_CLOCK_RSLT_ERR_SOURCE;
514     }
515     else if (NULL != tolerance) /* Verify within tolerance if one was provided. */
516     {
517         uint32_t achieved_tolerance = abs(_cyhal_utils_calculate_tolerance(tolerance->type, hz, best_clock_freq));
518         if (achieved_tolerance > tolerance->value)
519             retval = CYHAL_CLOCK_RSLT_ERR_FREQ;
520     }
521 
522     if (CY_RSLT_SUCCESS == retval)
523     {
524         *hf_source = best_clock;
525         *div = best_divider;
526     }
527 
528     return retval;
529 }
530 
_cyhal_utils_set_clock_frequency2(cyhal_clock_t * clock,uint32_t hz,const cyhal_clock_tolerance_t * tolerance)531 cy_rslt_t _cyhal_utils_set_clock_frequency2(cyhal_clock_t *clock, uint32_t hz, const cyhal_clock_tolerance_t *tolerance)
532 {
533     CY_ASSERT(NULL != clock);
534     CY_ASSERT(hz != 0);
535 
536     cyhal_clock_t hf_source;
537     uint32_t divider = 0;
538     cy_rslt_t retval = _cyhal_utils_find_hf_source_n_divider(clock, hz, tolerance, _cyhal_utils_find_hf_clk_div,
539                             &hf_source, &divider);
540     if (CY_RSLT_SUCCESS == retval)
541     {
542         retval = cyhal_clock_set_source(clock, &hf_source);
543     }
544     if (CY_RSLT_SUCCESS == retval)
545     {
546         retval = cyhal_clock_set_divider(clock, divider);
547     }
548 
549     return retval;
550 }
551 #endif
552 
553 #if defined(__cplusplus)
554 }
555 #endif
556