From 0ccfc39032979349a7b524e9806a45b4a4387493 Mon Sep 17 00:00:00 2001 From: Alberto Escolar Piedras Date: Wed, 3 Jul 2019 13:28:38 +0200 Subject: [PATCH 1/7] nrf52_bsim: Require v1.8 of the HW models The incomming new NRF clock driver requires extra nrf HAL and drivers functions, which are only supported in the HW models after version 1.8 Signed-off-by: Alberto Escolar Piedras --- boards/posix/nrf52_bsim/hw_models_version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/boards/posix/nrf52_bsim/hw_models_version b/boards/posix/nrf52_bsim/hw_models_version index b1f7421584f25..0d4d0cb457d7d 100644 --- a/boards/posix/nrf52_bsim/hw_models_version +++ b/boards/posix/nrf52_bsim/hw_models_version @@ -1 +1 @@ -v1.4 +v1.8 From 94a519a3f48c618cc1c0601653342f8d68c3177b Mon Sep 17 00:00:00 2001 From: Krzysztof Chruscinski Date: Wed, 26 Jun 2019 13:27:31 +0200 Subject: [PATCH 2/7] drivers: clock_control: nrf: reimplementation including API updates Reimplementation of clock control driver for nrf platform. It includes latest API changes: asynchronous starting and getting clock status. Additionally, it implements calibration algorithm which optionally skips calibration based on no temperature change. Internal temperature sensor is used for that. Signed-off-by: Krzysztof Chruscinski --- CODEOWNERS | 1 + drivers/clock_control/CMakeLists.txt | 1 + drivers/clock_control/Kconfig.nrf | 57 +- drivers/clock_control/nrf_clock_calibration.c | 331 +++++++++ drivers/clock_control/nrf_clock_calibration.h | 68 ++ drivers/clock_control/nrf_power_clock.c | 667 +++++++----------- drivers/sensor/nrf5/temp_nrf5.c | 19 +- .../drivers/clock_control/nrf_clock_control.h | 23 +- 8 files changed, 755 insertions(+), 412 deletions(-) create mode 100644 drivers/clock_control/nrf_clock_calibration.c create mode 100644 drivers/clock_control/nrf_clock_calibration.h diff --git a/CODEOWNERS b/CODEOWNERS index 32a9f0d37788d..3b6ed4a4ab43a 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -117,6 +117,7 @@ /drivers/bluetooth/ @joerchan @jhedberg @Vudentz /drivers/can/ @alexanderwachter /drivers/can/*mcp2515* @karstenkoenig +/drivers/clock_control/*nrf* @nordic-krch /drivers/counter/ @nordic-krch /drivers/counter/counter_cmos.c @gnuless /drivers/display/ @vanwinkeljan diff --git a/drivers/clock_control/CMakeLists.txt b/drivers/clock_control/CMakeLists.txt index 7d38bfa3dcb87..c73a5ac4c472b 100644 --- a/drivers/clock_control/CMakeLists.txt +++ b/drivers/clock_control/CMakeLists.txt @@ -7,6 +7,7 @@ zephyr_sources_ifdef(CONFIG_CLOCK_CONTROL_MCUX_PCC clock_control_mcux zephyr_sources_ifdef(CONFIG_CLOCK_CONTROL_MCUX_SCG clock_control_mcux_scg.c) zephyr_sources_ifdef(CONFIG_CLOCK_CONTROL_MCUX_SIM clock_control_mcux_sim.c) zephyr_sources_ifdef(CONFIG_CLOCK_CONTROL_NRF nrf_power_clock.c) +zephyr_sources_ifdef(CONFIG_CLOCK_CONTROL_NRF_K32SRC_RC_CALIBRATION nrf_clock_calibration.c) zephyr_sources_ifdef(CONFIG_CLOCK_CONTROL_RV32M1_PCC clock_control_rv32m1_pcc.c) if(CONFIG_CLOCK_CONTROL_STM32_CUBE) diff --git a/drivers/clock_control/Kconfig.nrf b/drivers/clock_control/Kconfig.nrf index df8ef05649d3d..642507a555ff8 100644 --- a/drivers/clock_control/Kconfig.nrf +++ b/drivers/clock_control/Kconfig.nrf @@ -47,14 +47,57 @@ config CLOCK_CONTROL_NRF_K32SRC_EXT_FULL_SWING endchoice -config CLOCK_CONTROL_NRF_K32SRC_BLOCKING - bool "Blocking 32KHz crystal oscillator startup" - depends on CLOCK_CONTROL_NRF_K32SRC_XTAL +config CLOCK_CONTROL_NRF_K32SRC_RC_CALIBRATION + bool + depends on !SOC_SERIES_NRF91X + default y if CLOCK_CONTROL_NRF_K32SRC_RC + +if CLOCK_CONTROL_NRF_K32SRC_RC_CALIBRATION + +config CLOCK_CONTROL_NRF_CALIBRATION_PERIOD + int "Calibration opportunity period (in 250ms units)" + default 16 + range 1 127 + help + Periodically, calibration action is performed. Action includes + temperature measurement followed by clock calibration. Calibration may + be skipped if temperature change (compared to measurement of previous + calibration) did not exceeded CLOCK_CONTROL_NRF_CALIBRATION_TEMP_DIFF + and number of consecutive skips did not exceeded + CLOCK_CONTROL_NRF_CALIBRATION_MAX_SKIP. + +config CLOCK_CONTROL_NRF_CALIBRATION_MAX_SKIP + int "Maximum number of calibration skips" + default 1 + range 0 255 + help + Calibration is skipped when temperature change since last calibration + was less than configured threshold. If number of consecutive skips + reaches configured value then calibration is performed + unconditionally. Set to 0 to perform calibration periodically + regardless of temperature change. + +config CLOCK_CONTROL_NRF_CALIBRATION_TEMP_DIFF + int "Temperature change triggering calibration (in 0.25 degree units)" + default 2 help - Clock control driver will spin wait in CPU sleep until 32KHz - crystal oscillator starts up. If not enabled, RC oscillator will - initially start running and automatically switch to crystal when - ready. + Calibration is triggered if the temperature has changed by at least + this amount since the last calibration. + +config CLOCK_CONTROL_NRF_CALIBRATION_DEBUG + bool "Calibration intrumentation" + help + Enables retrieving debug information like number of performed or + skipped calibrations. + +config CLOCK_CONTROL_NRF_USES_TEMP_SENSOR + bool + default y if CLOCK_CONTROL_NRF_CALIBRATION_MAX_SKIP > 0 && \ + CLOCK_CONTROL_NRF_CALIBRATION_TEMP_DIFF > 0 + select TEMP_NRF5 + select SENSOR + +endif # CLOCK_CONTROL_NRF_K32SRC_RC_CALIBRATION choice CLOCK_CONTROL_NRF_ACCURACY prompt "32KHz clock accuracy" diff --git a/drivers/clock_control/nrf_clock_calibration.c b/drivers/clock_control/nrf_clock_calibration.c new file mode 100644 index 0000000000000..a807d017b1ecf --- /dev/null +++ b/drivers/clock_control/nrf_clock_calibration.c @@ -0,0 +1,331 @@ +/* + * Copyright (c) 2019 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: Apache-2.0 + */ +#include +#include +#include "nrf_clock_calibration.h" +#include +#include +#include + +LOG_MODULE_DECLARE(clock_control, CONFIG_CLOCK_CONTROL_LOG_LEVEL); + +/* For platforms that do not have CTSTOPPED event CT timer can be started + * immediately after stop. Redefined events to avoid ifdefs in the code, + * CTSTOPPED interrupt handling will be removed during compilation. + */ +#ifndef CLOCK_EVENTS_CTSTOPPED_EVENTS_CTSTOPPED_Msk +#define NRF_CLOCK_EVENT_CTSTOPPED 0 +#endif + +#ifndef CLOCK_INTENSET_CTSTOPPED_Msk +#define NRF_CLOCK_INT_CTSTOPPED_MASK 0 +#endif + +#define TEMP_SENSOR_NAME \ + COND_CODE_1(CONFIG_TEMP_NRF5, (CONFIG_TEMP_NRF5_NAME), (NULL)) + +/* Calibration state enum */ +enum nrf_cal_state { + CAL_OFF, + CAL_IDLE, /* Calibration timer active, waiting for expiration. */ + CAL_HFCLK_REQ, /* HFCLK XTAL requested. */ + CAL_TEMP_REQ, /* Temperature measurement requested. */ + CAL_ACTIVE, /* Ongoing calibration. */ + CAL_ACTIVE_OFF /* Ongoing calibration, off requested. */ +}; + +static enum nrf_cal_state cal_state; /* Calibration state. */ +static s16_t prev_temperature; /* Previous temperature measurement. */ +static u8_t calib_skip_cnt; /* Counting down skipped calibrations. */ +static int total_cnt; /* Total number of calibrations. */ +static int total_skips_cnt; /* Total number of skipped calibrations. */ + +/* Callback called on hfclk started. */ +static void cal_hf_on_callback(struct device *dev, void *user_data); +static struct clock_control_async_data cal_hf_on_data = { + .cb = cal_hf_on_callback +}; + +static struct device *hfclk_dev; /* Handler to hfclk device. */ +static struct device *temp_sensor; /* Handler to temperature sensor device. */ + +static void measure_temperature(struct k_work *work); +static K_WORK_DEFINE(temp_measure_work, measure_temperature); + +static bool clock_event_check_and_clean(u32_t evt, u32_t intmask) +{ + bool ret = nrf_clock_event_check(evt) && + nrf_clock_int_enable_check(intmask); + + if (ret) { + nrf_clock_event_clear(evt); + } + + return ret; +} + +bool z_nrf_clock_calibration_start(struct device *dev) +{ + bool ret; + int key = irq_lock(); + + if (cal_state != CAL_ACTIVE_OFF) { + ret = true; + } else { + ret = false; + } + + cal_state = CAL_IDLE; + + irq_unlock(key); + + calib_skip_cnt = 0; + + return ret; +} + +void z_nrf_clock_calibration_lfclk_started(struct device *dev) +{ + /* Trigger unconditional calibration when lfclk is started. */ + cal_state = CAL_HFCLK_REQ; + clock_control_async_on(hfclk_dev, 0, &cal_hf_on_data); +} + +bool z_nrf_clock_calibration_stop(struct device *dev) +{ + int key; + bool ret = true; + + key = irq_lock(); + + nrf_clock_task_trigger(NRF_CLOCK_TASK_CTSTOP); + nrf_clock_event_clear(NRF_CLOCK_EVENT_CTTO); + + /* If calibration is active then pend until completed. + * Currently (and most likely in the future), LFCLK is never stopped so + * it is not an issue. + */ + if (cal_state == CAL_ACTIVE) { + cal_state = CAL_ACTIVE_OFF; + ret = false; + } else { + cal_state = CAL_OFF; + } + + irq_unlock(key); + LOG_DBG("Stop requested %s.", (cal_state == CAL_ACTIVE_OFF) ? + "during ongoing calibration" : ""); + + return ret; +} + +void z_nrf_clock_calibration_init(struct device *dev) +{ + /* Anomaly 36: After watchdog timeout reset, CPU lockup reset, soft + * reset, or pin reset EVENTS_DONE and EVENTS_CTTO are not reset. + */ + nrf_clock_event_clear(NRF_CLOCK_EVENT_DONE); + nrf_clock_event_clear(NRF_CLOCK_EVENT_CTTO); + + nrf_clock_int_enable(NRF_CLOCK_INT_DONE_MASK | + NRF_CLOCK_INT_CTTO_MASK | + NRF_CLOCK_INT_CTSTOPPED_MASK); + nrf_clock_cal_timer_timeout_set( + CONFIG_CLOCK_CONTROL_NRF_CALIBRATION_PERIOD); + + if (CONFIG_CLOCK_CONTROL_NRF_CALIBRATION_MAX_SKIP != 0) { + temp_sensor = device_get_binding(TEMP_SENSOR_NAME); + } + + hfclk_dev = dev; + total_cnt = 0; + total_skips_cnt = 0; +} + +/* Start calibration assuming that HFCLK XTAL is on. */ +static void start_calibration(void) +{ + cal_state = CAL_ACTIVE; + + /* Workaround for Errata 192 */ + if (IS_ENABLED(CONFIG_SOC_SERIES_NRF52X)) { + *(volatile uint32_t *)0x40000C34 = 0x00000002; + } + + nrf_clock_task_trigger(NRF_CLOCK_TASK_CAL); + calib_skip_cnt = CONFIG_CLOCK_CONTROL_NRF_CALIBRATION_MAX_SKIP; +} + +/* Restart calibration timer, release HFCLK XTAL. */ +static void to_idle(void) +{ + cal_state = CAL_IDLE; + clock_control_off(hfclk_dev, 0); + nrf_clock_task_trigger(NRF_CLOCK_TASK_CTSTART); +} + +/* Convert sensor value to 0.25'C units. */ +static inline s16_t sensor_value_to_temp_unit(struct sensor_value *val) +{ + return (s16_t)(4 * val->val1 + val->val2 / 250000); +} + +/* Function reads from temperature sensor and converts to 0.25'C units. */ +static s16_t get_temperature(void) +{ + struct sensor_value sensor_val; + + sensor_sample_fetch(temp_sensor); + sensor_channel_get(temp_sensor, SENSOR_CHAN_DIE_TEMP, &sensor_val); + + return sensor_value_to_temp_unit(&sensor_val); +} + +/* Function determines if calibration should be performed based on temperature + * measurement. Function is called from system work queue context. It is + * reading temperature from TEMP sensor and compares with last measurement. + */ +static void measure_temperature(struct k_work *work) +{ + s16_t temperature; + s16_t diff; + bool started = false; + int key; + + temperature = get_temperature(); + diff = abs(temperature - prev_temperature); + + key = irq_lock(); + + if (cal_state != CAL_OFF) { + if ((calib_skip_cnt == 0) || + (diff >= CONFIG_CLOCK_CONTROL_NRF_CALIBRATION_TEMP_DIFF)) { + prev_temperature = temperature; + start_calibration(); + started = true; + } else { + to_idle(); + calib_skip_cnt--; + total_skips_cnt++; + } + } + + irq_unlock(key); + + LOG_DBG("Calibration %s. Temperature diff: %d (in 0.25'C units).", + started ? "started" : "skipped", diff); +} + +/* Called when HFCLK XTAL is on. Schedules temperature measurement or triggers + * calibration. + */ +static void cal_hf_on_callback(struct device *dev, void *user_data) +{ + int key = irq_lock(); + + if (cal_state == CAL_HFCLK_REQ) { + if ((CONFIG_CLOCK_CONTROL_NRF_CALIBRATION_MAX_SKIP == 0) || + (IS_ENABLED(CONFIG_MULTITHREADING) == false)) { + start_calibration(); + } else { + cal_state = CAL_TEMP_REQ; + k_work_submit(&temp_measure_work); + } + } else { + clock_control_off(hfclk_dev, 0); + } + + irq_unlock(key); +} + +static void on_cal_done(void) +{ + /* Workaround for Errata 192 */ + if (IS_ENABLED(CONFIG_SOC_SERIES_NRF52X)) { + *(volatile uint32_t *)0x40000C34 = 0x00000000; + } + + total_cnt++; + LOG_DBG("Calibration done."); + + int key = irq_lock(); + + if (cal_state == CAL_ACTIVE_OFF) { + clock_control_off(hfclk_dev, 0); + nrf_clock_task_trigger(NRF_CLOCK_TASK_LFCLKSTOP); + cal_state = CAL_OFF; + } else { + to_idle(); + } + + irq_unlock(key); +} + +void z_nrf_clock_calibration_force_start(void) +{ + int key = irq_lock(); + + calib_skip_cnt = 0; + + if (cal_state == CAL_IDLE) { + cal_state = CAL_HFCLK_REQ; + clock_control_async_on(hfclk_dev, 0, &cal_hf_on_data); + } + + irq_unlock(key); +} + +void z_nrf_clock_calibration_isr(void) +{ + if (clock_event_check_and_clean(NRF_CLOCK_EVENT_CTTO, + NRF_CLOCK_INT_CTTO_MASK)) { + LOG_DBG("Calibration timeout."); + + /* Start XTAL HFCLK. It is needed for temperature measurement + * and calibration. + */ + if (cal_state == CAL_IDLE) { + cal_state = CAL_HFCLK_REQ; + clock_control_async_on(hfclk_dev, 0, &cal_hf_on_data); + } + } + + if (clock_event_check_and_clean(NRF_CLOCK_EVENT_DONE, + NRF_CLOCK_INT_DONE_MASK)) { + on_cal_done(); + } + + if (NRF_CLOCK_INT_CTSTOPPED_MASK && + clock_event_check_and_clean(NRF_CLOCK_EVENT_CTSTOPPED, + NRF_CLOCK_INT_CTSTOPPED_MASK)) { + LOG_INF("CT stopped."); + if (cal_state == CAL_IDLE) { + /* If LF clock was restarted then CT might not be + * started because it was not yet stopped. + */ + LOG_INF("restarting"); + nrf_clock_task_trigger(NRF_CLOCK_TASK_CTSTART); + } + } +} + +int z_nrf_clock_calibration_count(void) +{ + if (!IS_ENABLED(CONFIG_CLOCK_CONTROL_NRF_CALIBRATION_DEBUG)) { + return -1; + } + + return total_cnt; +} + +int z_nrf_clock_calibration_skips_count(void) +{ + if (!IS_ENABLED(CONFIG_CLOCK_CONTROL_NRF_CALIBRATION_DEBUG)) { + return -1; + } + + return total_skips_cnt; +} diff --git a/drivers/clock_control/nrf_clock_calibration.h b/drivers/clock_control/nrf_clock_calibration.h new file mode 100644 index 0000000000000..63e366170c10c --- /dev/null +++ b/drivers/clock_control/nrf_clock_calibration.h @@ -0,0 +1,68 @@ +/* + * Copyright (c) 2019 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: Apache-2.0 + */ +#ifndef ZEPHYR_DRIVERS_CLOCK_CONTROL_NRF_CLOCK_CALIBRATION_H_ +#define ZEPHYR_DRIVERS_CLOCK_CONTROL_NRF_CLOCK_CALIBRATION_H_ + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief Initialize LFCLK RC calibration. + * + * @param hfclk_dev HFCLK device. + */ +void z_nrf_clock_calibration_init(struct device *hfclk_dev); + +/** + * @brief Calibration interrupts handler + * + * Must be called from clock interrupt context. + */ +void z_nrf_clock_calibration_isr(void); + +/** + * @brief Start calibration. + * + * Function called when LFCLK RC clock is being started. + * + * @param dev LFCLK device. + * + * @retval true if clock can be started. + * @retval false if clock was not stopped due to ongoing calibration and don't + * need to be started again because it is still on. + */ +bool z_nrf_clock_calibration_start(struct device *dev); + +/** + * @brief Notify calibration module about LF clock start + * + * @param dev LFCLK device. + */ +void z_nrf_clock_calibration_lfclk_started(struct device *dev); + +/** + * @brief Stop calibration. + * + * Function called when LFCLK RC clock is being stopped. + * + * @param dev LFCLK device. + * + * @retval true if clock can be stopped. + * @retval false if due to ongoing calibration clock cannot be stopped. In that + * case calibration module will stop clock when calibration is + * completed. + */ +bool z_nrf_clock_calibration_stop(struct device *dev); + + +#ifdef __cplusplus +} +#endif + +#endif /* ZEPHYR_DRIVERS_CLOCK_CONTROL_NRF_CLOCK_CALIBRATION_H_ */ diff --git a/drivers/clock_control/nrf_power_clock.c b/drivers/clock_control/nrf_power_clock.c index 330d2be649e08..dadacbb51c1cb 100644 --- a/drivers/clock_control/nrf_power_clock.c +++ b/drivers/clock_control/nrf_power_clock.c @@ -1,280 +1,321 @@ /* - * Copyright (c) 2016 Nordic Semiconductor ASA + * Copyright (c) 2016-2019 Nordic Semiconductor ASA * Copyright (c) 2016 Vinayak Kariappa Chettimada * * SPDX-License-Identifier: Apache-2.0 */ #include -#include -#include -#include #include -#include -#include -#if defined(CONFIG_USB) && defined(CONFIG_SOC_NRF52840) -#include #include -#endif +#include "nrf_clock_calibration.h" +#include +#include -static u8_t m16src_ref; -static u8_t m16src_grd; -static u8_t k32src_initialized; +LOG_MODULE_REGISTER(clock_control, CONFIG_CLOCK_CONTROL_LOG_LEVEL); -static int m16src_start(struct device *dev, clock_control_subsys_t sub_system) -{ - bool blocking; - u32_t imask; - u32_t stat; - - /* If the clock is already started then just increment refcount. - * If the start and stop don't happen in pairs, a rollover will - * be caught and in that case system should assert. - */ - - /* Test for reference increment from zero and resource guard not taken. - */ - imask = irq_lock(); - - if (m16src_ref++) { - irq_unlock(imask); - goto hf_already_started; - } +/* Helper logging macros which prepends device name to the log. */ +#define CLOCK_LOG(lvl, dev, ...) \ + LOG_##lvl("%s: " GET_ARG1(__VA_ARGS__), dev->config->name \ + COND_CODE_0(NUM_VA_ARGS_LESS_1(__VA_ARGS__),\ + (), (, GET_ARGS_LESS_1(__VA_ARGS__)))) +#define ERR(dev, ...) CLOCK_LOG(ERR, dev, __VA_ARGS__) +#define WRN(dev, ...) CLOCK_LOG(WRN, dev, __VA_ARGS__) +#define INF(dev, ...) CLOCK_LOG(INF, dev, __VA_ARGS__) +#define DBG(dev, ...) CLOCK_LOG(DBG, dev, __VA_ARGS__) - if (m16src_grd) { - m16src_ref--; - irq_unlock(imask); - return -EAGAIN; - } +/* returns true if clock stopping or starting can be performed. If false then + * start/stop will be deferred and performed later on by handler owner. + */ +typedef bool (*nrf_clock_handler_t)(struct device *dev); - m16src_grd = 1U; +/* Clock instance structure */ +struct nrf_clock_control { + sys_slist_t list; /* List of users requesting callback */ + s8_t ref; /* Users counter */ + bool started; /* Indicated that clock is started */ +}; - irq_unlock(imask); +/* Clock instance static configuration */ +struct nrf_clock_control_config { + nrf_clock_handler_t start_handler; /* Called before start */ + nrf_clock_handler_t stop_handler; /* Called before stop */ + nrf_clock_event_t started_evt; /* Clock started event */ + nrf_clock_task_t start_tsk; /* Clock start task */ + nrf_clock_task_t stop_tsk; /* Clock stop task */ +}; - /* If blocking then spin-wait in CPU sleep until 16MHz clock settles. */ - blocking = POINTER_TO_UINT(sub_system); - if (blocking) { - u32_t intenset; +/* Return true if given event has enabled interrupt and is triggered. Event + * is cleared. + */ +static bool clock_event_check_and_clean(nrf_clock_event_t evt, u32_t intmask) +{ + bool ret = nrf_clock_event_check(evt) && + nrf_clock_int_enable_check(intmask); - irq_disable(DT_INST_0_NORDIC_NRF_CLOCK_IRQ_0); + if (ret) { + nrf_clock_event_clear(evt); + } - NRF_CLOCK->EVENTS_HFCLKSTARTED = 0; + return ret; +} - intenset = NRF_CLOCK->INTENSET; - nrf_clock_int_enable(NRF_CLOCK_INT_HF_STARTED_MASK); +static enum clock_control_status get_status(struct device *dev, + clock_control_subsys_t sys) +{ + struct nrf_clock_control *data = dev->driver_data; - nrf_clock_task_trigger(NRF_CLOCK_TASK_HFCLKSTART); + if (data->started) { + return CLOCK_CONTROL_STATUS_ON; + } - while (NRF_CLOCK->EVENTS_HFCLKSTARTED == 0) { - __WFE(); - __SEV(); - __WFE(); - } + if (data->ref > 0) { + return CLOCK_CONTROL_STATUS_STARTING; + } - NRF_CLOCK->EVENTS_HFCLKSTARTED = 0; + return CLOCK_CONTROL_STATUS_OFF; +} - if (!(intenset & CLOCK_INTENSET_HFCLKSTARTED_Msk)) { - nrf_clock_int_disable(NRF_CLOCK_INT_HF_STARTED_MASK); +static int clock_stop(struct device *dev, clock_control_subsys_t sub_system) +{ + const struct nrf_clock_control_config *config = + dev->config->config_info; + struct nrf_clock_control *data = dev->driver_data; + int err = 0; + int key; + + key = irq_lock(); + data->ref--; + if (data->ref == 0) { + bool do_stop; + + DBG(dev, "Stopping"); + sys_slist_init(&data->list); + + do_stop = (config->stop_handler) ? + config->stop_handler(dev) : true; + + if (do_stop) { + nrf_clock_task_trigger(config->stop_tsk); + /* It may happen that clock is being stopped when it + * has just been started and start is not yet handled + * (due to irq_lock). In that case after stopping the + * clock, started event is cleared to prevent false + * interrupt being triggered. + */ + nrf_clock_event_clear(config->started_evt); } - NVIC_ClearPendingIRQ(DT_INST_0_NORDIC_NRF_CLOCK_IRQ_0); - - irq_enable(DT_INST_0_NORDIC_NRF_CLOCK_IRQ_0); - } else { - NRF_CLOCK->EVENTS_HFCLKSTARTED = 0; - - nrf_clock_task_trigger(NRF_CLOCK_TASK_HFCLKSTART); + data->started = false; + } else if (data->ref < 0) { + data->ref = 0; + err = -EALREADY; } - /* release resource guard */ - m16src_grd = 0U; + irq_unlock(key); -hf_already_started: - /* rollover should not happen as start and stop shall be - * called in pairs. - */ - __ASSERT_NO_MSG(m16src_ref); - - stat = NRF_CLOCK_HFCLK_HIGH_ACCURACY | CLOCK_HFCLKSTAT_STATE_Msk; - if ((NRF_CLOCK->HFCLKSTAT & stat) == stat) { - return 0; - } else { - return -EINPROGRESS; - } + return err; } -static int m16src_stop(struct device *dev, clock_control_subsys_t sub_system) +static bool is_in_list(sys_slist_t *list, sys_snode_t *node) { - u32_t imask; + sys_snode_t *item = sys_slist_peek_head(list); - ARG_UNUSED(sub_system); + do { + if (item == node) { + return true; + } - /* Test for started resource, if so, decrement reference and acquire - * resource guard. - */ - imask = irq_lock(); + item = sys_slist_peek_next(item); + } while (item); - if (!m16src_ref) { - irq_unlock(imask); - return -EALREADY; - } + return false; +} - if (--m16src_ref) { - irq_unlock(imask); - return -EBUSY; - } +static int clock_async_start(struct device *dev, + clock_control_subsys_t sub_system, + struct clock_control_async_data *data) +{ + const struct nrf_clock_control_config *config = + dev->config->config_info; + struct nrf_clock_control *clk_data = dev->driver_data; + int key; + s8_t ref; + + __ASSERT_NO_MSG((data == NULL) || + ((data != NULL) && (data->cb != NULL))); + + key = irq_lock(); + ref = ++clk_data->ref; + irq_unlock(key); + + if (clk_data->started) { + if (data) { + data->cb(dev, data->user_data); + } + } else { + if (ref == 1) { + bool do_start; + + do_start = (config->start_handler) ? + config->start_handler(dev) : true; + if (do_start) { + nrf_clock_task_trigger(config->start_tsk); + DBG(dev, "Triggered start task"); + } else if (data) { + data->cb(dev, data->user_data); + } + } - if (m16src_grd) { - m16src_ref++; - irq_unlock(imask); - return -EAGAIN; + /* if node is in the list it means that it is scheduled for + * the second time. + */ + if (data) { + if (is_in_list(&clk_data->list, &data->node)) { + return -EALREADY; + } + + sys_slist_append(&clk_data->list, &data->node); + } } - m16src_grd = 1U; + return 0; +} - irq_unlock(imask); +static int clock_start(struct device *dev, clock_control_subsys_t sub_system) +{ + return clock_async_start(dev, sub_system, NULL); +} - /* re-entrancy and mult-context safe, and reference count is zero, */ +static void nrf_power_clock_isr(void *arg); - nrf_clock_task_trigger(NRF_CLOCK_TASK_HFCLKSTOP); +static int hfclk_init(struct device *dev) +{ + IRQ_CONNECT(DT_INST_0_NORDIC_NRF_CLOCK_IRQ_0, + DT_INST_0_NORDIC_NRF_CLOCK_IRQ_0_PRIORITY, + nrf_power_clock_isr, 0, 0); - /* release resource guard */ - m16src_grd = 0U; + irq_enable(DT_INST_0_NORDIC_NRF_CLOCK_IRQ_0); - return 0; -} + nrf_clock_lf_src_set(CLOCK_CONTROL_NRF_K32SRC); -static int k32src_start(struct device *dev, clock_control_subsys_t sub_system) -{ - u32_t lf_clk_src; - u32_t imask; - u32_t stat; - -#if defined(CONFIG_CLOCK_CONTROL_NRF_K32SRC_BLOCKING) - u32_t intenset; -#endif /* CONFIG_CLOCK_CONTROL_NRF_K32SRC_BLOCKING */ - - /* If the LF clock is already started, but wasn't initialized with - * this function, allow it to run once. This is needed because if a - * soft reset is triggered while watchdog is active, the LF clock will - * already be running, but won't be configured yet (watchdog forces LF - * clock to be running). - * - * That is, a hardware check won't work here, because even if the LF - * clock is already running it might not be initialized. We need an - * initialized flag. - */ - - imask = irq_lock(); - - if (k32src_initialized) { - irq_unlock(imask); - goto lf_already_started; + if (IS_ENABLED(CONFIG_CLOCK_CONTROL_NRF_K32SRC_RC_CALIBRATION)) { + z_nrf_clock_calibration_init(dev); } - k32src_initialized = 1U; + nrf_clock_int_enable(( + NRF_CLOCK_INT_HF_STARTED_MASK | + NRF_CLOCK_INT_LF_STARTED_MASK | + COND_CODE_1(CONFIG_USB_NRF52840, + (NRF_POWER_INT_USBDETECTED_MASK | + NRF_POWER_INT_USBREMOVED_MASK | + NRF_POWER_INT_USBPWRRDY_MASK), + (0)))); - irq_unlock(imask); + sys_slist_init(&((struct nrf_clock_control *)dev->driver_data)->list); - /* Clear events if any */ - NRF_CLOCK->EVENTS_LFCLKSTARTED = 0; + return 0; +} - /* Set LF Clock Source */ - lf_clk_src = POINTER_TO_UINT(sub_system); - NRF_CLOCK->LFCLKSRC = lf_clk_src; +static int lfclk_init(struct device *dev) +{ + sys_slist_init(&((struct nrf_clock_control *)dev->driver_data)->list); + return 0; +} -#if defined(CONFIG_CLOCK_CONTROL_NRF_K32SRC_BLOCKING) - irq_disable(DT_INST_0_NORDIC_NRF_CLOCK_IRQ_0); +static const struct clock_control_driver_api clock_control_api = { + .on = clock_start, + .off = clock_stop, + .async_on = clock_async_start, + .get_status = get_status, +}; - intenset = NRF_CLOCK->INTENSET; - nrf_clock_int_enable(NRF_CLOCK_INT_LF_STARTED_MASK); +static struct nrf_clock_control hfclk; +static const struct nrf_clock_control_config hfclk_config = { + .start_tsk = NRF_CLOCK_TASK_HFCLKSTART, + .started_evt = NRF_CLOCK_EVENT_HFCLKSTARTED, + .stop_tsk = NRF_CLOCK_TASK_HFCLKSTOP +}; - /* Start and spin-wait until clock settles */ - nrf_clock_task_trigger(NRF_CLOCK_TASK_LFCLKSTART); +DEVICE_AND_API_INIT(clock_nrf5_m16src, + DT_INST_0_NORDIC_NRF_CLOCK_LABEL "_16M", + hfclk_init, &hfclk, &hfclk_config, PRE_KERNEL_1, + CONFIG_KERNEL_INIT_PRIORITY_DEVICE, + &clock_control_api); + + +static struct nrf_clock_control lfclk; +static const struct nrf_clock_control_config lfclk_config = { + .start_tsk = NRF_CLOCK_TASK_LFCLKSTART, + .started_evt = NRF_CLOCK_EVENT_LFCLKSTARTED, + .stop_tsk = NRF_CLOCK_TASK_LFCLKSTOP, + .start_handler = + IS_ENABLED(CONFIG_CLOCK_CONTROL_NRF_K32SRC_RC_CALIBRATION) ? + z_nrf_clock_calibration_start : NULL, + .stop_handler = + IS_ENABLED(CONFIG_CLOCK_CONTROL_NRF_K32SRC_RC_CALIBRATION) ? + z_nrf_clock_calibration_stop : NULL +}; - while (NRF_CLOCK->EVENTS_LFCLKSTARTED == 0) { - __WFE(); - __SEV(); - __WFE(); +DEVICE_AND_API_INIT(clock_nrf5_k32src, + DT_INST_0_NORDIC_NRF_CLOCK_LABEL "_32K", + lfclk_init, &lfclk, &lfclk_config, PRE_KERNEL_1, + CONFIG_KERNEL_INIT_PRIORITY_DEVICE, + &clock_control_api); + +static void clkstarted_handle(struct device *dev) +{ + struct clock_control_async_data *async_data; + struct nrf_clock_control *data = dev->driver_data; + sys_snode_t *node = sys_slist_get(&data->list); + + DBG(dev, "Clock started"); + data->started = true; + + while (node != NULL) { + async_data = CONTAINER_OF(node, + struct clock_control_async_data, node); + async_data->cb(dev, async_data->user_data); + node = sys_slist_get(&data->list); } +} - NRF_CLOCK->EVENTS_LFCLKSTARTED = 0; +#if defined(CONFIG_USB_NRF52840) +static bool power_event_check_and_clean(nrf_power_event_t evt, u32_t intmask) +{ + bool ret = nrf_power_event_check(evt) && + nrf_power_int_enable_check(intmask); - if (!(intenset & CLOCK_INTENSET_LFCLKSTARTED_Msk)) { - nrf_clock_int_disable(NRF_CLOCK_INT_LF_STARTED_MASK); + if (ret) { + nrf_power_event_clear(evt); } - NVIC_ClearPendingIRQ(DT_INST_0_NORDIC_NRF_CLOCK_IRQ_0); - - irq_enable(DT_INST_0_NORDIC_NRF_CLOCK_IRQ_0); + return ret; +} +#endif -#else /* !CONFIG_CLOCK_CONTROL_NRF_K32SRC_BLOCKING */ - /* NOTE: LFCLK will initially start running from the LFRC if LFXO is - * selected. - */ - nrf_clock_int_enable(NRF_CLOCK_INT_LF_STARTED_MASK); - nrf_clock_task_trigger(NRF_CLOCK_TASK_LFCLKSTART); -#endif /* !CONFIG_CLOCK_CONTROL_NRF_K32SRC_BLOCKING */ - -#if NRF_CLOCK_HAS_CALIBRATION - /* If RC selected, calibrate and start timer for consecutive - * calibrations. - */ - nrf_clock_int_disable(NRF_CLOCK_INT_DONE_MASK | - NRF_CLOCK_INT_CTTO_MASK); - NRF_CLOCK->EVENTS_DONE = 0; - NRF_CLOCK->EVENTS_CTTO = 0; - - if ((lf_clk_src & CLOCK_LFCLKSRC_SRC_Msk) == CLOCK_LFCLKSRC_SRC_RC) { - int err; - - /* Set the Calibration Timer Initial Value */ - NRF_CLOCK->CTIV = 16; /* 4s in 0.25s units */ - - /* Enable DONE and CTTO IRQs */ - nrf_clock_int_enable(NRF_CLOCK_INT_DONE_MASK | - NRF_CLOCK_INT_CTTO_MASK); - - /* If non-blocking LF clock start, then start HF clock in ISR */ - if ((NRF_CLOCK->LFCLKSTAT & CLOCK_LFCLKSTAT_STATE_Msk) == 0) { - nrf_clock_int_enable(NRF_CLOCK_INT_LF_STARTED_MASK); - goto lf_already_started; - } +static void usb_power_isr(void) +{ +#if defined(CONFIG_USB_NRF52840) + extern void usb_dc_nrfx_power_event_callback(nrf_power_event_t event); - /* Start HF clock, if already started then explicitly - * assert IRQ. - * NOTE: The INTENSET is used as state flag to start - * calibration in ISR. - */ - nrf_clock_int_enable(NRF_CLOCK_INT_HF_STARTED_MASK); + if (power_event_check_and_clean(NRF_POWER_EVENT_USBDETECTED, + NRF_POWER_INT_USBDETECTED_MASK)) { + usb_dc_nrfx_power_event_callback(NRF_POWER_EVENT_USBDETECTED); + } - err = m16src_start(dev, false); - if (!err) { - NVIC_SetPendingIRQ(DT_INST_0_NORDIC_NRF_CLOCK_IRQ_0); - } else { - __ASSERT_NO_MSG(err == -EINPROGRESS); - } + if (power_event_check_and_clean(NRF_POWER_EVENT_USBPWRRDY, + NRF_POWER_INT_USBPWRRDY_MASK)) { + usb_dc_nrfx_power_event_callback(NRF_POWER_EVENT_USBPWRRDY); } -#endif /* NRF_CLOCK_HAS_CALIBRATION */ -lf_already_started: - stat = (NRF_CLOCK->LFCLKSRCCOPY & CLOCK_LFCLKSRCCOPY_SRC_Msk) | - CLOCK_LFCLKSTAT_STATE_Msk; - if ((NRF_CLOCK->LFCLKSTAT & stat) == stat) { - return 0; - } else { - return -EINPROGRESS; + if (power_event_check_and_clean(NRF_POWER_EVENT_USBREMOVED, + NRF_POWER_INT_USBREMOVED_MASK)) { + usb_dc_nrfx_power_event_callback(NRF_POWER_EVENT_USBREMOVED); } +#endif } -#if defined(CONFIG_USB) && defined(CONFIG_SOC_NRF52840) -static inline void power_event_cb(nrf_power_event_t event) -{ - extern void usb_dc_nrfx_power_event_callback(nrf_power_event_t event); - - usb_dc_nrfx_power_event_callback(event); -} -#endif /* Note: this function has public linkage, and MUST have this * particular name. The platform architecture itself doesn't care, @@ -286,203 +327,44 @@ static inline void power_event_cb(nrf_power_event_t event) */ void nrf_power_clock_isr(void *arg) { - u8_t pof, hf_intenset, hf, lf_intenset, lf; -#if NRF_CLOCK_HAS_CALIBRATION - u8_t ctto, done; - struct device *dev = arg; -#endif -#if defined(CONFIG_USB) && defined(CONFIG_SOC_NRF52840) - bool usb_detected, usb_pwr_rdy, usb_removed; -#endif - - pof = (NRF_POWER->EVENTS_POFWARN != 0); + ARG_UNUSED(arg); - hf_intenset = ((NRF_CLOCK->INTENSET & - CLOCK_INTENSET_HFCLKSTARTED_Msk) != 0); - hf = (NRF_CLOCK->EVENTS_HFCLKSTARTED != 0); + if (clock_event_check_and_clean(NRF_CLOCK_EVENT_HFCLKSTARTED, + NRF_CLOCK_INT_HF_STARTED_MASK)) { + struct device *hfclk_dev = DEVICE_GET(clock_nrf5_m16src); + struct nrf_clock_control *data = hfclk_dev->driver_data; - lf_intenset = ((NRF_CLOCK->INTENSET & - CLOCK_INTENSET_LFCLKSTARTED_Msk) != 0); - lf = (NRF_CLOCK->EVENTS_LFCLKSTARTED != 0); - -#if NRF_CLOCK_HAS_CALIBRATION - done = (NRF_CLOCK->EVENTS_DONE != 0); - ctto = (NRF_CLOCK->EVENTS_CTTO != 0); -#endif -#if defined(CONFIG_USB) && defined(CONFIG_SOC_NRF52840) - usb_detected = nrf_power_event_check(NRF_POWER_EVENT_USBDETECTED); - usb_pwr_rdy = nrf_power_event_check(NRF_POWER_EVENT_USBPWRRDY); - usb_removed = nrf_power_event_check(NRF_POWER_EVENT_USBREMOVED); -#endif - - __ASSERT_NO_MSG(pof || hf || hf_intenset || lf -#if NRF_CLOCK_HAS_CALIBRATION - || done || ctto -#endif -#if defined(CONFIG_USB) && defined(CONFIG_SOC_NRF52840) - || usb_detected || usb_pwr_rdy || usb_removed -#endif - ); - - if (pof) { - NRF_POWER->EVENTS_POFWARN = 0; - } - - if (hf) { - NRF_CLOCK->EVENTS_HFCLKSTARTED = 0; - } - - if (hf_intenset && (hf || ((NRF_CLOCK->HFCLKSTAT & - (CLOCK_HFCLKSTAT_STATE_Msk | - CLOCK_HFCLKSTAT_SRC_Msk)) == - (CLOCK_HFCLKSTAT_STATE_Msk | - CLOCK_HFCLKSTAT_SRC_Msk)))){ - /* INTENSET is used as state flag to start calibration, - * hence clear it here. + /* Check needed due to anomaly 201: + * HFCLKSTARTED may be generated twice. */ - NRF_CLOCK->INTENCLR = CLOCK_INTENCLR_HFCLKSTARTED_Msk; - -#if defined(CONFIG_SOC_SERIES_NRF52X) - /* NOTE: Errata [192] CLOCK: LFRC frequency offset after - * calibration. - * Calibration start, workaround. - */ - *(volatile u32_t *)0x40000C34 = 0x00000002; -#endif /* CONFIG_SOC_SERIES_NRF52X */ - -#if NRF_CLOCK_HAS_CALIBRATION - /* Start Calibration */ - NRF_CLOCK->TASKS_CAL = 1; -#endif - } - - if (lf) { - NRF_CLOCK->EVENTS_LFCLKSTARTED = 0; - - if (lf_intenset) { - /* INTENSET is used as state flag to start calibration, - * hence clear it here. - */ - NRF_CLOCK->INTENCLR = CLOCK_INTENCLR_LFCLKSTARTED_Msk; - -#if NRF_CLOCK_HAS_CALIBRATION - /* Start HF Clock if LF RC is used. */ - if ((NRF_CLOCK->LFCLKSRCCOPY & CLOCK_LFCLKSRCCOPY_SRC_Msk) == - CLOCK_LFCLKSRCCOPY_SRC_RC) { - ctto = 1U; - } -#endif + if (!data->started) { + clkstarted_handle(hfclk_dev); } } -#if NRF_CLOCK_HAS_CALIBRATION - if (done) { - int err; - -#if defined(CONFIG_SOC_SERIES_NRF52X) - /* NOTE: Errata [192] CLOCK: LFRC frequency offset after - * calibration. - * Calibration done, workaround. - */ - *(volatile u32_t *)0x40000C34 = 0x00000000; -#endif /* CONFIG_SOC_SERIES_NRF52X */ - - NRF_CLOCK->EVENTS_DONE = 0; - - /* Calibration done, stop 16M Xtal. */ - err = m16src_stop(dev, NULL); - __ASSERT_NO_MSG(!err || err == -EBUSY); - - /* Start timer for next calibration. */ - NRF_CLOCK->TASKS_CTSTART = 1; - } - - if (ctto) { - int err; + if (clock_event_check_and_clean(NRF_CLOCK_EVENT_LFCLKSTARTED, + NRF_CLOCK_INT_LF_STARTED_MASK)) { + struct device *lfclk_dev = DEVICE_GET(clock_nrf5_k32src); - NRF_CLOCK->EVENTS_CTTO = 0; - - /* Start HF clock, if already started - * then explicitly assert IRQ; we use the INTENSET - * as a state flag to start calibration. - */ - NRF_CLOCK->INTENSET = CLOCK_INTENSET_HFCLKSTARTED_Msk; - - err = m16src_start(dev, false); - if (!err) { - NVIC_SetPendingIRQ(DT_INST_0_NORDIC_NRF_CLOCK_IRQ_0); - } else { - __ASSERT_NO_MSG(err == -EINPROGRESS); + if (IS_ENABLED( + CONFIG_CLOCK_CONTROL_NRF_K32SRC_RC_CALIBRATION)) { + z_nrf_clock_calibration_lfclk_started(lfclk_dev); } + clkstarted_handle(lfclk_dev); } -#endif /* NRF_CLOCK_HAS_CALIBRATION */ -#if defined(CONFIG_USB) && defined(CONFIG_SOC_NRF52840) - if (usb_detected) { - nrf_power_event_clear(NRF_POWER_EVENT_USBDETECTED); - power_event_cb(NRF_POWER_EVENT_USBDETECTED); - } + usb_power_isr(); - if (usb_pwr_rdy) { - nrf_power_event_clear(NRF_POWER_EVENT_USBPWRRDY); - power_event_cb(NRF_POWER_EVENT_USBPWRRDY); + if (IS_ENABLED(CONFIG_CLOCK_CONTROL_NRF_K32SRC_RC_CALIBRATION)) { + z_nrf_clock_calibration_isr(); } - - if (usb_removed) { - nrf_power_event_clear(NRF_POWER_EVENT_USBREMOVED); - power_event_cb(NRF_POWER_EVENT_USBREMOVED); - } -#endif } -static int clock_control_init(struct device *dev) -{ - /* TODO: Initialization will be called twice, once for 32KHz and then - * for 16 MHz clock. The vector is also shared for other power related - * features. Hence, design a better way to init IRQISR when adding - * power peripheral driver and/or new SoC series. - * NOTE: Currently the operations here are idempotent. - */ - IRQ_CONNECT(DT_INST_0_NORDIC_NRF_CLOCK_IRQ_0, - DT_INST_0_NORDIC_NRF_CLOCK_IRQ_0_PRIORITY, - nrf_power_clock_isr, 0, 0); - - irq_enable(DT_INST_0_NORDIC_NRF_CLOCK_IRQ_0); - - return 0; -} - -static const struct clock_control_driver_api _m16src_clock_control_api = { - .on = m16src_start, - .off = m16src_stop, - .get_rate = NULL, -}; - -DEVICE_AND_API_INIT(clock_nrf5_m16src, - DT_INST_0_NORDIC_NRF_CLOCK_LABEL "_16M", - clock_control_init, NULL, NULL, PRE_KERNEL_1, - CONFIG_KERNEL_INIT_PRIORITY_DEVICE, - &_m16src_clock_control_api); - -static const struct clock_control_driver_api _k32src_clock_control_api = { - .on = k32src_start, - .off = NULL, - .get_rate = NULL, -}; - -DEVICE_AND_API_INIT(clock_nrf5_k32src, - DT_INST_0_NORDIC_NRF_CLOCK_LABEL "_32K", - clock_control_init, NULL, NULL, PRE_KERNEL_1, - CONFIG_KERNEL_INIT_PRIORITY_DEVICE, - &_k32src_clock_control_api); - -#if defined(CONFIG_USB) && defined(CONFIG_SOC_NRF52840) - void nrf5_power_usb_power_int_enable(bool enable) { +#ifdef CONFIG_USB_NRF52840 u32_t mask; - mask = NRF_POWER_INT_USBDETECTED_MASK | NRF_POWER_INT_USBREMOVED_MASK | NRF_POWER_INT_USBPWRRDY_MASK; @@ -493,6 +375,5 @@ void nrf5_power_usb_power_int_enable(bool enable) } else { nrf_power_int_disable(mask); } -} - #endif +} diff --git a/drivers/sensor/nrf5/temp_nrf5.c b/drivers/sensor/nrf5/temp_nrf5.c index b4f210ba4c7fb..9ad285128ee10 100644 --- a/drivers/sensor/nrf5/temp_nrf5.c +++ b/drivers/sensor/nrf5/temp_nrf5.c @@ -25,10 +25,18 @@ struct temp_nrf5_data { struct device *hfclk_dev; }; +static void hfclk_on_callback(struct device *dev, void *user_data) +{ + nrf_temp_task_trigger(NRF_TEMP, NRF_TEMP_TASK_START); +} static int temp_nrf5_sample_fetch(struct device *dev, enum sensor_channel chan) { struct temp_nrf5_data *data = dev->driver_data; + struct clock_control_async_data clk_data = { + .cb = hfclk_on_callback + }; + int r; @@ -36,18 +44,13 @@ static int temp_nrf5_sample_fetch(struct device *dev, enum sensor_channel chan) return -ENOTSUP; } - /* The clock driver for nrf51 currently overloads the - * subsystem parameter with a flag to indicate whether or not - * it should block. - */ - r = clock_control_on(data->hfclk_dev, (void *)1); + r = clock_control_async_on(data->hfclk_dev, NULL, &clk_data); __ASSERT_NO_MSG(!r); - nrf_temp_task_trigger(NRF_TEMP, NRF_TEMP_TASK_START); k_sem_take(&data->device_sync_sem, K_FOREVER); - r = clock_control_off(data->hfclk_dev, (void *)1); - __ASSERT_NO_MSG(!r || r == -EBUSY); + r = clock_control_off(data->hfclk_dev, 0); + __ASSERT_NO_MSG(!r); data->sample = nrf_temp_result_get(NRF_TEMP); LOG_DBG("sample: %d", data->sample); diff --git a/include/drivers/clock_control/nrf_clock_control.h b/include/drivers/clock_control/nrf_clock_control.h index b2c27642297b0..da5e466de581d 100644 --- a/include/drivers/clock_control/nrf_clock_control.h +++ b/include/drivers/clock_control/nrf_clock_control.h @@ -7,9 +7,7 @@ #ifndef ZEPHYR_INCLUDE_DRIVERS_CLOCK_CONTROL_NRF_CLOCK_CONTROL_H_ #define ZEPHYR_INCLUDE_DRIVERS_CLOCK_CONTROL_NRF_CLOCK_CONTROL_H_ -#if defined(CONFIG_USB) && defined(CONFIG_SOC_NRF52840) #include -#endif #include /* TODO: move all these to clock_control.h ? */ @@ -57,8 +55,25 @@ #define CLOCK_CONTROL_NRF_K32SRC_ACCURACY 7 #endif -#if defined(CONFIG_USB) && defined(CONFIG_SOC_NRF52840) void nrf5_power_usb_power_int_enable(bool enable); -#endif + +/** @brief Force LF clock calibration. */ +void z_nrf_clock_calibration_force_start(void); + +/** @brief Return number of calibrations performed. + * + * Valid when @ref CONFIG_CLOCK_CONTROL_NRF_CALIBRATION_DEBUG is set. + * + * @return Number of calibrations or -1 if feature is disabled. + */ +int z_nrf_clock_calibration_count(void); + +/** @brief Return number of attempts when calibration was skipped. + * + * Valid when @ref CONFIG_CLOCK_CONTROL_NRF_CALIBRATION_DEBUG is set. + * + * @return Number of calibrations or -1 if feature is disabled. + */ +int z_nrf_clock_calibration_skips_count(void); #endif /* ZEPHYR_INCLUDE_DRIVERS_CLOCK_CONTROL_NRF_CLOCK_CONTROL_H_ */ From b471f74d6447a6c4ba5f60a84389d88fe32cea97 Mon Sep 17 00:00:00 2001 From: Krzysztof Chruscinski Date: Wed, 26 Jun 2019 13:27:59 +0200 Subject: [PATCH 3/7] tests: drivers: clock_control: Add test suite Added test suite for clock control driver. It covers latest API update for starting clock asynchronously and getting status. Signed-off-by: Krzysztof Chruscinski --- .../clock_control_api/CMakeLists.txt | 9 + .../clock_control_api/nrf_lfclk_rc.conf | 2 + .../clock_control/clock_control_api/prj.conf | 1 + .../src/test_clock_control.c | 314 ++++++++++++++++++ .../clock_control_api/testcase.yaml | 10 + 5 files changed, 336 insertions(+) create mode 100644 tests/drivers/clock_control/clock_control_api/CMakeLists.txt create mode 100644 tests/drivers/clock_control/clock_control_api/nrf_lfclk_rc.conf create mode 100644 tests/drivers/clock_control/clock_control_api/prj.conf create mode 100644 tests/drivers/clock_control/clock_control_api/src/test_clock_control.c create mode 100644 tests/drivers/clock_control/clock_control_api/testcase.yaml diff --git a/tests/drivers/clock_control/clock_control_api/CMakeLists.txt b/tests/drivers/clock_control/clock_control_api/CMakeLists.txt new file mode 100644 index 0000000000000..7ac5982e37dd8 --- /dev/null +++ b/tests/drivers/clock_control/clock_control_api/CMakeLists.txt @@ -0,0 +1,9 @@ +# SPDX-License-Identifier: Apache-2.0 + +cmake_minimum_required(VERSION 3.13.1) + +include($ENV{ZEPHYR_BASE}/cmake/app/boilerplate.cmake NO_POLICY_SCOPE) +project(NONE) + +FILE(GLOB app_sources src/*.c) +target_sources(app PRIVATE ${app_sources}) diff --git a/tests/drivers/clock_control/clock_control_api/nrf_lfclk_rc.conf b/tests/drivers/clock_control/clock_control_api/nrf_lfclk_rc.conf new file mode 100644 index 0000000000000..f86b71aced3e3 --- /dev/null +++ b/tests/drivers/clock_control/clock_control_api/nrf_lfclk_rc.conf @@ -0,0 +1,2 @@ +CONFIG_ZTEST=y +CONFIG_CLOCK_CONTROL_NRF_K32SRC_RC=y diff --git a/tests/drivers/clock_control/clock_control_api/prj.conf b/tests/drivers/clock_control/clock_control_api/prj.conf new file mode 100644 index 0000000000000..9467c2926896d --- /dev/null +++ b/tests/drivers/clock_control/clock_control_api/prj.conf @@ -0,0 +1 @@ +CONFIG_ZTEST=y diff --git a/tests/drivers/clock_control/clock_control_api/src/test_clock_control.c b/tests/drivers/clock_control/clock_control_api/src/test_clock_control.c new file mode 100644 index 0000000000000..443b5335976ef --- /dev/null +++ b/tests/drivers/clock_control/clock_control_api/src/test_clock_control.c @@ -0,0 +1,314 @@ +/* + * Copyright (c) 2019, Nordic Semiconductor ASA + * + * SPDX-License-Identifier: Apache-2.0 + */ +#include +#include +#include +LOG_MODULE_REGISTER(test); + +#ifdef DT_INST_0_NORDIC_NRF_CLOCK_LABEL +#include +#endif + +struct device_data { + const char *name; + u32_t startup_us; +}; + +static const struct device_data devices[] = { +#ifdef DT_INST_0_NORDIC_NRF_CLOCK_LABEL + { + .name = DT_INST_0_NORDIC_NRF_CLOCK_LABEL "_16M", + .startup_us = IS_ENABLED(CONFIG_SOC_SERIES_NRF91X) ? 3000 : 400 + }, + + { + .name = DT_INST_0_NORDIC_NRF_CLOCK_LABEL "_32K", + .startup_us = (CLOCK_CONTROL_NRF_K32SRC == NRF_CLOCK_LFCLK_RC) ? + 1000 : 300000 + } +#endif +}; + + +typedef void (*test_func_t)(const char *dev_name, u32_t startup_us); + +typedef bool (*test_capability_check_t)(const char *dev_name); + +static void setup_instance(const char *dev_name) +{ + struct device *dev = device_get_binding(dev_name); + int err; + + k_busy_wait(1000); + do { + err = clock_control_off(dev, 0); + } while (err == 0); + LOG_INF("setup done"); +} + +static void tear_down_instance(const char *dev_name) +{ + struct device *dev = device_get_binding(dev_name); + + clock_control_on(dev, NULL); +} + +static void test_all_instances(test_func_t func, + test_capability_check_t capability_check) +{ + for (int i = 0; i < ARRAY_SIZE(devices); i++) { + if ((capability_check == NULL) || + capability_check(devices[i].name)) { + setup_instance(devices[i].name); + func(devices[i].name, devices[i].startup_us); + tear_down_instance(devices[i].name); + /* Allow logs to be printed. */ + k_sleep(100); + } + } +} + +/* + * Basic test for checking correctness of getting clock status. + */ +static void test_on_off_status_instance(const char *dev_name, u32_t startup_us) +{ + struct device *dev = device_get_binding(dev_name); + enum clock_control_status status; + int err; + + zassert_true(dev != NULL, "%s: Unknown device", dev_name); + + status = clock_control_get_status(dev, NULL); + zassert_equal(CLOCK_CONTROL_STATUS_OFF, status, + "%s: Unexpected status (%d)", dev_name, status); + + + err = clock_control_on(dev, NULL); + zassert_equal(0, err, "%s: Unexpected err (%d)", dev_name, err); + + status = clock_control_get_status(dev, NULL); + zassert_true((status == CLOCK_CONTROL_STATUS_STARTING) || + (status == CLOCK_CONTROL_STATUS_ON), + "%s: Unexpected status (%d)", dev_name, status); + + k_busy_wait(startup_us); + + status = clock_control_get_status(dev, NULL); + zassert_equal(CLOCK_CONTROL_STATUS_ON, status, + "%s: Unexpected status (%d)", dev_name, status); + + err = clock_control_off(dev, 0); + zassert_equal(0, err, "%s: Unexpected err (%d)", dev_name, err); + + status = clock_control_get_status(dev, NULL); + zassert_equal(CLOCK_CONTROL_STATUS_OFF, status, + "%s: Unexpected status (%d)", dev_name, status); +} + +static void test_on_off_status(void) +{ + test_all_instances(test_on_off_status_instance, NULL); +} + +/* + * Test validates that if number of enabling requests matches disabling requests + * then clock is disabled. + */ +static void test_multiple_users_instance(const char *dev_name, u32_t startup_us) +{ + struct device *dev = device_get_binding(dev_name); + enum clock_control_status status; + int users = 5; + int err; + + status = clock_control_get_status(dev, NULL); + zassert_equal(CLOCK_CONTROL_STATUS_OFF, status, + "%s: Unexpected status (%d)", dev_name, status); + + for (int i = 0; i < users; i++) { + err = clock_control_on(dev, NULL); + zassert_equal(0, err, "%s: Unexpected err (%d)", dev_name, err); + } + + status = clock_control_get_status(dev, NULL); + zassert_true((status == CLOCK_CONTROL_STATUS_STARTING) || + (status == CLOCK_CONTROL_STATUS_ON), + "%s: Unexpected status (%d)", dev_name, status); + + for (int i = 0; i < users; i++) { + err = clock_control_off(dev, 0); + zassert_equal(0, err, "%s: Unexpected err (%d)", dev_name, err); + } + + status = clock_control_get_status(dev, NULL); + zassert_true(status == CLOCK_CONTROL_STATUS_OFF, + "%s: Unexpected status (%d)", dev_name, status); + + err = clock_control_off(dev, 0); + zassert_equal(-EALREADY, err, "%s: Unexpected err (%d)", dev_name, err); +} + +static void test_multiple_users(void) +{ + test_all_instances(test_multiple_users_instance, NULL); +} + +static bool async_capable(const char *dev_name) +{ + struct device *dev = device_get_binding(dev_name); + + if (clock_control_async_on(dev, 0, NULL) != 0) { + return false; + } + + clock_control_off(dev, 0); + + return true; +} + +/* + * Test checks that callbacks are called after clock is started. + */ +static void clock_on_callback(struct device *dev, void *user_data) +{ + bool *executed = (bool *)user_data; + + *executed = true; +} + +static void test_async_on_instance(const char *dev_name, u32_t startup_us) +{ + struct device *dev = device_get_binding(dev_name); + enum clock_control_status status; + int err; + bool executed1 = false; + bool executed2 = false; + struct clock_control_async_data data1 = { + .cb = clock_on_callback, + .user_data = &executed1 + }; + struct clock_control_async_data data2 = { + .cb = clock_on_callback, + .user_data = &executed2 + }; + + status = clock_control_get_status(dev, NULL); + zassert_equal(CLOCK_CONTROL_STATUS_OFF, status, + "%s: Unexpected status (%d)", dev_name, status); + + err = clock_control_async_on(dev, 0, &data1); + zassert_equal(0, err, "%s: Unexpected err (%d)", dev_name, err); + + err = clock_control_async_on(dev, 0, &data2); + zassert_equal(0, err, "%s: Unexpected err (%d)", dev_name, err); + + /* wait for clock started. */ + k_busy_wait(startup_us); + + zassert_true(executed1, "%s: Expected flag to be true", dev_name); + zassert_true(executed2, "%s: Expected flag to be true", dev_name); +} + +static void test_async_on(void) +{ + test_all_instances(test_async_on_instance, async_capable); +} + +/* + * Test checks that when asynchronous clock enabling is scheduled but clock + * is disabled before being started then callback is never called. + */ +static void test_async_on_stopped_on_instance(const char *dev_name, + u32_t startup_us) +{ + struct device *dev = device_get_binding(dev_name); + enum clock_control_status status; + int err; + int key; + bool executed1 = false; + struct clock_control_async_data data1 = { + .cb = clock_on_callback, + .user_data = &executed1 + }; + + status = clock_control_get_status(dev, NULL); + zassert_equal(CLOCK_CONTROL_STATUS_OFF, status, + "%s: Unexpected status (%d)", dev_name, status); + + /* lock to prevent clock interrupt for fast starting clocks.*/ + key = irq_lock(); + err = clock_control_async_on(dev, 0, &data1); + zassert_equal(0, err, "%s: Unexpected err (%d)", dev_name, err); + + err = clock_control_off(dev, 0); + zassert_equal(0, err, "%s: Unexpected err (%d)", dev_name, err); + + irq_unlock(key); + + k_busy_wait(10000); + + zassert_false(executed1, "%s: Expected flag to be false", dev_name); +} + +static void test_async_on_stopped(void) +{ + test_all_instances(test_async_on_stopped_on_instance, async_capable); +} + +/* + * Test checks that when asynchronous clock enabling is called when clock is + * running then callback is immediate. + */ +static void test_immediate_cb_when_clock_on_on_instance(const char *dev_name, + u32_t startup_us) +{ + struct device *dev = device_get_binding(dev_name); + enum clock_control_status status; + int err; + bool executed1 = false; + struct clock_control_async_data data1 = { + .cb = clock_on_callback, + .user_data = &executed1 + }; + + status = clock_control_get_status(dev, NULL); + zassert_equal(CLOCK_CONTROL_STATUS_OFF, status, + "%s: Unexpected status (%d)", dev_name, status); + + err = clock_control_on(dev, NULL); + zassert_equal(0, err, "%s: Unexpected err (%d)", dev_name, err); + + /* wait for clock started. */ + k_busy_wait(startup_us); + + status = clock_control_get_status(dev, NULL); + zassert_equal(CLOCK_CONTROL_STATUS_ON, status, + "%s: Unexpected status (%d)", dev_name, status); + + err = clock_control_async_on(dev, 0, &data1); + zassert_equal(0, err, "%s: Unexpected err (%d)", dev_name, err); + + zassert_true(executed1, "%s: Expected flag to be false", dev_name); +} + +static void test_immediate_cb_when_clock_on(void) +{ + test_all_instances(test_immediate_cb_when_clock_on_on_instance, + async_capable); +} + +void test_main(void) +{ + ztest_test_suite(test_clock_control, + ztest_unit_test(test_on_off_status), + ztest_unit_test(test_multiple_users), + ztest_unit_test(test_async_on), + ztest_unit_test(test_async_on_stopped), + ztest_unit_test(test_immediate_cb_when_clock_on) + ); + ztest_run_test_suite(test_clock_control); +} diff --git a/tests/drivers/clock_control/clock_control_api/testcase.yaml b/tests/drivers/clock_control/clock_control_api/testcase.yaml new file mode 100644 index 0000000000000..e75b672c1d48b --- /dev/null +++ b/tests/drivers/clock_control/clock_control_api/testcase.yaml @@ -0,0 +1,10 @@ +tests: + peripheral.clock_control_nrf5: + tags: drivers + platform_whitelist: nrf51_pca10028 nrf52_pca10040 nrf52840_pca10056 + nrf9160_pca10090 + peripheral.clock_control_nrf5_lfclk_rc: + tags: drivers + platform_whitelist: nrf51_pca10028 nrf52_pca10040 nrf52840_pca10056 + extra_args: CONF_FILE="nrf_lfclk_rc.conf" + From f7670b3b224ebdf77a55c674df11c62b629fb275 Mon Sep 17 00:00:00 2001 From: Krzysztof Chruscinski Date: Mon, 8 Jul 2019 13:08:18 +0200 Subject: [PATCH 4/7] tests: drivers: clock_control: Add test for nRF clock calibration Added test for LF clock calibration when RC source is used. Signed-off-by: Krzysztof Chruscinski --- .../nrf_clock_calibration/CMakeLists.txt | 9 + .../nrf_clock_calibration/prj.conf | 6 + .../src/test_nrf_clock_calibration.c | 280 ++++++++++++++++++ .../nrf_clock_calibration/testcase.yaml | 5 + 4 files changed, 300 insertions(+) create mode 100644 tests/drivers/clock_control/nrf_clock_calibration/CMakeLists.txt create mode 100644 tests/drivers/clock_control/nrf_clock_calibration/prj.conf create mode 100644 tests/drivers/clock_control/nrf_clock_calibration/src/test_nrf_clock_calibration.c create mode 100644 tests/drivers/clock_control/nrf_clock_calibration/testcase.yaml diff --git a/tests/drivers/clock_control/nrf_clock_calibration/CMakeLists.txt b/tests/drivers/clock_control/nrf_clock_calibration/CMakeLists.txt new file mode 100644 index 0000000000000..7ac5982e37dd8 --- /dev/null +++ b/tests/drivers/clock_control/nrf_clock_calibration/CMakeLists.txt @@ -0,0 +1,9 @@ +# SPDX-License-Identifier: Apache-2.0 + +cmake_minimum_required(VERSION 3.13.1) + +include($ENV{ZEPHYR_BASE}/cmake/app/boilerplate.cmake NO_POLICY_SCOPE) +project(NONE) + +FILE(GLOB app_sources src/*.c) +target_sources(app PRIVATE ${app_sources}) diff --git a/tests/drivers/clock_control/nrf_clock_calibration/prj.conf b/tests/drivers/clock_control/nrf_clock_calibration/prj.conf new file mode 100644 index 0000000000000..45e9febc1d283 --- /dev/null +++ b/tests/drivers/clock_control/nrf_clock_calibration/prj.conf @@ -0,0 +1,6 @@ +CONFIG_ZTEST=y +CONFIG_CLOCK_CONTROL_NRF_K32SRC_RC=y +CONFIG_CLOCK_CONTROL_NRF_CALIBRATION_MAX_SKIP=0 +CONFIG_CLOCK_CONTROL_NRF_CALIBRATION_PERIOD=1 +CONFIG_CLOCK_CONTROL_NRF_CALIBRATION_DEBUG=y + diff --git a/tests/drivers/clock_control/nrf_clock_calibration/src/test_nrf_clock_calibration.c b/tests/drivers/clock_control/nrf_clock_calibration/src/test_nrf_clock_calibration.c new file mode 100644 index 0000000000000..934c77826e425 --- /dev/null +++ b/tests/drivers/clock_control/nrf_clock_calibration/src/test_nrf_clock_calibration.c @@ -0,0 +1,280 @@ +/* + * Copyright (c) 2019, Nordic Semiconductor ASA + * + * SPDX-License-Identifier: Apache-2.0 + */ +#include +#include +#include +#include +#include +LOG_MODULE_REGISTER(test); + +#ifndef CONFIG_CLOCK_CONTROL_NRF_K32SRC_RC +#error "LFCLK must use RC source" +#endif + +#if CONFIG_CLOCK_CONTROL_NRF_CALIBRATION_PERIOD != 1 +#error "Expected 250ms calibration period" +#endif + +static void turn_off_clock(struct device *dev) +{ + int err; + + do { + err = clock_control_off(dev, 0); + } while (err == 0); +} + +static void lfclk_started_cb(struct device *dev, void *user_data) +{ + *(bool *)user_data = true; +} + +/* Test checks if calibration clock is running and generates interrupt as + * expected and starts calibration. Validates that HF clock is turned on + * for calibration and turned off once calibration is done. + */ +static void test_clock_calibration(void) +{ + struct device *hfclk_dev = + device_get_binding(DT_INST_0_NORDIC_NRF_CLOCK_LABEL "_16M"); + struct device *lfclk_dev = + device_get_binding(DT_INST_0_NORDIC_NRF_CLOCK_LABEL "_32K"); + volatile bool started = false; + struct clock_control_async_data lfclk_data = { + .cb = lfclk_started_cb, + .user_data = (void *)&started + }; + int key; + u32_t cnt = 0; + u32_t max_cnt = 1000; + + turn_off_clock(hfclk_dev); + turn_off_clock(lfclk_dev); + + /* In case calibration needs to be completed. */ + k_busy_wait(100000); + + clock_control_async_on(lfclk_dev, NULL, &lfclk_data); + + while (started == false) { + } + + k_busy_wait(35000); + + key = irq_lock(); + while (nrf_clock_event_check(NRF_CLOCK_EVENT_CTTO) == 0) { + k_busy_wait(1000); + cnt++; + if (cnt == max_cnt) { + irq_unlock(key); + clock_control_off(lfclk_dev, NULL); + zassert_true(false, ""); + } + } + + zassert_within(cnt, 250, 10, "Expected 250ms period"); + + irq_unlock(key); + + while (clock_control_get_status(hfclk_dev, NULL) + != CLOCK_CONTROL_STATUS_ON) { + } + + key = irq_lock(); + cnt = 0; + while (nrf_clock_event_check(NRF_CLOCK_EVENT_DONE) == 0) { + k_busy_wait(1000); + cnt++; + if (cnt == max_cnt) { + irq_unlock(key); + clock_control_off(lfclk_dev, NULL); + zassert_true(false, ""); + } + } + + irq_unlock(key); + + zassert_equal(clock_control_get_status(hfclk_dev, NULL), + CLOCK_CONTROL_STATUS_OFF, + "Expected hfclk off after calibration."); + + key = irq_lock(); + cnt = 0; + + while (nrf_clock_event_check(NRF_CLOCK_EVENT_CTTO) == 0) { + k_busy_wait(1000); + cnt++; + if (cnt == max_cnt) { + irq_unlock(key); + clock_control_off(lfclk_dev, NULL); + zassert_true(false, ""); + } + } + + zassert_within(cnt, 250, 10, "Expected 250ms period (got %d)", cnt); + + irq_unlock(key); + + clock_control_off(lfclk_dev, NULL); +} + +/* Test checks that when calibration is active then LF clock is not stopped. + * Stopping is deferred until calibration is done. Test validates that after + * completing calibration LF clock is shut down. + */ +static void test_stopping_when_calibration(void) +{ + struct device *hfclk_dev = + device_get_binding(DT_INST_0_NORDIC_NRF_CLOCK_LABEL "_16M"); + struct device *lfclk_dev = + device_get_binding(DT_INST_0_NORDIC_NRF_CLOCK_LABEL "_32K"); + volatile bool started = false; + struct clock_control_async_data lfclk_data = { + .cb = lfclk_started_cb, + .user_data = (void *)&started + }; + int key; + u32_t cnt = 0; + u32_t max_cnt = 1000; + + turn_off_clock(hfclk_dev); + turn_off_clock(lfclk_dev); + + /* In case calibration needs to be completed. */ + k_busy_wait(100000); + + clock_control_async_on(lfclk_dev, NULL, &lfclk_data); + + while (started == false) { + } + + /* when lfclk is started first calibration is performed. Wait until it + * is done. + */ + k_busy_wait(35000); + + key = irq_lock(); + while (nrf_clock_event_check(NRF_CLOCK_EVENT_CTTO) == 0) { + k_busy_wait(1000); + cnt++; + if (cnt == max_cnt) { + irq_unlock(key); + clock_control_off(lfclk_dev, NULL); + zassert_true(false, ""); + } + } + + zassert_within(cnt, 250, 10, "Expected 250ms period"); + + irq_unlock(key); + + while (clock_control_get_status(hfclk_dev, NULL) + != CLOCK_CONTROL_STATUS_ON) { + } + + /* calibration started */ + key = irq_lock(); + clock_control_off(lfclk_dev, NULL); + + zassert_true(nrf_clock_lf_is_running(), "Expected LF still on"); + + while (nrf_clock_event_check(NRF_CLOCK_EVENT_DONE) == 0) { + k_busy_wait(1000); + cnt++; + if (cnt == max_cnt) { + irq_unlock(key); + clock_control_off(lfclk_dev, NULL); + zassert_true(false, ""); + } + } + + irq_unlock(key); + + /* wait some time after which clock should be off. */ + k_busy_wait(300); + + zassert_false(nrf_clock_lf_is_running(), "Expected LF off"); + + clock_control_off(lfclk_dev, NULL); +} + +static u32_t pend_on_next_calibration(void) +{ + u32_t stamp = k_uptime_get_32(); + int cnt = z_nrf_clock_calibration_count(); + + while (cnt == z_nrf_clock_calibration_count()) { + if ((k_uptime_get_32() - stamp) > 300) { + zassert_true(false, "Expected calibration"); + return UINT32_MAX; + } + } + + return k_uptime_get_32() - stamp; +} + +static void test_clock_calibration_force(void) +{ + struct device *hfclk_dev = + device_get_binding(DT_INST_0_NORDIC_NRF_CLOCK_LABEL "_16M"); + struct device *lfclk_dev = + device_get_binding(DT_INST_0_NORDIC_NRF_CLOCK_LABEL "_32K"); + volatile bool started = false; + struct clock_control_async_data lfclk_data = { + .cb = lfclk_started_cb, + .user_data = (void *)&started + }; + u32_t cnt = 0; + u32_t period; + + turn_off_clock(hfclk_dev); + turn_off_clock(lfclk_dev); + + /* In case calibration needs to be completed. */ + k_busy_wait(100000); + + clock_control_async_on(lfclk_dev, NULL, &lfclk_data); + + while (started == false) { + } + + cnt = z_nrf_clock_calibration_count(); + + pend_on_next_calibration(); + + zassert_equal(cnt + 1, z_nrf_clock_calibration_count(), + "Unexpected number of calibrations %d (expected: %d)", + z_nrf_clock_calibration_count(), cnt + 1); + + /* Next calibration should happen in 250ms, after forcing it should be + * done sooner. + */ + z_nrf_clock_calibration_force_start(); + period = pend_on_next_calibration(); + zassert_equal(cnt + 2, z_nrf_clock_calibration_count(), + "Unexpected number of calibrations"); + zassert_true(period < 100, "Unexpected calibration period."); + + k_busy_wait(80000); + + /* Next calibration should happen as scheduled. */ + period = pend_on_next_calibration(); + zassert_equal(cnt + 3, z_nrf_clock_calibration_count(), + "Unexpected number of calibrations"); + zassert_true(period < 200, + "Unexpected calibration period %d ms.", period); + +} + +void test_main(void) +{ + ztest_test_suite(test_nrf_clock_calibration, + ztest_unit_test(test_clock_calibration), + ztest_unit_test(test_stopping_when_calibration), + ztest_unit_test(test_clock_calibration_force) + ); + ztest_run_test_suite(test_nrf_clock_calibration); +} diff --git a/tests/drivers/clock_control/nrf_clock_calibration/testcase.yaml b/tests/drivers/clock_control/nrf_clock_calibration/testcase.yaml new file mode 100644 index 0000000000000..1e289f23130d0 --- /dev/null +++ b/tests/drivers/clock_control/nrf_clock_calibration/testcase.yaml @@ -0,0 +1,5 @@ +tests: + peripheral.nrf5_clock_calibration: + tags: drivers + platform_whitelist: nrf51_pca10028 nrf52_pca10040 nrf52840_pca10056 + From 62050372096511a683053896781c73f20b696d3e Mon Sep 17 00:00:00 2001 From: Krzysztof Chruscinski Date: Wed, 3 Jul 2019 09:19:51 +0200 Subject: [PATCH 5/7] bluetooth: controller: Align nrf link layer to new clock control API Align to use clock_control_get_status for blocking clock start. Signed-off-by: Krzysztof Chruscinski --- subsys/bluetooth/controller/ll_sw/ll.c | 2 +- subsys/bluetooth/controller/ll_sw/ll_test.c | 6 +++++- subsys/bluetooth/controller/ll_sw/nordic/lll/lll.c | 12 ++++++++---- .../controller/ll_sw/nordic/lll/lll_clock.c | 4 +++- 4 files changed, 17 insertions(+), 7 deletions(-) diff --git a/subsys/bluetooth/controller/ll_sw/ll.c b/subsys/bluetooth/controller/ll_sw/ll.c index f9cad852350c7..13be818cf595b 100644 --- a/subsys/bluetooth/controller/ll_sw/ll.c +++ b/subsys/bluetooth/controller/ll_sw/ll.c @@ -132,7 +132,7 @@ int ll_init(struct k_sem *sem_rx) return -ENODEV; } - clock_control_on(clk_k32, (void *)CLOCK_CONTROL_NRF_K32SRC); + clock_control_on(clk_k32, NULL); entropy = device_get_binding(CONFIG_ENTROPY_NAME); if (!entropy) { diff --git a/subsys/bluetooth/controller/ll_sw/ll_test.c b/subsys/bluetooth/controller/ll_sw/ll_test.c index 86fdf42f36e79..09dc469d78d08 100644 --- a/subsys/bluetooth/controller/ll_sw/ll_test.c +++ b/subsys/bluetooth/controller/ll_sw/ll_test.c @@ -171,7 +171,11 @@ static u32_t init(u8_t chan, u8_t phy, void (*isr)(void *param)) /* Setup resources required by Radio */ hf_clock = radio_hf_clock_get(); - clock_control_on(hf_clock, (void *)1); /* start clock, blocking. */ + clock_control_on(hf_clock, NULL); /* start clock, blocking. */ + + while (clock_control_get_status(hf_clock, NULL) != + CLOCK_CONTROL_STATUS_ON) { + } /* Reset Radio h/w */ radio_reset(); diff --git a/subsys/bluetooth/controller/ll_sw/nordic/lll/lll.c b/subsys/bluetooth/controller/ll_sw/nordic/lll/lll.c index 90d7a6a5bb126..8a078acc2d826 100644 --- a/subsys/bluetooth/controller/ll_sw/nordic/lll/lll.c +++ b/subsys/bluetooth/controller/ll_sw/nordic/lll/lll.c @@ -139,7 +139,7 @@ int lll_init(void) return -ENODEV; } - clock_control_on(clk_k32, (void *)CLOCK_CONTROL_NRF_K32SRC); + clock_control_on(clk_k32, NULL); /* Initialize HF CLK */ lll.clk_hf = @@ -327,11 +327,15 @@ int lll_clk_on_wait(void) int err; /* turn on radio clock in blocking mode. */ - err = clock_control_on(lll.clk_hf, (void *)1); - if (!err || err == -EINPROGRESS) { - DEBUG_RADIO_XTAL(1); + err = clock_control_on(lll.clk_hf, NULL); + + while (clock_control_get_status(lll.clk_hf, NULL) != + CLOCK_CONTROL_STATUS_ON) { + k_cpu_idle(); } + DEBUG_RADIO_XTAL(1); + return err; } diff --git a/subsys/bluetooth/controller/ll_sw/nordic/lll/lll_clock.c b/subsys/bluetooth/controller/ll_sw/nordic/lll/lll_clock.c index cd84a26b83e88..8452fee71f52f 100644 --- a/subsys/bluetooth/controller/ll_sw/nordic/lll/lll_clock.c +++ b/subsys/bluetooth/controller/ll_sw/nordic/lll/lll_clock.c @@ -28,7 +28,9 @@ void lll_clock_wait(void) LL_ASSERT(lf_clock); - while (clock_control_on(lf_clock, (void *)CLOCK_CONTROL_NRF_K32SRC)) { + clock_control_on(lf_clock, NULL); + while (clock_control_get_status(lf_clock, NULL) != + CLOCK_CONTROL_STATUS_ON) { DEBUG_CPU_SLEEP(1); k_cpu_idle(); DEBUG_CPU_SLEEP(0); From 84abe4a76361cf5c68535a428c2abff0e93068a8 Mon Sep 17 00:00:00 2001 From: Krzysztof Chruscinski Date: Wed, 3 Jul 2019 09:22:06 +0200 Subject: [PATCH 6/7] drivers: Align nrf counter, timer and usb driver to new clock_control Align drivers to use new clock control API. Signed-off-by: Krzysztof Chruscinski --- drivers/counter/counter_nrfx_rtc.c | 2 +- drivers/timer/nrf_rtc_timer.c | 2 +- drivers/usb/device/usb_dc_nrfx.c | 15 ++++++--------- 3 files changed, 8 insertions(+), 11 deletions(-) diff --git a/drivers/counter/counter_nrfx_rtc.c b/drivers/counter/counter_nrfx_rtc.c index 944610c3ce731..91fa498bc6541 100644 --- a/drivers/counter/counter_nrfx_rtc.c +++ b/drivers/counter/counter_nrfx_rtc.c @@ -295,7 +295,7 @@ static int init_rtc(struct device *dev, return -ENODEV; } - clock_control_on(clock, (void *)CLOCK_CONTROL_NRF_K32SRC); + clock_control_on(clock, NULL); nrfx_err_t result = nrfx_rtc_init(rtc, config, handler); diff --git a/drivers/timer/nrf_rtc_timer.c b/drivers/timer/nrf_rtc_timer.c index 53f72121a6238..2e1688a23cb98 100644 --- a/drivers/timer/nrf_rtc_timer.c +++ b/drivers/timer/nrf_rtc_timer.c @@ -85,7 +85,7 @@ int z_clock_driver_init(struct device *device) return -1; } - clock_control_on(clock, (void *)CLOCK_CONTROL_NRF_K32SRC); + clock_control_on(clock, NULL); /* TODO: replace with counter driver to access RTC */ nrf_rtc_prescaler_set(RTC, 0); diff --git a/drivers/usb/device/usb_dc_nrfx.c b/drivers/usb/device/usb_dc_nrfx.c index c0fc37ed68bd3..f97b782d3839b 100644 --- a/drivers/usb/device/usb_dc_nrfx.c +++ b/drivers/usb/device/usb_dc_nrfx.c @@ -550,7 +550,11 @@ static int hf_clock_enable(bool on, bool blocking) /* Do not request HFCLK multiple times. */ return 0; } - ret = clock_control_on(clock, (void *)blocking); + ret = clock_control_on(clock, NULL); + while (blocking && + clock_control_get_status(clock, NULL) != + CLOCK_CONTROL_STATUS_ON) { + } } else { if (!clock_requested) { /* Cancel the operation if clock has not @@ -558,14 +562,7 @@ static int hf_clock_enable(bool on, bool blocking) */ return 0; } - ret = clock_control_off(clock, (void *)blocking); - if (ret == -EBUSY) { - /* This is an expected behaviour. - * -EBUSY means that some other module has also - * requested the clock to run. - */ - ret = 0; - } + ret = clock_control_off(clock, NULL); } if (ret && (blocking || (ret != -EINPROGRESS))) { From 7b6754d6b43d58855d092b02e21f0dcd6809932c Mon Sep 17 00:00:00 2001 From: Krzysztof Chruscinski Date: Tue, 9 Jul 2019 17:19:44 +0200 Subject: [PATCH 7/7] tests: bluetooth: mesh: Fix RAM shortage failure for bbc_microbit Test for bbc_microbit was failing due to RAM usage. Reduced main stack size and turned off temperature algorithm to fit. Signed-off-by: Krzysztof Chruscinski --- tests/bluetooth/mesh/microbit.conf | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/tests/bluetooth/mesh/microbit.conf b/tests/bluetooth/mesh/microbit.conf index fb20712359334..06988f80ea8d4 100644 --- a/tests/bluetooth/mesh/microbit.conf +++ b/tests/bluetooth/mesh/microbit.conf @@ -1,6 +1,6 @@ CONFIG_TEST=y CONFIG_INIT_STACKS=y -CONFIG_MAIN_STACK_SIZE=512 +CONFIG_MAIN_STACK_SIZE=480 CONFIG_DISPLAY=y CONFIG_MICROBIT_DISPLAY=y CONFIG_GPIO=y @@ -53,3 +53,5 @@ CONFIG_BT_MESH_LABEL_COUNT=0 #CONFIG_BT_MESH_DEBUG_CRYPTO=y #CONFIG_BT_MESH_DEBUG_ADV=y #CONFIG_BT_MESH_DEBUG_ACCESS=y + +CONFIG_CLOCK_CONTROL_NRF_CALIBRATION_MAX_SKIP=0