diff --git a/hw/drivers/ipc_cmac/include/ipc_cmac/shm.h b/hw/drivers/ipc_cmac/include/ipc_cmac/shm.h index 376095366f..e8ec5aa96d 100644 --- a/hw/drivers/ipc_cmac/include/ipc_cmac/shm.h +++ b/hw/drivers/ipc_cmac/include/ipc_cmac/shm.h @@ -38,7 +38,7 @@ extern "C" { #define CMAC_SHM_CB_MAGIC 0xc3ac -#define CMAC_SHM_CB_PENDING_OP_LP_CLK 0x0001 +#define CMAC_SHM_CB_PENDING_OP_SLEEP_UPDATE 0x0001 #define CMAC_SHM_CB_PENDING_OP_RF_CAL 0x0002 #define CMAC_SHM_VECT_MAGIC 0xc3ac0001 @@ -59,7 +59,7 @@ struct cmac_shm_ctrl { uint16_t magic; uint16_t pending_ops; uint16_t lp_clock_freq; - uint16_t xtal32m_settle_us; + uint16_t wakeup_lpclk_ticks; }; struct cmac_shm_mbox { diff --git a/hw/drivers/ipc_cmac/include/ipc_cmac/shm_hs.h b/hw/drivers/ipc_cmac/include/ipc_cmac/shm_hs.h index 1de158a94d..4aefa9d243 100644 --- a/hw/drivers/ipc_cmac/include/ipc_cmac/shm_hs.h +++ b/hw/drivers/ipc_cmac/include/ipc_cmac/shm_hs.h @@ -38,6 +38,8 @@ extern volatile struct cmac_shm_debugdata *g_cmac_shm_debugdata; void cmac_host_init(void); void cmac_host_signal2cmac(void); + +void cmac_host_req_sleep_update(void); void cmac_host_rf_calibrate(void); #ifdef __cplusplus diff --git a/hw/drivers/ipc_cmac/src/shm_hs.c b/hw/drivers/ipc_cmac/src/shm_hs.c index 444de258a5..ea40393a08 100644 --- a/hw/drivers/ipc_cmac/src/shm_hs.c +++ b/hw/drivers/ipc_cmac/src/shm_hs.c @@ -36,6 +36,7 @@ #include #include "trng/trng.h" #include "console/console.h" +#include "mcu/da1469x_sleep.h" extern char _binary_cmac_img_bin_start[]; extern char _binary_cmac_img_bin_end; @@ -196,20 +197,10 @@ cmac_host_rand_chk_fill(void) } } -static void -cmac_host_lpclk_cb(uint32_t freq) +static bool +shm_synced(void) { - /* No need to wakeup CMAC if LP clock frequency did not change */ - if (g_cmac_shm_ctrl->lp_clock_freq == freq) { - return; - } - - cmac_shm_lock(); - g_cmac_shm_ctrl->lp_clock_freq = freq; - g_cmac_shm_ctrl->pending_ops |= CMAC_SHM_CB_PENDING_OP_LP_CLK; - cmac_shm_unlock(); - - cmac_host_signal2cmac(); + return g_cmac_shm_ctrl && (g_cmac_shm_ctrl->magic == CMAC_SHM_CB_MAGIC); } static void @@ -234,8 +225,8 @@ shm_configure(void) struct cmac_shm_trim *trim; uint32_t *trim_data; - g_cmac_shm_ctrl->xtal32m_settle_us = - MYNEWT_VAL(MCU_CLOCK_XTAL32M_SETTLE_TIME_US); + g_cmac_shm_ctrl->lp_clock_freq = 0; + g_cmac_shm_ctrl->wakeup_lpclk_ticks = 0; trim = (struct cmac_shm_trim *)g_cmac_shm_trim; trim_data = trim->data; @@ -415,7 +406,7 @@ cmac_start(void) /* Release CMAC from reset and sync */ CRG_TOP->CLK_RADIO_REG &= ~CRG_TOP_CLK_RADIO_REG_CMAC_SYNCH_RESET_Msk; - while (g_cmac_shm_ctrl->magic != CMAC_SHM_CB_MAGIC) { + while (!shm_synced()) { /* Wait for CMAC to initialize */ } NVIC_EnableIRQ(CMAC2SYS_IRQn); @@ -443,7 +434,7 @@ cmac_host_init(void) cmac_start(); - da1469x_lpclk_register_cmac_cb(cmac_host_lpclk_cb); + cmac_host_req_sleep_update(); #if MYNEWT_VAL(CMAC_DEBUG_HOST_PRINT_ENABLE) && MYNEWT_VAL(CMAC_DEBUG_DATA_ENABLE) /* Trim values are calculated on RF init, so are valid after synced with CMAC */ @@ -462,9 +453,40 @@ cmac_host_signal2cmac(void) da1469x_pdc_set(g_cmac_host_pdc_sys2cmac); } +void +cmac_host_req_sleep_update(void) +{ + uint16_t lpclk_freq; + uint32_t wakeup_lpclk_ticks; + + if (!shm_synced()) { + return; + } + + lpclk_freq = da1469x_lpclk_freq_get(); + wakeup_lpclk_ticks = da1469x_sleep_wakeup_ticks_get(); + + if ((g_cmac_shm_ctrl->lp_clock_freq == lpclk_freq) && + (g_cmac_shm_ctrl->wakeup_lpclk_ticks == wakeup_lpclk_ticks)) { + return; + } + + cmac_shm_lock(); + g_cmac_shm_ctrl->lp_clock_freq = lpclk_freq; + g_cmac_shm_ctrl->wakeup_lpclk_ticks = wakeup_lpclk_ticks; + g_cmac_shm_ctrl->pending_ops |= CMAC_SHM_CB_PENDING_OP_SLEEP_UPDATE; + cmac_shm_unlock(); + + cmac_host_signal2cmac(); +} + void cmac_host_rf_calibrate(void) { + if (!shm_synced()) { + return; + } + cmac_shm_lock(); g_cmac_shm_ctrl->pending_ops |= CMAC_SHM_CB_PENDING_OP_RF_CAL; cmac_shm_unlock(); diff --git a/hw/mcu/dialog/cmac/include/mcu/cmac_timer.h b/hw/mcu/dialog/cmac/include/mcu/cmac_timer.h index 0c12647336..b8e56dd730 100644 --- a/hw/mcu/dialog/cmac/include/mcu/cmac_timer.h +++ b/hw/mcu/dialog/cmac/include/mcu/cmac_timer.h @@ -35,7 +35,7 @@ extern struct cmac_timer_ctrl g_cmac_timer_ctrl; void cmac_timer_init(void); void cmac_timer_slp_enable(uint32_t ticks); void cmac_timer_slp_disable(uint32_t exp_ticks); -bool cmac_timer_slp_update(void); +void cmac_timer_slp_update(uint16_t lp_clock_freq); bool cmac_timer_slp_is_ready(void); #if MYNEWT_VAL(MCU_SLP_TIMER_32K_ONLY) static inline uint32_t diff --git a/hw/mcu/dialog/cmac/src/cmac_isr.c b/hw/mcu/dialog/cmac/src/cmac_isr.c index a460687d3c..3147133076 100644 --- a/hw/mcu/dialog/cmac/src/cmac_isr.c +++ b/hw/mcu/dialog/cmac/src/cmac_isr.c @@ -17,8 +17,10 @@ * under the License. */ +#include #include #include +#include #include #include #include @@ -40,8 +42,9 @@ SYS2CMAC_IRQHandler(void) cmac_mbox_read(); cmac_rand_read(); - if (pending_ops & CMAC_SHM_CB_PENDING_OP_LP_CLK) { - cmac_sleep_recalculate(); + if (pending_ops & CMAC_SHM_CB_PENDING_OP_SLEEP_UPDATE) { + cmac_timer_slp_update(g_cmac_shm_ctrl.lp_clock_freq); + cmac_sleep_wakeup_time_update(g_cmac_shm_ctrl.wakeup_lpclk_ticks); } if (pending_ops & CMAC_SHM_CB_PENDING_OP_RF_CAL) { diff --git a/hw/mcu/dialog/cmac/src/cmac_priv.h b/hw/mcu/dialog/cmac/src/cmac_priv.h index 45edf43daf..eab0309d0c 100644 --- a/hw/mcu/dialog/cmac/src/cmac_priv.h +++ b/hw/mcu/dialog/cmac/src/cmac_priv.h @@ -30,7 +30,7 @@ extern "C" { extern int8_t g_cmac_pdc_cmac2sys; void cmac_sleep(void); -void cmac_sleep_recalculate(void); +void cmac_sleep_wakeup_time_update(uint16_t wakeup_lpclk_ticks); #ifdef __cplusplus } diff --git a/hw/mcu/dialog/cmac/src/cmac_sleep.c b/hw/mcu/dialog/cmac/src/cmac_sleep.c index cc78f70471..c7214382cc 100644 --- a/hw/mcu/dialog/cmac/src/cmac_sleep.c +++ b/hw/mcu/dialog/cmac/src/cmac_sleep.c @@ -72,9 +72,9 @@ static uint32_t g_retained_regs_val[ ARRAY_SIZE(retained_regs) ]; static uint32_t g_mcu_wait_for_swd_start; /* Minimum time required to go to sleep (until switch to SLP) and then wake up */ -static uint32_t g_mcu_wakeup_usecs_min; +static uint32_t g_mcu_sleep_lp_ticks_min; -static bool +static inline bool cmac_sleep_is_switch_allowed(void) { return (ble_phy_xcvr_state_get() == 0) && @@ -168,44 +168,34 @@ cmac_sleep_wait4xtal(void) *(volatile uint32_t *)0x5000001c = 1; } -#define T_USEC(_t) (_t) -#define T_LPTICK(_t) ((_t) * cmac_timer_slp_tick_us()) -#define T_LPTICK_U(_t) (T_LPTICK(_t) * 15 / 10) +#define T_USEC(_t) (((_t) + cmac_timer_slp_tick_us() - 1) / \ + cmac_timer_slp_tick_us()) +#define T_LPTICK(_t) (_t) -static void -cmac_sleep_calculate_wakeup_time(void) +void +cmac_sleep_wakeup_time_update(uint16_t wakeup_lpclk_ticks) { - assert(g_cmac_shm_ctrl.xtal32m_settle_us); + if (wakeup_lpclk_ticks == 0) { + g_mcu_sleep_lp_ticks_min = 0; + return; + } - g_mcu_wakeup_usecs_min = + g_mcu_sleep_lp_ticks_min = /* - * We need ~12us to prepare for sleep before starting switch to SLP. + * We need ~15us to prepare for sleep before starting switch to SLP. * Switch to SLP is done by switching SLP clock to LPCLK first and then * enabling SLP. The former has to be synchronized with negative edge of * LPCLK and the latter happens on positive edge of LPCLK so we just * assume 2 LPCLK ticks in worst case. */ - T_USEC(12) + T_LPTICK(2) + + T_USEC(15) + T_LPTICK(2) + /* - * On wake up we assume fast wake up mode which has 3 phases that take - * up to 2, 2 and 3 LPCLK ticks respectively (need to add some margin - * here for worst-worst case). XTAL32M is started at 3rd phase and we - * need to wait for it to settle before switch back to LLT. This is done - * by disabling SLP and then switching SLP clock to PCLK. Both actions - * are synchronized with LPCLK negative edge so take 2 LPCLK ticks in - * worst case. Finally, LLP compensation takes around 50us. + * After wakeup (this includes XTAL32M settling) we need to switch back + * to LLT. This is done by disabling SLP and then switching SLP clock to + * PCLK. Both actions are synchronized with LPCLK negative edge so take + * 2 LPCLK ticks in worst case. Finally, LLT compensation takes ~50us. */ - T_LPTICK_U(2) + T_LPTICK_U(2) + - max(T_LPTICK_U(3), T_USEC(g_cmac_shm_ctrl.xtal32m_settle_us)) + - T_LPTICK(2) + T_USEC(50); -} - -void -cmac_sleep_recalculate(void) -{ - if (cmac_timer_slp_update()) { - cmac_sleep_calculate_wakeup_time(); - } + T_LPTICK(wakeup_lpclk_ticks) + T_LPTICK(2) + T_USEC(50); } extern bool ble_rf_try_recalibrate(uint32_t idle_time_us); @@ -228,25 +218,21 @@ cmac_sleep(void) cmac_pdc_ack_all(); wakeup_at = cmac_timer_next_at(); + sleep_usecs = wakeup_at - cmac_timer_read32(); - /* - * At this point in time we know exactly when next LLT interrupt should - * happen so need to make sure we can be up and running on time. - */ + if (ble_rf_try_recalibrate(sleep_usecs)) { + goto skip_sleep; + } - sleep_usecs = wakeup_at - cmac_timer_read32() - g_mcu_wakeup_usecs_min; - if ((int32_t)sleep_usecs <= 0) { + if (g_mcu_sleep_lp_ticks_min == 0) { switch_to_slp = false; deep_sleep = false; goto do_sleep; } - if (ble_rf_try_recalibrate(sleep_usecs)) { - goto skip_sleep; - } - - sleep_lp_ticks = cmac_timer_usecs_to_lp_ticks(sleep_usecs); - if (sleep_lp_ticks <= 1) { + sleep_lp_ticks = cmac_timer_usecs_to_lp_ticks(sleep_usecs) - + g_mcu_sleep_lp_ticks_min; + if ((int32_t)sleep_lp_ticks <= 1) { switch_to_slp = false; deep_sleep = false; goto do_sleep; diff --git a/hw/mcu/dialog/cmac/src/cmac_timer.c b/hw/mcu/dialog/cmac/src/cmac_timer.c index 1820362eee..a230c9efdb 100644 --- a/hw/mcu/dialog/cmac/src/cmac_timer.c +++ b/hw/mcu/dialog/cmac/src/cmac_timer.c @@ -334,15 +334,11 @@ cmac_timer_slp_disable(uint32_t exp_ticks) assert(CMAC->CM_LL_INT_STAT_REG == 0); } -bool -cmac_timer_slp_update(void) +void +cmac_timer_slp_update(uint16_t lp_clock_freq) { - uint32_t lp_clock_freq; - - lp_clock_freq = g_cmac_shm_ctrl.lp_clock_freq; - if (lp_clock_freq == g_cmac_timer_slp.freq) { - return false; + return; } g_cmac_timer_slp.freq = lp_clock_freq; @@ -353,8 +349,6 @@ cmac_timer_slp_update(void) g_cmac_timer_slp.tick_ns = 1000000000 / g_cmac_timer_slp.freq; } #endif - - return true; } bool @@ -363,7 +357,7 @@ cmac_timer_slp_is_ready(void) #if MYNEWT_VAL(MCU_SLP_TIMER_32K_ONLY) return g_cmac_timer_slp.freq == 32768; #else - return g_cmac_timer_slp.freq; + return g_cmac_timer_slp.freq != 0; #endif } diff --git a/hw/mcu/dialog/da1469x/include/mcu/da1469x_lpclk.h b/hw/mcu/dialog/da1469x/include/mcu/da1469x_lpclk.h index 5f0ace4fc5..9cdfe95eb9 100644 --- a/hw/mcu/dialog/da1469x/include/mcu/da1469x_lpclk.h +++ b/hw/mcu/dialog/da1469x/include/mcu/da1469x_lpclk.h @@ -26,9 +26,8 @@ extern "C" { #endif -typedef void (da1469x_lpclk_cb)(uint32_t freq); +uint16_t da1469x_lpclk_freq_get(void); -void da1469x_lpclk_register_cmac_cb(da1469x_lpclk_cb *cb); /* Stable lp clock enabled (e.g. switched to XTAL after settling) */ void da1469x_lpclk_enabled(void); /* Frequency of lp clock changed (e.g. after RCX recalibration) */ diff --git a/hw/mcu/dialog/da1469x/include/mcu/da1469x_sleep.h b/hw/mcu/dialog/da1469x/include/mcu/da1469x_sleep.h index 9083d715dd..79730890f8 100644 --- a/hw/mcu/dialog/da1469x/include/mcu/da1469x_sleep.h +++ b/hw/mcu/dialog/da1469x/include/mcu/da1469x_sleep.h @@ -33,6 +33,7 @@ struct da1469x_sleep_cb { }; void da1469x_sleep_cb_register(struct da1469x_sleep_cb *cb); +uint32_t da1469x_sleep_wakeup_ticks_get(void); #ifdef __cplusplus } diff --git a/hw/mcu/dialog/da1469x/src/da1469x_clock.c b/hw/mcu/dialog/da1469x/src/da1469x_clock.c index 114023f24f..22ea67bb39 100644 --- a/hw/mcu/dialog/da1469x/src/da1469x_clock.c +++ b/hw/mcu/dialog/da1469x/src/da1469x_clock.c @@ -51,11 +51,14 @@ da1469x_clock_sys_xtal32m_init(void) uint32_t reg; int xtalrdy_cnt; - /* - * Number of 256kHz clock cycles (~4.085us) assuming worst case when actual frequency is 244800. - * RC32M is in range <30.6, 32.6> so 256Khz can ba as low as 30.6MHz / 125 = 244.8kHz. + /* Number of 256kHz clock cycles for XTAL32M to settle. + * To make sure we wait no less than configured settle time, we need to + * calculate using the shortest possible clock cycle, i.e. the max possible + * frequency of RC32M (32.6 MHz). + * + * Max frequency of 256kHz clock: 32.6 MHz / 125 = 260.8 kHz -> 3.834 us */ - xtalrdy_cnt = MYNEWT_VAL(MCU_CLOCK_XTAL32M_SETTLE_TIME_US) * 1000 / 4085; + xtalrdy_cnt = MYNEWT_VAL(MCU_CLOCK_XTAL32M_SETTLE_TIME_US) * 1000 / 3834; reg = CRG_XTAL->XTALRDY_CTRL_REG; reg &= ~(CRG_XTAL_XTALRDY_CTRL_REG_XTALRDY_CNT_Msk); diff --git a/hw/mcu/dialog/da1469x/src/da1469x_lpclk.c b/hw/mcu/dialog/da1469x/src/da1469x_lpclk.c index 3b603cbf5a..b562977d02 100644 --- a/hw/mcu/dialog/da1469x/src/da1469x_lpclk.c +++ b/hw/mcu/dialog/da1469x/src/da1469x_lpclk.c @@ -28,11 +28,12 @@ #include "hal/hal_timer.h" #include "os/os_cputime.h" #include "da1469x_priv.h" +#if MYNEWT_PKG_apache_mynewt_core__hw_drivers_ipc_cmac +#include "ipc_cmac/shm.h" +#endif bool g_mcu_lpclk_available; -static da1469x_lpclk_cb *g_da1469x_lpclk_cmac_cb; - #if MYNEWT_VAL_CHOICE(MCU_LPCLK_SOURCE, XTAL32K) static void da1469x_lpclk_settle_tmr_cb(void *arg) @@ -45,22 +46,29 @@ da1469x_lpclk_settle_tmr_cb(void *arg) static void da1469x_lpclk_notify(void) { - if (!g_da1469x_lpclk_cmac_cb || !g_mcu_lpclk_available) { + if (!g_mcu_lpclk_available) { return; } -#if MYNEWT_VAL_CHOICE(MCU_LPCLK_SOURCE, XTAL32K) - g_da1469x_lpclk_cmac_cb(32768); -#elif MYNEWT_VAL_CHOICE(MCU_LPCLK_SOURCE, RCX) - g_da1469x_lpclk_cmac_cb(da1469x_clock_lp_rcx_freq_get()); +#if MYNEWT_PKG_apache_mynewt_core__hw_drivers_ipc_cmac + cmac_host_req_sleep_update(); #endif } -void -da1469x_lpclk_register_cmac_cb(da1469x_lpclk_cb *cb) +uint16_t +da1469x_lpclk_freq_get(void) { - g_da1469x_lpclk_cmac_cb = cb; - da1469x_lpclk_notify(); + if (!g_mcu_lpclk_available) { + return 0; + } + +#if MYNEWT_VAL_CHOICE(MCU_LPCLK_SOURCE, XTAL32K) + return 32768; +#elif MYNEWT_VAL_CHOICE(MCU_LPCLK_SOURCE, RCX) + return da1469x_clock_lp_rcx_freq_get(); +#else + return 0; +#endif } void @@ -79,6 +87,10 @@ da1469x_lpclk_updated(void) void da1469x_lpclk_init(void) { +#if MYNEWT_VAL_CHOICE(MCU_LPCLK_SOURCE, RCX) + da1469x_lpclk_enabled(); +#endif + #if MYNEWT_VAL_CHOICE(MCU_LPCLK_SOURCE, XTAL32K) static struct hal_timer lpclk_settle_tmr; da1469x_clock_lp_xtal32k_enable(); diff --git a/hw/mcu/dialog/da1469x/src/da1469x_sleep.c b/hw/mcu/dialog/da1469x/src/da1469x_sleep.c index fa0f9d2e9a..6a5656b406 100644 --- a/hw/mcu/dialog/da1469x/src/da1469x_sleep.c +++ b/hw/mcu/dialog/da1469x/src/da1469x_sleep.c @@ -19,6 +19,7 @@ #include #include "mcu/da1469x_clock.h" +#include "mcu/da1469x_lpclk.h" #include "mcu/da1469x_pd.h" #include "mcu/da1469x_pdc.h" #include "mcu/da1469x_prail.h" @@ -167,3 +168,40 @@ da1469x_sleep_cb_register(struct da1469x_sleep_cb *cb) { } #endif + +#define FAST_WAKEUP_TICKS 12 + +uint32_t +da1469x_sleep_wakeup_ticks_get(void) +{ + uint16_t rc32k_freq; + uint16_t lpclk_freq; + uint32_t wakeup_lpclk_ticks; + uint32_t xtal32m_settle_us; + + rc32k_freq = da1469x_clock_lp_rc32k_freq_get(); + lpclk_freq = da1469x_lpclk_freq_get(); + + if (lpclk_freq == 0) { + wakeup_lpclk_ticks = 0; + } else if (CRG_TOP->PMU_SLEEP_REG & CRG_TOP_PMU_SLEEP_REG_FAST_WAKEUP_Msk) { + /* Calculate worst case XTAL32M settling time, i.e. at the lowest + * frequency of RC32M (30.6 MHz) + * + * Min frequency of 256kHz clock: 30.6 MHz / 125 = 244.8 kHz -> 4.085 us + */ + xtal32m_settle_us = + (CRG_XTAL->XTALRDY_CTRL_REG & CRG_XTAL_XTALRDY_CTRL_REG_XTALRDY_CNT_Msk) * 4085 / 1000; + + wakeup_lpclk_ticks = + /* Wakeup ticks converted from RC32K ticks to lpclk ticks */ + (FAST_WAKEUP_TICKS * lpclk_freq + rc32k_freq - 1) / rc32k_freq + + /* XTAL32M settling time converted to lpclk ticks */ + (xtal32m_settle_us * lpclk_freq + 999999) / 1000000; + } else { + /* TODO add calculations for other wakeup modes */ + wakeup_lpclk_ticks = 0; + } + + return wakeup_lpclk_ticks; +} diff --git a/hw/mcu/dialog/da1469x/src/hal_system.c b/hw/mcu/dialog/da1469x/src/hal_system.c index 0a503915d4..d6c3a5f704 100644 --- a/hw/mcu/dialog/da1469x/src/hal_system.c +++ b/hw/mcu/dialog/da1469x/src/hal_system.c @@ -130,7 +130,6 @@ hal_system_clock_start(void) da1469x_clock_lp_rcx_enable(); da1469x_clock_lp_rcx_switch(); da1469x_clock_lp_rcx_calibrate(); - da1469x_lpclk_enabled(); #else /* * We cannot switch lp_clk to XTAL32K here since it needs some time to