diff --git a/nrfx/drivers/include/nrfx_clock.h b/nrfx/drivers/include/nrfx_clock.h index c87cd428..4342dbb0 100644 --- a/nrfx/drivers/include/nrfx_clock.h +++ b/nrfx/drivers/include/nrfx_clock.h @@ -40,6 +40,13 @@ #if defined(LFRC_PRESENT) #include #endif +#if NRF_CLOCK_HAS_HFCLK +#include +#endif +#if NRF_CLOCK_HAS_XO +#include +#endif +#include #ifdef __cplusplus extern "C" { @@ -58,19 +65,20 @@ extern "C" { /** @brief Clock events. */ typedef enum { - NRFX_CLOCK_EVT_HFCLK_STARTED = NRFX_BITMASK_TO_BITPOS(NRF_CLOCK_INT_HF_STARTED_MASK), ///< HFCLK has been started. +#if NRF_CLOCK_HAS_HFCLK + NRFX_CLOCK_EVT_HFCLK_STARTED = NRFX_CLOCK_HFCLK_EVT_HFCLK_STARTED, ///< HFCLK has been started. +#else + NRFX_CLOCK_EVT_HFCLK_STARTED = NRFX_CLOCK_XO_EVT_HFCLK_STARTED, ///< HFCLK has been started. +#endif #if NRF_CLOCK_HAS_PLL - NRFX_CLOCK_EVT_PLL_STARTED = NRFX_BITMASK_TO_BITPOS(NRF_CLOCK_INT_PLL_STARTED_MASK), ///< PLL has been started. + NRFX_CLOCK_EVT_PLL_STARTED = NRFX_CLOCK_XO_EVT_PLL_STARTED, ///< PLL has been started. #endif - NRFX_CLOCK_EVT_LFCLK_STARTED = NRFX_BITMASK_TO_BITPOS(NRF_CLOCK_INT_LF_STARTED_MASK), ///< LFCLK has been started. + NRFX_CLOCK_EVT_LFCLK_STARTED = NRFX_CLOCK_LFCLK_EVT_LFCLK_STARTED, ///< LFCLK has been started. #if NRF_CLOCK_HAS_CALIBRATION_TIMER - NRFX_CLOCK_EVT_CTTO = NRFX_BITMASK_TO_BITPOS(NRF_CLOCK_INT_CTTO_MASK), ///< Calibration timeout. + NRFX_CLOCK_EVT_CTTO = NRFX_CLOCK_LFCLK_EVT_CTTO, ///< Calibration timeout. #endif -#if NRF_CLOCK_HAS_CALIBRATION - NRFX_CLOCK_EVT_CAL_DONE = NRFX_BITMASK_TO_BITPOS(NRF_CLOCK_INT_DONE_MASK), ///< Calibration has been done. -#elif NRFX_CHECK(NRF_LFRC_HAS_CALIBRATION) - NRFX_CLOCK_EVT_CAL_DONE = (NRFX_BITMASK_TO_BITPOS(NRF_LFRC_INT_CALDONE_MASK) + \ - NRFX_CLOCK_LFRC_EVT_OFFSET), ///< Calibration has been done. +#if NRF_CLOCK_HAS_CALIBRATION || NRFX_CHECK(NRF_LFRC_HAS_CALIBRATION) + NRFX_CLOCK_EVT_CAL_DONE = NRFX_CLOCK_LFCLK_EVT_CAL_DONE, ///< Calibration has been done. #endif #if NRF_CLOCK_HAS_HFCLKAUDIO NRFX_CLOCK_EVT_HFCLKAUDIO_STARTED = NRFX_BITMASK_TO_BITPOS(NRF_CLOCK_INT_HFAUDIO_STARTED_MASK), ///< HFCLKAUDIO has been started. @@ -82,9 +90,9 @@ typedef enum NRFX_CLOCK_EVT_HFCLK192M_STARTED = NRFX_BITMASK_TO_BITPOS(NRF_CLOCK_INT_HF192M_STARTED_MASK), ///< HFCLK192M has been started. #endif #if NRF_CLOCK_HAS_XO_TUNE - NRFX_CLOCK_EVT_XO_TUNED = NRFX_BITMASK_TO_BITPOS(NRF_CLOCK_INT_XOTUNED_MASK), ///< XO tune has been done. - NRFX_CLOCK_EVT_XO_TUNE_ERROR = NRFX_BITMASK_TO_BITPOS(NRF_CLOCK_INT_XOTUNEERROR_MASK), ///< XO is not tuned. - NRFX_CLOCK_EVT_XO_TUNE_FAILED = NRFX_BITMASK_TO_BITPOS(NRF_CLOCK_INT_XOTUNEFAILED_MASK), ///< XO tune operation failed. + NRFX_CLOCK_EVT_XO_TUNED = NRFX_CLOCK_XO_EVT_XO_TUNED, ///< XO tune has been done. + NRFX_CLOCK_EVT_XO_TUNE_ERROR = NRFX_CLOCK_XO_EVT_XO_TUNE_ERROR, ///< XO is not tuned. + NRFX_CLOCK_EVT_XO_TUNE_FAILED = NRFX_CLOCK_XO_EVT_XO_TUNE_FAILED, ///< XO tune operation failed. #endif } nrfx_clock_evt_type_t; @@ -181,55 +189,6 @@ nrfx_err_t nrfx_clock_divider_set(nrf_clock_domain_t domain, NRFX_STATIC_INLINE nrf_clock_hfclk_div_t nrfx_clock_divider_get(nrf_clock_domain_t domain); #endif -/** - * @brief Function for starting the LFCLK. - * - * @note This function is deprecated. Use @ref nrfx_clock_start instead. - */ -NRFX_STATIC_INLINE void nrfx_clock_lfclk_start(void); - -/** - * @brief Function for stopping the LFCLK. - * - * @note This function is deprecated. Use @ref nrfx_clock_stop instead. - */ -NRFX_STATIC_INLINE void nrfx_clock_lfclk_stop(void); - -/** - * @brief Function for checking the LFCLK state. - * - * @note This function is deprecated. Use @ref nrfx_clock_is_running instead. - * - * @retval true The LFCLK is running. - * @retval false The LFCLK is not running. - */ -NRFX_STATIC_INLINE bool nrfx_clock_lfclk_is_running(void); - -/** - * @brief Function for starting the high-accuracy source HFCLK. - * - * @note This function is deprecated. Use @ref nrfx_clock_start instead. - */ -NRFX_STATIC_INLINE void nrfx_clock_hfclk_start(void); - -/** - * @brief Function for stopping the external high-accuracy source HFCLK. - * - * @note This function is deprecated. Use @ref nrfx_clock_stop instead. - */ -NRFX_STATIC_INLINE void nrfx_clock_hfclk_stop(void); - -/** - * @brief Function for checking the HFCLK state. - * - * @note This function is deprecated. Use @ref nrfx_clock_is_running instead. - * - * @retval true The HFCLK is running (XTAL source). - * @retval false The HFCLK is not running. - */ -NRFX_STATIC_INLINE bool nrfx_clock_hfclk_is_running(void); - - #if NRF_CLOCK_HAS_HFCLKAUDIO || defined(__NRFX_DOXYGEN__) /** * @brief Function for setting the HFCLKAUDIO configuration. @@ -379,26 +338,6 @@ NRFX_STATIC_INLINE nrf_clock_hfclk_div_t nrfx_clock_divider_get(nrf_clock_domain } #endif // defined(CLOCK_FEATURE_HFCLK_DIVIDE_PRESENT) || NRF_CLOCK_HAS_HFCLK192M -NRFX_STATIC_INLINE void nrfx_clock_lfclk_start(void) -{ - nrfx_clock_start(NRF_CLOCK_DOMAIN_LFCLK); -} - -NRFX_STATIC_INLINE void nrfx_clock_lfclk_stop(void) -{ - nrfx_clock_stop(NRF_CLOCK_DOMAIN_LFCLK); -} - -NRFX_STATIC_INLINE void nrfx_clock_hfclk_start(void) -{ - nrfx_clock_start(NRF_CLOCK_DOMAIN_HFCLK); -} - -NRFX_STATIC_INLINE void nrfx_clock_hfclk_stop(void) -{ - nrfx_clock_stop(NRF_CLOCK_DOMAIN_HFCLK); -} - NRFX_STATIC_INLINE uint32_t nrfx_clock_task_address_get(nrf_clock_task_t task) { return nrf_clock_task_address_get(NRF_CLOCK, task); @@ -411,19 +350,19 @@ NRFX_STATIC_INLINE uint32_t nrfx_clock_event_address_get(nrf_clock_event_t event NRFX_STATIC_INLINE bool nrfx_clock_is_running(nrf_clock_domain_t domain, void * p_clk_src) { - return nrf_clock_is_running(NRF_CLOCK, domain, p_clk_src); -} - -NRFX_STATIC_INLINE bool nrfx_clock_hfclk_is_running(void) -{ - nrf_clock_hfclk_t clk_src; - bool ret = nrfx_clock_is_running(NRF_CLOCK_DOMAIN_HFCLK, &clk_src); - return (ret && (clk_src == NRF_CLOCK_HFCLK_HIGH_ACCURACY)); -} - -NRFX_STATIC_INLINE bool nrfx_clock_lfclk_is_running(void) -{ - return nrfx_clock_is_running(NRF_CLOCK_DOMAIN_LFCLK, NULL); + switch(domain) + { + case NRF_CLOCK_DOMAIN_HFCLK: +#if NRF_CLOCK_HAS_HFCLK + return nrfx_clock_hfclk_running_check(p_clk_src); +#elif NRF_CLOCK_HAS_XO + return nrfx_clock_xo_running_check(p_clk_src); +#endif + case NRF_CLOCK_DOMAIN_LFCLK: + return nrfx_clock_lfclk_running_check(p_clk_src); + default: + return nrf_clock_is_running(NRF_CLOCK, domain, p_clk_src); + } } #if NRF_CLOCK_HAS_HFCLKAUDIO diff --git a/nrfx/drivers/include/nrfx_clock_hfclk.h b/nrfx/drivers/include/nrfx_clock_hfclk.h new file mode 100644 index 00000000..d4aefd33 --- /dev/null +++ b/nrfx/drivers/include/nrfx_clock_hfclk.h @@ -0,0 +1,69 @@ +/*$$$LICENCE_NORDIC_STANDARD<2025>$$$*/ + +#ifndef NRFX_CLOCK_HFCLK_H__ +#define NRFX_CLOCK_HFCLK_H__ + +#include + +#define NRFX_CLOCK_HFCLK_EVT_HFCLK_STARTED NRFX_BITMASK_TO_BITPOS(NRF_CLOCK_INT_HF_STARTED_MASK) + +/** + * @brief Clock event handler. + * + * @param[in] event Event. + */ +typedef void (*nrfx_clock_hfclk_event_handler_t)(void); + +/** + * @brief Function for initializing internal structures in the nrfx_clock_hfclk module. + * + * After initialization, the module is in power off state (clock is not started). + * + * @param[in] event_handler Event handler provided by the user. + * If not provided, driver works in blocking mode. + * + * @retval NRFX_SUCCESS The procedure is successful. + * @retval NRFX_ERROR_ALREADY The driver is already initialized. + */ +nrfx_err_t nrfx_clock_hfclk_init(nrfx_clock_hfclk_event_handler_t event_handler); + +/** @brief Function for uninitializing the clock module. */ +void nrfx_clock_hfclk_uninit(void); + +/** @brief Function for starting the hfclk clock domain. */ +void nrfx_clock_hfclk_start(void); + +/** @brief Function for stopping the hfclk clock domain. */ +void nrfx_clock_hfclk_stop(void); + +#if defined(CLOCK_FEATURE_HFCLK_DIVIDE_PRESENT) +/** + * @brief Function for setting the hfclk divider. + * + * @param[in] div New divider for the hfclk. + * + * @retval NRFX_SUCCESS Divider successfully set. + * @retval NRFX_ERROR_INVALID_PARAM Divider not supported by the specified domain. + */ +nrfx_err_t nrfx_clock_hfclk_divider_set(nrf_clock_hfclk_div_t div); +#endif + +/** + * @brief Function for checking the hfclk state. + * + * @param[out] p_clk_src Pointer to a clock source that is running. + * + * @retval true The hfclk is running. + * @retval false The hfclk is not running. + */ +NRFX_STATIC_INLINE bool nrfx_clock_hfclk_running_check(nrf_clock_hfclk_t * p_clk_src); + +void nrfx_clock_hfclk_irq_handler(void); + +NRFX_STATIC_INLINE bool nrfx_clock_hfclk_running_check(nrf_clock_hfclk_t * p_clk_src) +{ + bool ret = nrf_clock_is_running(NRF_CLOCK, NRF_CLOCK_DOMAIN_HFCLK, p_clk_src); + return (ret && (*p_clk_src == NRF_CLOCK_HFCLK_HIGH_ACCURACY)); +} + +#endif // NRFX_CLOCK_HFCLK_H__ diff --git a/nrfx/drivers/include/nrfx_clock_hfclk192m.h b/nrfx/drivers/include/nrfx_clock_hfclk192m.h new file mode 100644 index 00000000..e842f499 --- /dev/null +++ b/nrfx/drivers/include/nrfx_clock_hfclk192m.h @@ -0,0 +1,354 @@ +/* + * Copyright (c) 2016 - 2025, Nordic Semiconductor ASA + * All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its + * contributors may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef NRFX_CLOCK_HFCLK192M_H__ +#define NRFX_CLOCK_HFCLK192M_H__ + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @defgroup nrfx_clock_hfclk192m HFCLK192M driver + * @{ + * @ingroup nrf_clock_hfclk192m + * @brief HFCLK192M peripheral driver. + */ + +/** @brief Clock events. */ +typedef enum +{ +#if NRF_CLOCK_HAS_HFCLK192M + NRFX_CLOCK_HFCLK192M_EVT_HFCLK192M_STARTED = NRFX_BITMASK_TO_BITPOS(NRF_CLOCK_INT_HF192M_STARTED_MASK), ///< HFCLK192M has been started. +#endif +} nrfx_clock_hfclk192m_evt_type_t; + +/** + * @brief Clock event handler. + * + * @param[in] event Event. + */ +typedef void (*nrfx_clock_hfclks192m_event_handler_t)(nrfx_clock_hfclk192m_evt_type_t event); + +/** + * @brief Function for initializing internal structures in the nrfx_clock module. + * + * After initialization, the module is in power off state (clocks are not started). + * + * @param[in] event_handler Event handler provided by the user. + * If not provided, driver works in blocking mode. + * + * @retval NRFX_SUCCESS The procedure is successful. + * @retval NRFX_ERROR_ALREADY The driver is already initialized. + */ +nrfx_err_t nrfx_clock_init(nrfx_clock_hfclks192m_event_handler_t event_handler); + +/** @brief Function for enabling interrupts in the clock module. */ +void nrfx_clock_enable(void); + +/** @brief Function for disabling interrupts in the clock module. */ +void nrfx_clock_disable(void); + +/** @brief Function for uninitializing the clock module. */ +void nrfx_clock_uninit(void); + +/** + * @brief Function for checking if the clock driver is initialized. + * + * @retval true Driver is already initialized. + * @retval false Driver is not initialized. + */ +bool nrfx_clock_init_check(void); + +/** + * @brief Function for starting the specified clock domain. + * + * @param[in] domain Clock domain. + */ +void nrfx_clock_start(nrf_clock_domain_t domain); + +/** + * @brief Function for stopping the specified clock domain. + * + * @param[in] domain Clock domain. + */ +void nrfx_clock_stop(nrf_clock_domain_t domain); + +/** + * @brief Function for checking the specified clock domain state. + * + * XTAL source is assumed for domains with multiple sources. + * + * @param[in] domain Clock domain. + * @param[out] p_clk_src Pointer to a clock source that is running. Set to NULL if not needed. + * Ignored for HFCLKAUDIO domain. Variable pointed by @p p_clk_src + * must be of either @ref nrf_clock_lfclk_t type for LFCLK + * or @ref nrf_clock_hfclk_t type for HFCLK and HFCLK192M. + * + * @retval true The clock domain is running. + * @retval false The clock domain is not running. + */ +NRFX_STATIC_INLINE bool nrfx_clock_is_running(nrf_clock_domain_t domain, void * p_clk_src); + +#if defined(CLOCK_FEATURE_HFCLK_DIVIDE_PRESENT) || NRF_CLOCK_HAS_HFCLK192M || \ + defined(__NRFX_DOXYGEN__) +/** + * @brief Function for setting the specified clock domain divider. + * + * @param[in] domain Clock domain. + * @param[in] div New divider for the clock domain. + * + * @retval NRFX_SUCCESS Divider successfully set. + * @retval NRFX_ERROR_NOT_SUPPORTED Domain does not support setting the divider. + * @retval NRFX_ERROR_INVALID_PARAM Divider not supported by the specified domain. + */ +nrfx_err_t nrfx_clock_divider_set(nrf_clock_domain_t domain, + nrf_clock_hfclk_div_t div); + +/** + * @brief Function for getting the specified clock domain divider. + * + * @param[in] domain Clock domain. + * + * @return Current divider for the specified clock domain. + */ + +NRFX_STATIC_INLINE nrf_clock_hfclk_div_t nrfx_clock_divider_get(nrf_clock_domain_t domain); +#endif + +#if NRF_CLOCK_HAS_HFCLKAUDIO || defined(__NRFX_DOXYGEN__) +/** + * @brief Function for setting the HFCLKAUDIO configuration. + * + * The frequency of HFCLKAUDIO ranges from 10.666 MHz to 13.333 MHz in 40.7 Hz steps. + * To calculate @p freq_value corresponding to the chosen frequency, use the following equation: + * FREQ_VALUE = 2^16 * ((12 * f_out / 32M) - 4) + * + * @warning Chosen frequency must fit in 11.176 MHz - 11.402 MHz or 12.165 MHz - 12.411 MHz + * frequency bands. + * + * @param[in] freq_value New FREQ_VALUE for HFCLKAUDIO. + */ +NRFX_STATIC_INLINE void nrfx_clock_hfclkaudio_config_set(uint16_t freq_value); + +/** + * @brief Function for getting the HFCLKAUDIO configuration. + * + * The frequency of HFCLKAUDIO ranges from 10.666 MHz to 13.333 MHz in 40.7 Hz steps. + * To calculate frequency corresponding to the returned FREQ_VALUE, use the following equation: + * f_out = 32M * (4 + FREQ_VALUE * 2^(-16))/12 + * + * @return Current value of FREQ_VALUE for HFCLKAUDIO. + */ +NRFX_STATIC_INLINE uint16_t nrfx_clock_hfclkaudio_config_get(void); +#endif + +#if NRF_CLOCK_HAS_XO_TUNE + +/** + * @brief Function for starting tune of crystal HFCLK. + * + * This function starts tuning process of the HFCLK. + * + * @retval NRFX_SUCCESS The procedure is successful. + * @retval NRFX_ERROR_INVALID_STATE The high-frequency XO clock is off or operation is in progress. + * @retval NRFX_ERROR_INTERNAL XO tune operation failed. + */ +nrfx_err_t nrfx_clock_xo_tune_start(void); + +/** + * @brief Function for aborting tune of crystal HFCLK. + * + * This function aborts tuning process. + * + * @retval NRFX_SUCCESS The procedure is successful. + * @retval NRFX_ERROR_INVALID_STATE The high-frequency XO clock is off or operation is not in progress. + */ +nrfx_err_t nrfx_clock_xo_tune_abort(void); + +/** + * @brief Function for checking if XO tune error occurred. + * + * @note Must be used only if @p event_handler was not provided during driver initialization. + * + * @retval true XO tune procedure failed. + * @retval false No error. + */ +bool nrfx_clock_xo_tune_error_check(void); + +/** + * @brief Function for checking if XO has been successfully tuned. + * + * @retval true XO is successfully tuned. + * @retval false XO is not tuned. + */ +bool nrfx_clock_xo_tune_status_check(void); + +#endif + +#if ((NRF_CLOCK_HAS_CALIBRATION || NRFX_CHECK(NRF_LFRC_HAS_CALIBRATION)) && \ + NRFX_CHECK(NRFX_CLOCK_CONFIG_LF_CAL_ENABLED)) || defined(__NRFX_DOXYGEN__) +/** + * @brief Function for starting the calibration of internal LFCLK. + * + * This function starts the calibration process. The process cannot be aborted. LFCLK and HFCLK + * must be running before this function is called. + * + * @retval NRFX_SUCCESS The procedure is successful. + * @retval NRFX_ERROR_INVALID_STATE The low-frequency or high-frequency clock is off. + * @retval NRFX_ERROR_BUSY Clock is in the calibration phase. + */ +nrfx_err_t nrfx_clock_calibration_start(void); + +/** + * @brief Function for checking if calibration is in progress. + * + * This function indicates that the system is in calibration phase. + * + * @retval NRFX_SUCCESS The procedure is successful. + * @retval NRFX_ERROR_BUSY Clock is in the calibration phase. + */ +nrfx_err_t nrfx_clock_is_calibrating(void); + +#if (NRF_CLOCK_HAS_CALIBRATION_TIMER && NRFX_CHECK(NRFX_CLOCK_CONFIG_CT_ENABLED)) || \ + defined(__NRFX_DOXYGEN__) +/** + * @brief Function for starting calibration timer. + * + * @param[in] interval Time after which the CTTO event and interrupt will be generated (in 0.25 s units). + */ +void nrfx_clock_calibration_timer_start(uint8_t interval); + +/** @brief Function for stopping the calibration timer. */ +void nrfx_clock_calibration_timer_stop(void); +#endif +#endif /* ((NRF_CLOCK_HAS_CALIBRATION || NRFX_CHECK(NRF_LFRC_HAS_CALIBRATION)) && \ + NRFX_CHECK(NRFX_CLOCK_CONFIG_LF_CAL_ENABLED)) || defined(__NRFX_DOXYGEN__) */ + +/** + * @brief Function for returning a requested task address for the clock driver module. + * + * @param[in] task One of the peripheral tasks. + * + * @return Task address. + */ +NRFX_STATIC_INLINE uint32_t nrfx_clock_task_address_get(nrf_clock_task_t task); + +/** + * @brief Function for returning a requested event address for the clock driver module. + * + * @param[in] event One of the peripheral events. + * + * @return Event address. + */ +NRFX_STATIC_INLINE uint32_t nrfx_clock_event_address_get(nrf_clock_event_t event); + +#ifndef NRFX_DECLARE_ONLY + +#if defined(CLOCK_FEATURE_HFCLK_DIVIDE_PRESENT) || NRF_CLOCK_HAS_HFCLK192M +NRFX_STATIC_INLINE nrf_clock_hfclk_div_t nrfx_clock_divider_get(nrf_clock_domain_t domain) +{ + switch (domain) + { +#if defined(CLOCK_FEATURE_HFCLK_DIVIDE_PRESENT) + case NRF_CLOCK_DOMAIN_HFCLK: + return nrf_clock_hfclk_div_get(NRF_CLOCK); +#endif +#if NRF_CLOCK_HAS_HFCLK192M + case NRF_CLOCK_DOMAIN_HFCLK192M: + return nrf_clock_hfclk192m_div_get(NRF_CLOCK); +#endif + default: + NRFX_ASSERT(0); + return (nrf_clock_hfclk_div_t)0; + } +} +#endif // defined(CLOCK_FEATURE_HFCLK_DIVIDE_PRESENT) || NRF_CLOCK_HAS_HFCLK192M + +NRFX_STATIC_INLINE uint32_t nrfx_clock_task_address_get(nrf_clock_task_t task) +{ + return nrf_clock_task_address_get(NRF_CLOCK, task); +} + +NRFX_STATIC_INLINE uint32_t nrfx_clock_event_address_get(nrf_clock_event_t event) +{ + return nrf_clock_event_address_get(NRF_CLOCK, event); +} + +NRFX_STATIC_INLINE bool nrfx_clock_is_running(nrf_clock_domain_t domain, void * p_clk_src) +{ + switch(domain) + { + case NRF_CLOCK_DOMAIN_HFCLK: +#if NRF_CLOCK_HAS_HFCLK + return nrfx_clock_hfclk_running_check(p_clk_src); +#elif NRF_CLOCK_HAS_XO + return nrfx_clock_xo_running_check(p_clk_src); +#endif + case NRF_CLOCK_DOMAIN_LFCLK: + return nrfx_clock_lfclk_running_check(p_clk_src); + default: + return nrf_clock_is_running(NRF_CLOCK, domain, p_clk_src); + } +} + +#if NRF_CLOCK_HAS_HFCLKAUDIO + +NRFX_STATIC_INLINE void nrfx_clock_hfclkaudio_config_set(uint16_t freq_value) +{ + nrf_clock_hfclkaudio_config_set(NRF_CLOCK, freq_value); +} + +NRFX_STATIC_INLINE uint16_t nrfx_clock_hfclkaudio_config_get(void) +{ + return nrf_clock_hfclkaudio_config_get(NRF_CLOCK); +} + +#endif + +#endif // NRFX_DECLARE_ONLY + +/** @} */ + + +void nrfx_clock_irq_handler(void); + + +#ifdef __cplusplus +} +#endif + +#endif // NRFX_CLOCK_HFCLK192M_H__ diff --git a/nrfx/drivers/include/nrfx_clock_lfclk.h b/nrfx/drivers/include/nrfx_clock_lfclk.h new file mode 100644 index 00000000..cd65bab7 --- /dev/null +++ b/nrfx/drivers/include/nrfx_clock_lfclk.h @@ -0,0 +1,184 @@ +/* + * Copyright (c) 2016 - 2025, Nordic Semiconductor ASA + * All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * 3. Nittner the name of the copyright holder nor the names of its + * contributors may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef NRFX_CLOCK_LFCLK_H__ +#define NRFX_CLOCK_LFCLK_H__ + +#include +#include + +#if defined(LFRC_PRESENT) +#include +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @defgroup nrfx_clock_lfclk LFCLK driver + * @{ + * @ingroup nrf_clock_lfclk + * @brief LFCLK clock driver. + */ + + /** @brief Symbol specifying driver event offset for LFRC hardware events. */ +#define NRFX_CLOCK_LFCLK_LFRC_EVT_OFFSET 32 + +/** @brief Clock events. */ +typedef enum +{ + NRFX_CLOCK_LFCLK_EVT_LFCLK_STARTED = NRFX_BITMASK_TO_BITPOS(NRF_CLOCK_INT_LF_STARTED_MASK), ///< LFCLK has been started. +#if NRF_CLOCK_HAS_CALIBRATION_TIMER + NRFX_CLOCK_LFCLK_EVT_CTTO = NRFX_BITMASK_TO_BITPOS(NRF_CLOCK_INT_CTTO_MASK), ///< Calibration timeout. +#endif +#if NRF_CLOCK_HAS_CALIBRATION + NRFX_CLOCK_LFCLK_EVT_CAL_DONE = NRFX_BITMASK_TO_BITPOS(NRF_CLOCK_INT_DONE_MASK), ///< Calibration has been done. +#elif NRFX_CHECK(NRF_LFRC_HAS_CALIBRATION) + NRFX_CLOCK_LFCLK_EVT_CAL_DONE = (NRFX_BITMASK_TO_BITPOS(NRF_LFRC_INT_CALDONE_MASK) + \ + NRFX_CLOCK_LFCLK_LFRC_EVT_OFFSET), ///< Calibration has been done. +#endif + + +} nrfx_clock_lfclk_evt_type_t; + +/** + * @brief Lfclk event handler. + * + * @param[in] event Event. + */ +typedef void (*nrfx_clock_lfclk_event_handler_t)(nrfx_clock_lfclk_evt_type_t event); + +/** + * @brief Function for initializing internal structures in the nrfx_clock_lfclk module. + * + * After initialization, the module is in power off state (clocks are not started). + * + * @param[in] event_handler Event handler provided by the user. + * If not provided, driver works in blocking mode. + * + * @retval NRFX_SUCCESS The procedure is successful. + * @retval NRFX_ERROR_ALREADY The driver is already initialized. + */ +nrfx_err_t nrfx_clock_lfclk_init(nrfx_clock_lfclk_event_handler_t event_handler); + +/** @brief Function for uninitializing the lfclk module. */ +void nrfx_clock_lfclk_uninit(void); + +/** + * @brief Function for checking if the lfclk driver is initialized. + * + * @retval true Driver is already initialized. + * @retval false Driver is not initialized. + */ +bool nrfx_clock_lfclk_init_check(void); + +/** + * @brief Function for starting the LFCLK. + */ +void nrfx_clock_lfclk_start(void); + +/** + * @brief Function for stopping the LFCLK. + */ +void nrfx_clock_lfclk_stop(void); + +/** + * @brief Function for checking the LFCLK state. + * + * XTAL source is assumed for domains with multiple sources. + * + * @param[out] p_clk_src Pointer to a clock source that is running. Set to NULL if not needed. + * + * @retval true The clock domain is running. + * @retval false The clock domain is not running. + */ +NRFX_STATIC_INLINE bool nrfx_clock_lfclk_running_check(nrf_clock_lfclk_t * p_clk_src); + +#if ((NRF_CLOCK_HAS_CALIBRATION || NRFX_CHECK(NRF_LFRC_HAS_CALIBRATION)) && \ + NRFX_CHECK(NRFX_CLOCK_CONFIG_LF_CAL_ENABLED)) || defined(__NRFX_DOXYGEN__) +/** + * @brief Function for starting the calibration of internal LFCLK. + * + * This function starts the calibration process. The process cannot be aborted. LFCLK and (HFCLK or XO) + * must be running before this function is called. + * + * @retval NRFX_SUCCESS The procedure is successful. + * @retval NRFX_ERROR_INVALID_STATE The low-frequency or high-frequency clock is off. + * @retval NRFX_ERROR_BUSY Clock is in the calibration phase. + */ +nrfx_err_t nrfx_clock_lfclk_calibration_start(void); + +/** + * @brief Function for checking if calibration is in progress. + * + * This function indicates that the system is in calibration phase. + * + * @retval NRFX_SUCCESS The procedure is successful. + * @retval NRFX_ERROR_BUSY Clock is in the calibration phase. + */ +nrfx_err_t nrfx_clock_lfclk_calibrating_check(void); + +#if (NRF_CLOCK_HAS_CALIBRATION_TIMER && NRFX_CHECK(NRFX_CLOCK_CONFIG_CT_ENABLED)) || \ + defined(__NRFX_DOXYGEN__) +/** + * @brief Function for starting calibration timer. + * + * @param[in] interval Time after which the CTTO event and interrupt will be generated (in 0.25 s units). + */ +void nrfx_clock_lfclk_calibration_timer_start(uint8_t interval); + +/** @brief Function for stopping the calibration timer. */ +void nrfx_clock_lfclk_calibration_timer_stop(void); +#endif +#endif /* ((NRF_CLOCK_HAS_CALIBRATION || NRFX_CHECK(NRF_LFRC_HAS_CALIBRATION)) && \ + NRFX_CHECK(NRFX_CLOCK_CONFIG_LF_CAL_ENABLED)) || defined(__NRFX_DOXYGEN__) */ + +#ifndef NRFX_DECLARE_ONLY + +NRFX_STATIC_INLINE bool nrfx_clock_lfclk_running_check(nrf_clock_lfclk_t * p_clk_src) +{ + return nrf_clock_is_running(NRF_CLOCK, NRF_CLOCK_DOMAIN_LFCLK, (void*)p_clk_src); +} + +#endif // NRFX_DECLARE_ONLY + +/** @} */ + +void nrfx_clock_lfclk_irq_handler(void); + +#ifdef __cplusplus +} +#endif + +#endif // NRFX_CLOCK_LFCLK_H__ diff --git a/nrfx/drivers/include/nrfx_clock_xo.h b/nrfx/drivers/include/nrfx_clock_xo.h new file mode 100644 index 00000000..b7fe80ea --- /dev/null +++ b/nrfx/drivers/include/nrfx_clock_xo.h @@ -0,0 +1,179 @@ +/* + * Copyright (c) 2016 - 2025, Nordic Semiconductor ASA + * All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its + * contributors may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef NRFX_CLOCK_XO_H__ +#define NRFX_CLOCK_XO_H__ + +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @defgroup nrfx_clock_xo XO driver + * @{ + * @ingroup nrf_clock_xo + * @brief XO clock driver. + */ + +/** @brief XO clock events. */ +typedef enum +{ + NRFX_CLOCK_XO_EVT_HFCLK_STARTED = NRFX_BITMASK_TO_BITPOS(NRF_CLOCK_INT_HF_STARTED_MASK), ///< XO has been started. +#if NRF_CLOCK_HAS_PLL + NRFX_CLOCK_XO_EVT_PLL_STARTED = NRFX_BITMASK_TO_BITPOS(NRF_CLOCK_INT_PLL_STARTED_MASK), ///< PLL has been started. +#endif +#if NRF_CLOCK_HAS_XO_TUNE + NRFX_CLOCK_XO_EVT_XO_TUNED = NRFX_BITMASK_TO_BITPOS(NRF_CLOCK_INT_XOTUNED_MASK), ///< XO tune has been done. + NRFX_CLOCK_XO_EVT_XO_TUNE_ERROR = NRFX_BITMASK_TO_BITPOS(NRF_CLOCK_INT_XOTUNEERROR_MASK), ///< XO is not tuned. + NRFX_CLOCK_XO_EVT_XO_TUNE_FAILED = NRFX_BITMASK_TO_BITPOS(NRF_CLOCK_INT_XOTUNEFAILED_MASK), ///< XO tune operation failed. +#endif +} nrfx_clock_xo_evt_type_t; + +/** + * @brief XO clock event handler. + * + * @param[in] event Event. + */ +typedef void (*nrfx_clock_xo_event_handler_t)(nrfx_clock_xo_evt_type_t event); + +/** + * @brief Function for initializing internal structures in the nrfx_clock_xo module. + * + * After initialization, the module is in power off state (clocks are not started). + * + * @param[in] event_handler Event handler provided by the user. + * If not provided, driver works in blocking mode. + * + * @retval NRFX_SUCCESS The procedure is successful. + * @retval NRFX_ERROR_ALREADY The driver is already initialized. + */ +nrfx_err_t nrfx_clock_xo_init(nrfx_clock_xo_event_handler_t event_handler); + +/** @brief Function for uninitializing the clock module. */ +void nrfx_clock_xo_uninit(void); + +/** + * @brief Function for checking if the clock driver is initialized. + * + * @retval true Driver is already initialized. + * @retval false Driver is not initialized. + */ +bool nrfx_clock_xo_init_check(void); + +/** + * @brief Function for starting the xo clock. + */ +void nrfx_clock_xo_start(void); + +/** + * @brief Function for stopping the xo clock. + */ +void nrfx_clock_xo_stop(void); + +/** + * @brief Function for checking the xo clock state. + * + * @param[out] p_clk_src Pointer to a clock source that is running. + * + * @retval true The xo clock is running. + * @retval false The xo clock is not running. + */ +NRFX_STATIC_INLINE bool nrfx_clock_xo_running_check(nrf_clock_hfclk_t * p_clk_src); + + +#if NRF_CLOCK_HAS_XO_TUNE + +/** + * @brief Function for starting tune of crystal XO. + * + * This function starts tuning process of the XO. + * + * @retval NRFX_SUCCESS The procedure is successful. + * @retval NRFX_ERROR_INVALID_STATE The high-frequency XO clock is off or operation is in progress. + * @retval NRFX_ERROR_INTERNAL XO tune operation failed. + */ +nrfx_err_t nrfx_clock_xo_tune_start(void); + +/** + * @brief Function for aborting tune of crystal XO. + * + * This function aborts tuning process. + * + * @retval NRFX_SUCCESS The procedure is successful. + * @retval NRFX_ERROR_INVALID_STATE The high-frequency XO clock is off or operation is not in progress. + */ +nrfx_err_t nrfx_clock_xo_tune_abort(void); + +/** + * @brief Function for checking if XO tune error occurred. + * + * @note Must be used only if @p event_handler was not provided during driver initialization. + * + * @retval true XO tune procedure failed. + * @retval false No error. + */ +bool nrfx_clock_xo_tune_error_check(void); + +/** + * @brief Function for checking if XO has been successfully tuned. + * + * @retval true XO is successfully tuned. + * @retval false XO is not tuned. + */ +bool nrfx_clock_xo_tune_status_check(void); + +#endif + +void nrfx_clock_xo_irq_handler(void); + +#ifndef NRFX_DECLARE_ONLY + +NRFX_STATIC_INLINE bool nrfx_clock_xo_running_check(nrf_clock_hfclk_t * p_clk_src) +{ + bool ret = nrf_clock_is_running(NRF_CLOCK, NRF_CLOCK_DOMAIN_HFCLK, p_clk_src); + return (ret && (*p_clk_src == NRF_CLOCK_HFCLK_HIGH_ACCURACY)); +} + +#endif // NRFX_DECLARE_ONLY + +/** @} */ + +#ifdef __cplusplus +} +#endif + +#endif // NRFX_CLOCK_XO_H__ diff --git a/nrfx/drivers/src/nrfx_clock.c b/nrfx/drivers/src/nrfx_clock.c index 7754231a..a1d7f1d7 100644 --- a/nrfx/drivers/src/nrfx_clock.c +++ b/nrfx/drivers/src/nrfx_clock.c @@ -122,6 +122,17 @@ extern bool nrfx_power_irq_enabled; #error "Calibration timer is not available in the SoC that is used." #endif +#define INTERRUPT_MASK \ + NRF_CLOCK_INT_LF_STARTED_MASK | \ + NRFX_COND_CODE_1(NRF_CLOCK_HAS_LFCLK_SRC_CHANGED, (NRF_CLOCK_INT_LF_SRC_CHANGED_MASK |), ()) \ + NRFX_COND_CODE_1(NRF_CLOCK_HAS_CALIBRATION, (NRF_CLOCK_INT_DONE_MASK |), ()) \ + NRFX_COND_CODE_1(NRF_CLOCK_HAS_CALIBRATION_TIMER, (NRF_CLOCK_INT_CTTO_MASK |), ()) \ + NRFX_COND_CODE_1(defined(CLOCK_INTENSET_CTSTARTED_Msk) || defined(__NRFX_DOXYGEN__), (NRF_CLOCK_INT_CTSTARTED_MASK | NRF_CLOCK_INT_CTSTOPPED_MASK |), ()) \ + NRFX_COND_CODE_1(NRF_CLOCK_HAS_HFCLKAUDIO, (NRF_CLOCK_INT_HFAUDIO_STARTED_MASK |), ()) \ + NRFX_COND_CODE_1(NRF_CLOCK_HAS_HFCLK24M, (NRF_CLOCK_INT_HFCLK24M_STARTED_MASK |), ()) \ + NRFX_COND_CODE_1(NRF_CLOCK_HAS_HFCLK192M, (NRF_CLOCK_INT_HF192M_STARTED_MASK |), ()) \ + 0 + #if NRFX_CHECK(NRFX_CLOCK_CONFIG_LF_CAL_ENABLED) typedef enum { @@ -308,32 +319,23 @@ static nrf_clock_lfclk_t clock_initial_lfclksrc_get(void) #endif } -/** - * @brief Function for tweaking the specified low-frequency clock source given current driver state. - * - * @warning This function may stop currently running low-frequency clock source. - * - * @param[in,out] p_lfclksrc Pointer to the variable containing low-frequency clock source. - * It is set to adequate value in case of being inappropriate - * for current driver configuration. - * - * @return True if the specified clock source was correct, false otherwise. - */ -static bool clock_lfclksrc_tweak(nrf_clock_lfclk_t * p_lfclksrc) +#if NRF_CLOCK_HAS_HFCLK +static void hfclk_event_handler(void) { - bool is_correct_clk = (*p_lfclksrc == NRFX_CLOCK_CONFIG_LF_SRC); -#if NRFX_CHECK(NRFX_CLOCK_CONFIG_LFXO_TWO_STAGE_ENABLED) - // In case of two-stage LFXO start procedure RC source is valid as well. - is_correct_clk = is_correct_clk || (*p_lfclksrc == NRF_CLOCK_LFCLK_RC); -#endif - if (!is_correct_clk) - { - // Inappropriate LF clock source is chosen. - // Stop currently active LF clock source and choose the correct one to start. - clock_stop(NRF_CLOCK_DOMAIN_LFCLK); - *p_lfclksrc = clock_initial_lfclksrc_get(); - } - return is_correct_clk; + m_clock_cb.event_handler(NRFX_CLOCK_EVT_HFCLK_STARTED); +} +#endif // NRF_CLOCK_HAS_HFCLK + +#if NRF_CLOCK_HAS_XO +static void xo_event_handler(nrfx_clock_xo_evt_type_t event) +{ + m_clock_cb.event_handler((nrfx_clock_evt_type_t)event); +} +#endif // NRF_CLOCK_HAS_XO + +static void lfclk_event_handler(nrfx_clock_lfclk_evt_type_t event) +{ + m_clock_cb.event_handler((nrfx_clock_evt_type_t)event); } nrfx_err_t nrfx_clock_init(nrfx_clock_event_handler_t event_handler) @@ -342,6 +344,8 @@ nrfx_err_t nrfx_clock_init(nrfx_clock_event_handler_t event_handler) if (m_clock_cb.module_initialized) { err_code = NRFX_ERROR_ALREADY; + NRFX_LOG_INFO("Function: %s, error code: %s.", __func__, NRFX_LOG_ERROR_STRING_GET(err_code)); + return err_code; } else { @@ -354,8 +358,31 @@ nrfx_err_t nrfx_clock_init(nrfx_clock_event_handler_t event_handler) m_clock_cb.hfclk_started = false; #endif } +//Temp code, until nordic_nrf_clock compat is present +#if CONFIG_CLOCK_CONTROL_NRF_COMMON + return err_code; +#endif + +#if NRF_CLOCK_HAS_HFCLK + err_code |= m_clock_cb.event_handler ? nrfx_clock_hfclk_init(&hfclk_event_handler) : + nrfx_clock_hfclk_init(NULL); +#endif // NRF_CLOCK_HAS_HFCLK + +#if NRF_CLOCK_HAS_XO + err_code |= m_clock_cb.event_handler ? nrfx_clock_xo_init(&xo_event_handler) : + nrfx_clock_xo_init(NULL); +#endif // NRF_CLOCK_HAS_XO + + err_code |= m_clock_cb.event_handler ? nrfx_clock_lfclk_init(&lfclk_event_handler) : + nrfx_clock_lfclk_init(NULL); + + if(err_code != NRFX_SUCCESS) + { + NRFX_LOG_INFO("Function: %s, error code: %s.", __func__, + NRFX_LOG_ERROR_STRING_GET(err_code)); + return err_code; + } - NRFX_LOG_INFO("Function: %s, error code: %s.", __func__, NRFX_LOG_ERROR_STRING_GET(err_code)); return err_code; } @@ -367,9 +394,6 @@ void nrfx_clock_enable(void) nrfx_power_clock_irq_init(); } nrf_clock_lf_src_set(NRF_CLOCK, clock_initial_lfclksrc_get()); -#if NRF_CLOCK_HAS_HFCLKSRC - nrf_clock_hf_src_set(NRF_CLOCK, NRF_CLOCK_HFCLK_HIGH_ACCURACY); -#endif #if NRF_CLOCK_HAS_HFCLK192M nrf_clock_hfclk192m_src_set(NRF_CLOCK, (nrf_clock_hfclk_t)NRFX_CLOCK_CONFIG_HFCLK192M_SRC); #endif @@ -420,8 +444,9 @@ void nrfx_clock_disable(void) void nrfx_clock_uninit(void) { NRFX_ASSERT(m_clock_cb.module_initialized); - clock_stop(NRF_CLOCK_DOMAIN_LFCLK); - clock_stop(NRF_CLOCK_DOMAIN_HFCLK); + + nrfx_clock_lfclk_uninit(); + #if NRF_CLOCK_HAS_HFCLK192M clock_stop(NRF_CLOCK_DOMAIN_HFCLK192M); #endif @@ -430,6 +455,12 @@ void nrfx_clock_uninit(void) #endif #if NRF_CLOCK_HAS_HFCLK24M clock_stop(NRF_CLOCK_DOMAIN_HFCLK24M); +#endif +#if NRF_CLOCK_HAS_HFCLK + nrfx_clock_hfclk_uninit(); +#endif +#if NRF_CLOCK_HAS_XO + nrfx_clock_xo_uninit(); #endif m_clock_cb.module_initialized = false; NRFX_LOG_INFO("Uninitialized."); @@ -450,72 +481,16 @@ void nrfx_clock_start(nrf_clock_domain_t domain) switch (domain) { case NRF_CLOCK_DOMAIN_LFCLK: - { - nrf_clock_lfclk_t lfclksrc; - if (nrf_clock_is_running(NRF_CLOCK, NRF_CLOCK_DOMAIN_LFCLK, &lfclksrc)) - { - // LF clock is already running. Inspect its source. - // If LF clock source is inappropriate then it will be stopped and modified. - // Ignore return value as LF clock will be started again regardless of the result. - (void)clock_lfclksrc_tweak(&lfclksrc); - } - else if (nrf_clock_start_task_check(NRF_CLOCK, NRF_CLOCK_DOMAIN_LFCLK)) - { - // LF clock is not active yet but was started already. Inspect its source. - lfclksrc = nrf_clock_lf_srccopy_get(NRF_CLOCK); - if (clock_lfclksrc_tweak(&lfclksrc)) - { - // LF clock was started already and the configured source - // corresponds to the user configuration. - // No action is needed as the chosen LF clock source will become active soon. - if (m_clock_cb.event_handler) - { - nrf_clock_int_enable(NRF_CLOCK, NRF_CLOCK_INT_LF_STARTED_MASK); - } - else - { - while (!nrf_clock_event_check(NRF_CLOCK, NRF_CLOCK_EVENT_LFCLKSTARTED)) - {} - nrf_clock_event_clear(NRF_CLOCK, NRF_CLOCK_EVENT_LFCLKSTARTED); - } - return; - } - // Otherwise LF clock was started already but with inappropriate source. - // LF clock was stopped and modified. Now it will be restarted. - } - else - { - // LF clock not active and not started. - lfclksrc = clock_initial_lfclksrc_get(); - } - nrf_clock_lf_src_set(NRF_CLOCK, lfclksrc); - } - event = NRF_CLOCK_EVENT_LFCLKSTARTED; - int_mask = NRF_CLOCK_INT_LF_STARTED_MASK; - task = NRF_CLOCK_TASK_LFCLKSTART; - break; + nrfx_clock_lfclk_start(); + return; case NRF_CLOCK_DOMAIN_HFCLK: - event = NRF_CLOCK_EVENT_HFCLKSTARTED; - int_mask = NRF_CLOCK_INT_HF_STARTED_MASK | -#if NRF_CLOCK_HAS_XO_TUNE - NRF_CLOCK_INT_XOTUNED_MASK | - NRF_CLOCK_INT_XOTUNEERROR_MASK | - NRF_CLOCK_INT_XOTUNEFAILED_MASK | -#endif - 0; -#if NRF_CLOCK_HAS_XO_TUNE - nrf_clock_event_clear(NRF_CLOCK, NRF_CLOCK_EVENT_XOTUNED); - nrf_clock_event_clear(NRF_CLOCK, NRF_CLOCK_EVENT_XOTUNEFAILED); - nrf_clock_event_clear(NRF_CLOCK, NRF_CLOCK_EVENT_XOTUNEERROR); -#endif - task = NRF_CLOCK_TASK_HFCLKSTART; -#if NRFX_CHECK(NRF54L_ERRATA_39_ENABLE_WORKAROUND) - if (nrf54l_errata_39()) - { - nrf_clock_task_trigger(NRF_CLOCK, NRF_CLOCK_TASK_PLLSTART); - } -#endif - break; +#if NRF_CLOCK_HAS_XO + nrfx_clock_xo_start(); +#elif NRF_CLOCK_HAS_HFCLK + nrfx_clock_hfclk_start(); +#endif // NRF_CLOCK_HAS_HFCLK + return; + #if NRF_CLOCK_HAS_HFCLK192M case NRF_CLOCK_DOMAIN_HFCLK192M: event = NRF_CLOCK_EVENT_HFCLK192MSTARTED; @@ -565,207 +540,49 @@ void nrfx_clock_start(nrf_clock_domain_t domain) void nrfx_clock_stop(nrf_clock_domain_t domain) { NRFX_ASSERT(m_clock_cb.module_initialized); - clock_stop(domain); + switch(domain) + { + case NRF_CLOCK_DOMAIN_HFCLK: +#if NRF_CLOCK_HAS_XO + nrfx_clock_xo_stop(); +#elif NRF_CLOCK_HAS_HFCLK + nrfx_clock_hfclk_stop(); +#endif // NRF_CLOCK_HAS_XO + break; + case NRF_CLOCK_DOMAIN_LFCLK: + nrfx_clock_lfclk_stop(); + break; + default: + clock_stop(domain); + break; + } } #if ((NRF_CLOCK_HAS_CALIBRATION || NRFX_CHECK(NRF_LFRC_HAS_CALIBRATION)) && \ NRFX_CHECK(NRFX_CLOCK_CONFIG_LF_CAL_ENABLED)) nrfx_err_t nrfx_clock_calibration_start(void) { - nrfx_err_t err_code = NRFX_SUCCESS; - - nrf_clock_hfclk_t clk_src; - if (!nrfx_clock_is_running(NRF_CLOCK_DOMAIN_HFCLK, &clk_src)) - { - err_code = NRFX_ERROR_INVALID_STATE; - } - else if (clk_src != NRF_CLOCK_HFCLK_HIGH_ACCURACY) - { - err_code = NRFX_ERROR_INVALID_STATE; - } - else if (!nrfx_clock_is_running(NRF_CLOCK_DOMAIN_LFCLK, NULL)) - { - err_code = NRFX_ERROR_INVALID_STATE; - } - - if (err_code != NRFX_SUCCESS) - { - NRFX_LOG_WARNING("Function: %s, error code: %s.", - __func__, - NRFX_LOG_ERROR_STRING_GET(err_code)); - return err_code; - } - - if (m_clock_cb.cal_state == CAL_STATE_IDLE) - { -#if NRFX_CHECK(NRF_LFRC_HAS_CALIBRATION) - nrf_lfrc_event_clear(NRF_LFRC, NRF_LFRC_EVENT_CALDONE); -#else - nrf_clock_event_clear(NRF_CLOCK, NRF_CLOCK_EVENT_DONE); -#endif - - m_clock_cb.cal_state = CAL_STATE_CAL; -#if NRFX_CHECK(USE_WORKAROUND_FOR_ANOMALY_192) - *(volatile uint32_t *)0x40000C34 = 0x00000002; -#endif -#if NRFX_CHECK(NRF_LFRC_HAS_CALIBRATION) - nrf_lfrc_task_trigger(NRF_LFRC, NRF_LFRC_TASK_CAL); - if (m_clock_cb.event_handler) - { - nrf_lfrc_int_enable(NRF_LFRC, NRF_LFRC_INT_CALDONE_MASK); - } - else - { - while (!nrf_lfrc_event_check(NRF_LFRC, NRF_LFRC_EVENT_CALDONE)) - {} - nrf_lfrc_event_clear(NRF_LFRC, NRF_LFRC_EVENT_CALDONE); - } -#else - nrf_clock_task_trigger(NRF_CLOCK, NRF_CLOCK_TASK_CAL); - if (m_clock_cb.event_handler) - { - nrf_clock_int_enable(NRF_CLOCK, NRF_CLOCK_INT_DONE_MASK); - } - else - { - while (!nrf_clock_event_check(NRF_CLOCK, NRF_CLOCK_EVENT_DONE)) - {} - nrf_clock_event_clear(NRF_CLOCK, NRF_CLOCK_EVENT_DONE); - } -#endif - } - else - { - err_code = NRFX_ERROR_BUSY; - NRFX_LOG_WARNING("Function: %s, error code: %s.", - __func__, - NRFX_LOG_ERROR_STRING_GET(err_code)); - return err_code; - } - - NRFX_LOG_INFO("Initialized."); - return err_code; + return nrfx_clock_lfclk_calibration_start(); } nrfx_err_t nrfx_clock_is_calibrating(void) { - if (m_clock_cb.cal_state == CAL_STATE_CAL) - { - return NRFX_ERROR_BUSY; - } - return NRFX_SUCCESS; + return nrfx_clock_lfclk_is_calibrating(); } #if NRF_CLOCK_HAS_CALIBRATION_TIMER && NRFX_CHECK(NRFX_CLOCK_CONFIG_CT_ENABLED) void nrfx_clock_calibration_timer_start(uint8_t interval) { - nrf_clock_cal_timer_timeout_set(NRF_CLOCK, interval); - nrf_clock_event_clear(NRF_CLOCK, NRF_CLOCK_EVENT_CTTO); - - nrf_clock_task_trigger(NRF_CLOCK, NRF_CLOCK_TASK_CTSTART); - if (m_clock_cb.event_handler) - { - nrf_clock_int_enable(NRF_CLOCK, NRF_CLOCK_INT_CTTO_MASK); - } - else - { - while (!nrf_clock_event_check(NRF_CLOCK, NRF_CLOCK_EVENT_CTTO)) - {} - nrf_clock_event_clear(NRF_CLOCK, NRF_CLOCK_EVENT_CTTO); - } + nrfx_clock_lfclk_calibration_timer_start(interval); } void nrfx_clock_calibration_timer_stop(void) { - nrf_clock_int_disable(NRF_CLOCK, NRF_CLOCK_INT_CTTO_MASK); - nrf_clock_task_trigger(NRF_CLOCK, NRF_CLOCK_TASK_CTSTOP); + nrfx_clock_lfclk_calibration_timer_stop(); } #endif // NRF_CLOCK_HAS_CALIBRATION_TIMER && NRFX_CHECK(NRFX_CLOCK_CONFIG_CT_ENABLED) -#endif // NRF_CLOCK_HAS_CALIBRATION && NRFX_CHECK(NRFX_CLOCK_CONFIG_LF_CAL_ENABLED) - -#if NRF_CLOCK_HAS_XO_TUNE -nrfx_err_t nrfx_clock_xo_tune_start(void) -{ - nrf_clock_hfclk_t hfclksrc = nrf_clock_hf_src_get(NRF_CLOCK); - if ((hfclksrc != NRF_CLOCK_HFCLK_HIGH_ACCURACY) || (m_clock_cb.xo_state == XO_STATE_TUNING)) - { - return NRFX_ERROR_INVALID_STATE; - } - - nrf_clock_event_clear(NRF_CLOCK, NRF_CLOCK_EVENT_XOTUNED); - nrf_clock_event_clear(NRF_CLOCK, NRF_CLOCK_EVENT_XOTUNEFAILED); - - if (m_clock_cb.event_handler) - { - uint32_t int_mask = NRF_CLOCK_INT_XOTUNED_MASK | NRF_CLOCK_INT_XOTUNEFAILED_MASK; - nrf_clock_int_enable(NRF_CLOCK, int_mask); - } - - // XOTUNEERROR can occur at any moment and it is not related to this operation - m_clock_cb.xo_state = XO_STATE_TUNING; - nrf_clock_task_trigger(NRF_CLOCK, NRF_CLOCK_TASK_XOTUNE); - - if (!m_clock_cb.event_handler) - { - bool evt_xotuned; - bool evt_xotunefailed; - do - { - evt_xotuned = nrf_clock_event_check(NRF_CLOCK, NRF_CLOCK_EVENT_XOTUNED); - evt_xotunefailed = nrf_clock_event_check(NRF_CLOCK, NRF_CLOCK_EVENT_XOTUNEFAILED); - } while (!(evt_xotuned | evt_xotunefailed)); - m_clock_cb.xo_state = evt_xotuned ? XO_STATE_TUNED : XO_STATE_NOT_TUNED; - - nrf_clock_event_clear(NRF_CLOCK, NRF_CLOCK_EVENT_XOTUNED); - nrf_clock_event_clear(NRF_CLOCK, NRF_CLOCK_EVENT_XOTUNEFAILED); - - if (evt_xotunefailed) - { - return NRFX_ERROR_INTERNAL; - } - } - - return NRFX_SUCCESS; -} - -nrfx_err_t nrfx_clock_xo_tune_abort(void) -{ - nrf_clock_hfclk_t hfclksrc = nrf_clock_hf_src_get(NRF_CLOCK); - if ((hfclksrc != NRF_CLOCK_HFCLK_HIGH_ACCURACY) || (m_clock_cb.xo_state != XO_STATE_TUNING)) - { - return NRFX_ERROR_FORBIDDEN; - } - - nrf_clock_task_trigger(NRF_CLOCK, NRF_CLOCK_TASK_XOTUNEABORT); - m_clock_cb.xo_state = XO_STATE_NOT_TUNED; - - if (m_clock_cb.event_handler) - { - uint32_t int_mask = NRF_CLOCK_INT_XOTUNED_MASK | NRF_CLOCK_INT_XOTUNEFAILED_MASK; - nrf_clock_int_disable(NRF_CLOCK, int_mask); - } - - return NRFX_SUCCESS; -} - -bool nrfx_clock_xo_tune_error_check(void) -{ - NRFX_ASSERT(!m_clock_cb.event_handler); - - bool quality_issue = nrf_clock_event_check(NRF_CLOCK, NRF_CLOCK_EVENT_XOTUNEERROR); - if (quality_issue) - { - nrf_clock_event_clear(NRF_CLOCK, NRF_CLOCK_EVENT_XOTUNEERROR); - } - return quality_issue; -} - -bool nrfx_clock_xo_tune_status_check(void) -{ - return m_clock_cb.xo_state == XO_STATE_TUNED; -} - -#endif // NRF_CLOCK_HAS_XO +#endif /* ((NRF_CLOCK_HAS_CALIBRATION || NRFX_CHECK(NRF_LFRC_HAS_CALIBRATION)) && + NRFX_CHECK(NRFX_CLOCK_CONFIG_LF_CAL_ENABLED)) */ #if defined(CLOCK_FEATURE_HFCLK_DIVIDE_PRESENT) || NRF_CLOCK_HAS_HFCLK192M nrfx_err_t nrfx_clock_divider_set(nrf_clock_domain_t domain, @@ -774,57 +591,11 @@ nrfx_err_t nrfx_clock_divider_set(nrf_clock_domain_t domain, switch(domain) { #if defined(CLOCK_FEATURE_HFCLK_DIVIDE_PRESENT) +#if NRF_CLOCK_HAS_HFCLK case NRF_CLOCK_DOMAIN_HFCLK: - switch (div) - { - case NRF_CLOCK_HFCLK_DIV_2: -#if !defined(NRF_TRUSTZONE_NONSECURE) && NRFX_CHECK(NRF53_ERRATA_4_ENABLE_WORKAROUND) - if (nrf53_errata_4()) - { - NRFX_CRITICAL_SECTION_ENTER(); - __DSB(); - - nrf_clock_hfclk_div_set(NRF_CLOCK, div); - - *(volatile uint32_t *)0x5084450C = 0x0; - *(volatile uint32_t *)0x50026548 = 0x0; - *(volatile uint32_t *)0x50081EE4 = 0x0D; - - NRFX_CRITICAL_SECTION_EXIT(); - } - else -#endif - { - nrf_clock_hfclk_div_set(NRF_CLOCK, div); - } - break; - case NRF_CLOCK_HFCLK_DIV_1: -#if !defined(NRF_TRUSTZONE_NONSECURE) && NRFX_CHECK(NRF53_ERRATA_4_ENABLE_WORKAROUND) - if (nrf53_errata_4()) - { - NRFX_CRITICAL_SECTION_ENTER(); - __DSB(); - - *(volatile uint32_t *)0x5084450C = 0x4040; - *(volatile uint32_t *)0x50026548 = 0x40; - *(volatile uint32_t *)0x50081EE4 = 0x4D; - - nrf_clock_hfclk_div_set(NRF_CLOCK, div); - - NRFX_CRITICAL_SECTION_EXIT(); - } - else -#endif - { - nrf_clock_hfclk_div_set(NRF_CLOCK, div); - } - break; - default: - return NRFX_ERROR_INVALID_PARAM; - } - SystemCoreClockUpdate(); - return NRFX_SUCCESS; -#endif + return nrfx_clock_hfclk_divider_set(div); +#endif // NRF_CLOCK_HAS_HFCLK +#endif // CLOCK_FEATURE_HFCLK_DIVIDE_PRESENT #if NRF_CLOCK_HAS_HFCLK192M case NRF_CLOCK_DOMAIN_HFCLK192M: if (div > NRF_CLOCK_HFCLK_DIV_4) @@ -861,6 +632,7 @@ void nrfx_clock_irq_handler(void) #else uint32_t intpend = nrf_clock_int_enable_check(NRF_CLOCK, UINT32_MAX); #endif + intpend &= INTERRUPT_MASK; while (intpend != 0) { @@ -883,18 +655,6 @@ void nrfx_clock_irq_handler(void) NRFX_LOG_DEBUG("Event: %s", NRFX_CLOCK_EVT2STR(evt_type)); switch (int_mask) { - case NRF_CLOCK_INT_HF_STARTED_MASK: -#if NRFX_CHECK(USE_WORKAROUND_FOR_ANOMALY_201) - if (!m_clock_cb.hfclk_started) - { - m_clock_cb.hfclk_started = true; - } - else - { - call_handler = false; - } -#endif - break; case NRF_CLOCK_INT_LF_STARTED_MASK: { #if NRFX_CHECK(NRFX_CLOCK_CONFIG_LFXO_TWO_STAGE_ENABLED) @@ -938,19 +698,6 @@ void nrfx_clock_irq_handler(void) #if NRF_CLOCK_HAS_HFCLK192M case NRF_CLOCK_INT_HF192M_STARTED_MASK: break; -#endif -#if NRFX_CHECK(NRF_CLOCK_HAS_XO_TUNE) - case NRF_CLOCK_INT_XOTUNED_MASK: - m_clock_cb.xo_state = XO_STATE_TUNED; - // Enable XOTUNEERROR interrupt to handle situation when XO is out of tune. - nrf_clock_event_clear(NRF_CLOCK, NRF_CLOCK_EVENT_XOTUNEERROR); - nrf_clock_int_enable(NRF_CLOCK, NRF_CLOCK_INT_XOTUNEERROR_MASK); - break; - case NRF_CLOCK_INT_XOTUNEERROR_MASK: - break; - case NRF_CLOCK_INT_XOTUNEFAILED_MASK: - m_clock_cb.xo_state = XO_STATE_NOT_TUNED; - break; #endif default: NRFX_ASSERT(0); @@ -963,6 +710,8 @@ void nrfx_clock_irq_handler(void) } #if NRF_CLOCK_HAS_INTPEND intpend = nrf_clock_int_pending_get(NRF_CLOCK); + + intpend &= INTERRUPT_MASK; #endif } } diff --git a/nrfx/drivers/src/nrfx_clock_hfclk.c b/nrfx/drivers/src/nrfx_clock_hfclk.c new file mode 100644 index 00000000..5471e82e --- /dev/null +++ b/nrfx/drivers/src/nrfx_clock_hfclk.c @@ -0,0 +1,214 @@ +/*$$$LICENCE_NORDIC_STANDARD<2025>$$$*/ + +#include +#include + +#if NRFX_CHECK(NRFX_CLOCK_ENABLED) + +#define NRFX_LOG_MODULE CLOCK_HFCLK +#include + +#if !defined(USE_WORKAROUND_FOR_ANOMALY_201) && \ + (defined(NRF52810_XXAA) || \ + defined(NRF52832_XXAA) || defined(NRF52832_XXAB) || \ + defined(NRF52840_XXAA)) + // Enable workaround for nRF52 anomaly 201 (EVENTS_HFCLKSTARTED might be generated twice). + #define USE_WORKAROUND_FOR_ANOMALY_201 1 +#endif + +/** @brief CLOCK control block. */ +typedef struct +{ + nrfx_clock_hfclk_event_handler_t event_handler; + bool module_initialized; /*< Indicate the state of module */ +#if NRFX_CHECK(USE_WORKAROUND_FOR_ANOMALY_201) + bool hfclk_started; /*< Anomaly 201 workaround. */ +#endif +} nrfx_clock_hfclk_cb_t; + +static nrfx_clock_hfclk_cb_t m_clock_cb; + +static void clock_stop() +{ + nrf_clock_int_disable(NRF_CLOCK, NRF_CLOCK_INT_HF_STARTED_MASK); + nrf_clock_task_trigger(NRF_CLOCK, NRF_CLOCK_TASK_HFCLKSTOP); + nrf_clock_event_clear(NRF_CLOCK, NRF_CLOCK_EVENT_HFCLKSTARTED); +#if NRFX_CHECK(NRF54L_ERRATA_39_ENABLE_WORKAROUND) + if (nrf54l_errata_39()) + { + nrf_clock_task_trigger(NRF_CLOCK, NRF_CLOCK_TASK_PLLSTOP); + } +#endif + + bool stopped; + nrf_clock_hfclk_t clk_src = NRF_CLOCK_HFCLK_HIGH_ACCURACY; + NRFX_WAIT_FOR(!nrfx_clock_hfclk_running_check(&clk_src), 10000, 1, stopped); + if (!stopped) + { + NRFX_LOG_ERROR("Failed to stop clock domain: NRF_CLOCK_DOMAIN_HFCLK."); + } + else + { +#if NRFX_CHECK(USE_WORKAROUND_FOR_ANOMALY_201) + m_clock_cb.hfclk_started = false; +#endif + } +} + +nrfx_err_t nrfx_clock_hfclk_init(nrfx_clock_hfclk_event_handler_t event_handler) +{ + nrfx_err_t err_code = NRFX_SUCCESS; + if (m_clock_cb.module_initialized) + { + err_code = NRFX_ERROR_ALREADY; + } + else + { + m_clock_cb.event_handler = event_handler; + m_clock_cb.module_initialized = true; +#if NRFX_CHECK(USE_WORKAROUND_FOR_ANOMALY_201) + m_clock_cb.hfclk_started = false; +#endif + } +#if NRF_CLOCK_HAS_HFCLKSRC + nrf_clock_hf_src_set(NRF_CLOCK, NRF_CLOCK_HFCLK_HIGH_ACCURACY); +#endif // NRF_CLOCK_HAS_HFCLKSRC + NRFX_LOG_INFO("Function: %s, error code: %s.", __func__, NRFX_LOG_ERROR_STRING_GET(err_code)); + return err_code; +} + +void nrfx_clock_hfclk_uninit(void) +{ + NRFX_ASSERT(m_clock_cb.module_initialized); + clock_stop(); + + m_clock_cb.module_initialized = false; + NRFX_LOG_INFO("Uninitialized."); +} + +void nrfx_clock_hfclk_start(void) +{ + NRFX_ASSERT(m_clock_cb.module_initialized); + + nrf_clock_event_clear(NRF_CLOCK, NRF_CLOCK_EVENT_HFCLKSTARTED); + nrf_clock_task_trigger(NRF_CLOCK, NRF_CLOCK_TASK_HFCLKSTART); + if (m_clock_cb.event_handler) + { + nrf_clock_int_enable(NRF_CLOCK, NRF_CLOCK_INT_HF_STARTED_MASK); + } + else + { + while (!nrf_clock_event_check(NRF_CLOCK, NRF_CLOCK_EVENT_HFCLKSTARTED)) + {} + nrf_clock_event_clear(NRF_CLOCK, NRF_CLOCK_EVENT_HFCLKSTARTED); + } +} + +void nrfx_clock_hfclk_stop(void) +{ + NRFX_ASSERT(m_clock_cb.module_initialized); + clock_stop(); +} + +#if defined(CLOCK_FEATURE_HFCLK_DIVIDE_PRESENT) +nrfx_err_t nrfx_clock_hfclk_divider_set(nrf_clock_hfclk_div_t div) +{ + switch (div) + { + case NRF_CLOCK_HFCLK_DIV_2: +#if !defined(NRF_TRUSTZONE_NONSECURE) && NRFX_CHECK(NRF53_ERRATA_4_ENABLE_WORKAROUND) + if (nrf53_errata_4()) + { + NRFX_CRITICAL_SECTION_ENTER(); + __DSB(); + + nrf_clock_hfclk_div_set(NRF_CLOCK, div); + + *(volatile uint32_t *)0x5084450C = 0x0; + *(volatile uint32_t *)0x50026548 = 0x0; + *(volatile uint32_t *)0x50081EE4 = 0x0D; + + NRFX_CRITICAL_SECTION_EXIT(); + } + else +#endif + { + nrf_clock_hfclk_div_set(NRF_CLOCK, div); + } + break; + case NRF_CLOCK_HFCLK_DIV_1: +#if !defined(NRF_TRUSTZONE_NONSECURE) && NRFX_CHECK(NRF53_ERRATA_4_ENABLE_WORKAROUND) + if (nrf53_errata_4()) + { + NRFX_CRITICAL_SECTION_ENTER(); + __DSB(); + + *(volatile uint32_t *)0x5084450C = 0x4040; + *(volatile uint32_t *)0x50026548 = 0x40; + *(volatile uint32_t *)0x50081EE4 = 0x4D; + + nrf_clock_hfclk_div_set(NRF_CLOCK, div); + + NRFX_CRITICAL_SECTION_EXIT(); + } + else +#endif + { + nrf_clock_hfclk_div_set(NRF_CLOCK, div); + } + break; + default: + return NRFX_ERROR_INVALID_PARAM; + } + SystemCoreClockUpdate(); + return NRFX_SUCCESS; +} +#endif + +void nrfx_clock_hfclk_irq_handler(void) +{ +#if NRF_CLOCK_HAS_INTPEND + uint32_t intpend = nrf_clock_int_pending_get(NRF_CLOCK); +#else + uint32_t intpend = nrf_clock_int_enable_check(NRF_CLOCK, UINT32_MAX); +#endif + + if(!(intpend & NRF_CLOCK_INT_HF_STARTED_MASK)) + { + return; + } + + uint32_t int_bit = 31 - NRF_CLZ(NRF_CLOCK_INT_HF_STARTED_MASK); + nrf_clock_event_t evt = (nrf_clock_event_t)NRFY_INT_BITPOS_TO_EVENT(int_bit); + bool call_handler = true; + +#if !NRF_CLOCK_HAS_INTPEND + // Check if event is set for that interrupt and if not continue. + if (!nrf_clock_event_check(NRF_CLOCK, evt)) { + return; + } +#endif + + nrf_clock_event_clear(NRF_CLOCK, evt); + nrf_clock_int_disable(NRF_CLOCK, NRF_CLOCK_INT_HF_STARTED_MASK); + + NRFX_LOG_DEBUG("Event: HFCLK_STARTED"); + +#if NRFX_CHECK(USE_WORKAROUND_FOR_ANOMALY_201) + if (!m_clock_cb.hfclk_started) + { + m_clock_cb.hfclk_started = true; + } + else + { + call_handler = false; + } +#endif + + if (call_handler) + { + m_clock_cb.event_handler(); + } +} + +#endif // NRFX_CHECK(NRFX_CLOCK_ENABLED) diff --git a/nrfx/drivers/src/nrfx_clock_hfclk192m.c b/nrfx/drivers/src/nrfx_clock_hfclk192m.c new file mode 100644 index 00000000..d6e6655b --- /dev/null +++ b/nrfx/drivers/src/nrfx_clock_hfclk192m.c @@ -0,0 +1,719 @@ +/* + * Copyright (c) 2016 - 2025, Nordic Semiconductor ASA + * All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its + * contributors may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include + +#if NRFX_CHECK(NRFX_CLOCK_ENABLED) + +#include +#include + +#define NRFX_LOG_MODULE CLOCK +#include + +#if NRFX_CHECK(NRFX_POWER_ENABLED) +extern bool nrfx_power_irq_enabled; +#endif + +#if defined(CLOCK_LFCLKSRC_SRC_RC) || defined(__NRFX_DOXYGEN__) + #define LF_SRC_RC CLOCK_LFCLKSRC_SRC_RC +#elif defined(CLOCK_LFCLKSRC_SRC_LFRC) + #define LF_SRC_RC CLOCK_LFCLKSRC_SRC_LFRC +#else + #define LF_SRC_RC CLOCK_LFCLK_SRC_SRC_LFRC +#endif +#if NRFX_CHECK(NRFX_CLOCK_CONFIG_LF_CAL_ENABLED) + #if (NRF_CLOCK_HAS_CALIBRATION == 0 && NRFX_CHECK(NRF_LFRC_HAS_CALIBRATION) == 0) + #error "Calibration is not available in the SoC that is used." + #endif + #if (NRFX_CLOCK_CONFIG_LF_SRC != LF_SRC_RC) + #error "Calibration can be performed only for the RC Oscillator." + #endif +#endif + +#if !defined(USE_WORKAROUND_FOR_ANOMALY_132) && \ + (defined(NRF52832_XXAA) || defined(NRF52832_XXAB)) + // ANOMALY 132 - LFCLK needs to avoid frame from 66us to 138us after LFCLK stop. This solution + // applies delay of 138us before starting LFCLK. + #define USE_WORKAROUND_FOR_ANOMALY_132 1 + + // Convert time to cycles (nRF52832 is clocked with 64 MHz, use delay of 138 us). + #define ANOMALY_132_DELAY_CYCLES (64UL * 138) +#endif + +#if !defined(USE_WORKAROUND_FOR_ANOMALY_192) && \ + (defined(NRF52810_XXAA) || \ + defined(NRF52832_XXAA) || defined(NRF52832_XXAB) || \ + defined(NRF52840_XXAA)) + // Enable workaround for nRF52 anomaly 192 (LFRC oscillator frequency is wrong + // after calibration, exceeding 500 ppm). + #define USE_WORKAROUND_FOR_ANOMALY_192 1 +#endif + +#if !defined(USE_WORKAROUND_FOR_ANOMALY_201) && \ + (defined(NRF52810_XXAA) || \ + defined(NRF52832_XXAA) || defined(NRF52832_XXAB) || \ + defined(NRF52840_XXAA)) + // Enable workaround for nRF52 anomaly 201 (EVENTS_HFCLKSTARTED might be generated twice). + #define USE_WORKAROUND_FOR_ANOMALY_201 1 +#endif + +#if defined(CLOCK_LFCLKSRC_SRC_Xtal) + #define LF_SRC_LFXO CLOCK_LFCLKSRC_SRC_Xtal +#elif NRF_CLOCK_HAS_LFCLK_TYPE + #define LF_SRC_LFXO CLOCK_LFCLK_SRC_SRC_LFXO +#else + #define LF_SRC_LFXO CLOCK_LFCLKSRC_SRC_LFXO +#endif + +#if defined(NRF_CLOCK_USE_EXTERNAL_LFCLK_SOURCES) + #define LF_SRC_XTAL_LOW (CLOCK_LFCLKSRC_SRC_Xtal | \ + (CLOCK_LFCLKSRC_EXTERNAL_Enabled << CLOCK_LFCLKSRC_EXTERNAL_Pos)) + #define LF_SRC_XTAL_FULL (CLOCK_LFCLKSRC_SRC_Xtal | \ + (CLOCK_LFCLKSRC_BYPASS_Enabled << CLOCK_LFCLKSRC_BYPASS_Pos) | \ + (CLOCK_LFCLKSRC_EXTERNAL_Enabled << CLOCK_LFCLKSRC_EXTERNAL_Pos)) +#else + #define LF_SRC_XTAL_LOW LF_SRC_LFXO + #define LF_SRC_XTAL_FULL LF_SRC_LFXO +#endif + +#if NRFX_CHECK(NRFX_CLOCK_CONFIG_LFXO_TWO_STAGE_ENABLED) && \ + NRFX_CLOCK_CONFIG_LF_SRC != LF_SRC_LFXO && \ + NRFX_CLOCK_CONFIG_LF_SRC != LF_SRC_XTAL_LOW && \ + NRFX_CLOCK_CONFIG_LF_SRC != LF_SRC_XTAL_FULL + #error "Two-stage LFXO start procedure enabled but LFCLK source is not set to LFXO!" +#endif + +#if !defined(NRFX_CLOCK_CONFIG_CT_ENABLED) && NRF_CLOCK_HAS_CALIBRATION_TIMER +#define NRFX_CLOCK_CONFIG_CT_ENABLED 1 +#endif + +#if NRFX_CHECK(NRFX_CLOCK_CONFIG_CT_ENABLED) && !NRF_CLOCK_HAS_CALIBRATION_TIMER + #error "Calibration timer is not available in the SoC that is used." +#endif + +#define INTERRUPT_MASK \ + NRF_CLOCK_INT_LF_STARTED_MASK | \ + NRFX_COND_CODE_1(NRF_CLOCK_HAS_LFCLK_SRC_CHANGED, (NRF_CLOCK_INT_LF_SRC_CHANGED_MASK |), ()) \ + NRFX_COND_CODE_1(NRF_CLOCK_HAS_CALIBRATION, (NRF_CLOCK_INT_DONE_MASK |), ()) \ + NRFX_COND_CODE_1(NRF_CLOCK_HAS_CALIBRATION_TIMER, (NRF_CLOCK_INT_CTTO_MASK |), ()) \ + NRFX_COND_CODE_1(defined(CLOCK_INTENSET_CTSTARTED_Msk) || defined(__NRFX_DOXYGEN__), (NRF_CLOCK_INT_CTSTARTED_MASK | NRF_CLOCK_INT_CTSTOPPED_MASK |), ()) \ + NRFX_COND_CODE_1(NRF_CLOCK_HAS_HFCLKAUDIO, (NRF_CLOCK_INT_HFAUDIO_STARTED_MASK |), ()) \ + NRFX_COND_CODE_1(NRF_CLOCK_HAS_HFCLK24M, (NRF_CLOCK_INT_HFCLK24M_STARTED_MASK |), ()) \ + NRFX_COND_CODE_1(NRF_CLOCK_HAS_HFCLK192M, (NRF_CLOCK_INT_HF192M_STARTED_MASK |), ()) \ + 0 + +#if NRFX_CHECK(NRFX_CLOCK_CONFIG_LF_CAL_ENABLED) +typedef enum +{ + CAL_STATE_IDLE, + CAL_STATE_CAL +} nrfx_clock_cal_state_t; +#endif + +#if NRFX_CHECK(NRF_CLOCK_HAS_XO_TUNE) +typedef enum +{ + XO_STATE_NOT_TUNED, + XO_STATE_TUNING, + XO_STATE_TUNED +} nrfx_clock_xo_state_t; +#endif + +#define NRFX_CLOCK_EVT2STR(evt_type) \ + evt_type == NRFX_CLOCK_EVT_HFCLK_STARTED ? "HFCLK_STARTED" : \ + NRFX_COND_CODE_1(NRF_CLOCK_HAS_PLL, \ + (evt_type == NRFX_CLOCK_EVT_PLL_STARTED ? "PLL_STARTED" : ), ()) \ + evt_type == NRFX_CLOCK_EVT_LFCLK_STARTED ? "LFCLK_STARTED" : \ + NRFX_COND_CODE_1(NRF_CLOCK_HAS_CALIBRATION_TIMER, \ + (evt_type == NRFX_CLOCK_EVT_CTTO ? "CTTO" : ), ()) \ + NRFX_COND_CODE_1(NRF_CLOCK_HAS_CALIBRATION, \ + (evt_type == NRFX_CLOCK_EVT_CAL_DONE ? "CAL_DONE" : ), ()) \ + NRFX_COND_CODE_1(NRF_CLOCK_HAS_HFCLKAUDIO, \ + (evt_type == NRFX_CLOCK_EVT_HFCLKAUDIO_STARTED ? "HFAUDIO_STARTED" : ), ()) \ + NRFX_COND_CODE_1(NRF_CLOCK_HAS_HFCLK24M, \ + (evt_type == NRFX_CLOCK_EVT_HFCLK24M_STARTED ? "HFCLK24M_STARTED" : ), ()) \ + NRFX_COND_CODE_1(NRF_CLOCK_HAS_HFCLK192M, \ + (evt_type == NRFX_CLOCK_HFCLK192M_EVT_HFCLK192M_STARTED ? "HF192M_STARTED" : ), ()) \ + NRFX_COND_CODE_1(NRF_CLOCK_HAS_XO_TUNE, \ + (evt_type == NRFX_CLOCK_EVT_XO_TUNED ? "XO_TUNED" : \ + evt_type == NRFX_CLOCK_EVT_XO_TUNE_ERROR ? "XO_TUNE_ERROR" : \ + evt_type == NRFX_CLOCK_EVT_XO_TUNE_FAILED ? "XO_TUNE_FAILED" :), ()) "Unknown" + +/** @brief CLOCK control block. */ +typedef struct +{ + nrfx_clock_hfclks192m_event_handler_t event_handler; + bool module_initialized; /*< Indicate the state of module */ +#if NRFX_CHECK(USE_WORKAROUND_FOR_ANOMALY_201) + bool hfclk_started; /*< Anomaly 201 workaround. */ +#endif + +#if NRFX_CHECK(NRFX_CLOCK_CONFIG_LF_CAL_ENABLED) + volatile nrfx_clock_cal_state_t cal_state; +#endif + +#if NRFX_CHECK(NRF_CLOCK_HAS_XO_TUNE) + volatile nrfx_clock_xo_state_t xo_state; +#endif +} nrfx_clock_cb_t; + +static nrfx_clock_cb_t m_clock_cb; + +/** + * This variable is used to check whether common POWER_CLOCK common interrupt + * should be disabled or not if @ref nrfx_power tries to disable the interrupt. + */ +#if NRFX_CHECK(NRFX_POWER_ENABLED) +bool nrfx_clock_irq_enabled; +#endif + +#if NRFX_CHECK(USE_WORKAROUND_FOR_ANOMALY_132) +/** + * @brief Function for applying delay of 138us before starting LFCLK. + */ +static void nrfx_clock_anomaly_132(void) +{ + uint32_t cyccnt_inital; + uint32_t core_debug; + uint32_t dwt_ctrl; + + // Preserve DEMCR register to do not influence into its configuration. Enable the trace and + // debug blocks. It is required to read and write data to DWT block. + core_debug = CoreDebug->DEMCR; + CoreDebug->DEMCR = core_debug | CoreDebug_DEMCR_TRCENA_Msk; + + // Preserve CTRL register in DWT block to do not influence into its configuration. Make sure + // that cycle counter is enabled. + dwt_ctrl = DWT->CTRL; + DWT->CTRL = dwt_ctrl | DWT_CTRL_CYCCNTENA_Msk; + + // Store start value of cycle counter. + cyccnt_inital = DWT->CYCCNT; + + // Delay required time. + while ((DWT->CYCCNT - cyccnt_inital) < ANOMALY_132_DELAY_CYCLES) + {} + + // Restore preserved registers. + DWT->CTRL = dwt_ctrl; + CoreDebug->DEMCR = core_debug; +} +#endif // NRFX_CHECK(USE_WORKAROUND_FOR_ANOMALY_132) + +static void clock_stop(nrf_clock_domain_t domain) +{ + uint32_t int_mask; + nrf_clock_event_t event; + nrf_clock_task_t task; + switch (domain) + { + case NRF_CLOCK_DOMAIN_LFCLK: + int_mask = NRF_CLOCK_INT_LF_STARTED_MASK; + task = NRF_CLOCK_TASK_LFCLKSTOP; + event = NRF_CLOCK_EVENT_LFCLKSTARTED; + break; + case NRF_CLOCK_DOMAIN_HFCLK: + int_mask = NRF_CLOCK_INT_HF_STARTED_MASK | +#if NRF_CLOCK_HAS_XO_TUNE + NRF_CLOCK_INT_XOTUNED_MASK | + NRF_CLOCK_INT_XOTUNEFAILED_MASK | + NRF_CLOCK_INT_XOTUNEERROR_MASK | +#endif + 0; + task = NRF_CLOCK_TASK_HFCLKSTOP; + event = NRF_CLOCK_EVENT_HFCLKSTARTED; + break; +#if NRF_CLOCK_HAS_HFCLK192M + case NRF_CLOCK_DOMAIN_HFCLK192M: + int_mask = NRF_CLOCK_INT_HF192M_STARTED_MASK; + task = NRF_CLOCK_TASK_HFCLK192MSTOP; + event = NRF_CLOCK_EVENT_HFCLK192MSTARTED; + break; +#endif +#if NRF_CLOCK_HAS_HFCLKAUDIO + case NRF_CLOCK_DOMAIN_HFCLKAUDIO: + int_mask = NRF_CLOCK_INT_HFAUDIO_STARTED_MASK; + task = NRF_CLOCK_TASK_HFCLKAUDIOSTOP; + event = NRF_CLOCK_EVENT_HFCLKAUDIOSTARTED; + break; +#endif +#if NRF_CLOCK_HAS_HFCLK24M + case NRF_CLOCK_DOMAIN_HFCLK24M: + int_mask = NRF_CLOCK_INT_HFCLK24M_STARTED_MASK; + task = NRF_CLOCK_TASK_HFCLK24MSTOP; + event = NRF_CLOCK_EVENT_HFCLK24MSTARTED; + break; +#endif + default: + NRFX_ASSERT(0); + return; + } + + nrf_clock_int_disable(NRF_CLOCK, int_mask); + nrf_clock_task_trigger(NRF_CLOCK, task); + nrf_clock_event_clear(NRF_CLOCK, event); +#if NRFX_CHECK(NRF54L_ERRATA_39_ENABLE_WORKAROUND) + if (nrf54l_errata_39() && (domain == NRF_CLOCK_DOMAIN_HFCLK)) + { + nrf_clock_task_trigger(NRF_CLOCK, NRF_CLOCK_TASK_PLLSTOP); + } +#endif + + bool stopped; + nrf_clock_hfclk_t clk_src = NRF_CLOCK_HFCLK_HIGH_ACCURACY; + nrf_clock_hfclk_t *p_clk_src = (domain == NRF_CLOCK_DOMAIN_HFCLK) ? &clk_src : NULL; + NRFX_WAIT_FOR((!nrfx_clock_is_running(domain, p_clk_src) || + (p_clk_src && clk_src != NRF_CLOCK_HFCLK_HIGH_ACCURACY)), 10000, 1, stopped); + if (!stopped) + { + NRFX_LOG_ERROR("Failed to stop clock domain: %d.", domain); + } + else if (domain == NRF_CLOCK_DOMAIN_HFCLK) + { +#if NRF_CLOCK_HAS_XO_TUNE + m_clock_cb.xo_state = XO_STATE_NOT_TUNED; +#endif +#if NRFX_CHECK(USE_WORKAROUND_FOR_ANOMALY_201) + m_clock_cb.hfclk_started = false; +#endif + } +} + +static nrf_clock_lfclk_t clock_initial_lfclksrc_get(void) +{ +#if NRFX_CHECK(NRFX_CLOCK_CONFIG_LFXO_TWO_STAGE_ENABLED) + return NRF_CLOCK_LFCLK_RC; +#else + return (nrf_clock_lfclk_t)NRFX_CLOCK_CONFIG_LF_SRC; +#endif +} + +#if NRF_CLOCK_HAS_HFCLK +static void hfclk_event_handler(void) +{ + m_clock_cb.event_handler(NRFX_CLOCK_EVT_HFCLK_STARTED); +} +#endif // NRF_CLOCK_HAS_HFCLK + +#if NRF_CLOCK_HAS_XO +static void xo_event_handler(nrfx_clock_xo_evt_type_t event) +{ + m_clock_cb.event_handler((nrfx_clock_hfclk192m_evt_type_t)event); +} +#endif // NRF_CLOCK_HAS_XO + +static void lfclk_event_handler(nrfx_clock_lfclk_evt_type_t event) +{ + m_clock_cb.event_handler((nrfx_clock_hfclk192m_evt_type_t)event); +} + +nrfx_err_t nrfx_clock_init(nrfx_clock_hfclks192m_event_handler_t event_handler) +{ + nrfx_err_t err_code = NRFX_SUCCESS; + if (m_clock_cb.module_initialized) + { + err_code = NRFX_ERROR_ALREADY; + NRFX_LOG_INFO("Function: %s, error code: %s.", __func__, NRFX_LOG_ERROR_STRING_GET(err_code)); + return err_code; + } + else + { +#if NRFX_CHECK(NRFX_CLOCK_CONFIG_LF_CAL_ENABLED) + m_clock_cb.cal_state = CAL_STATE_IDLE; +#endif + m_clock_cb.event_handler = event_handler; + m_clock_cb.module_initialized = true; +#if NRFX_CHECK(USE_WORKAROUND_FOR_ANOMALY_201) + m_clock_cb.hfclk_started = false; +#endif + } +//Temp code, until nordic_nrf_clock compat is present +#if CONFIG_CLOCK_CONTROL_NRF_COMMON + return err_code; +#endif + +#if NRF_CLOCK_HAS_HFCLK + err_code |= m_clock_cb.event_handler ? nrfx_clock_hfclk_init(&hfclk_event_handler) : + nrfx_clock_hfclk_init(NULL); +#endif // NRF_CLOCK_HAS_HFCLK + +#if NRF_CLOCK_HAS_XO + err_code |= m_clock_cb.event_handler ? nrfx_clock_xo_init(&xo_event_handler) : + nrfx_clock_xo_init(NULL); +#endif // NRF_CLOCK_HAS_XO + + err_code |= m_clock_cb.event_handler ? nrfx_clock_lfclk_init(&lfclk_event_handler) : + nrfx_clock_lfclk_init(NULL); + + if(err_code != NRFX_SUCCESS) + { + NRFX_LOG_INFO("Function: %s, error code: %s.", __func__, + NRFX_LOG_ERROR_STRING_GET(err_code)); + return err_code; + } + + return err_code; +} + +void nrfx_clock_enable(void) +{ + NRFX_ASSERT(m_clock_cb.module_initialized); + if (m_clock_cb.event_handler) + { + nrfx_power_clock_irq_init(); + } + nrf_clock_lf_src_set(NRF_CLOCK, clock_initial_lfclksrc_get()); +#if NRF_CLOCK_HAS_HFCLK192M + nrf_clock_hfclk192m_src_set(NRF_CLOCK, (nrf_clock_hfclk_t)NRFX_CLOCK_CONFIG_HFCLK192M_SRC); +#endif +#if NRFX_CHECK(NRFX_POWER_ENABLED) + nrfx_clock_irq_enabled = true; +#endif + NRFX_LOG_INFO("Module enabled."); +} + +void nrfx_clock_disable(void) +{ + NRFX_ASSERT(m_clock_cb.module_initialized); + + if (m_clock_cb.event_handler) + { +#if NRFX_CHECK(NRFX_POWER_ENABLED) + NRFX_ASSERT(nrfx_clock_irq_enabled); + if (!nrfx_power_irq_enabled) +#endif + { +#if defined(NRF54L05_XXAA) || defined(NRF54L10_XXAA) || defined(NRF54L15_XXAA) + IRQn_Type irqn = CLOCK_POWER_IRQn; +#else + IRQn_Type irqn = nrfx_get_irq_number(NRF_CLOCK); +#endif + NRFX_IRQ_DISABLE(irqn); + } + } + nrf_clock_int_disable(NRF_CLOCK, NRF_CLOCK_INT_HF_STARTED_MASK | + NRF_CLOCK_INT_LF_STARTED_MASK | +#if NRFX_CHECK(NRFX_CLOCK_CONFIG_LF_CAL_ENABLED) && (NRF_CLOCK_HAS_CALIBRATION) + NRF_CLOCK_INT_DONE_MASK | +#if NRF_CLOCK_HAS_CALIBRATION_TIMER + NRF_CLOCK_INT_CTTO_MASK | +#endif +#endif // NRFX_CHECK(NRFX_CLOCK_CONFIG_LF_CAL_ENABLED) + 0); +#if NRFX_CHECK(NRFX_POWER_ENABLED) + nrfx_clock_irq_enabled = false; +#endif +#if NRFX_CHECK(NRF_LFRC_HAS_CALIBRATION) && NRFX_CHECK(NRFX_CLOCK_CONFIG_LF_CAL_ENABLED) + NRFX_IRQ_DISABLE(LFRC_IRQn); + nrf_lfrc_int_disable(NRF_LFRC, NRF_LFRC_INT_CALDONE_MASK); +#endif + NRFX_LOG_INFO("Module disabled."); +} + +void nrfx_clock_uninit(void) +{ + NRFX_ASSERT(m_clock_cb.module_initialized); + + nrfx_clock_lfclk_uninit(); + +#if NRF_CLOCK_HAS_HFCLK192M + clock_stop(NRF_CLOCK_DOMAIN_HFCLK192M); +#endif +#if NRF_CLOCK_HAS_HFCLKAUDIO + clock_stop(NRF_CLOCK_DOMAIN_HFCLKAUDIO); +#endif +#if NRF_CLOCK_HAS_HFCLK24M + clock_stop(NRF_CLOCK_DOMAIN_HFCLK24M); +#endif +#if NRF_CLOCK_HAS_HFCLK + nrfx_clock_hfclk_uninit(); +#endif +#if NRF_CLOCK_HAS_XO + nrfx_clock_xo_uninit(); +#endif + m_clock_cb.module_initialized = false; + NRFX_LOG_INFO("Uninitialized."); +} + +bool nrfx_clock_init_check(void) +{ + return m_clock_cb.module_initialized; +} + +void nrfx_clock_start(nrf_clock_domain_t domain) +{ + uint32_t int_mask; + nrf_clock_event_t event; + nrf_clock_task_t task; + + NRFX_ASSERT(m_clock_cb.module_initialized); + switch (domain) + { + case NRF_CLOCK_DOMAIN_LFCLK: + nrfx_clock_lfclk_start(); + return; + case NRF_CLOCK_DOMAIN_HFCLK: +#if NRF_CLOCK_HAS_XO + nrfx_clock_xo_start(); +#elif NRF_CLOCK_HAS_HFCLK + nrfx_clock_hfclk_start(); +#endif // NRF_CLOCK_HAS_HFCLK + return; + +#if NRF_CLOCK_HAS_HFCLK192M + case NRF_CLOCK_DOMAIN_HFCLK192M: + event = NRF_CLOCK_EVENT_HFCLK192MSTARTED; + int_mask = NRF_CLOCK_INT_HF192M_STARTED_MASK; + task = NRF_CLOCK_TASK_HFCLK192MSTART; + break; +#endif +#if NRF_CLOCK_HAS_HFCLKAUDIO + case NRF_CLOCK_DOMAIN_HFCLKAUDIO: + event = NRF_CLOCK_EVENT_HFCLKAUDIOSTARTED; + int_mask = NRF_CLOCK_INT_HFAUDIO_STARTED_MASK; + task = NRF_CLOCK_TASK_HFCLKAUDIOSTART; + break; +#endif +#if NRF_CLOCK_HAS_HFCLK24M + case NRF_CLOCK_DOMAIN_HFCLK24M: + event = NRF_CLOCK_EVENT_HFCLK24MSTARTED; + int_mask = NRF_CLOCK_INT_HFCLK24M_STARTED_MASK; + task = NRF_CLOCK_TASK_HFCLK24MSTART; + break; +#endif + default: + NRFX_ASSERT(0); + return; + } + + nrf_clock_event_clear(NRF_CLOCK, event); +#if NRFX_CHECK(USE_WORKAROUND_FOR_ANOMALY_132) + if (event == NRF_CLOCK_EVENT_LFCLKSTARTED) + { + nrfx_clock_anomaly_132(); + } +#endif + nrf_clock_task_trigger(NRF_CLOCK, task); + if (m_clock_cb.event_handler) + { + nrf_clock_int_enable(NRF_CLOCK, int_mask); + } + else + { + while (!nrf_clock_event_check(NRF_CLOCK, event)) + {} + nrf_clock_event_clear(NRF_CLOCK, event); + } +} + +void nrfx_clock_stop(nrf_clock_domain_t domain) +{ + NRFX_ASSERT(m_clock_cb.module_initialized); + switch(domain) + { + case NRF_CLOCK_DOMAIN_HFCLK: +#if NRF_CLOCK_HAS_XO + nrfx_clock_xo_stop(); +#elif NRF_CLOCK_HAS_HFCLK + nrfx_clock_hfclk_stop(); +#endif // NRF_CLOCK_HAS_XO + break; + case NRF_CLOCK_DOMAIN_LFCLK: + nrfx_clock_lfclk_stop(); + break; + default: + clock_stop(domain); + break; + } +} + +#if ((NRF_CLOCK_HAS_CALIBRATION || NRFX_CHECK(NRF_LFRC_HAS_CALIBRATION)) && \ + NRFX_CHECK(NRFX_CLOCK_CONFIG_LF_CAL_ENABLED)) +nrfx_err_t nrfx_clock_calibration_start(void) +{ + return nrfx_clock_lfclk_calibration_start(); +} + +nrfx_err_t nrfx_clock_is_calibrating(void) +{ + return nrfx_clock_lfclk_is_calibrating(); +} + +#if NRF_CLOCK_HAS_CALIBRATION_TIMER && NRFX_CHECK(NRFX_CLOCK_CONFIG_CT_ENABLED) +void nrfx_clock_calibration_timer_start(uint8_t interval) +{ + nrfx_clock_lfclk_calibration_timer_start(interval); +} + +void nrfx_clock_calibration_timer_stop(void) +{ + nrfx_clock_lfclk_calibration_timer_stop(); +} +#endif // NRF_CLOCK_HAS_CALIBRATION_TIMER && NRFX_CHECK(NRFX_CLOCK_CONFIG_CT_ENABLED) +#endif /* ((NRF_CLOCK_HAS_CALIBRATION || NRFX_CHECK(NRF_LFRC_HAS_CALIBRATION)) && + NRFX_CHECK(NRFX_CLOCK_CONFIG_LF_CAL_ENABLED)) */ + +#if defined(CLOCK_FEATURE_HFCLK_DIVIDE_PRESENT) || NRF_CLOCK_HAS_HFCLK192M +nrfx_err_t nrfx_clock_divider_set(nrf_clock_domain_t domain, + nrf_clock_hfclk_div_t div) +{ + switch(domain) + { +#if defined(CLOCK_FEATURE_HFCLK_DIVIDE_PRESENT) +#if NRF_CLOCK_HAS_HFCLK + case NRF_CLOCK_DOMAIN_HFCLK: + return nrfx_clock_hfclk_divider_set(div); +#endif // NRF_CLOCK_HAS_HFCLK +#endif // CLOCK_FEATURE_HFCLK_DIVIDE_PRESENT +#if NRF_CLOCK_HAS_HFCLK192M + case NRF_CLOCK_DOMAIN_HFCLK192M: + if (div > NRF_CLOCK_HFCLK_DIV_4) + { + return NRFX_ERROR_INVALID_PARAM; + } + else + { + nrf_clock_hfclk192m_div_set(NRF_CLOCK, div); + } + return NRFX_SUCCESS; +#endif + default: + NRFX_ASSERT(0); + return NRFX_ERROR_NOT_SUPPORTED; + } +} +#endif + +void nrfx_clock_irq_handler(void) +{ +#if NRFX_CHECK(NRFX_CLOCK_CONFIG_USE_LFRC_CALIBRATION) && \ + NRFX_CHECK(NRFX_CLOCK_CONFIG_LF_CAL_ENABLED) + if (nrf_lfrc_event_check(NRF_LFRC, NRF_LFRC_EVENT_CALDONE)) + { + nrf_lfrc_event_clear(NRF_LFRC, NRF_LFRC_EVENT_CALDONE); + nrf_lfrc_int_disable(NRF_LFRC, NRF_LFRC_INT_CALDONE_MASK); + m_clock_cb.cal_state = CAL_STATE_IDLE; + m_clock_cb.event_handler(NRFX_CLOCK_EVT_CAL_DONE); + } +#endif +#if NRF_CLOCK_HAS_INTPEND + uint32_t intpend = nrf_clock_int_pending_get(NRF_CLOCK); +#else + uint32_t intpend = nrf_clock_int_enable_check(NRF_CLOCK, UINT32_MAX); +#endif + intpend &= INTERRUPT_MASK; + + while (intpend != 0) + { + uint32_t int_bit = 31 - NRF_CLZ(intpend); + nrf_clock_int_mask_t int_mask = (nrf_clock_int_mask_t)NRFX_BIT(int_bit); + nrf_clock_event_t evt = (nrf_clock_event_t)NRFY_INT_BITPOS_TO_EVENT(int_bit); + nrfx_clock_hfclk192m_evt_type_t evt_type = (nrfx_clock_hfclk192m_evt_type_t)int_bit; + bool call_handler = true; +#if !NRF_CLOCK_HAS_INTPEND + intpend &= ~int_mask; + // Check if event is set for that interrupt and if not continue. + if (!nrf_clock_event_check(NRF_CLOCK, evt)) { + continue; + } +#endif + + nrf_clock_event_clear(NRF_CLOCK, evt); + nrf_clock_int_disable(NRF_CLOCK, int_mask); + + NRFX_LOG_DEBUG("Event: %s", NRFX_CLOCK_EVT2STR(evt_type)); + switch (int_mask) + { + case NRF_CLOCK_INT_LF_STARTED_MASK: + { +#if NRFX_CHECK(NRFX_CLOCK_CONFIG_LFXO_TWO_STAGE_ENABLED) + nrf_clock_lfclk_t lfclksrc; + (void)nrf_clock_is_running(NRF_CLOCK, NRF_CLOCK_DOMAIN_LFCLK, &lfclksrc); + if (lfclksrc == NRF_CLOCK_LFCLK_RC) + { + // After the LFRC oscillator start switch to external source. + nrf_clock_lf_src_set(NRF_CLOCK, (nrf_clock_lfclk_t)NRFX_CLOCK_CONFIG_LF_SRC); + nrf_clock_int_enable(NRF_CLOCK, int_mask); + nrf_clock_task_trigger(NRF_CLOCK, NRF_CLOCK_TASK_LFCLKSTART); + call_handler = false; + } + else +#endif + { + // After the LF clock external source start invoke user callback. + } + break; + } +#if NRFX_CHECK(NRFX_CLOCK_CONFIG_LF_CAL_ENABLED) && NRF_CLOCK_HAS_CALIBRATION +#if NRF_CLOCK_HAS_CALIBRATION_TIMER && NRFX_CHECK(NRFX_CLOCK_CONFIG_CT_ENABLED) + case NRF_CLOCK_INT_CTTO_MASK: + break; +#endif + case NRF_CLOCK_INT_DONE_MASK: +#if NRFX_CHECK(USE_WORKAROUND_FOR_ANOMALY_192) + *(volatile uint32_t *)0x40000C34 = 0x00000000; +#endif + m_clock_cb.cal_state = CAL_STATE_IDLE; + break; +#endif // (NRFX_CLOCK_CONFIG_LF_CAL_ENABLED && NRF_CLOCK_HAS_CALIBRATION) +#if NRF_CLOCK_HAS_HFCLKAUDIO + case NRF_CLOCK_INT_HFAUDIO_STARTED_MASK: + break; +#endif +#if NRF_CLOCK_HAS_HFCLK24M + case NRF_CLOCK_INT_HFCLK24M_STARTED_MASK: + break; +#endif +#if NRF_CLOCK_HAS_HFCLK192M + case NRF_CLOCK_INT_HF192M_STARTED_MASK: + break; +#endif + default: + NRFX_ASSERT(0); + break; + } + + if (call_handler) + { + m_clock_cb.event_handler(evt_type); + } +#if NRF_CLOCK_HAS_INTPEND + intpend = nrf_clock_int_pending_get(NRF_CLOCK); + + intpend &= INTERRUPT_MASK; +#endif + } +} + +#endif // NRFX_CHECK(NRFX_CLOCK_ENABLED) diff --git a/nrfx/drivers/src/nrfx_clock_lfclk.c b/nrfx/drivers/src/nrfx_clock_lfclk.c new file mode 100644 index 00000000..38cb21d1 --- /dev/null +++ b/nrfx/drivers/src/nrfx_clock_lfclk.c @@ -0,0 +1,557 @@ +/* + * Copyright (c) 2016 - 2025, Nordic Semiconductor ASA + * All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its + * contributors may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include + +#if NRFX_CHECK(NRFX_CLOCK_ENABLED) + +#include +#include + +#if NRF_CLOCK_HAS_HFCLK +#include +#endif + +#if NRF_CLOCK_HAS_XO +#include +#endif + +#define NRFX_LOG_MODULE CLOCK +#include + +#if (NRF_CLOCK_HAS_CALIBRATION && NRFX_CHECK(NRFX_CLOCK_CONFIG_LF_CAL_ENABLED) && \ + !NRF_CLOCK_HAS_HFCLK && !NRF_CLOCK_HAS_XO) +#error "Calibration requires HFCLK or XO to be present in the SoC that is used." +#endif + +#if defined(CLOCK_LFCLKSRC_SRC_RC) || defined(__NRFX_DOXYGEN__) + #define LF_SRC_RC CLOCK_LFCLKSRC_SRC_RC +#elif defined(CLOCK_LFCLKSRC_SRC_LFRC) + #define LF_SRC_RC CLOCK_LFCLKSRC_SRC_LFRC +#else + #define LF_SRC_RC CLOCK_LFCLK_SRC_SRC_LFRC +#endif +#if NRFX_CHECK(NRFX_CLOCK_CONFIG_LF_CAL_ENABLED) + #if (NRF_CLOCK_HAS_CALIBRATION == 0 && NRFX_CHECK(NRF_LFRC_HAS_CALIBRATION) == 0) + #error "Calibration is not available in the SoC that is used." + #endif + #if (NRFX_CLOCK_CONFIG_LF_SRC != LF_SRC_RC) + #error "Calibration can be performed only for the RC Oscillator." + #endif +#endif + +#if !defined(USE_WORKAROUND_FOR_ANOMALY_132) && \ + (defined(NRF52832_XXAA) || defined(NRF52832_XXAB)) + // ANOMALY 132 - LFCLK needs to avoid frame from 66us to 138us after LFCLK stop. This solution + // applies delay of 138us before starting LFCLK. + #define USE_WORKAROUND_FOR_ANOMALY_132 1 + + // Convert time to cycles (nRF52832 is clocked with 64 MHz, use delay of 138 us). + #define ANOMALY_132_DELAY_CYCLES (64UL * 138) +#endif + +#if !defined(USE_WORKAROUND_FOR_ANOMALY_192) && \ + (defined(NRF52810_XXAA) || \ + defined(NRF52832_XXAA) || defined(NRF52832_XXAB) || \ + defined(NRF52840_XXAA)) + // Enable workaround for nRF52 anomaly 192 (LFRC oscillator frequency is wrong + // after calibration, exceeding 500 ppm). + #define USE_WORKAROUND_FOR_ANOMALY_192 1 +#endif + +#if defined(CLOCK_LFCLKSRC_SRC_Xtal) + #define LF_SRC_LFXO CLOCK_LFCLKSRC_SRC_Xtal +#elif NRF_CLOCK_HAS_LFCLK_TYPE + #define LF_SRC_LFXO CLOCK_LFCLK_SRC_SRC_LFXO +#else + #define LF_SRC_LFXO CLOCK_LFCLKSRC_SRC_LFXO +#endif + +#if defined(NRF_CLOCK_USE_EXTERNAL_LFCLK_SOURCES) + #define LF_SRC_XTAL_LOW (CLOCK_LFCLKSRC_SRC_Xtal | \ + (CLOCK_LFCLKSRC_EXTERNAL_Enabled << CLOCK_LFCLKSRC_EXTERNAL_Pos)) + #define LF_SRC_XTAL_FULL (CLOCK_LFCLKSRC_SRC_Xtal | \ + (CLOCK_LFCLKSRC_BYPASS_Enabled << CLOCK_LFCLKSRC_BYPASS_Pos) | \ + (CLOCK_LFCLKSRC_EXTERNAL_Enabled << CLOCK_LFCLKSRC_EXTERNAL_Pos)) +#else + #define LF_SRC_XTAL_LOW LF_SRC_LFXO + #define LF_SRC_XTAL_FULL LF_SRC_LFXO +#endif + +#if NRFX_CHECK(NRFX_CLOCK_CONFIG_LFXO_TWO_STAGE_ENABLED) && \ + NRFX_CLOCK_CONFIG_LF_SRC != LF_SRC_LFXO && \ + NRFX_CLOCK_CONFIG_LF_SRC != LF_SRC_XTAL_LOW && \ + NRFX_CLOCK_CONFIG_LF_SRC != LF_SRC_XTAL_FULL + #error "Two-stage LFXO start procedure enabled but LFCLK source is not set to LFXO!" +#endif + +#if !defined(NRFX_CLOCK_CONFIG_CT_ENABLED) && NRF_CLOCK_HAS_CALIBRATION_TIMER +#define NRFX_CLOCK_CONFIG_CT_ENABLED 1 +#endif + +#if NRFX_CHECK(NRFX_CLOCK_CONFIG_CT_ENABLED) && !NRF_CLOCK_HAS_CALIBRATION_TIMER + #error "Calibration timer is not available in the SoC that is used." +#endif + +#define INTERRUPT_MASK \ + NRF_CLOCK_INT_LF_STARTED_MASK | \ + NRFX_COND_CODE_1(NRF_CLOCK_HAS_LFCLK_SRC_CHANGED, (NRF_CLOCK_INT_LF_SRC_CHANGED_MASK |), ()) \ + NRFX_COND_CODE_1(NRF_CLOCK_HAS_CALIBRATION, (NRF_CLOCK_INT_DONE_MASK |), ()) \ + NRFX_COND_CODE_1(NRF_CLOCK_HAS_CALIBRATION_TIMER, (NRF_CLOCK_INT_CTTO_MASK |), ()) \ + 0 + +#if NRFX_CHECK(NRFX_CLOCK_CONFIG_LF_CAL_ENABLED) +typedef enum +{ + CAL_STATE_IDLE, + CAL_STATE_CAL +} nrfx_clock_lfclk_cal_state_t; +#endif + +#define NRFX_CLOCK_EVT2STR(evt_type) \ + evt_type == NRFX_CLOCK_LFCLK_EVT_LFCLK_STARTED ? "LFCLK_STARTED" : \ + NRFX_COND_CODE_1(NRF_CLOCK_HAS_CALIBRATION_TIMER, \ + (evt_type == NRFX_CLOCK_LFCLK_EVT_CTTO ? "CTTO" : ), ()) \ + NRFX_COND_CODE_1(NRF_CLOCK_HAS_CALIBRATION, \ + (evt_type == NRFX_CLOCK_LFCLK_EVT_CAL_DONE ? "CAL_DONE" : ), ()) "Unknown" + +/** @brief CLOCK control block. */ +typedef struct +{ + nrfx_clock_lfclk_event_handler_t event_handler; + bool module_initialized; /*< Indicate the state of module */ +#if NRFX_CHECK(NRFX_CLOCK_CONFIG_LF_CAL_ENABLED) + volatile nrfx_clock_lfclk_cal_state_t cal_state; +#endif +} nrfx_clock_lfclk_cb_t; + +static nrfx_clock_lfclk_cb_t m_clock_cb; + +#if NRFX_CHECK(USE_WORKAROUND_FOR_ANOMALY_132) +/** + * @brief Function for applying delay of 138us before starting LFCLK. + */ +static void nrfx_clock_anomaly_132(void) +{ + uint32_t cyccnt_inital; + uint32_t core_debug; + uint32_t dwt_ctrl; + + // Preserve DEMCR register to do not influence into its configuration. Enable the trace and + // debug blocks. It is required to read and write data to DWT block. + core_debug = CoreDebug->DEMCR; + CoreDebug->DEMCR = core_debug | CoreDebug_DEMCR_TRCENA_Msk; + + // Preserve CTRL register in DWT block to do not influence into its configuration. Make sure + // that cycle counter is enabled. + dwt_ctrl = DWT->CTRL; + DWT->CTRL = dwt_ctrl | DWT_CTRL_CYCCNTENA_Msk; + + // Store start value of cycle counter. + cyccnt_inital = DWT->CYCCNT; + + // Delay required time. + while ((DWT->CYCCNT - cyccnt_inital) < ANOMALY_132_DELAY_CYCLES) + {} + + // Restore preserved registers. + DWT->CTRL = dwt_ctrl; + CoreDebug->DEMCR = core_debug; +} +#endif // NRFX_CHECK(USE_WORKAROUND_FOR_ANOMALY_132) + +static void clock_stop() +{ + nrf_clock_int_disable(NRF_CLOCK, NRF_CLOCK_INT_LF_STARTED_MASK); + nrf_clock_task_trigger(NRF_CLOCK, NRF_CLOCK_TASK_LFCLKSTOP); + nrf_clock_event_clear(NRF_CLOCK, NRF_CLOCK_EVENT_LFCLKSTARTED); + + bool stopped; + NRFX_WAIT_FOR((!nrfx_clock_lfclk_running_check(NULL)), 10000, 1, stopped); + if (!stopped) + { + NRFX_LOG_ERROR("Failed to stop clock domain: %d.", NRF_CLOCK_DOMAIN_LFCLK); + } +} + +static nrf_clock_lfclk_t clock_initial_lfclksrc_get(void) +{ +#if NRFX_CHECK(NRFX_CLOCK_CONFIG_LFXO_TWO_STAGE_ENABLED) + return NRF_CLOCK_LFCLK_RC; +#else + return (nrf_clock_lfclk_t)NRFX_CLOCK_CONFIG_LF_SRC; +#endif +} + +/** + * @brief Function for tweaking the specified low-frequency clock source given current driver state. + * + * @warning This function may stop currently running low-frequency clock source. + * + * @param[in,out] p_lfclksrc Pointer to the variable containing low-frequency clock source. + * It is set to adequate value in case of being inappropriate + * for current driver configuration. + * + * @return True if the specified clock source was correct, false otherwise. + */ +static bool clock_lfclksrc_tweak(nrf_clock_lfclk_t * p_lfclksrc) +{ + bool is_correct_clk = (*p_lfclksrc == NRFX_CLOCK_CONFIG_LF_SRC); +#if NRFX_CHECK(NRFX_CLOCK_CONFIG_LFXO_TWO_STAGE_ENABLED) + // In case of two-stage LFXO start procedure RC source is valid as well. + is_correct_clk = is_correct_clk || (*p_lfclksrc == NRF_CLOCK_LFCLK_RC); +#endif + if (!is_correct_clk) + { + // Inappropriate LF clock source is chosen. + // Stop currently active LF clock source and choose the correct one to start. + clock_stop(); + *p_lfclksrc = clock_initial_lfclksrc_get(); + } + return is_correct_clk; +} + +nrfx_err_t nrfx_clock_lfclk_init(nrfx_clock_lfclk_event_handler_t event_handler) +{ + nrfx_err_t err_code = NRFX_SUCCESS; + if (m_clock_cb.module_initialized) + { + err_code = NRFX_ERROR_ALREADY; + NRFX_LOG_INFO("Function: %s, error code: %s.", __func__, NRFX_LOG_ERROR_STRING_GET(err_code)); + return err_code; + } + else + { +#if NRFX_CHECK(NRFX_CLOCK_CONFIG_LF_CAL_ENABLED) + m_clock_cb.cal_state = CAL_STATE_IDLE; +#endif + m_clock_cb.event_handler = event_handler; + m_clock_cb.module_initialized = true; + + } + + nrf_clock_lf_src_set(NRF_CLOCK, clock_initial_lfclksrc_get()); + + return err_code; +} + +void nrfx_clock_lfclk_uninit(void) +{ + NRFX_ASSERT(m_clock_cb.module_initialized); + clock_stop(); + + m_clock_cb.module_initialized = false; + NRFX_LOG_INFO("Uninitialized."); +} + +bool nrfx_clock_lfclk_init_check(void) +{ + return m_clock_cb.module_initialized; +} + +void nrfx_clock_lfclk_start(void) +{ + NRFX_ASSERT(m_clock_cb.module_initialized); + + nrf_clock_lfclk_t lfclksrc; + if (nrf_clock_is_running(NRF_CLOCK, NRF_CLOCK_DOMAIN_LFCLK, &lfclksrc)) + { + // LF clock is already running. Inspect its source. + // If LF clock source is inappropriate then it will be stopped and modified. + // Ignore return value as LF clock will be started again regardless of the result. + (void)clock_lfclksrc_tweak(&lfclksrc); + } + else if (nrf_clock_start_task_check(NRF_CLOCK, NRF_CLOCK_DOMAIN_LFCLK)) + { + // LF clock is not active yet but was started already. Inspect its source. + lfclksrc = nrf_clock_lf_srccopy_get(NRF_CLOCK); + if (clock_lfclksrc_tweak(&lfclksrc)) + { + // LF clock was started already and the configured source + // corresponds to the user configuration. + // No action is needed as the chosen LF clock source will become active soon. + if (m_clock_cb.event_handler) + { + nrf_clock_int_enable(NRF_CLOCK, NRF_CLOCK_INT_LF_STARTED_MASK); + } + else + { + while (!nrf_clock_event_check(NRF_CLOCK, NRF_CLOCK_EVENT_LFCLKSTARTED)) + {} + nrf_clock_event_clear(NRF_CLOCK, NRF_CLOCK_EVENT_LFCLKSTARTED); + } + return; + } + // Otherwise LF clock was started already but with inappropriate source. + // LF clock was stopped and modified. Now it will be restarted. + } + else + { + // LF clock not active and not started. + lfclksrc = clock_initial_lfclksrc_get(); + } + nrf_clock_lf_src_set(NRF_CLOCK, lfclksrc); + + nrf_clock_event_clear(NRF_CLOCK, NRF_CLOCK_EVENT_LFCLKSTARTED); +#if NRFX_CHECK(USE_WORKAROUND_FOR_ANOMALY_132) + nrfx_clock_anomaly_132(); +#endif + nrf_clock_task_trigger(NRF_CLOCK, NRF_CLOCK_TASK_LFCLKSTART); + if (m_clock_cb.event_handler) + { + nrf_clock_int_enable(NRF_CLOCK, NRF_CLOCK_INT_LF_STARTED_MASK); + } + else + { + while (!nrf_clock_event_check(NRF_CLOCK, NRF_CLOCK_EVENT_LFCLKSTARTED)) + {} + nrf_clock_event_clear(NRF_CLOCK, NRF_CLOCK_EVENT_LFCLKSTARTED); + } +} + +void nrfx_clock_lfclk_stop(void) +{ + NRFX_ASSERT(m_clock_cb.module_initialized); + clock_stop(); +} + +#if ((NRF_CLOCK_HAS_CALIBRATION || NRFX_CHECK(NRF_LFRC_HAS_CALIBRATION)) && \ + NRFX_CHECK(NRFX_CLOCK_CONFIG_LF_CAL_ENABLED)) +nrfx_err_t nrfx_clock_lfclk_calibration_start(void) +{ + nrfx_err_t err_code = NRFX_SUCCESS; + + nrf_clock_hfclk_t clk_src; +#if NRF_CLOCK_HAS_HFCLK + if (!nrfx_clock_hfclk_running_check(&clk_src)) +#else + if (!nrfx_clock_xo_running_check(&clk_src)) +#endif + { + err_code = NRFX_ERROR_INVALID_STATE; + } + else if (clk_src != NRF_CLOCK_HFCLK_HIGH_ACCURACY) + { + err_code = NRFX_ERROR_INVALID_STATE; + } + else if (!nrfx_clock_lfclk_running_check(NULL)) + { + err_code = NRFX_ERROR_INVALID_STATE; + } + + if (err_code != NRFX_SUCCESS) + { + NRFX_LOG_WARNING("Function: %s, error code: %s.", + __func__, + NRFX_LOG_ERROR_STRING_GET(err_code)); + return err_code; + } + + if (m_clock_cb.cal_state == CAL_STATE_IDLE) + { +#if NRFX_CHECK(NRF_LFRC_HAS_CALIBRATION) + nrf_lfrc_event_clear(NRF_LFRC, NRF_LFRC_EVENT_CALDONE); +#else + nrf_clock_event_clear(NRF_CLOCK, NRF_CLOCK_EVENT_DONE); +#endif + + m_clock_cb.cal_state = CAL_STATE_CAL; +#if NRFX_CHECK(USE_WORKAROUND_FOR_ANOMALY_192) + *(volatile uint32_t *)0x40000C34 = 0x00000002; +#endif +#if NRFX_CHECK(NRF_LFRC_HAS_CALIBRATION) + nrf_lfrc_task_trigger(NRF_LFRC, NRF_LFRC_TASK_CAL); + if (m_clock_cb.event_handler) + { + nrf_lfrc_int_enable(NRF_LFRC, NRF_LFRC_INT_CALDONE_MASK); + } + else + { + while (!nrf_lfrc_event_check(NRF_LFRC, NRF_LFRC_EVENT_CALDONE)) + {} + nrf_lfrc_event_clear(NRF_LFRC, NRF_LFRC_EVENT_CALDONE); + } +#else + nrf_clock_task_trigger(NRF_CLOCK, NRF_CLOCK_TASK_CAL); + if (m_clock_cb.event_handler) + { + nrf_clock_int_enable(NRF_CLOCK, NRF_CLOCK_INT_DONE_MASK); + } + else + { + while (!nrf_clock_event_check(NRF_CLOCK, NRF_CLOCK_EVENT_DONE)) + {} + nrf_clock_event_clear(NRF_CLOCK, NRF_CLOCK_EVENT_DONE); + } +#endif + } + else + { + err_code = NRFX_ERROR_BUSY; + NRFX_LOG_WARNING("Function: %s, error code: %s.", + __func__, + NRFX_LOG_ERROR_STRING_GET(err_code)); + return err_code; + } + + NRFX_LOG_INFO("Initialized."); + return err_code; +} + +nrfx_err_t nrfx_clock_lfclk_calibrating_check(void) +{ + if (m_clock_cb.cal_state == CAL_STATE_CAL) + { + return NRFX_ERROR_BUSY; + } + return NRFX_SUCCESS; +} + +#if NRF_CLOCK_HAS_CALIBRATION_TIMER && NRFX_CHECK(NRFX_CLOCK_CONFIG_CT_ENABLED) +void nrfx_clock_lfclk_calibration_timer_start(uint8_t interval) +{ + nrf_clock_cal_timer_timeout_set(NRF_CLOCK, interval); + nrf_clock_event_clear(NRF_CLOCK, NRF_CLOCK_EVENT_CTTO); + + nrf_clock_task_trigger(NRF_CLOCK, NRF_CLOCK_TASK_CTSTART); + if (m_clock_cb.event_handler) + { + nrf_clock_int_enable(NRF_CLOCK, NRF_CLOCK_INT_CTTO_MASK); + } + else + { + while (!nrf_clock_event_check(NRF_CLOCK, NRF_CLOCK_EVENT_CTTO)) + {} + nrf_clock_event_clear(NRF_CLOCK, NRF_CLOCK_EVENT_CTTO); + } +} + +void nrfx_clock_lfclk_calibration_timer_stop(void) +{ + nrf_clock_int_disable(NRF_CLOCK, NRF_CLOCK_INT_CTTO_MASK); + nrf_clock_task_trigger(NRF_CLOCK, NRF_CLOCK_TASK_CTSTOP); +} +#endif // NRF_CLOCK_HAS_CALIBRATION_TIMER && NRFX_CHECK(NRFX_CLOCK_CONFIG_CT_ENABLED) +#endif /* ((NRF_CLOCK_HAS_CALIBRATION || NRFX_CHECK(NRF_LFRC_HAS_CALIBRATION)) && + NRFX_CHECK(NRFX_CLOCK_CONFIG_LF_CAL_ENABLED)) */ + +void nrfx_clock_lfclk_irq_handler(void) +{ +#if NRFX_CHECK(NRFX_CLOCK_CONFIG_USE_LFRC_CALIBRATION) && \ + NRFX_CHECK(NRFX_CLOCK_CONFIG_LF_CAL_ENABLED) + if (nrf_lfrc_event_check(NRF_LFRC, NRF_LFRC_EVENT_CALDONE)) + { + nrf_lfrc_event_clear(NRF_LFRC, NRF_LFRC_EVENT_CALDONE); + nrf_lfrc_int_disable(NRF_LFRC, NRF_LFRC_INT_CALDONE_MASK); + m_clock_cb.cal_state = CAL_STATE_IDLE; + m_clock_cb.event_handler(NRFX_CLOCK_EVT_CAL_DONE); + } +#endif +#if NRF_CLOCK_HAS_INTPEND + uint32_t intpend = nrf_clock_int_pending_get(NRF_CLOCK); +#else + uint32_t intpend = nrf_clock_int_enable_check(NRF_CLOCK, UINT32_MAX); +#endif + intpend &= INTERRUPT_MASK; + + while (intpend != 0) + { + uint32_t int_bit = 31 - NRF_CLZ(intpend); + nrf_clock_int_mask_t int_mask = (nrf_clock_int_mask_t)NRFX_BIT(int_bit); + nrf_clock_event_t evt = (nrf_clock_event_t)NRFY_INT_BITPOS_TO_EVENT(int_bit); + nrfx_clock_lfclk_evt_type_t evt_type = (nrfx_clock_lfclk_evt_type_t)int_bit; + bool call_handler = true; +#if !NRF_CLOCK_HAS_INTPEND + intpend &= ~int_mask; + // Check if event is set for that interrupt and if not continue. + if (!nrf_clock_event_check(NRF_CLOCK, evt)) { + continue; + } +#endif + + nrf_clock_event_clear(NRF_CLOCK, evt); + nrf_clock_int_disable(NRF_CLOCK, int_mask); + + NRFX_LOG_DEBUG("Event: %s", NRFX_CLOCK_EVT2STR(evt_type)); + switch (int_mask) + { + case NRF_CLOCK_INT_LF_STARTED_MASK: + { +#if NRFX_CHECK(NRFX_CLOCK_CONFIG_LFXO_TWO_STAGE_ENABLED) + nrf_clock_lfclk_t lfclksrc; + (void)nrf_clock_is_running(NRF_CLOCK, NRF_CLOCK_DOMAIN_LFCLK, &lfclksrc); + if (lfclksrc == NRF_CLOCK_LFCLK_RC) + { + // After the LFRC oscillator start switch to external source. + nrf_clock_lf_src_set(NRF_CLOCK, (nrf_clock_lfclk_t)NRFX_CLOCK_CONFIG_LF_SRC); + nrf_clock_int_enable(NRF_CLOCK, int_mask); + nrf_clock_task_trigger(NRF_CLOCK, NRF_CLOCK_TASK_LFCLKSTART); + call_handler = false; + } + else +#endif + { + // After the LF clock external source start invoke user callback. + } + break; + } +#if NRFX_CHECK(NRFX_CLOCK_CONFIG_LF_CAL_ENABLED) && NRF_CLOCK_HAS_CALIBRATION +#if NRF_CLOCK_HAS_CALIBRATION_TIMER && NRFX_CHECK(NRFX_CLOCK_CONFIG_CT_ENABLED) + case NRF_CLOCK_INT_CTTO_MASK: + break; +#endif + case NRF_CLOCK_INT_DONE_MASK: +#if NRFX_CHECK(USE_WORKAROUND_FOR_ANOMALY_192) + *(volatile uint32_t *)0x40000C34 = 0x00000000; +#endif + m_clock_cb.cal_state = CAL_STATE_IDLE; + break; +#endif // (NRFX_CLOCK_CONFIG_LF_CAL_ENABLED && NRF_CLOCK_HAS_CALIBRATION) + default: + NRFX_ASSERT(0); + break; + } + + if (call_handler) + { + m_clock_cb.event_handler(evt_type); + } +#if NRF_CLOCK_HAS_INTPEND + intpend = nrf_clock_int_pending_get(NRF_CLOCK); + + intpend &= INTERRUPT_MASK; +#endif + } +} + +#endif // NRFX_CHECK(NRFX_CLOCK_ENABLED) diff --git a/nrfx/drivers/src/nrfx_clock_xo.c b/nrfx/drivers/src/nrfx_clock_xo.c new file mode 100644 index 00000000..5766b27d --- /dev/null +++ b/nrfx/drivers/src/nrfx_clock_xo.c @@ -0,0 +1,362 @@ +/* + * Copyright (c) 2016 - 2025, Nordic Semiconductor ASA + * All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its + * contributors may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include + +#if NRFX_CHECK(NRFX_CLOCK_ENABLED) + +#include +#include +#include + +#define NRFX_LOG_MODULE CLOCK_XO +#include + +#if !defined(USE_WORKAROUND_FOR_ANOMALY_201) && \ + (defined(NRF52810_XXAA) || \ + defined(NRF52832_XXAA) || defined(NRF52832_XXAB) || \ + defined(NRF52840_XXAA)) +// Enable workaround for nRF52 anomaly 201 (EVENTS_HFCLKSTARTED might be generated twice). +#define USE_WORKAROUND_FOR_ANOMALY_201 1 +#endif + +#define INTERRUPT_MASK \ + NRFX_COND_CODE_1(NRF_CLOCK_HAS_HFDOMAIN, (NRF_CLOCK_INT_HF_STARTED_MASK |), ()) \ + NRFX_COND_CODE_1(NRF_CLOCK_HAS_XO_TUNE, (NRF_CLOCK_INT_XOTUNED_MASK | NRF_CLOCK_INT_XOTUNEERROR_MASK | NRF_CLOCK_INT_XOTUNEFAILED_MASK |), ()) \ + 0 + +#if NRFX_CHECK(NRF_CLOCK_HAS_XO_TUNE) +typedef enum +{ + XO_STATE_NOT_TUNED, + XO_STATE_TUNING, + XO_STATE_TUNED +} nrfx_clock_xo_state_t; +#endif + +#define NRFX_CLOCK_EVT2STR(evt_type) \ + evt_type == NRFX_CLOCK_XO_EVT_HFCLK_STARTED ? "XO_STARTED" : \ + NRFX_COND_CODE_1(NRF_CLOCK_HAS_PLL, (evt_type == NRFX_CLOCK_XO_EVT_PLL_STARTED ? "PLL_STARTED" :), ()) \ + NRFX_COND_CODE_1(NRF_CLOCK_HAS_XO_TUNE, (evt_type == NRFX_CLOCK_XO_EVT_XO_TUNED ? "XO_TUNED" : \ + evt_type == NRFX_CLOCK_XO_EVT_XO_TUNE_ERROR ? "XO_TUNE_ERROR" : \ + evt_type == NRFX_CLOCK_XO_EVT_XO_TUNE_FAILED ? "XO_TUNE_FAILED" :), ()) "Unknown" + +/** @brief CLOCK control block. */ +typedef struct +{ + nrfx_clock_xo_event_handler_t event_handler; + bool module_initialized; /*< Indicate the state of module */ +#if NRFX_CHECK(USE_WORKAROUND_FOR_ANOMALY_201) + bool hfclk_started; /*< Anomaly 201 workaround. */ +#endif + +#if NRFX_CHECK(NRF_CLOCK_HAS_XO_TUNE) + volatile nrfx_clock_xo_state_t xo_state; +#endif +} nrfx_clock_xo_cb_t; + +static nrfx_clock_xo_cb_t m_clock_cb; + +static void clock_stop(void) +{ + nrf_clock_int_disable(NRF_CLOCK, INTERRUPT_MASK); + nrf_clock_task_trigger(NRF_CLOCK, NRF_CLOCK_TASK_HFCLKSTOP); + nrf_clock_event_clear(NRF_CLOCK, NRF_CLOCK_EVENT_HFCLKSTARTED); +#if NRFX_CHECK(NRF54L_ERRATA_39_ENABLE_WORKAROUND) + if (nrf54l_errata_39()) + { + nrf_clock_task_trigger(NRF_CLOCK, NRF_CLOCK_TASK_PLLSTOP); + } +#endif + + bool stopped; + nrf_clock_hfclk_t clk_src = NRF_CLOCK_HFCLK_HIGH_ACCURACY; + NRFX_WAIT_FOR(!nrfx_clock_xo_running_check(&clk_src), 10000, 1, stopped); + if (!stopped) + { + NRFX_LOG_ERROR("Failed to stop clock XO."); + } + else + { +#if NRF_CLOCK_HAS_XO_TUNE + m_clock_cb.xo_state = XO_STATE_NOT_TUNED; +#endif +#if NRFX_CHECK(USE_WORKAROUND_FOR_ANOMALY_201) + m_clock_cb.hfclk_started = false; +#endif + } +} + +nrfx_err_t nrfx_clock_xo_init(nrfx_clock_xo_event_handler_t event_handler) +{ + nrfx_err_t err_code = NRFX_SUCCESS; + if (m_clock_cb.module_initialized) + { + err_code = NRFX_ERROR_ALREADY; + NRFX_LOG_INFO("Function: %s, error code: %s.", __func__, NRFX_LOG_ERROR_STRING_GET(err_code)); + return err_code; + } + else + { + m_clock_cb.event_handler = event_handler; + m_clock_cb.module_initialized = true; +#if NRFX_CHECK(USE_WORKAROUND_FOR_ANOMALY_201) + m_clock_cb.hfclk_started = false; +#endif + } + + return err_code; +} + +void nrfx_clock_xo_uninit(void) +{ + NRFX_ASSERT(m_clock_cb.module_initialized); + clock_stop(); + + m_clock_cb.module_initialized = false; + NRFX_LOG_INFO("Uninitialized."); +} + +bool nrfx_clock_xo_init_check(void) +{ + return m_clock_cb.module_initialized; +} + +void nrfx_clock_xo_start(void) +{ + uint32_t int_mask; + + NRFX_ASSERT(m_clock_cb.module_initialized); + + int_mask = NRF_CLOCK_INT_HF_STARTED_MASK | +#if NRF_CLOCK_HAS_XO_TUNE + NRF_CLOCK_INT_XOTUNED_MASK | + NRF_CLOCK_INT_XOTUNEERROR_MASK | + NRF_CLOCK_INT_XOTUNEFAILED_MASK | +#endif + 0; +#if NRF_CLOCK_HAS_XO_TUNE + nrf_clock_event_clear(NRF_CLOCK, NRF_CLOCK_EVENT_XOTUNED); + nrf_clock_event_clear(NRF_CLOCK, NRF_CLOCK_EVENT_XOTUNEFAILED); + nrf_clock_event_clear(NRF_CLOCK, NRF_CLOCK_EVENT_XOTUNEERROR); +#endif + +#if NRFX_CHECK(NRF54L_ERRATA_39_ENABLE_WORKAROUND) + if (nrf54l_errata_39()) + { + nrf_clock_task_trigger(NRF_CLOCK, NRF_CLOCK_TASK_PLLSTART); + } +#endif + + nrf_clock_event_clear(NRF_CLOCK, NRF_CLOCK_EVENT_HFCLKSTARTED); + nrf_clock_task_trigger(NRF_CLOCK, NRF_CLOCK_TASK_HFCLKSTART); + if (m_clock_cb.event_handler) + { + nrf_clock_int_enable(NRF_CLOCK, INTERRUPT_MASK); + } + else + { + while (!nrf_clock_event_check(NRF_CLOCK, NRF_CLOCK_EVENT_HFCLKSTARTED)) + { + } + nrf_clock_event_clear(NRF_CLOCK, NRF_CLOCK_EVENT_HFCLKSTARTED); + } +} + +void nrfx_clock_xo_stop(void) +{ + NRFX_ASSERT(m_clock_cb.module_initialized); + + clock_stop(); +} + +#if NRF_CLOCK_HAS_XO_TUNE +nrfx_err_t nrfx_clock_xo_tune_start(void) +{ + nrf_clock_hfclk_t hfclksrc = nrf_clock_hf_src_get(NRF_CLOCK); + if ((hfclksrc != NRF_CLOCK_HFCLK_HIGH_ACCURACY) || (m_clock_cb.xo_state == XO_STATE_TUNING)) + { + return NRFX_ERROR_INVALID_STATE; + } + + nrf_clock_event_clear(NRF_CLOCK, NRF_CLOCK_EVENT_XOTUNED); + nrf_clock_event_clear(NRF_CLOCK, NRF_CLOCK_EVENT_XOTUNEFAILED); + + if (m_clock_cb.event_handler) + { + uint32_t int_mask = NRF_CLOCK_INT_XOTUNED_MASK | NRF_CLOCK_INT_XOTUNEFAILED_MASK; + nrf_clock_int_enable(NRF_CLOCK, int_mask); + } + + // XOTUNEERROR can occur at any moment and it is not related to this operation + m_clock_cb.xo_state = XO_STATE_TUNING; + nrf_clock_task_trigger(NRF_CLOCK, NRF_CLOCK_TASK_XOTUNE); + + if (!m_clock_cb.event_handler) + { + bool evt_xotuned; + bool evt_xotunefailed; + do + { + evt_xotuned = nrf_clock_event_check(NRF_CLOCK, NRF_CLOCK_EVENT_XOTUNED); + evt_xotunefailed = nrf_clock_event_check(NRF_CLOCK, NRF_CLOCK_EVENT_XOTUNEFAILED); + } while (!(evt_xotuned | evt_xotunefailed)); + m_clock_cb.xo_state = evt_xotuned ? XO_STATE_TUNED : XO_STATE_NOT_TUNED; + + nrf_clock_event_clear(NRF_CLOCK, NRF_CLOCK_EVENT_XOTUNED); + nrf_clock_event_clear(NRF_CLOCK, NRF_CLOCK_EVENT_XOTUNEFAILED); + + if (evt_xotunefailed) + { + return NRFX_ERROR_INTERNAL; + } + } + + return NRFX_SUCCESS; +} + +nrfx_err_t nrfx_clock_xo_tune_abort(void) +{ + nrf_clock_hfclk_t hfclksrc = nrf_clock_hf_src_get(NRF_CLOCK); + if ((hfclksrc != NRF_CLOCK_HFCLK_HIGH_ACCURACY) || (m_clock_cb.xo_state != XO_STATE_TUNING)) + { + return NRFX_ERROR_FORBIDDEN; + } + + nrf_clock_task_trigger(NRF_CLOCK, NRF_CLOCK_TASK_XOTUNEABORT); + m_clock_cb.xo_state = XO_STATE_NOT_TUNED; + + if (m_clock_cb.event_handler) + { + uint32_t int_mask = NRF_CLOCK_INT_XOTUNED_MASK | NRF_CLOCK_INT_XOTUNEFAILED_MASK; + nrf_clock_int_disable(NRF_CLOCK, int_mask); + } + + return NRFX_SUCCESS; +} + +bool nrfx_clock_xo_tune_error_check(void) +{ + NRFX_ASSERT(!m_clock_cb.event_handler); + + bool quality_issue = nrf_clock_event_check(NRF_CLOCK, NRF_CLOCK_EVENT_XOTUNEERROR); + if (quality_issue) + { + nrf_clock_event_clear(NRF_CLOCK, NRF_CLOCK_EVENT_XOTUNEERROR); + } + return quality_issue; +} + +bool nrfx_clock_xo_tune_status_check(void) +{ + return m_clock_cb.xo_state == XO_STATE_TUNED; +} + +#endif // NRF_CLOCK_HAS_XO + +void nrfx_clock_xo_irq_handler(void) +{ + +#if NRF_CLOCK_HAS_INTPEND + uint32_t intpend = nrf_clock_int_pending_get(NRF_CLOCK); +#else + uint32_t intpend = nrf_clock_int_enable_check(NRF_CLOCK, UINT32_MAX); +#endif + + intpend &= INTERRUPT_MASK; + + while (intpend != 0) + { + uint32_t int_bit = 31 - NRF_CLZ(intpend); + nrf_clock_int_mask_t int_mask = (nrf_clock_int_mask_t)NRFX_BIT(int_bit); + nrf_clock_event_t evt = (nrf_clock_event_t)NRFY_INT_BITPOS_TO_EVENT(int_bit); + nrfx_clock_xo_evt_type_t evt_type = (nrfx_clock_xo_evt_type_t)int_bit; + bool call_handler = true; +#if !NRF_CLOCK_HAS_INTPEND + intpend &= ~int_mask; + // Check if event is set for that interrupt and if not continue. + if (!nrf_clock_event_check(NRF_CLOCK, evt)) + { + continue; + } +#endif + + nrf_clock_event_clear(NRF_CLOCK, evt); + nrf_clock_int_disable(NRF_CLOCK, int_mask); + + NRFX_LOG_DEBUG("Event: %s", NRFX_CLOCK_EVT2STR(evt_type)); + switch (int_mask) + { + case NRF_CLOCK_INT_HF_STARTED_MASK: +#if NRFX_CHECK(USE_WORKAROUND_FOR_ANOMALY_201) + if (!m_clock_cb.hfclk_started) + { + m_clock_cb.hfclk_started = true; + } + else + { + call_handler = false; + } +#endif + break; +#if NRFX_CHECK(NRF_CLOCK_HAS_XO_TUNE) + case NRF_CLOCK_INT_XOTUNED_MASK: + m_clock_cb.xo_state = XO_STATE_TUNED; + // Enable XOTUNEERROR interrupt to handle situation when XO is out of tune. + nrf_clock_event_clear(NRF_CLOCK, NRF_CLOCK_EVENT_XOTUNEERROR); + nrf_clock_int_enable(NRF_CLOCK, NRF_CLOCK_INT_XOTUNEERROR_MASK); + break; + case NRF_CLOCK_INT_XOTUNEERROR_MASK: + break; + case NRF_CLOCK_INT_XOTUNEFAILED_MASK: + m_clock_cb.xo_state = XO_STATE_NOT_TUNED; + break; +#endif + default: + NRFX_ASSERT(0); + break; + } + + if (call_handler) + { + m_clock_cb.event_handler(evt_type); + } +#if NRF_CLOCK_HAS_INTPEND + intpend = nrf_clock_int_pending_get(NRF_CLOCK); + + intpend &= INTERRUPT_MASK; +#endif + } +} + +#endif // NRFX_CHECK(NRFX_CLOCK_ENABLED) diff --git a/nrfx/drivers/src/nrfx_power.c b/nrfx/drivers/src/nrfx_power.c index 58c872b4..b7f8168a 100644 --- a/nrfx/drivers/src/nrfx_power.c +++ b/nrfx/drivers/src/nrfx_power.c @@ -429,6 +429,13 @@ void nrfx_power_clock_irq_handler(void) { nrfx_power_irq_handler(); nrfx_clock_irq_handler(); +#if NRF_CLOCK_HAS_HFCLK + nrfx_clock_hfclk_irq_handler(); +#endif +#if NRF_CLOCK_HAS_XO + nrfx_clock_xo_irq_handler(); +#endif + nrfx_clock_lfclk_irq_handler(); } #endif diff --git a/nrfx/hal/nrf_clock.h b/nrfx/hal/nrf_clock.h index 2f00da5c..01f60653 100644 --- a/nrfx/hal/nrf_clock.h +++ b/nrfx/hal/nrf_clock.h @@ -116,6 +116,13 @@ extern "C" { #define NRF_CLOCK_HAS_HFCLK_ALWAYSRUN 0 #endif +#if defined(CLOCK_HFCLKRUN_STATUS_Msk) || defined(__NRFX_DOXYGEN__) +/** @brief Symbol indicating whether the HFCLK clock is present. */ +#define NRF_CLOCK_HAS_HFCLK 1 +#else +#define NRF_CLOCK_HAS_HFCLK 0 +#endif + #if defined(CLOCK_HFCLKSRC_SRC_Msk) || defined(__NRFX_DOXYGEN__) /** @brief Symbol indicating whether the HFCLKSRC register is present. */ #define NRF_CLOCK_HAS_HFCLKSRC 1