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