From 6e09b7a32fe2d54051edc88303ded9652b0ccd9b Mon Sep 17 00:00:00 2001 From: Bjarki Arge Andreasen Date: Tue, 3 Dec 2024 12:39:45 +0100 Subject: [PATCH] [nrf fromtree] drivers: clock_control: nrf: hfxo: impl zero-latency isr API Implement the zero latency interrupt safe APIs to the HFXO clock commonly used by the bluetooth stach from zero latency interrupt context. Co-authored-by: Piotr Pryga Signed-off-by: Bjarki Arge Andreasen Signed-off-by: Piotr Pryga (cherry picked from commit 73a45a70126df8145a00d2e3adb1dad60818dde0) Signed-off-by: Piotr Pryga --- .../clock_control/clock_control_nrf2_hfxo.c | 111 +++++++++++++++++- 1 file changed, 105 insertions(+), 6 deletions(-) diff --git a/drivers/clock_control/clock_control_nrf2_hfxo.c b/drivers/clock_control/clock_control_nrf2_hfxo.c index 977c0aaa0d6..43998427f8b 100644 --- a/drivers/clock_control/clock_control_nrf2_hfxo.c +++ b/drivers/clock_control/clock_control_nrf2_hfxo.c @@ -21,6 +21,9 @@ struct dev_data_hfxo { onoff_notify_fn notify; struct k_timer timer; sys_snode_t hfxo_node; +#if defined(CONFIG_ZERO_LATENCY_IRQS) + uint16_t request_count; +#endif /* CONFIG_ZERO_LATENCY_IRQS */ }; struct dev_config_hfxo { @@ -29,6 +32,31 @@ struct dev_config_hfxo { k_timeout_t start_up_time; }; +#if defined(CONFIG_ZERO_LATENCY_IRQS) +static uint32_t full_irq_lock(void) +{ + uint32_t mcu_critical_state; + + if (IS_ENABLED(CONFIG_ZERO_LATENCY_IRQS)) { + mcu_critical_state = __get_PRIMASK(); + __disable_irq(); + } else { + mcu_critical_state = irq_lock(); + } + + return mcu_critical_state; +} + +static void full_irq_unlock(uint32_t mcu_critical_state) +{ + if (IS_ENABLED(CONFIG_ZERO_LATENCY_IRQS)) { + __set_PRIMASK(mcu_critical_state); + } else { + irq_unlock(mcu_critical_state); + } +} +#endif /* CONFIG_ZERO_LATENCY_IRQS */ + static void hfxo_start_up_timer_handler(struct k_timer *timer) { struct dev_data_hfxo *dev_data = @@ -48,6 +76,40 @@ static void hfxo_start_up_timer_handler(struct k_timer *timer) } } +static void start_hfxo(struct dev_data_hfxo *dev_data) +{ + nrf_lrcconf_event_clear(NRF_LRCCONF010, NRF_LRCCONF_EVENT_HFXOSTARTED); + soc_lrcconf_poweron_request(&dev_data->hfxo_node, NRF_LRCCONF_POWER_MAIN); + nrf_lrcconf_task_trigger(NRF_LRCCONF010, NRF_LRCCONF_TASK_REQHFXO); +} + +static void request_hfxo(struct dev_data_hfxo *dev_data) +{ +#if defined(CONFIG_ZERO_LATENCY_IRQS) + unsigned int key; + + key = full_irq_lock(); + if (dev_data->request_count == 0) { + start_hfxo(dev_data); + } + + dev_data->request_count++; + full_irq_unlock(key); +#else + start_hfxo(dev_data); +#endif /* CONFIG_ZERO_LATENCY_IRQS */ +} + +#if IS_ENABLED(CONFIG_ZERO_LATENCY_IRQS) +void nrf_clock_control_hfxo_request(void) +{ + const struct device *dev = DEVICE_DT_INST_GET(0); + struct dev_data_hfxo *dev_data = dev->data; + + request_hfxo(dev_data); +} +#endif /* CONFIG_ZERO_LATENCY_IRQS */ + static void onoff_start_hfxo(struct onoff_manager *mgr, onoff_notify_fn notify) { struct dev_data_hfxo *dev_data = @@ -56,10 +118,7 @@ static void onoff_start_hfxo(struct onoff_manager *mgr, onoff_notify_fn notify) const struct dev_config_hfxo *dev_config = dev->config; dev_data->notify = notify; - - nrf_lrcconf_event_clear(NRF_LRCCONF010, NRF_LRCCONF_EVENT_HFXOSTARTED); - soc_lrcconf_poweron_request(&dev_data->hfxo_node, NRF_LRCCONF_POWER_MAIN); - nrf_lrcconf_task_trigger(NRF_LRCCONF010, NRF_LRCCONF_TASK_REQHFXO); + request_hfxo(dev_data); /* Due to a hardware issue, the HFXOSTARTED event is currently * unreliable. Hence the timer is used to simply wait the expected @@ -68,13 +127,53 @@ static void onoff_start_hfxo(struct onoff_manager *mgr, onoff_notify_fn notify) k_timer_start(&dev_data->timer, dev_config->start_up_time, K_NO_WAIT); } +static void stop_hfxo(struct dev_data_hfxo *dev_data) +{ + nrf_lrcconf_task_trigger(NRF_LRCCONF010, NRF_LRCCONF_TASK_STOPREQHFXO); + soc_lrcconf_poweron_release(&dev_data->hfxo_node, NRF_LRCCONF_POWER_MAIN); +} + +static void release_hfxo(struct dev_data_hfxo *dev_data) +{ +#if IS_ENABLED(CONFIG_ZERO_LATENCY_IRQS) + unsigned int key; + + key = full_irq_lock(); + if (dev_data->request_count < 1) { + full_irq_unlock(key); + /* Misuse of the API, release without request? */ + __ASSERT_NO_MSG(false); + /* In case asserts are disabled early return due to no requests pending */ + return; + } + + dev_data->request_count--; + if (dev_data->request_count < 1) { + stop_hfxo(dev_data); + } + + full_irq_unlock(key); +#else + stop_hfxo(dev_data); +#endif /* CONFIG_ZERO_LATENCY_IRQS */ +} + +#if IS_ENABLED(CONFIG_ZERO_LATENCY_IRQS) +void nrf_clock_control_hfxo_release(void) +{ + const struct device *dev = DEVICE_DT_INST_GET(0); + struct dev_data_hfxo *dev_data = dev->data; + + release_hfxo(dev_data); +} +#endif /* IS_ENABLED(CONFIG_ZERO_LATENCY_IRQS) */ + static void onoff_stop_hfxo(struct onoff_manager *mgr, onoff_notify_fn notify) { struct dev_data_hfxo *dev_data = CONTAINER_OF(mgr, struct dev_data_hfxo, mgr); - nrf_lrcconf_task_trigger(NRF_LRCCONF010, NRF_LRCCONF_TASK_STOPREQHFXO); - soc_lrcconf_poweron_release(&dev_data->hfxo_node, NRF_LRCCONF_POWER_MAIN); + release_hfxo(dev_data); notify(mgr, 0); }