Files
E3_boot/middleware/udelay/udelay.c
2025-11-07 10:05:24 +08:00

101 lines
2.6 KiB
C

/**
* @file udelay.c
* @brief udelay common source
*
* @copyright Copyright (c) 2022 Semidrive Semiconductor.
* All rights reserved.
*/
#include <udelay/udelay.h>
#include <armv7-r/pmu.h>
#include <sdrv_ckgen.h>
#include <clock_ip.h>
#include <debug.h>
static uint32_t core_frequency;
static uint32_t cnt_per_us;
static inline uint32_t udelay_us_to_counter(uint32_t us)
{
if (us > MAX_UDELAY) {
us = MAX_UDELAY;
}
if (cnt_per_us == 0x0) {
core_frequency = sdrv_ckgen_bus_get_rate(CLK_NODE(g_ckgen_bus_cr5_sf),
CKGEN_BUS_CLK_OUT_M);
cnt_per_us = (uint32_t)(core_frequency / 1000000);
}
return cnt_per_us * us;
}
/**
* @brief Initialize ARM Performance Monitor cycle counter
*
* sdrv udelay is implement by arm pmu cycle counter, you must
* call this function before use udelay().
*
* note: this function must called after cpu clock has configurated.
*/
void sdrv_pmu_counter_init(void)
{
core_frequency = sdrv_ckgen_bus_get_rate(CLK_NODE(g_ckgen_bus_cr5_sf),
CKGEN_BUS_CLK_OUT_M);
cnt_per_us = (uint32_t)(core_frequency / 1000000);
if (!pmu_cycle_cntr_status()) {
pmu_enable();
pmu_start_cycle_cntr(false);
}
else {
pmu_stop_clear_cycle_cntr();
pmu_start_cycle_cntr(false);
}
}
/**
* @brief delay for microseconds
*
* @param us microseconds
*/
void udelay(uint32_t us)
{
#if CONFIG_ARM_WITH_PMU
if (!pmu_cycle_cntr_status()) {
sdrv_pmu_counter_init();
}
if (pmu_cycle_cntr_status() && (us > 0)) {
uint32_t tick_to, start_tick, current_tick;
uint32_t tmt = 0u;
start_tick = pmu_get_cycle_cntr();
/* make sure cycled information: 100 loops takes ~5us */
while ((start_tick == pmu_get_cycle_cntr()) && (tmt++ < 100u));
/* timeout check is not a must here, just in case the pmccntr abnormal */
if (tmt < 100u) {
tick_to = start_tick + udelay_us_to_counter(us);
if (tick_to > start_tick) {
/* tick_to not overflowed */
do {
current_tick = pmu_get_cycle_cntr();
} while ((current_tick < tick_to)
&& (current_tick > start_tick));
} else {
/* current_tick overflowed */
do {
current_tick = pmu_get_cycle_cntr();
} while ((current_tick > start_tick) || (current_tick < tick_to));
}
}
}
#endif
}