Files
Beslan 0ef1717155
Some checks failed
Basic Checks / license-check (push) Has been cancelled
Basic Checks / include-check (push) Has been cancelled
Basic Checks / style-check (push) Has been cancelled
Basic Checks / docs-check (push) Has been cancelled
Basic Checks / python-tests (push) Has been cancelled
Basic Checks / pin-validation (push) Has been cancelled
Basic Checks / cmake-checks (push) Has been cancelled
Basic Checks / frozen-tools-check (push) Has been cancelled
Publish or Update docker image for head of branch / prepare-tags (push) Has been cancelled
Release or update docker image for a released mbed-os version / prepare-tags (push) Has been cancelled
Publish or Update docker image for head of branch / build-container (push) Has been cancelled
Publish or Update docker image for head of branch / test-container (linux/amd64) (push) Has been cancelled
Publish or Update docker image for head of branch / test-container (linux/arm64) (push) Has been cancelled
Publish or Update docker image for head of branch / deploy-container (push) Has been cancelled
Release or update docker image for a released mbed-os version / build-container (push) Has been cancelled
Release or update docker image for a released mbed-os version / test-container (linux/amd64) (push) Has been cancelled
Release or update docker image for a released mbed-os version / test-container (linux/arm64) (push) Has been cancelled
Release or update docker image for a released mbed-os version / deploy-container (push) Has been cancelled
Prune temporary docker images / prune-images (push) Has been cancelled
Mirror mbed-os-6.15.0
2026-07-10 18:42:39 +03:00

456 lines
15 KiB
C

/***************************************************************************//**
* \file cyhal_utils.c
*
* \brief
* Provides utility functions for working with the CAT1/CAT2 HAL implementation.
*
********************************************************************************
* \copyright
* Copyright 2018-2021 Cypress Semiconductor Corporation
* SPDX-License-Identifier: Apache-2.0
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*******************************************************************************/
#include <stdlib.h>
#include <stdarg.h>
#include "cyhal_utils.h"
#include "cyhal_hwmgr.h"
#include "cyhal_interconnect.h"
#include "cyhal_gpio.h"
#include "cyhal_clock.h"
#if defined(__cplusplus)
extern "C"
{
#endif
const cyhal_resource_pin_mapping_t *_cyhal_utils_get_resource(cyhal_gpio_t pin, const cyhal_resource_pin_mapping_t* mappings, size_t count,
const cyhal_resource_inst_t* block_res)
{
if (NC != pin)
{
for (uint32_t i = 0; i < count; i++)
{
if (pin == mappings[i].pin)
{
if ((NULL == block_res) || (_cyhal_utils_resources_equal(mappings[i].inst, block_res)))
{
return &mappings[i];
}
}
}
}
return NULL;
}
const cyhal_resource_pin_mapping_t* _cyhal_utils_try_alloc(cyhal_gpio_t pin, const cyhal_resource_pin_mapping_t *pin_map, size_t count)
{
for (uint32_t i = 0; i < count; i++)
{
if (pin == pin_map[i].pin)
{
if (CY_RSLT_SUCCESS == cyhal_hwmgr_reserve(pin_map[i].inst))
{
return &pin_map[i];
}
}
}
return NULL;
}
cy_rslt_t _cyhal_utils_reserve_and_connect(cyhal_gpio_t pin, const cyhal_resource_pin_mapping_t *mapping)
{
cyhal_resource_inst_t pinRsc = _cyhal_utils_get_gpio_resource(pin);
cy_rslt_t status = cyhal_hwmgr_reserve(&pinRsc);
if (CY_RSLT_SUCCESS == status)
{
status = cyhal_connect_pin(mapping);
if (CY_RSLT_SUCCESS != status)
{
cyhal_hwmgr_free(&pinRsc);
}
}
return status;
}
void _cyhal_utils_disconnect_and_free(cyhal_gpio_t pin)
{
cy_rslt_t rslt = cyhal_disconnect_pin(pin);
CY_UNUSED_PARAMETER(rslt); /* CY_ASSERT only processes in DEBUG, ignores for others */
CY_ASSERT(CY_RSLT_SUCCESS == rslt);
cyhal_resource_inst_t rsc = _cyhal_utils_get_gpio_resource(pin);
cyhal_hwmgr_free(&rsc);
}
void _cyhal_utils_release_if_used(cyhal_gpio_t *pin)
{
if (CYHAL_NC_PIN_VALUE != *pin)
{
_cyhal_utils_disconnect_and_free(*pin);
*pin = CYHAL_NC_PIN_VALUE;
}
}
bool _cyhal_utils_resources_equal(const cyhal_resource_inst_t *resource1, const cyhal_resource_inst_t *resource2)
{
return (resource1->type == resource2->type) &&
(resource1->block_num == resource2->block_num) &&
(resource1->channel_num == resource2->channel_num);
}
bool _cyhal_utils_resources_equal_all(uint32_t count, ...)
{
CY_ASSERT(count >= 2);
va_list args;
bool equal = true;
const cyhal_resource_inst_t *curr;
va_start(args, count);
const cyhal_resource_inst_t *first = va_arg(args, const cyhal_resource_inst_t *);
for (uint32_t i = 1; i < count; i++)
{
curr = va_arg(args, const cyhal_resource_inst_t *);
equal &= _cyhal_utils_resources_equal(first, curr);
}
va_end(args);
return equal;
}
uint32_t _cyhal_utils_convert_flags(const uint32_t map[], uint32_t count, uint32_t source_flags)
{
uint32_t result_flags = 0;
// Index 0 is the default value if nothing else is set.
for (uint8_t i = 1; i < count; i++)
{
if (source_flags & (1 << (i - 1)))
result_flags |= map[i];
}
if (0 == result_flags)
result_flags = map[0];
return result_flags;
}
cy_en_syspm_callback_mode_t _cyhal_utils_convert_haltopdl_pm_mode(cyhal_syspm_callback_mode_t mode)
{
switch (mode)
{
case CYHAL_SYSPM_CHECK_READY:
return CY_SYSPM_CHECK_READY;
case CYHAL_SYSPM_CHECK_FAIL:
return CY_SYSPM_CHECK_FAIL;
case CYHAL_SYSPM_BEFORE_TRANSITION:
return CY_SYSPM_BEFORE_TRANSITION;
case CYHAL_SYSPM_AFTER_TRANSITION:
return CY_SYSPM_AFTER_TRANSITION;
default:
/* Should not get here */
CY_ASSERT(false);
return CY_SYSPM_CHECK_READY;
}
}
cyhal_syspm_callback_mode_t _cyhal_utils_convert_pdltohal_pm_mode(cy_en_syspm_callback_mode_t mode)
{
switch (mode)
{
case CY_SYSPM_CHECK_READY:
return CYHAL_SYSPM_CHECK_READY;
case CY_SYSPM_CHECK_FAIL:
return CYHAL_SYSPM_CHECK_FAIL;
case CY_SYSPM_BEFORE_TRANSITION:
return CYHAL_SYSPM_BEFORE_TRANSITION;
case CY_SYSPM_AFTER_TRANSITION:
return CYHAL_SYSPM_AFTER_TRANSITION;
default:
/* Should not get here */
CY_ASSERT(false);
return CYHAL_SYSPM_CHECK_READY;
}
}
void _cyhal_utils_get_peri_clock_details(const cyhal_clock_t *clock, cy_en_divider_types_t *div_type, uint32_t *div_num)
{
#if defined(COMPONENT_CAT1A)
if (_cyhal_utils_is_new_clock_format(clock))
{
#endif
CY_ASSERT(clock->reserved);
*div_num = clock->channel;
*div_type = (cy_en_divider_types_t)clock->block;
#if defined(COMPONENT_CAT1A)
}
else
{
*div_num = clock->div_num;
*div_type = (cy_en_divider_types_t)clock->div_type;
}
#endif
}
int32_t _cyhal_utils_calculate_tolerance(cyhal_clock_tolerance_unit_t type, uint32_t desired_hz, uint32_t actual_hz)
{
switch (type)
{
case CYHAL_TOLERANCE_HZ:
return (int32_t)(desired_hz - actual_hz);
case CYHAL_TOLERANCE_PPM:
return (int32_t)(((int64_t)(desired_hz - actual_hz)) * 1000000) / ((int32_t)desired_hz);
case CYHAL_TOLERANCE_PERCENT:
return (int32_t)((((int64_t)desired_hz - actual_hz) * 100) / desired_hz);
default:
CY_ASSERT(false);
return 0;
}
}
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)
{
CY_ASSERT(NULL != clocked_item);
#if defined(COMPONENT_CAT1A) || defined(COMPONENT_CAT1B)
cyhal_resource_inst_t clock_rsc;
#endif
switch (clocked_item->type)
{
#if defined(COMPONENT_CAT1A) || defined(COMPONENT_CAT1B)
/* High frequency clock assignments are device specific. */
#if defined(CY_DEVICE_PSOC6ABLE2) || defined(CY_DEVICE_PSOC6A2M)
case CYHAL_RSC_I2S:
case CYHAL_RSC_PDM:
clock_rsc = CYHAL_CLOCK_HF[1];
break;
#endif
#if defined(CY_DEVICE_PSOC6ABLE2) || defined(CY_DEVICE_PSOC6A2M) || defined(CY_DEVICE_PSOC6A512K) || defined(CY_DEVICE_PSOC6A256K)
case CYHAL_RSC_SMIF:
clock_rsc = CYHAL_CLOCK_HF[2];
break;
case CYHAL_RSC_USB:
clock_rsc = CYHAL_CLOCK_HF[3];
break;
#endif
#if defined(CY_DEVICE_PSOC6A2M)
case CYHAL_RSC_SDHC:
clock_rsc = (clocked_item->block_num == 0)
? CYHAL_CLOCK_HF[4]
: CYHAL_CLOCK_HF[2];
break;
#elif defined(CY_DEVICE_PSOC6A512K)
case CYHAL_RSC_SDHC:
clock_rsc = CYHAL_CLOCK_HF[4];
break;
#endif
#endif
case CYHAL_RSC_CLOCK:
CY_ASSERT(false); /* Use APIs provided by the clock driver */
return CYHAL_CLOCK_RSLT_ERR_NOT_SUPPORTED;
default:
{
const cyhal_clock_block_t PERI_DIVIDERS[] =
{
CYHAL_CLOCK_BLOCK_PERIPHERAL_8BIT,
CYHAL_CLOCK_BLOCK_PERIPHERAL_16BIT,
CYHAL_CLOCK_BLOCK_PERIPHERAL_16_5BIT,
CYHAL_CLOCK_BLOCK_PERIPHERAL_24_5BIT
};
cy_rslt_t result = CYHAL_HWMGR_RSLT_ERR_NONE_FREE;
bool found_minimum = false;
for(size_t i = 0; i < sizeof(PERI_DIVIDERS) / sizeof(PERI_DIVIDERS[0]); ++i)
{
if(PERI_DIVIDERS[i] == div)
{
found_minimum = true;
}
if(found_minimum)
{
result = cyhal_clock_allocate(clock, PERI_DIVIDERS[i]);
if(CY_RSLT_SUCCESS == result || !accept_larger)
{
break;
}
}
}
return result;
}
}
#if defined(COMPONENT_CAT1A) || defined(COMPONENT_CAT1B)
cy_rslt_t result = cyhal_clock_get(clock, &clock_rsc);
if(CY_RSLT_SUCCESS == result)
{
result = cyhal_clock_init(clock);
}
return result;
#endif
}
#if defined(COMPONENT_CAT1A) || defined(COMPONENT_CAT1B)
cy_rslt_t _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)
{
const uint8_t HFCLK_DIVIDERS[] = { 1, 2, 4, 8};
cy_rslt_t retval = CYHAL_CLOCK_RSLT_ERR_FREQ;
uint32_t tolerance_check_value = (NULL != tolerance) ? tolerance->value : 0xFFFFFFFF;
cyhal_clock_tolerance_unit_t tolerance_type = (NULL != tolerance) ? tolerance->type : CYHAL_TOLERANCE_HZ;
for(uint8_t i = 0; i < sizeof(HFCLK_DIVIDERS) / sizeof(HFCLK_DIVIDERS[0]); ++i)
{
const uint32_t divider = HFCLK_DIVIDERS[i];
uint32_t actual_freq = hz_src / divider;
if ((actual_freq > desired_hz) && only_below_desired)
continue;
uint32_t achieved_tolerance = abs(_cyhal_utils_calculate_tolerance(tolerance_type, desired_hz, actual_freq));
if (achieved_tolerance < tolerance_check_value)
{
*div = divider;
retval = CY_RSLT_SUCCESS;
if ((NULL != tolerance) || (achieved_tolerance == 0))
break;
tolerance_check_value = achieved_tolerance;
}
else if (only_below_desired)
{
/* We are going from smallest divider, to highest. If we've not achieved better tolerance in
* this iteration, we will no achieve it in futher for sure. */
break;
}
}
return retval;
}
cy_rslt_t _cyhal_utils_set_clock_frequency(cyhal_clock_t* clock, uint32_t hz, const cyhal_clock_tolerance_t *tolerance)
{
if(clock->block == CYHAL_CLOCK_BLOCK_HF)
{
uint32_t divider;
cy_en_clkhf_in_sources_t source = Cy_SysClk_ClkHfGetSource(clock->channel);
uint32_t source_hz = Cy_SysClk_ClkPathGetFrequency((uint32_t)source);
if (CY_RSLT_SUCCESS == _cyhal_utils_find_hf_clk_div(source_hz, hz, tolerance, false, &divider))
{
return cyhal_clock_set_divider(clock, divider);
}
return CYHAL_CLOCK_RSLT_ERR_FREQ;
}
else
{
// Defer to the clock driver
return cyhal_clock_set_frequency(clock, hz, tolerance);
}
}
cy_rslt_t _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)
{
CY_ASSERT(NULL != clock);
CY_ASSERT(hz != 0);
uint32_t count;
const cyhal_resource_inst_t ** sources;
cy_rslt_t retval = cyhal_clock_get_sources(clock, &sources, &count);
if (CY_RSLT_SUCCESS != retval)
return retval;
uint32_t best_tolerance_hz = 0xFFFFFFFF;
cyhal_clock_t best_clock;
uint32_t best_clock_freq = 0;
uint32_t best_divider = 1;
/* Go through all possible HFCLK clock sources and check what source fits best */
for (uint32_t i = 0; i < count; ++i)
{
cyhal_clock_t temp_clock;
if (CY_RSLT_SUCCESS == cyhal_clock_get(&temp_clock, sources[i]))
{
uint32_t cur_hf_source_freq = cyhal_clock_get_frequency(&temp_clock);
/* source frequency is much lower than desired, no reason to continue */
if ((0 == cur_hf_source_freq) ||
((NULL != tolerance) && (_cyhal_utils_calculate_tolerance(tolerance->type, hz, cur_hf_source_freq) > (int32_t)tolerance->value)))
{
continue;
}
/* Covering situation when PATHMUX has enabled FLL / PLL on its way. In that case FLL / PLL frequency
is observed on PATHMUX which is covered in other iterations of the sources loop */
if (CYHAL_CLOCK_BLOCK_PATHMUX == temp_clock.block)
{
if (((sources[i]->channel_num == 0) && Cy_SysClk_FllIsEnabled()) ||
((sources[i]->channel_num > 0) && (sources[i]->channel_num <= SRSS_NUM_PLL) &&
Cy_SysClk_PllIsEnabled(sources[i]->channel_num)))
{
continue;
}
}
uint32_t cur_clock_divider;
if (CY_RSLT_SUCCESS == div_find_func(cur_hf_source_freq, hz, NULL, true, &cur_clock_divider))
{
uint32_t cur_divided_freq = cur_hf_source_freq / cur_clock_divider;
uint32_t cur_clock_tolerance = abs(_cyhal_utils_calculate_tolerance(CYHAL_TOLERANCE_HZ, hz, cur_divided_freq));
if (cur_clock_tolerance < best_tolerance_hz)
{
best_clock = temp_clock;
best_tolerance_hz = cur_clock_tolerance;
best_clock_freq = cur_divided_freq;
best_divider = cur_clock_divider;
if (cur_divided_freq == hz)
break;
}
}
}
}
/* Verify within tolerance if one was provided. */
if (NULL != tolerance)
{
uint32_t achieved_tolerance = abs(_cyhal_utils_calculate_tolerance(tolerance->type, hz, best_clock_freq));
if ((0 == best_clock_freq) || (achieved_tolerance > tolerance->value))
retval = CYHAL_CLOCK_RSLT_ERR_FREQ;
}
if (CY_RSLT_SUCCESS == retval)
{
*hf_source = best_clock;
*div = best_divider;
}
return retval;
}
cy_rslt_t _cyhal_utils_set_clock_frequency2(cyhal_clock_t *clock, uint32_t hz, const cyhal_clock_tolerance_t *tolerance)
{
CY_ASSERT(NULL != clock);
CY_ASSERT(hz != 0);
cyhal_clock_t hf_source;
uint32_t divider = 0;
cy_rslt_t retval = _cyhal_utils_find_hf_source_n_divider(clock, hz, tolerance, _cyhal_utils_find_hf_clk_div,
&hf_source, &divider);
if (CY_RSLT_SUCCESS == retval)
{
retval = cyhal_clock_set_source(clock, &hf_source);
}
if (CY_RSLT_SUCCESS == retval)
{
retval = cyhal_clock_set_divider(clock, divider);
}
return retval;
}
#endif
#if defined(__cplusplus)
}
#endif