diff --git a/epicardium/support.c b/epicardium/support.c index 023d3e704d0d5f731185f4863488fc65c2bc07fc..f5ad5afc0e02893a6c29cc91f72c0aaa78bd5c3d 100644 --- a/epicardium/support.c +++ b/epicardium/support.c @@ -9,13 +9,45 @@ #include "user_core/user_core.h" #include "os/core.h" #include "drivers/drivers.h" +#include "modules/modules.h" + +#define BB_CLK_RATE_HZ 1600000 #include "usb.h" #include "mxc_sys.h" #include "wut.h" +#include "wsf_types.h" +#include "wsf_os.h" + +#include "sch_api_ble.h" +#include "bb_drv.h" #include "card10.h" +#define US_TO_BBTICKS(x) (((x) * (BB_CLK_RATE_HZ / 100000) + 9) / 10) + +#define MIN(a, b) (a < b) ? a : b + +#define MIN_SLEEP_TIME_MS 1 + +static int32_t ble_sleep_ticks(void) +{ + uint32_t nextDbbEventDue; + bool_t dueValid = SchBleGetNextDueTime(&nextDbbEventDue); + int sleep_ticks = nextDbbEventDue - BbDrvGetCurrentTime(); + + if (dueValid) { + if (sleep_ticks > 0) { + uint32_t bb_idle = BB_TICKS_TO_US(sleep_ticks) / 1000; + return bb_idle; + } else { + return 0; + } + } else { + return -1; + } +} + /* * This hook is called before FreeRTOS enters tickless idle. */ @@ -28,63 +60,91 @@ void pre_idle_sleep(TickType_t xExpectedIdleTime) * epicardium if it wants to issue an API call. */ - /* - * TODO: Ensure this is actually correct and does not have any - * race conditions. - */ - /* If the other core is waiting for a call to return and we are able * to sleep, reduce the system clock to save energy. */ - if (xExpectedIdleTime >= pdMS_TO_TICKS(10) && - api_dispatcher_call_pending()) { - uint32_t ms = xExpectedIdleTime - 1; - /* Initialize Wakeup timer */ - WUT_Init(WUT_PRES_1); - wut_cfg_t wut_cfg; - wut_cfg.mode = WUT_MODE_COMPARE; - wut_cfg.cmp_cnt = 0xFFFFFFFF; - WUT_Config(&wut_cfg); - WUT_Enable(); - - /* Enable WUT as a wakup source */ - NVIC_EnableIRQ(WUT_IRQn); - - uint32_t targetTick; - targetTick = WUT_GetCount(); - targetTick += ((uint64_t)(ms)*SYS_WUT_GetFreq() / 1000); - WUT_SetCompare(targetTick); - - /* Stop SysTick */ - uint32_t val = SysTick->VAL; - SysTick->CTRL &= ~(SysTick_CTRL_ENABLE_Msk); - - if (usb_get_status() & MAXUSB_STATUS_VBUS_ON) { - SYS_Clock_Select(SYS_CLOCK_HIRC, NULL); - } else { - SYS_Clock_Select(SYS_CLOCK_HIRC, NULL); - SYS_ClockSourceDisable(SYS_CLOCK_HIRC96); - } - disp_update_backlight_clock(); - __asm volatile("dsb" ::: "memory"); - __asm volatile("wfe"); - __asm volatile("isb"); - SYS_Clock_Select(SYS_CLOCK_HIRC96, NULL); - disp_update_backlight_clock(); - SYS_ClockSourceDisable(SYS_CLOCK_HIRC); - SysTick->LOAD = val; - SysTick->VAL = 0; - SysTick->CTRL |= SysTick_CTRL_ENABLE_Msk; - - ms = (WUT_GetCount() * 1000) / SYS_WUT_GetFreq(); - for (uint32_t t = 0; t < ms; t++) { - xTaskIncrementTick(); + if (xExpectedIdleTime >= pdMS_TO_TICKS(MIN_SLEEP_TIME_MS) && + api_dispatcher_call_pending() && wsfOsReadyToSleep()) { + uint32_t ms = xExpectedIdleTime; + + if (ble_is_enabled()) { + int32_t ble_idle = ble_sleep_ticks(); + if (ble_idle >= 0) { + ms = + MIN(xExpectedIdleTime, + (uint32_t)ble_idle); + } } - } else { - __asm volatile("dsb" ::: "memory"); - __asm volatile("wfe"); - __asm volatile("isb"); + // Us the WUT only if we can sleep a significant amount of time + if (ms >= MIN_SLEEP_TIME_MS) { + /* Initialize Wakeup timer */ + WUT_Init(WUT_PRES_1); + wut_cfg_t wut_cfg; + wut_cfg.mode = WUT_MODE_COMPARE; + wut_cfg.cmp_cnt = 0xFFFFFFFF; + WUT_Config(&wut_cfg); + WUT_Enable(); + + /* Enable WUT as a wakup source */ + NVIC_EnableIRQ(WUT_IRQn); + + uint32_t targetTick; + targetTick = WUT_GetCount(); + targetTick += + ((uint64_t)(ms)*SYS_WUT_GetFreq() / + 1000); + WUT_SetCompare(targetTick); + + /* Stop SysTick */ + uint32_t val = SysTick->VAL; + SysTick->CTRL &= ~(SysTick_CTRL_ENABLE_Msk); + + if (usb_get_status() & MAXUSB_STATUS_VBUS_ON) { + /* Need to stay on 96 MHz. USB serial becomes + * unstable otherwise. Don't know why. */ + //SYS_Clock_Select(SYS_CLOCK_HIRC, NULL); + } else { + MXC_GCR->clkcn |= + MXC_S_GCR_CLKCN_PSC_DIV4; + SYS_Clock_Select(SYS_CLOCK_HIRC, NULL); + SYS_ClockSourceDisable( + SYS_CLOCK_HIRC96 + ); + } + disp_update_backlight_clock(); + __asm volatile("dsb" ::: "memory"); + __asm volatile("wfe"); + __asm volatile("isb"); + MXC_GCR->clkcn &= ~(MXC_S_GCR_CLKCN_PSC_DIV4); + SYS_Clock_Select(SYS_CLOCK_HIRC96, NULL); +#if 0 + /* Allow to serve high priority interrupts before + * doing the lengthy xTaskIncrementTick loop. */ + portDISABLE_INTERRUPTS(); + __set_PRIMASK(0); + __set_PRIMASK(1); + portENABLE_INTERRUPTS(); +#endif + disp_update_backlight_clock(); + SYS_ClockSourceDisable(SYS_CLOCK_HIRC); + SysTick->LOAD = val; + SysTick->VAL = 0; + SysTick->CTRL |= SysTick_CTRL_ENABLE_Msk; + + ms = (WUT_GetCount() * 1000) / + SYS_WUT_GetFreq(); + for (uint32_t t = 0; t < ms; t++) { + xTaskIncrementTick(); + } + return; + } } + + /* Fall back to light sleep with clocks on if we can't + * sleep long enough for the WUT */ + __asm volatile("dsb" ::: "memory"); + __asm volatile("wfe"); + __asm volatile("isb"); } }