Merge pull request #15804 from haukepetersen/opt_nrf5x_hfxo

cpu/nrf5x: enhance external HF clock source handling to allow for substantial energy savings
This commit is contained in:
Hauke Petersen 2021-01-29 11:49:44 +01:00 committed by GitHub
commit 42ff2ab778
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 94 additions and 8 deletions

View File

@ -27,6 +27,7 @@
#include "cpu.h" #include "cpu.h"
#include "mutex.h" #include "mutex.h"
#include "nrf_clock.h"
#include "net/ieee802154.h" #include "net/ieee802154.h"
#include "periph/timer.h" #include "periph/timer.h"
@ -247,6 +248,11 @@ static int _init(netdev_t *dev)
txbuf[0] = 0; txbuf[0] = 0;
_state = 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 */ /* power on peripheral */
NRF_RADIO->POWER = 1; NRF_RADIO->POWER = 1;

View File

@ -23,6 +23,7 @@
#include "cpu.h" #include "cpu.h"
#include "luid.h" #include "luid.h"
#include "nrf_clock.h"
#include "net/ieee802154.h" #include "net/ieee802154.h"
#include "periph/timer.h" #include "periph/timer.h"
@ -101,6 +102,22 @@ ieee802154_dev_t nrf802154_hal_dev = {
.driver = &nrf802154_ops, .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) static bool _l2filter(uint8_t *mhr)
{ {
uint8_t dst_addr[IEEE802154_LONG_ADDRESS_LEN]; uint8_t dst_addr[IEEE802154_LONG_ADDRESS_LEN];
@ -445,7 +462,8 @@ int nrf802154_init(void)
(void)result; (void)result;
timer_stop(NRF802154_TIMER); 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; NRF_RADIO->POWER = 0;
return 0; return 0;
@ -574,7 +592,7 @@ static int _request_on(ieee802154_dev_t *dev)
(void) dev; (void) dev;
_state = STATE_IDLE; _state = STATE_IDLE;
DEBUG("[nrf802154]: Request to turn on\n"); DEBUG("[nrf802154]: Request to turn on\n");
NRF_RADIO->POWER = 1; _power_on();
/* make sure the radio is disabled/stopped */ /* make sure the radio is disabled/stopped */
_disable(); _disable();
/* we configure it to run in IEEE802.15.4 mode */ /* we configure it to run in IEEE802.15.4 mode */
@ -639,7 +657,7 @@ static int _off(ieee802154_dev_t *dev)
{ {
(void) dev; (void) dev;
DEBUG("[nrf802154] Turning off the radio\n"); DEBUG("[nrf802154] Turning off the radio\n");
NRF_RADIO->POWER = 0; _power_off();
return 0; return 0;
} }

View File

@ -18,6 +18,9 @@
*/ */
#include "cpu.h" #include "cpu.h"
#include "irq.h"
#include "assert.h"
#include "nrf_clock.h"
#include "periph_conf.h" #include "periph_conf.h"
/* make sure both clocks are configured */ /* make sure both clocks are configured */
@ -28,6 +31,8 @@
#error "Clock init: CLOCK_LFCLK is not defined by your board!" #error "Clock init: CLOCK_LFCLK is not defined by your board!"
#endif #endif
static unsigned _hfxo_requests = 0;
void clock_init_hf(void) void clock_init_hf(void)
{ {
/* for the nRF51 we can chose the XTAL frequency */ /* for the nRF51 we can chose the XTAL frequency */
@ -39,14 +44,38 @@ void clock_init_hf(void)
#endif #endif
#endif #endif
#if CLOCK_HFCLK /* allow to always enable the HFXO as non-default option */
/* start the HF clock */ #if CLOCK_HFXO_ONBOOT
NRF_CLOCK->EVENTS_HFCLKSTARTED = 0; clock_hfxo_request();
NRF_CLOCK->TASKS_HFCLKSTART = 1;
while (NRF_CLOCK->EVENTS_HFCLKSTARTED == 0) {}
#endif #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) void clock_start_lf(void)
{ {
/* abort if LF clock is already running */ /* abort if LF clock is already running */

View File

@ -23,12 +23,33 @@
extern "C" { extern "C" {
#endif #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 * @brief Initialize the high frequency clock (HFCLK) as configured in the
* board's periph_conf.h * board's periph_conf.h
*/ */
void clock_init_hf(void); 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 * @brief Start the low frequency clock (LFCLK) as configured in the board's
* periph_conf. * periph_conf.

View File

@ -25,6 +25,7 @@
#include "cpu.h" #include "cpu.h"
#include "assert.h" #include "assert.h"
#include "nrf_clock.h"
#include "nrfble.h" #include "nrfble.h"
#include "net/netdev/ble.h" #include "net/netdev/ble.h"
@ -238,6 +239,11 @@ static int _nrfble_init(netdev_t *dev)
(void)dev; (void)dev;
assert(_nrfble_dev.driver && _nrfble_dev.event_callback); 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 */ /* power on the NRFs radio */
NRF_RADIO->POWER = 1; NRF_RADIO->POWER = 1;
/* configure variable parameters to default values */ /* configure variable parameters to default values */

View File

@ -24,6 +24,7 @@
#include "cpu.h" #include "cpu.h"
#include "mutex.h" #include "mutex.h"
#include "assert.h" #include "assert.h"
#include "nrf_clock.h"
#include "periph_conf.h" #include "periph_conf.h"
#include "periph/cpuid.h" #include "periph/cpuid.h"
@ -401,6 +402,11 @@ static int nrfmin_init(netdev_t *dev)
my_addr ^= cpuid[i] << (8 * (i & 0x01)); 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 */ /* power on the NRFs radio */
NRF_RADIO->POWER = 1; NRF_RADIO->POWER = 1;
/* load driver specific configuration */ /* load driver specific configuration */