1 /***************************************************************************//**
2 * \file cyhal_utils_impl.h
3 *
4 * \brief
5 * Provides utility functions for working with the CAT1/CAT2 HAL implementation.
6 *
7 ********************************************************************************
8 * \copyright
9 * Copyright 2018-2022 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 /** \cond INTERNAL */
28 /**
29 * \addtogroup group_hal_impl
30 * \{
31 * Common utility macros & functions used by multiple HAL drivers.
32 */
33 
34 #pragma once
35 
36 #include "cy_result.h"
37 #include "cyhal_hw_types.h"
38 #include "cy_utils.h"
39 #include "cyhal_utils.h"
40 
41 #if defined(__cplusplus)
42 extern "C" {
43 #endif
44 
45 #if defined(COMPONENT_CAT1D)
46 #define _CYHAL_UTILS_PACK_INSTANCE_GROUP(instance, group)   (((instance) << 4) | (group))
47 #define _CYHAL_UTILS_UNPACK_INSTANCE(value)                 (((value) >> 4) & 0x3)
48 #define _CYHAL_UTILS_UNPACK_GROUP(value)                    ((value) & 0xF)
49 #endif /* defined(COMPONENT_CAT1D) */
50 
51 /**
52 * \addtogroup group_hal_impl_pin_package
53 * \{
54 */
55 
56 /** Converts the provided gpio pin to a resource instance object
57  *
58  * @param[in] pin  The pin to get a resource object for
59  * @return The equivalent resource instance object for the provided pin.
60  */
_cyhal_utils_get_gpio_resource(cyhal_gpio_t pin)61 static inline cyhal_resource_inst_t _cyhal_utils_get_gpio_resource(cyhal_gpio_t pin)
62 {
63     cyhal_resource_inst_t rsc = { CYHAL_RSC_GPIO, CYHAL_GET_PORT(pin), CYHAL_GET_PIN(pin) };
64     return rsc;
65 }
66 
67 /** Attempts to reserve the specified pin and then initialize it to connect to the item defined by the provided mapping object.
68  * @param[in] mapping    The pin/hardware block connection mapping information
69  * @param[in] drive_mode The drive mode for the pin
70  * @return CY_RSLT_SUCCESS if everything was ok, else an error.
71  */
72 cy_rslt_t _cyhal_utils_reserve_and_connect(const cyhal_resource_pin_mapping_t *mapping, uint8_t drive_mode);
73 
74 /** Disconnects any routing for the pin from the interconnect driver and then free's the pin from the hwmgr.
75  *
76  * @param[in] pin       The pin to disconnect and free
77  */
78 void _cyhal_utils_disconnect_and_free(cyhal_gpio_t pin);
79 
80 /** \} group_hal_impl_pin_package */
81 
82 #if defined(COMPONENT_CAT1B) || defined(COMPONENT_CAT1C) || defined(COMPONENT_CAT1D)
83 
84 /** Gets the HF clock for the specified peripheral clock group.
85  * @param[in] peri_group    The peripheral clock divider group to get the associated HF clock for
86  * @return The HF clock that drives the specified peripheral group
87  */
88 uint8_t _cyhal_utils_get_hfclk_for_peri_group(uint8_t peri_group);
89 
90 /** Gets the peripheral clock group that drives the specified item.
91  * @param[in] clocked_item  The item to get the source clock information for
92  * @return The peripheral group that feeds the clock tree for the item
93  */
94 uint8_t _cyhal_utils_get_peri_group(const cyhal_resource_inst_t *clocked_item);
95 #endif
96 
97 /** Gets the number of clock instances in a particular block
98   *
99   * @param block The block block for which to get the instance count
100   * @return The number of clocks of the specified type
101   */
102 uint32_t _cyhal_utils_get_clock_count(cyhal_clock_block_t block);
103 
104 /** Gets the peripheral clock frequency that is feeding the clock tree for the specified
105  * resource.
106  *
107  * @param[in] clocked_item  The resource to get the frequency for
108  * @return The peripheral clock frequency for the provided resource type
109  */
_cyhal_utils_get_peripheral_clock_frequency(const cyhal_resource_inst_t * clocked_item)110 static inline uint32_t _cyhal_utils_get_peripheral_clock_frequency(const cyhal_resource_inst_t *clocked_item)
111 {
112     #if defined(COMPONENT_CAT1A)
113     CY_UNUSED_PARAMETER(clocked_item);
114     return Cy_SysClk_ClkPeriGetFrequency();
115     #elif defined(COMPONENT_CAT1B) || defined(COMPONENT_CAT1C) || defined(COMPONENT_CAT1D)
116     CY_ASSERT(NULL != clocked_item);
117     uint8_t peri_group = _cyhal_utils_get_peri_group(clocked_item);
118     uint8_t hfclk = _cyhal_utils_get_hfclk_for_peri_group(peri_group);
119     return Cy_SysClk_ClkHfGetFrequency(hfclk);
120     #elif defined(COMPONENT_CAT2)
121     CY_UNUSED_PARAMETER(clocked_item);
122     return Cy_SysClk_ClkSysGetFrequency();
123     #endif
124 }
125 
126 /** Calculate a peripheral clock divider value that needs to be set to reach the frequency closest
127  * to the one requested.
128  * \note The caller may need to subtract one from the value returned in order to align with the
129  * API/register requirements. This is necessary if the API/register expects a value of 0 to mean
130  * divide by 1.
131  *
132  * @param[in] clocked_item  The resource to get the frequency for
133  * @param[in] frequency     The desired frequency
134  * @param[in] frac_bits     The number of fractional bits that the divider has
135  * @return The calculated divider value
136  */
_cyhal_utils_divider_value(const cyhal_resource_inst_t * clocked_item,uint32_t frequency,uint32_t frac_bits)137 static inline uint32_t _cyhal_utils_divider_value(const cyhal_resource_inst_t *clocked_item, uint32_t frequency, uint32_t frac_bits)
138 {
139     return ((_cyhal_utils_get_peripheral_clock_frequency(clocked_item) * (1 << frac_bits)) + (frequency / 2)) / frequency;
140 }
141 
142 /** Converts a hal pm mode to a pdl mode
143  *
144  * @param[in] mode          hal power management callback mode.
145  * @return Equivalent pdl syspm mode.
146  */
147 cy_en_syspm_callback_mode_t _cyhal_utils_convert_haltopdl_pm_mode(cyhal_syspm_callback_mode_t mode);
148 
149 /** Converts a pdl pm mode to a hal mode
150  *
151  * @param[in] mode          pdl syspm power management callback mode.
152  * @return Equivalent hal pm callback mode.
153  */
154 cyhal_syspm_callback_mode_t _cyhal_utils_convert_pdltohal_pm_mode(cy_en_syspm_callback_mode_t mode);
155 
156 /**
157  * Calculates clock tolerance in the specified units given a desired and actual frequency
158  *
159  * @param[in] type                  tolerance type
160  * @param[in] desired_hz            desired clock frequency in hertz
161  * @param[in] actual_hz             actual clock frequency in hertz
162  * @return the computed tolerance
163  */
164 int32_t _cyhal_utils_calculate_tolerance(cyhal_clock_tolerance_unit_t type, uint32_t desired_hz, uint32_t actual_hz);
165 
166 /**
167  * Allocates a clock that can drive the specified instance.
168  *
169  * @param[out]  clock               The clock object to initialize
170  * @param[in]   clocked_item        The destination that the allocated clock must be able to drive
171  * @param[in]   div                 The divider width that is required. This is ignored if the block is hard-wired to
172  *                                  an HFCLK output
173  * @param[in]   accept_larger       If no dividers of the specified width are available, can a wider divider be
174  *                                  substituted.
175  */
176 cy_rslt_t _cyhal_utils_allocate_clock(cyhal_clock_t *clock, const cyhal_resource_inst_t *clocked_item,
177                         cyhal_clock_block_t div, bool accept_larger);
178 
179 /**
180  * Attempts to set the clock to the specified frequency. This is similar to cyhal_clock_set_frequency,
181  * but it will also make an attempt to set the frequency for HFCLK outputs, which are not supported by the public
182  * API due to their limited range of supported dividers (1, 2, 4, 8)
183  *
184  * @param[in] clock                 The clock instance to set the frequency for.
185  * @param[in] hz                    The frequency, in hertz, to set the clock to.
186  * @param[in] tolerance             The allowed tolerance from the desired hz that is acceptable, use NULL if no
187  *                                  tolerance check is required.
188  */
189 cy_rslt_t _cyhal_utils_set_clock_frequency(cyhal_clock_t* clock, uint32_t hz, const cyhal_clock_tolerance_t *tolerance);
190 
191 #if defined(COMPONENT_CAT1A) || defined(COMPONENT_CAT1B) || defined(COMPONENT_CAT1C) || defined(COMPONENT_CAT1D)
192 /**
193  * Finds best divider for HF clock according to source and desired frequency data
194  *
195  * @param[in] hz_src                clock source frequency in hertz
196  * @param[in] desired_hz            desired clock frequency in hertz
197  * @param[in] tolerance             desired clock tolerance to achieve. If NULL provided, all possible dividers will
198  *                                  be checked and selected most suitable.
199  * @param[in] only_below_desired    resulting clock frequencies, that are above desired_hz will be skipped
200  * @param[out] div                  resulting divider
201  * @return CYHAL_CLOCK_RSLT_ERR_FREQ if divider is not found, CY_RSLT_SUCCESS in other situations
202  */
203 cy_rslt_t _cyhal_utils_find_hf_clk_div(uint32_t hz_src, uint32_t desired_hz, const cyhal_clock_tolerance_t *tolerance,
204                         bool only_below_desired, uint32_t *div);
205 
206 /** Function for finding most suitable divider for provided clock */
207 typedef cy_rslt_t (*_cyhal_utils_clk_div_func_t)(uint32_t hz_src, uint32_t desired_hz,
208                         const cyhal_clock_tolerance_t *tolerance, bool only_below_desired, uint32_t *div);
209 
210 /**
211  * Finds for provided HF clock most suitable source to achieve target clock frequency and returns it with
212  * corresponding divider value. No clock configuration changed in this function.
213  *
214  * @param[in] clock                 The HFCLK clock instance that needs clock configuration.
215  * @param[in] hz                    The maximum frequency, in hertz, that needs to be achieved. The clock will not exceed
216  *                                  this value.
217  * @param[in] tolerance             The allowed tolerance below the desired hz that is acceptable, use NULL if no
218  *                                  tolerance check is required.
219  * @param[in] div_find_func         Pointer to _cyhal_utils_clk_div_func_t - type function, that will find most suitable
220  *                                  divider for provided clock.
221  * @param[out] hf_source            Resulting HF source clock, switching to which, in combination with resulting divider,
222  *                                  will provide frequency closest to desired.
223  * @param[out] div                  Resulting divider for resulting HF source clock.
224  */
225 cy_rslt_t _cyhal_utils_find_hf_source_n_divider(cyhal_clock_t *clock, uint32_t hz,
226                         const cyhal_clock_tolerance_t *tolerance, _cyhal_utils_clk_div_func_t div_find_func,
227                         cyhal_clock_t *hf_source, uint32_t *div);
228 
229 /**
230  * Attempts to set the clock to the specified frequency. This is similar to cyhal_clock_set_frequency, but it will also
231  * make an attempt to set the frequency for HFCLK outputs. This is an enhancement beyond _cyhal_utils_set_clock_frequency
232  * as this will also attemt to adjust the source clock as well as change the divider.
233  *
234  * @param[in] clock                 The HFCLK clock instance to set the frequency for.
235  * @param[in] hz                    The maximum frequency, in hertz, to set the clock to. The clock will not exceed this
236  *                                  value.
237  * @param[in] tolerance             The allowed tolerance below the desired hz that is acceptable, use NULL if no
238  *                                  tolerance check is required.
239  */
240 cy_rslt_t _cyhal_utils_set_clock_frequency2(cyhal_clock_t *clock, uint32_t hz, const cyhal_clock_tolerance_t *tolerance);
241 
242 /* Compatibility macros until other environments are updated to not use this anymore. */
243 #define CY_UTILS_GET_RESOURCE(pin, mappings)            _CYHAL_UTILS_GET_RESOURCE((pin), (mappings))
244 #define cyhal_utils_get_resource(pin, mappings, count)  _cyhal_utils_get_resource((pin), (mappings), (count), NULL)
245 #define cyhal_utils_get_gpio_resource(pin)              _cyhal_utils_get_gpio_resource((pin))
246 #define CYHAL_SCB_BASE_ADDRESSES                        _CYHAL_SCB_BASE_ADDRESSES
247 #define CYHAL_TCPWM_DATA                                _CYHAL_TCPWM_DATA
248 
249 #endif /* defined(COMPONENT_CAT1A) || defined(COMPONENT_CAT1B) || defined(COMPONENT_CAT1C)*/
250 
251 
252 
_cyhal_utils_peri_pclk_set_divider(en_clk_dst_t clk_dest,const cyhal_clock_t * clock,uint32_t div)253 static inline cy_rslt_t _cyhal_utils_peri_pclk_set_divider(en_clk_dst_t clk_dest, const cyhal_clock_t *clock, uint32_t div)
254 {
255     #if defined(COMPONENT_CAT1B) || defined(COMPONENT_CAT1C) || defined(COMPONENT_CAT1D)
256     return Cy_SysClk_PeriPclkSetDivider(clk_dest, _CYHAL_PERIPHERAL_GROUP_GET_DIVIDER_TYPE(clock->block), clock->channel, div);
257     #else
258     CY_UNUSED_PARAMETER(clk_dest);
259     return Cy_SysClk_PeriphSetDivider(_CYHAL_PERIPHERAL_GROUP_GET_DIVIDER_TYPE(clock->block), clock->channel, div);
260     #endif
261 }
_cyhal_utils_peri_pclk_get_divider(en_clk_dst_t clk_dest,const cyhal_clock_t * clock)262 static inline uint32_t _cyhal_utils_peri_pclk_get_divider(en_clk_dst_t clk_dest, const cyhal_clock_t *clock)
263 {
264     #if defined(COMPONENT_CAT1B) || defined(COMPONENT_CAT1C) || defined(COMPONENT_CAT1D)
265     return Cy_SysClk_PeriPclkGetDivider(clk_dest, _CYHAL_PERIPHERAL_GROUP_GET_DIVIDER_TYPE(clock->block), clock->channel);
266     #else
267     CY_UNUSED_PARAMETER(clk_dest);
268     return Cy_SysClk_PeriphGetDivider(_CYHAL_PERIPHERAL_GROUP_GET_DIVIDER_TYPE(clock->block), clock->channel);
269     #endif
270 }
_cyhal_utils_peri_pclk_set_frac_divider(en_clk_dst_t clk_dest,const cyhal_clock_t * clock,uint32_t div_int,uint32_t div_frac)271 static inline cy_rslt_t _cyhal_utils_peri_pclk_set_frac_divider(en_clk_dst_t clk_dest, const cyhal_clock_t *clock, uint32_t div_int, uint32_t div_frac)
272 {
273     #if defined(COMPONENT_CAT1B) || defined(COMPONENT_CAT1C) || defined(COMPONENT_CAT1D)
274     return Cy_SysClk_PeriPclkSetFracDivider(clk_dest, _CYHAL_PERIPHERAL_GROUP_GET_DIVIDER_TYPE(clock->block), clock->channel, div_int, div_frac);
275     #else
276     CY_UNUSED_PARAMETER(clk_dest);
277     return Cy_SysClk_PeriphSetFracDivider(_CYHAL_PERIPHERAL_GROUP_GET_DIVIDER_TYPE(clock->block), clock->channel, div_int, div_frac);
278     #endif
279 }
_cyhal_utils_peri_pclk_get_frac_divider(en_clk_dst_t clk_dest,const cyhal_clock_t * clock,uint32_t * div_int,uint32_t * div_frac)280 static inline void _cyhal_utils_peri_pclk_get_frac_divider(en_clk_dst_t clk_dest, const cyhal_clock_t *clock, uint32_t *div_int, uint32_t *div_frac)
281 {
282     #if defined(COMPONENT_CAT1B) || defined(COMPONENT_CAT1C) || defined(COMPONENT_CAT1D)
283     Cy_SysClk_PeriPclkGetFracDivider(clk_dest, _CYHAL_PERIPHERAL_GROUP_GET_DIVIDER_TYPE(clock->block), clock->channel, div_int, div_frac);
284     #else
285     CY_UNUSED_PARAMETER(clk_dest);
286     Cy_SysClk_PeriphGetFracDivider(_CYHAL_PERIPHERAL_GROUP_GET_DIVIDER_TYPE(clock->block), clock->channel, div_int, div_frac);
287     #endif
288 }
_cyhal_utils_peri_pclk_get_frequency(en_clk_dst_t clk_dest,const cyhal_clock_t * clock)289 static inline uint32_t _cyhal_utils_peri_pclk_get_frequency(en_clk_dst_t clk_dest, const cyhal_clock_t *clock)
290 {
291     #if defined(COMPONENT_CAT1B) || defined(COMPONENT_CAT1C) || defined(COMPONENT_CAT1D)
292     return Cy_SysClk_PeriPclkGetFrequency(clk_dest, _CYHAL_PERIPHERAL_GROUP_GET_DIVIDER_TYPE(clock->block), clock->channel);
293     #else
294     CY_UNUSED_PARAMETER(clk_dest);
295     return Cy_SysClk_PeriphGetFrequency(_CYHAL_PERIPHERAL_GROUP_GET_DIVIDER_TYPE(clock->block), clock->channel);
296     #endif
297 }
_cyhal_utils_peri_pclk_assign_divider(en_clk_dst_t clk_dest,const cyhal_clock_t * clock)298 static inline cy_rslt_t _cyhal_utils_peri_pclk_assign_divider(en_clk_dst_t clk_dest, const cyhal_clock_t *clock)
299 {
300     #if defined(COMPONENT_CAT1B) || defined(COMPONENT_CAT1C) || defined(COMPONENT_CAT1D)
301         return Cy_SysClk_PeriPclkAssignDivider(clk_dest, _CYHAL_PERIPHERAL_GROUP_GET_DIVIDER_TYPE(clock->block), clock->channel);
302     #else
303         return Cy_SysClk_PeriphAssignDivider(clk_dest, _CYHAL_PERIPHERAL_GROUP_GET_DIVIDER_TYPE(clock->block), clock->channel);
304     #endif
305 }
_cyhal_utils_peri_pclk_get_assigned_divider(en_clk_dst_t clk_dest)306 static inline uint32_t _cyhal_utils_peri_pclk_get_assigned_divider(en_clk_dst_t clk_dest)
307 {
308     #if defined(COMPONENT_CAT1B) || defined(COMPONENT_CAT1C) || defined(COMPONENT_CAT1D)
309         return Cy_SysClk_PeriPclkGetAssignedDivider(clk_dest);
310     #else
311         return Cy_SysClk_PeriphGetAssignedDivider(clk_dest);
312     #endif
313 }
_cyhal_utils_peri_pclk_enable_divider(en_clk_dst_t clk_dest,const cyhal_clock_t * clock)314 static inline cy_rslt_t _cyhal_utils_peri_pclk_enable_divider(en_clk_dst_t clk_dest, const cyhal_clock_t *clock)
315 {
316     #if defined(COMPONENT_CAT1B) || defined(COMPONENT_CAT1C) || defined(COMPONENT_CAT1D)
317     return Cy_SysClk_PeriPclkEnableDivider(clk_dest, _CYHAL_PERIPHERAL_GROUP_GET_DIVIDER_TYPE(clock->block), clock->channel);
318     #else
319     CY_UNUSED_PARAMETER(clk_dest);
320     return Cy_SysClk_PeriphEnableDivider(_CYHAL_PERIPHERAL_GROUP_GET_DIVIDER_TYPE(clock->block), clock->channel);
321     #endif
322 }
_cyhal_utils_peri_pclk_disable_divider(en_clk_dst_t clk_dest,const cyhal_clock_t * clock)323 static inline cy_rslt_t _cyhal_utils_peri_pclk_disable_divider(en_clk_dst_t clk_dest, const cyhal_clock_t *clock)
324 {
325     #if defined(COMPONENT_CAT1B) || defined(COMPONENT_CAT1C) || defined(COMPONENT_CAT1D)
326     return Cy_SysClk_PeriPclkDisableDivider(clk_dest, _CYHAL_PERIPHERAL_GROUP_GET_DIVIDER_TYPE(clock->block), clock->channel);
327     #else
328     CY_UNUSED_PARAMETER(clk_dest);
329     return Cy_SysClk_PeriphDisableDivider(_CYHAL_PERIPHERAL_GROUP_GET_DIVIDER_TYPE(clock->block), clock->channel);
330     #endif
331 }
_cyhal_utils_peri_pclk_enable_phase_align_divider(en_clk_dst_t clk_dest,const cyhal_clock_t * clock,const cyhal_clock_t * clock2)332 static inline cy_rslt_t _cyhal_utils_peri_pclk_enable_phase_align_divider(en_clk_dst_t clk_dest, const cyhal_clock_t *clock, const cyhal_clock_t *clock2)
333 {
334     #if defined(COMPONENT_CAT1B) || defined(COMPONENT_CAT1C) || defined(COMPONENT_CAT1D)
335     return Cy_SysClk_PeriPclkEnablePhaseAlignDivider(clk_dest, _CYHAL_PERIPHERAL_GROUP_GET_DIVIDER_TYPE(clock->block), clock->channel,
336                                                     _CYHAL_PERIPHERAL_GROUP_GET_DIVIDER_TYPE(clock2->block), clock2->channel);
337     #else
338     CY_UNUSED_PARAMETER(clk_dest);
339     return Cy_SysClk_PeriphEnablePhaseAlignDivider(_CYHAL_PERIPHERAL_GROUP_GET_DIVIDER_TYPE(clock->block), clock->channel,
340                                                    _CYHAL_PERIPHERAL_GROUP_GET_DIVIDER_TYPE(clock2->block), clock2->channel);
341     #endif
342 }
_cyhal_utils_peri_pclk_is_divider_enabled(en_clk_dst_t clk_dest,const cyhal_clock_t * clock)343 static inline bool _cyhal_utils_peri_pclk_is_divider_enabled(en_clk_dst_t clk_dest, const cyhal_clock_t *clock)
344 {
345     #if defined(COMPONENT_CAT1B) || defined(COMPONENT_CAT1C) || defined(COMPONENT_CAT1D)
346     return Cy_SysClk_PeriPclkGetDividerEnabled(clk_dest, _CYHAL_PERIPHERAL_GROUP_GET_DIVIDER_TYPE(clock->block), clock->channel);
347     #elif defined(COMPONENT_CAT2)
348     CY_UNUSED_PARAMETER(clk_dest);
349     return Cy_SysClk_PeriphDividerIsEnabled(_CYHAL_PERIPHERAL_GROUP_GET_DIVIDER_TYPE(clock->block), clock->channel);
350     #else
351     CY_UNUSED_PARAMETER(clk_dest);
352     return Cy_SysClk_PeriphGetDividerEnabled(_CYHAL_PERIPHERAL_GROUP_GET_DIVIDER_TYPE(clock->block), clock->channel);
353     #endif
354 }
355 
356 #if defined(__cplusplus)
357 }
358 #endif
359 
360 /** \} group_hal_impl_utils */
361 /** \} group_hal_impl */
362 /** \endcond */
363