soc: stm32wba: Implement BLE controller lib APIs over Zephyr

In order to enable BLE support on STM32WBA, following APIs are implemented:
- HostStack_: BLE Controller scheduling
- ll_sys_: Link layer API required for scheduling
- UTIL_TIMER_: BLE Controller timer utility
- LINKLAYER_PLAT_: BLE controller utilities

Signed-off-by: Erwan Gouriou <erwan.gouriou@st.com>
This commit is contained in:
Erwan Gouriou 2023-12-04 15:44:58 +01:00 committed by Fabio Baltieri
commit 6f6410061d
8 changed files with 641 additions and 0 deletions

View file

@ -9,4 +9,14 @@ zephyr_sources_ifdef(CONFIG_PM
power.c
)
if(CONFIG_BT_STM32WBA)
zephyr_include_directories(hci_if)
zephyr_sources(hci_if/linklayer_plat.c)
zephyr_sources(hci_if/bleplat.c)
zephyr_sources(hci_if/host_stack_if.c)
zephyr_sources(hci_if/ll_sys_if.c)
zephyr_sources(hci_if/stm32_timer.c)
endif()
set(SOC_LINKER_SCRIPT ${ZEPHYR_BASE}/include/zephyr/arch/arm/cortex_m/scripts/linker.ld CACHE INTERNAL "")

View file

@ -13,4 +13,30 @@ config SOC_SERIES
config STM32_LPTIM_TIMER
default y if PM
choice BT_HCI_BUS_TYPE
default BT_STM32WBA
depends on BT
endchoice
config BT_STM32WBA
select DYNAMIC_INTERRUPTS
select DYNAMIC_DIRECT_INTERRUPTS
select ENTROPY_GENERATOR
select USE_STM32_HAL_RAMCFG
if BT_STM32WBA
choice LIBC_IMPLEMENTATION
default NEWLIB_LIBC
endchoice
choice LINKER_ORPHAN_CONFIGURATION
default LINKER_ORPHAN_SECTION_PLACE
endchoice
config ENTROPY_STM32_CLK_CHECK
default n
endif
endif # SOC_SERIES_STM32WBAX

View file

@ -0,0 +1,100 @@
/*
* Copyright (c) 2023 STMicroelectronics
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <zephyr/irq.h>
#include <zephyr/drivers/entropy.h>
#include <zephyr/logging/log.h>
#include "bleplat.h"
#include "bpka.h"
#include "linklayer_plat.h"
#define LOG_LEVEL CONFIG_SOC_LOG_LEVEL
LOG_MODULE_REGISTER(ble_plat);
RAMCFG_HandleTypeDef hramcfg_SRAM1;
const struct device *rng_dev;
void BLEPLAT_Init(void)
{
BPKA_Reset();
rng_dev = DEVICE_DT_GET(DT_CHOSEN(zephyr_entropy));
if (!device_is_ready(rng_dev)) {
LOG_ERR("error: random device not ready");
}
}
void BLEPLAT_RngGet(uint8_t n, uint32_t *val)
{
LINKLAYER_PLAT_GetRNG((uint8_t *)val, 4 * n);
}
int BLEPLAT_PkaStartP256Key(const uint32_t *local_private_key)
{
return BPKA_StartP256Key(local_private_key);
}
void BLEPLAT_PkaReadP256Key(uint32_t *local_public_key)
{
BPKA_ReadP256Key(local_public_key);
}
int BLEPLAT_PkaStartDhKey(const uint32_t *local_private_key,
const uint32_t *remote_public_key)
{
return BPKA_StartDhKey(local_private_key, remote_public_key);
}
int BLEPLAT_PkaReadDhKey(uint32_t *dh_key)
{
return BPKA_ReadDhKey(dh_key);
}
void BPKACB_Complete(void)
{
BLEPLATCB_PkaComplete();
}
void MX_RAMCFG_Init(void)
{
/* Initialize RAMCFG SRAM1 */
hramcfg_SRAM1.Instance = RAMCFG_SRAM1;
if (HAL_RAMCFG_Init(&hramcfg_SRAM1) != HAL_OK) {
LOG_ERR("Could not init RAMCFG");
}
}
void *ble_memcpy(void *dst, const void *src, uint8_t n)
{
memcpy(dst, src, (size_t)n);
return dst;
}
void *ble_memset(void *dst, uint8_t c, uint16_t n)
{
memset((void *)dst, (int)c, (size_t)n);
return dst;
}
int8_t ble_memcmp(const void *a, const void *b, uint16_t n)
{
return (int8_t)memcmp(a, b, (size_t)n);
}
void Error_Handler(void)
{
LOG_ERR("");
}
/* BLE ctlr should not disable HSI on its own */
void SCM_HSI_CLK_OFF(void) {}
/* BLE ctlr should not alter RNG clocks */
void HW_RNG_DisableClock(uint8_t) {}
void HW_RNG_EnableClock(uint8_t) {}

View file

@ -0,0 +1,82 @@
/*
* Copyright (c) 2023 STMicroelectronics
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <zephyr/kernel.h>
#include <zephyr/init.h>
#include "app_conf.h"
#include "blestack.h"
#include "bpka.h"
#include "ll_intf.h"
K_MUTEX_DEFINE(ble_ctlr_stack_mutex);
struct k_work_q ble_ctlr_work_q, ll_work_q;
struct k_work ble_ctlr_stack_work, bpka_work;
uint8_t ll_state_busy;
#define BLE_CTLR_TASK_STACK_SIZE (256 * 7)
#define LL_TASK_STACK_SIZE (256 * 7)
#define BLE_CTLR_TASK_PRIO (14)
#define LL_TASK_PRIO (14)
K_THREAD_STACK_DEFINE(ble_ctlr_work_area, BLE_CTLR_TASK_STACK_SIZE);
K_THREAD_STACK_DEFINE(ll_work_area, LL_TASK_STACK_SIZE);
void HostStack_Process(void)
{
k_work_submit_to_queue(&ble_ctlr_work_q, &ble_ctlr_stack_work);
}
static void ble_ctlr_stack_handler(struct k_work *work)
{
uint8_t running = 0x0;
change_state_options_t options;
k_mutex_lock(&ble_ctlr_stack_mutex, K_FOREVER);
running = BleStack_Process();
k_mutex_unlock(&ble_ctlr_stack_mutex);
if (ll_state_busy == 1) {
options.combined_value = 0x0F;
ll_intf_chng_evnt_hndlr_state(options);
ll_state_busy = 0;
}
if (running == BLE_SLEEPMODE_RUNNING) {
HostStack_Process();
}
}
void BPKACB_Process(void)
{
k_work_submit_to_queue(&ble_ctlr_work_q, &bpka_work);
}
static void bpka_work_handler(struct k_work *work)
{
BPKA_BG_Process();
}
static int stm32wba_ble_ctlr_init(void)
{
k_work_queue_init(&ble_ctlr_work_q);
k_work_queue_start(&ble_ctlr_work_q, ble_ctlr_work_area,
K_THREAD_STACK_SIZEOF(ble_ctlr_work_area),
BLE_CTLR_TASK_PRIO, NULL);
k_work_queue_init(&ll_work_q);
k_work_queue_start(&ll_work_q, ll_work_area,
K_THREAD_STACK_SIZEOF(ll_work_area),
LL_TASK_PRIO, NULL);
k_work_init(&ble_ctlr_stack_work, &ble_ctlr_stack_handler);
k_work_init(&bpka_work, &bpka_work_handler);
return 0;
}
SYS_INIT(stm32wba_ble_ctlr_init, POST_KERNEL, CONFIG_KERNEL_INIT_PRIORITY_DEFAULT);

View file

@ -0,0 +1,304 @@
/*
* Copyright (c) 2023 STMicroelectronics
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <zephyr/irq.h>
#include <zephyr/kernel.h>
#include <zephyr/arch/cpu.h>
#include <zephyr/sys/util.h>
#include <zephyr/drivers/entropy.h>
#include <zephyr/logging/log.h>
#include <cmsis_core.h>
#include <linklayer_plat_local.h>
#include <stm32_ll_pwr.h>
#include "scm.h"
#define LOG_LEVEL CONFIG_SOC_LOG_LEVEL
LOG_MODULE_REGISTER(linklayer_plat);
#define RADIO_INTR_PRIO_HIGH_Z (RADIO_INTR_PRIO_HIGH + _IRQ_PRIO_OFFSET)
#define RADIO_INTR_PRIO_LOW_Z (RADIO_INTR_PRIO_LOW + _IRQ_PRIO_OFFSET)
/* 2.4GHz RADIO ISR callbacks */
typedef void (*radio_isr_cb_t) (void);
radio_isr_cb_t radio_callback;
radio_isr_cb_t low_isr_callback;
extern const struct device *rng_dev;
/* Radio critical sections */
volatile int32_t prio_high_isr_counter;
volatile int32_t prio_low_isr_counter;
volatile int32_t prio_sys_isr_counter;
volatile int32_t irq_counter;
volatile uint32_t local_basepri_value;
/* Radio SW low ISR global variable */
volatile uint8_t radio_sw_low_isr_is_running_high_prio;
void LINKLAYER_PLAT_ClockInit(void)
{
LL_PWR_EnableBkUpAccess();
/* Select LSE as Sleep CLK */
__HAL_RCC_RADIOSLPTIM_CONFIG(RCC_RADIOSTCLKSOURCE_LSE);
LL_PWR_DisableBkUpAccess();
/* Enable AHB5ENR peripheral clock (bus CLK) */
__HAL_RCC_RADIO_CLK_ENABLE();
}
void LINKLAYER_PLAT_DelayUs(uint32_t delay)
{
k_busy_wait(delay);
}
void LINKLAYER_PLAT_WaitHclkRdy(void)
{
while (HAL_RCCEx_GetRadioBusClockReadiness() != RCC_RADIO_BUS_CLOCK_READY) {
}
}
void LINKLAYER_PLAT_AclkCtrl(uint8_t enable)
{
LOG_DBG("enable: %d", enable);
if (enable) {
/* Enable RADIO baseband clock (active CLK) */
HAL_RCCEx_EnableRadioBBClock();
/* Polling on HSE32 activation */
while (LL_RCC_HSE_IsReady() == 0) {
}
} else {
/* Disable RADIO baseband clock (active CLK) */
HAL_RCCEx_DisableRadioBBClock();
}
}
void LINKLAYER_PLAT_GetRNG(uint8_t *ptr_rnd, uint32_t len)
{
int ret;
/* Read 32-bit random values from HW driver */
ret = entropy_get_entropy_isr(rng_dev, (char *)ptr_rnd, len, 0);
if (ret < 0) {
LOG_ERR("Error: entropy_get_entropy failed: %d", ret);
}
LOG_DBG("n %d, val: %p", len, (void *)ptr_rnd);
}
void LINKLAYER_PLAT_SetupRadioIT(void (*intr_cb)())
{
radio_callback = intr_cb;
}
void LINKLAYER_PLAT_SetupSwLowIT(void (*intr_cb)())
{
low_isr_callback = intr_cb;
}
void radio_high_prio_isr(void)
{
radio_callback();
HAL_RCCEx_DisableRequestUponRadioWakeUpEvent();
__ISB();
ISR_DIRECT_PM();
}
void radio_low_prio_isr(void)
{
irq_disable((IRQn_Type)RADIO_SW_LOW_INTR_NUM);
low_isr_callback();
/* Check if nested SW radio low interrupt has been requested*/
if (radio_sw_low_isr_is_running_high_prio != 0) {
NVIC_SetPriority((IRQn_Type) RADIO_SW_LOW_INTR_NUM, RADIO_INTR_PRIO_LOW);
radio_sw_low_isr_is_running_high_prio = 0;
}
/* Re-enable SW radio low interrupt */
irq_enable((IRQn_Type)RADIO_SW_LOW_INTR_NUM);
ISR_DIRECT_PM();
}
void link_layer_register_isr(void)
{
ARM_IRQ_DIRECT_DYNAMIC_CONNECT(RADIO_INTR_NUM, 0, 0, reschedule);
/* Ensure the IRQ is disabled before enabling it at run time */
irq_disable((IRQn_Type)RADIO_INTR_NUM);
irq_connect_dynamic((IRQn_Type)RADIO_INTR_NUM, RADIO_INTR_PRIO_HIGH_Z,
(void (*)(const void *))radio_high_prio_isr, NULL, 0);
irq_enable((IRQn_Type)RADIO_INTR_NUM);
ARM_IRQ_DIRECT_DYNAMIC_CONNECT(RADIO_SW_LOW_INTR_NUM, 0, 0, reschedule);
/* Ensure the IRQ is disabled before enabling it at run time */
irq_disable((IRQn_Type)RADIO_SW_LOW_INTR_NUM);
irq_connect_dynamic((IRQn_Type)RADIO_SW_LOW_INTR_NUM, RADIO_SW_LOW_INTR_PRIO,
(void (*)(const void *))radio_low_prio_isr, NULL, 0);
irq_enable((IRQn_Type)RADIO_SW_LOW_INTR_NUM);
}
void LINKLAYER_PLAT_TriggerSwLowIT(uint8_t priority)
{
uint8_t low_isr_priority = RADIO_INTR_PRIO_LOW_Z;
LOG_DBG("Priotity: %d", priority);
/* Check if a SW low interrupt as already been raised.
* Nested call far radio low isr are not supported
**/
if (NVIC_GetActive(RADIO_SW_LOW_INTR_NUM) == 0) {
/* No nested SW low ISR, default behavior */
if (priority == 0) {
low_isr_priority = RADIO_SW_LOW_INTR_PRIO;
}
NVIC_SetPriority((IRQn_Type)RADIO_SW_LOW_INTR_NUM, low_isr_priority);
} else {
/* Nested call detected */
/* No change for SW radio low interrupt priority for the moment */
if (priority != 0) {
/* At the end of current SW radio low ISR, this pending SW
* low interrupt will run with RADIO_INTR_PRIO_LOW_Z priority
**/
radio_sw_low_isr_is_running_high_prio = 1;
}
}
NVIC_SetPendingIRQ((IRQn_Type)RADIO_SW_LOW_INTR_NUM);
}
void LINKLAYER_PLAT_EnableIRQ(void)
{
irq_counter = MAX(0, irq_counter - 1);
if (irq_counter == 0) {
__enable_irq();
}
}
void LINKLAYER_PLAT_DisableIRQ(void)
{
__disable_irq();
irq_counter++;
}
void LINKLAYER_PLAT_Assert(uint8_t condition)
{
__ASSERT_NO_MSG(condition);
}
void LINKLAYER_PLAT_EnableSpecificIRQ(uint8_t isr_type)
{
LOG_DBG("isr_type: %d", isr_type);
if ((isr_type & LL_HIGH_ISR_ONLY) != 0) {
prio_high_isr_counter--;
if (prio_high_isr_counter == 0) {
irq_enable(RADIO_INTR_NUM);
}
}
if ((isr_type & LL_LOW_ISR_ONLY) != 0) {
prio_low_isr_counter--;
if (prio_low_isr_counter == 0) {
irq_enable(RADIO_SW_LOW_INTR_NUM);
}
}
if ((isr_type & SYS_LOW_ISR) != 0) {
prio_sys_isr_counter--;
if (prio_sys_isr_counter == 0) {
__set_BASEPRI(local_basepri_value);
}
}
}
void LINKLAYER_PLAT_DisableSpecificIRQ(uint8_t isr_type)
{
LOG_DBG("isr_type: %d", isr_type);
if ((isr_type & LL_HIGH_ISR_ONLY) != 0) {
prio_high_isr_counter++;
if (prio_high_isr_counter == 1) {
irq_disable(RADIO_INTR_NUM);
}
}
if ((isr_type & LL_LOW_ISR_ONLY) != 0) {
prio_low_isr_counter++;
if (prio_low_isr_counter == 1) {
irq_disable(RADIO_SW_LOW_INTR_NUM);
}
}
if ((isr_type & SYS_LOW_ISR) != 0) {
prio_sys_isr_counter++;
if (prio_sys_isr_counter == 1) {
local_basepri_value = __get_BASEPRI();
__set_BASEPRI_MAX(RADIO_INTR_PRIO_LOW_Z << 4);
}
}
}
void LINKLAYER_PLAT_EnableRadioIT(void)
{
irq_enable((IRQn_Type)RADIO_INTR_NUM);
}
void LINKLAYER_PLAT_DisableRadioIT(void)
{
irq_disable((IRQn_Type)RADIO_INTR_NUM);
}
void LINKLAYER_PLAT_StartRadioEvt(void)
{
__HAL_RCC_RADIO_CLK_SLEEP_ENABLE();
NVIC_SetPriority(RADIO_INTR_NUM, RADIO_INTR_PRIO_HIGH_Z);
scm_notifyradiostate(SCM_RADIO_ACTIVE);
}
void LINKLAYER_PLAT_StopRadioEvt(void)
{
__HAL_RCC_RADIO_CLK_SLEEP_DISABLE();
NVIC_SetPriority(RADIO_INTR_NUM, RADIO_INTR_PRIO_LOW_Z);
scm_notifyradiostate(SCM_RADIO_NOT_ACTIVE);
}
void LINKLAYER_PLAT_RequestTemperature(void) {}
void LINKLAYER_PLAT_SCHLDR_TIMING_UPDATE_NOT(Evnt_timing_t *p_evnt_timing) {}
void LINKLAYER_PLAT_EnableOSContextSwitch(void) {}
void LINKLAYER_PLAT_DisableOSContextSwitch(void) {}

View file

@ -0,0 +1,13 @@
/*
* Copyright (c) 2023 STMicroelectronics
*
* SPDX-License-Identifier: Apache-2.0
*/
#ifndef _STM32WBA_LINK_LAYER_PLAT_LOCAL_H_
#define _STM32WBA_LINK_LAYER_PLAT_LOCAL_H_
void link_layer_register_isr(void);
#endif /* _STM32WBA_LINK_LAYER_PLAT_LOCAL_H_ */

View file

@ -0,0 +1,47 @@
/*
* Copyright (c) 2023 STMicroelectronics
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <zephyr/kernel.h>
#include <zephyr/logging/log.h>
#define LOG_LEVEL CONFIG_SOC_LOG_LEVEL
LOG_MODULE_REGISTER(ll_sys_if);
#include "ll_intf.h"
#include "ll_sys.h"
#include "linklayer_plat.h"
#include "app_conf.h"
extern struct k_mutex ble_ctlr_stack_mutex;
extern struct k_work_q ll_work_q;
struct k_work ll_sys_work;
void ll_sys_schedule_bg_process(void)
{
k_work_submit_to_queue(&ll_work_q, &ll_sys_work);
}
void ll_sys_schedule_bg_process_isr(void)
{
k_work_submit_to_queue(&ll_work_q, &ll_sys_work);
}
static void ll_sys_bg_process_handler(struct k_work *work)
{
k_mutex_lock(&ble_ctlr_stack_mutex, K_FOREVER);
ll_sys_bg_process();
k_mutex_unlock(&ble_ctlr_stack_mutex);
}
void ll_sys_bg_process_init(void)
{
k_work_init(&ll_sys_work, &ll_sys_bg_process_handler);
}
void ll_sys_config_params(void)
{
ll_intf_config_ll_ctx_params(USE_RADIO_LOW_ISR, NEXT_EVENT_SCHEDULING_FROM_ISR);
}

View file

@ -0,0 +1,59 @@
/*
* Copyright (c) 2023 STMicroelectronics
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <zephyr/kernel.h>
#include <zephyr/sys_clock.h>
#include "stm32_timer.h"
#define TICKS_PER_MS (CONFIG_SYS_CLOCK_TICKS_PER_SEC / MSEC_PER_SEC)
static struct k_timer timer;
UTIL_TIMER_Status_t UTIL_TIMER_Create(UTIL_TIMER_Object_t *timer_object,
uint32_t period, UTIL_TIMER_Mode_t mode,
void (*callback)(void *), void *argument)
{
if (timer_object == NULL) {
return UTIL_TIMER_INVALID_PARAM;
}
timer_object->ReloadValue = period * TICKS_PER_MS;
timer_object->Mode = mode;
timer_object->Callback = callback;
timer_object->argument = argument;
timer_object->IsRunning = false;
timer_object->IsReloadStopped = false;
timer_object->Next = NULL;
return UTIL_TIMER_OK;
}
UTIL_TIMER_Status_t UTIL_TIMER_Start(UTIL_TIMER_Object_t *timer_object)
{
if (timer_object == NULL) {
return UTIL_TIMER_INVALID_PARAM;
}
k_timer_user_data_set(&timer, timer_object);
k_timer_start(&timer, K_TICKS(timer_object->ReloadValue),
K_TICKS(timer_object->ReloadValue));
timer_object->IsRunning = true;
return UTIL_TIMER_OK;
}
UTIL_TIMER_Status_t UTIL_TIMER_Stop(UTIL_TIMER_Object_t *timer_object)
{
if (timer_object == NULL) {
return UTIL_TIMER_INVALID_PARAM;
}
k_timer_stop(&timer);
timer_object->IsRunning = false;
return UTIL_TIMER_OK;
}