From 01fe8ae443e48c79de05af4b82e070ac36abfe2f Mon Sep 17 00:00:00 2001 From: Andrzej Kaczmarek Date: Tue, 1 Aug 2023 15:46:17 +0200 Subject: [PATCH] hw/mcu/dialog: Rework CMAC sleep calculations Sleep time calculation on CMAC assumed incorrectly that wakeup fsm uses lpclk but in fact RC32K is used so we need to take it into account. To make things easier to handle on CMAC side we will calculate wakeup time on M33 side and pass it to CMAC instead of XTAL32M settle time. This way we don't really need to care about how wakeup fsm is configured. It's now expected that M33 will take care of RC32K calibration and will calculate proper wakeup time based on wakeup fsm settings and XTAL32M settle time, and then notify CMAC on any change so we can recalculate our min sleep time. --- hw/drivers/ipc_cmac/include/ipc_cmac/shm.h | 9 +-- hw/drivers/ipc_cmac/include/ipc_cmac/shm_hs.h | 3 + hw/drivers/ipc_cmac/src/shm_hs.c | 46 ++++++++++----- hw/mcu/dialog/cmac/include/mcu/cmac_timer.h | 3 +- hw/mcu/dialog/cmac/src/cmac_isr.c | 11 +++- hw/mcu/dialog/cmac/src/cmac_priv.h | 2 +- hw/mcu/dialog/cmac/src/cmac_sleep.c | 58 +++++++++---------- hw/mcu/dialog/cmac/src/cmac_timer.c | 28 ++++----- 8 files changed, 90 insertions(+), 70 deletions(-) diff --git a/hw/drivers/ipc_cmac/include/ipc_cmac/shm.h b/hw/drivers/ipc_cmac/include/ipc_cmac/shm.h index 376095366f..a438d54f5b 100644 --- a/hw/drivers/ipc_cmac/include/ipc_cmac/shm.h +++ b/hw/drivers/ipc_cmac/include/ipc_cmac/shm.h @@ -38,8 +38,9 @@ extern "C" { #define CMAC_SHM_CB_MAGIC 0xc3ac -#define CMAC_SHM_CB_PENDING_OP_LP_CLK 0x0001 -#define CMAC_SHM_CB_PENDING_OP_RF_CAL 0x0002 +#define CMAC_SHM_CB_PENDING_OP_LPCLK_UPDATE 0x0001 +#define CMAC_SHM_CB_PENDING_OP_WAKEUP_UPDATE 0x0002 +#define CMAC_SHM_CB_PENDING_OP_RF_CAL 0x0004 #define CMAC_SHM_VECT_MAGIC 0xc3ac0001 #define CMAC_SHM_VECT_CRASHINFO 0x00000001 @@ -58,8 +59,8 @@ struct cmac_shm_config { struct cmac_shm_ctrl { uint16_t magic; uint16_t pending_ops; - uint16_t lp_clock_freq; - uint16_t xtal32m_settle_us; + uint16_t lpclk_freq; + uint16_t wakeup_time_us; }; 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..068a81df07 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,9 @@ extern volatile struct cmac_shm_debugdata *g_cmac_shm_debugdata; void cmac_host_init(void); void cmac_host_signal2cmac(void); + +void cmac_host_lpclk_update(uint32_t freq); +void cmac_host_wakeup_update(uint32_t wakeup_time_us); 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..2d6380d6ef 100644 --- a/hw/drivers/ipc_cmac/src/shm_hs.c +++ b/hw/drivers/ipc_cmac/src/shm_hs.c @@ -199,17 +199,7 @@ cmac_host_rand_chk_fill(void) static void cmac_host_lpclk_cb(uint32_t freq) { - /* 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(); + cmac_host_lpclk_update(freq); } static void @@ -234,8 +224,7 @@ 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->wakeup_time_us = 0; trim = (struct cmac_shm_trim *)g_cmac_shm_trim; trim_data = trim->data; @@ -444,6 +433,7 @@ cmac_host_init(void) cmac_start(); da1469x_lpclk_register_cmac_cb(cmac_host_lpclk_cb); + cmac_host_wakeup_update(da1469x_sleep_wakeup_time_us_get()); #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,6 +452,36 @@ cmac_host_signal2cmac(void) da1469x_pdc_set(g_cmac_host_pdc_sys2cmac); } +void +cmac_host_lpclk_update(uint32_t freq) +{ + if (g_cmac_shm_ctrl->lpclk_freq == freq) { + return; + } + + cmac_shm_lock(); + g_cmac_shm_ctrl->lpclk_freq = freq; + g_cmac_shm_ctrl->pending_ops |= CMAC_SHM_CB_PENDING_OP_LPCLK_UPDATE; + cmac_shm_unlock(); + + cmac_host_signal2cmac(); +} + +void +cmac_host_wakeup_update(uint32_t wakeup_time_us) +{ + if (g_cmac_shm_ctrl->wakeup_time_us == wakeup_time_us) { + return; + } + + cmac_shm_lock(); + g_cmac_shm_ctrl->wakeup_time_us = wakeup_time_us; + g_cmac_shm_ctrl->pending_ops |= CMAC_SHM_CB_PENDING_OP_WAKEUP_UPDATE; + cmac_shm_unlock(); + + cmac_host_signal2cmac(); +} + void cmac_host_rf_calibrate(void) { diff --git a/hw/mcu/dialog/cmac/include/mcu/cmac_timer.h b/hw/mcu/dialog/cmac/include/mcu/cmac_timer.h index 0c12647336..88e27b0cd6 100644 --- a/hw/mcu/dialog/cmac/include/mcu/cmac_timer.h +++ b/hw/mcu/dialog/cmac/include/mcu/cmac_timer.h @@ -35,7 +35,8 @@ 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 lpclk_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..53bc584cd1 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,13 @@ 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_LPCLK_UPDATE) { + cmac_timer_slp_update(g_cmac_shm_ctrl.lpclk_freq); + cmac_sleep_wakeup_time_update(g_cmac_shm_ctrl.wakeup_time_us); + } + + if (pending_ops & CMAC_SHM_CB_PENDING_OP_WAKEUP_UPDATE) { + cmac_sleep_wakeup_time_update(g_cmac_shm_ctrl.wakeup_time_us); } 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..054c040cc2 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_time_us); #ifdef __cplusplus } diff --git a/hw/mcu/dialog/cmac/src/cmac_sleep.c b/hw/mcu/dialog/cmac/src/cmac_sleep.c index cc78f70471..de87991ded 100644 --- a/hw/mcu/dialog/cmac/src/cmac_sleep.c +++ b/hw/mcu/dialog/cmac/src/cmac_sleep.c @@ -72,10 +72,10 @@ 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_time_us_min; -static bool -cmac_sleep_is_switch_allowed(void) +static inline bool +cmac_sleep_is_slp_allowed(void) { return (ble_phy_xcvr_state_get() == 0) && !os_arch_cmac_pending_irq() && @@ -98,6 +98,10 @@ sub27(uint32_t x, uint32_t y) static bool cmac_sleep_is_deep_sleep_allowed(void) { + if (g_mcu_sleep_time_us_min == 0) { + return false; + } + /* * We wait for SWD attach until high part of LL Timer increases by 2 which * is anywhere in 1-2ms range which is enough. @@ -170,42 +174,31 @@ cmac_sleep_wait4xtal(void) #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) -static void -cmac_sleep_calculate_wakeup_time(void) +void +cmac_sleep_wakeup_time_update(uint16_t wakeup_time_us) { - assert(g_cmac_shm_ctrl.xtal32m_settle_us); + if (wakeup_time_us == 0) { + g_mcu_sleep_time_us_min = 0; + return; + } - g_mcu_wakeup_usecs_min = + g_mcu_sleep_time_us_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_USEC(wakeup_time_us) + T_LPTICK(2) + T_USEC(50); } extern bool ble_rf_try_recalibrate(uint32_t idle_time_us); @@ -234,7 +227,7 @@ cmac_sleep(void) * happen so need to make sure we can be up and running on time. */ - sleep_usecs = wakeup_at - cmac_timer_read32() - g_mcu_wakeup_usecs_min; + sleep_usecs = wakeup_at - cmac_timer_read32() - g_mcu_sleep_time_us_min; if ((int32_t)sleep_usecs <= 0) { switch_to_slp = false; deep_sleep = false; @@ -245,14 +238,15 @@ cmac_sleep(void) goto skip_sleep; } - sleep_lp_ticks = cmac_timer_usecs_to_lp_ticks(sleep_usecs); - if (sleep_lp_ticks <= 1) { + /* XXX off by one? */ + sleep_lp_ticks = cmac_timer_usecs_to_lp_ticks(sleep_usecs) - 1; + if ((int32_t)sleep_lp_ticks <= 1) { switch_to_slp = false; deep_sleep = false; goto do_sleep; } - if (!cmac_sleep_is_switch_allowed()) { + if (!cmac_sleep_is_slp_allowed()) { switch_to_slp = false; deep_sleep = false; } else if (!cmac_sleep_is_deep_sleep_allowed()) { diff --git a/hw/mcu/dialog/cmac/src/cmac_timer.c b/hw/mcu/dialog/cmac/src/cmac_timer.c index 1820362eee..0a52ad534a 100644 --- a/hw/mcu/dialog/cmac/src/cmac_timer.c +++ b/hw/mcu/dialog/cmac/src/cmac_timer.c @@ -32,7 +32,7 @@ (CMAC_CM_EV_LATCHED_REG_EV1C_CLK_1US_X1_Msk << ((_num) - 1))) struct cmac_timer_slp { - uint32_t freq; + uint16_t lpclk_freq; #if !MYNEWT_VAL(MCU_SLP_TIMER_32K_ONLY) uint32_t conv; uint32_t tick_ns; @@ -334,36 +334,30 @@ 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 lpclk_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; + if (lpclk_freq == g_cmac_timer_slp.lpclk_freq) { + return; } - g_cmac_timer_slp.freq = lp_clock_freq; + g_cmac_timer_slp.lpclk_freq = lpclk_freq; #if !MYNEWT_VAL(MCU_SLP_TIMER_32K_ONLY) - if (g_cmac_timer_slp.freq) { - g_cmac_timer_slp.conv = g_cmac_timer_slp.freq * 32768 / 1000000; - g_cmac_timer_slp.tick_ns = 1000000000 / g_cmac_timer_slp.freq; + if (g_cmac_timer_slp.lpclk_freq) { + g_cmac_timer_slp.conv = g_cmac_timer_slp.lpclk_freq * 32768 / 1000000; + g_cmac_timer_slp.tick_ns = 1000000000 / g_cmac_timer_slp.lpclk_freq; } #endif - - return true; } bool cmac_timer_slp_is_ready(void) { #if MYNEWT_VAL(MCU_SLP_TIMER_32K_ONLY) - return g_cmac_timer_slp.freq == 32768; + return g_cmac_timer_slp.lpclk_freq == 32768; #else - return g_cmac_timer_slp.freq; + return g_cmac_timer_slp.lpclk_freq != 0; #endif }