/** * @file udelay.c * @brief udelay common source * * @copyright Copyright (c) 2022 Semidrive Semiconductor. * All rights reserved. */ #include #include #include #include #include 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 }