diff --git a/cpu/nrf52/radio/nrf802154/nrf802154.c b/cpu/nrf52/radio/nrf802154/nrf802154.c index 687805e7c7..38d81244eb 100644 --- a/cpu/nrf52/radio/nrf802154/nrf802154.c +++ b/cpu/nrf52/radio/nrf802154/nrf802154.c @@ -27,6 +27,7 @@ #include "cpu.h" #include "mutex.h" +#include "nrf_clock.h" #include "net/ieee802154.h" #include "periph/timer.h" @@ -247,6 +248,11 @@ static int _init(netdev_t *dev) txbuf[0] = 0; _state = 0; + /* the radio need the external HF clock source to be enabled */ + /* @todo add proper handling to release the clock whenever the radio is + * idle */ + clock_hfxo_request(); + /* power on peripheral */ NRF_RADIO->POWER = 1; diff --git a/cpu/nrf52/radio/nrf802154/nrf802154_radio.c b/cpu/nrf52/radio/nrf802154/nrf802154_radio.c index 26d13b2517..8725290337 100644 --- a/cpu/nrf52/radio/nrf802154/nrf802154_radio.c +++ b/cpu/nrf52/radio/nrf802154/nrf802154_radio.c @@ -23,6 +23,7 @@ #include "cpu.h" #include "luid.h" +#include "nrf_clock.h" #include "net/ieee802154.h" #include "periph/timer.h" @@ -101,6 +102,22 @@ ieee802154_dev_t nrf802154_hal_dev = { .driver = &nrf802154_ops, }; +static void _power_on(void) +{ + if (NRF_RADIO->POWER == 0) { + clock_hfxo_request(); + NRF_RADIO->POWER = 1; + } +} + +static void _power_off(void) +{ + if (NRF_RADIO->POWER == 1) { + NRF_RADIO->POWER = 0; + clock_hfxo_release(); + } +} + static bool _l2filter(uint8_t *mhr) { uint8_t dst_addr[IEEE802154_LONG_ADDRESS_LEN]; @@ -445,7 +462,8 @@ int nrf802154_init(void) (void)result; timer_stop(NRF802154_TIMER); - /* power off peripheral */ + /* power off peripheral (but do not release the HFXO as we never requested + * it so far) */ NRF_RADIO->POWER = 0; return 0; @@ -574,7 +592,7 @@ static int _request_on(ieee802154_dev_t *dev) (void) dev; _state = STATE_IDLE; DEBUG("[nrf802154]: Request to turn on\n"); - NRF_RADIO->POWER = 1; + _power_on(); /* make sure the radio is disabled/stopped */ _disable(); /* we configure it to run in IEEE802.15.4 mode */ @@ -639,7 +657,7 @@ static int _off(ieee802154_dev_t *dev) { (void) dev; DEBUG("[nrf802154] Turning off the radio\n"); - NRF_RADIO->POWER = 0; + _power_off(); return 0; } diff --git a/cpu/nrf5x_common/clock.c b/cpu/nrf5x_common/clock.c index 6fb89472ed..4eef401553 100644 --- a/cpu/nrf5x_common/clock.c +++ b/cpu/nrf5x_common/clock.c @@ -18,6 +18,9 @@ */ #include "cpu.h" +#include "irq.h" +#include "assert.h" +#include "nrf_clock.h" #include "periph_conf.h" /* make sure both clocks are configured */ @@ -28,6 +31,8 @@ #error "Clock init: CLOCK_LFCLK is not defined by your board!" #endif +static unsigned _hfxo_requests = 0; + void clock_init_hf(void) { /* for the nRF51 we can chose the XTAL frequency */ @@ -39,14 +44,38 @@ void clock_init_hf(void) #endif #endif -#if CLOCK_HFCLK - /* start the HF clock */ - NRF_CLOCK->EVENTS_HFCLKSTARTED = 0; - NRF_CLOCK->TASKS_HFCLKSTART = 1; - while (NRF_CLOCK->EVENTS_HFCLKSTARTED == 0) {} + /* allow to always enable the HFXO as non-default option */ +#if CLOCK_HFXO_ONBOOT + clock_hfxo_request(); #endif } +void clock_hfxo_request(void) +{ + unsigned state = irq_disable(); + ++_hfxo_requests; + if (_hfxo_requests == 1) { + NRF_CLOCK->EVENTS_HFCLKSTARTED = 0; + NRF_CLOCK->TASKS_HFCLKSTART = 1; + while (NRF_CLOCK->EVENTS_HFCLKSTARTED == 0) {} + } + irq_restore(state); +} + +void clock_hfxo_release(void) +{ + /* if this function is called while the counter is zero the state in a + * driver requesting the HFXO is broken! */ + assert(_hfxo_requests); + + unsigned state = irq_disable(); + --_hfxo_requests; + if (_hfxo_requests == 0) { + NRF_CLOCK->TASKS_HFCLKSTOP = 1; + } + irq_restore(state); +} + void clock_start_lf(void) { /* abort if LF clock is already running */ diff --git a/cpu/nrf5x_common/include/nrf_clock.h b/cpu/nrf5x_common/include/nrf_clock.h index 39f8a45eeb..a2a3fd3ff9 100644 --- a/cpu/nrf5x_common/include/nrf_clock.h +++ b/cpu/nrf5x_common/include/nrf_clock.h @@ -23,12 +23,33 @@ extern "C" { #endif +/** + * @brief The high frequency clock (HFCLK) uses the internal oscillator per + * default. Setting this define to 1 will enable the HFXO clock source + * on boot so it will always be active. + */ +#ifndef CLOCK_HFXO_ONBOOT +#define CLOCK_HFXO_ONBOOT 0 +#endif + /** * @brief Initialize the high frequency clock (HFCLK) as configured in the * board's periph_conf.h */ void clock_init_hf(void); +/** + * @brief Request the external high frequency crystal (HFXO) as HF clock + * source. If this is the first request, the HFXO will be enabled. + */ +void clock_hfxo_request(void); + +/** + * @brief Release the use of the HFXO. If this was the last active request, + * the HFXO will be disabled + */ +void clock_hfxo_release(void); + /** * @brief Start the low frequency clock (LFCLK) as configured in the board's * periph_conf. diff --git a/cpu/nrf5x_common/radio/nrfble/nrfble.c b/cpu/nrf5x_common/radio/nrfble/nrfble.c index 60624eb6db..f66f52c4f6 100644 --- a/cpu/nrf5x_common/radio/nrfble/nrfble.c +++ b/cpu/nrf5x_common/radio/nrfble/nrfble.c @@ -25,6 +25,7 @@ #include "cpu.h" #include "assert.h" +#include "nrf_clock.h" #include "nrfble.h" #include "net/netdev/ble.h" @@ -238,6 +239,11 @@ static int _nrfble_init(netdev_t *dev) (void)dev; assert(_nrfble_dev.driver && _nrfble_dev.event_callback); + /* the radio need the external HF clock source to be enabled */ + /* @todo add proper handling to release the clock whenever the radio is + * idle */ + clock_hfxo_request(); + /* power on the NRFs radio */ NRF_RADIO->POWER = 1; /* configure variable parameters to default values */ diff --git a/cpu/nrf5x_common/radio/nrfmin/nrfmin.c b/cpu/nrf5x_common/radio/nrfmin/nrfmin.c index 4787cb6109..1be5616d2b 100644 --- a/cpu/nrf5x_common/radio/nrfmin/nrfmin.c +++ b/cpu/nrf5x_common/radio/nrfmin/nrfmin.c @@ -24,6 +24,7 @@ #include "cpu.h" #include "mutex.h" #include "assert.h" +#include "nrf_clock.h" #include "periph_conf.h" #include "periph/cpuid.h" @@ -401,6 +402,11 @@ static int nrfmin_init(netdev_t *dev) my_addr ^= cpuid[i] << (8 * (i & 0x01)); } + /* the radio need the external HF clock source to be enabled */ + /* @todo add proper handling to release the clock whenever the radio is + * idle */ + clock_hfxo_request(); + /* power on the NRFs radio */ NRF_RADIO->POWER = 1; /* load driver specific configuration */