drivers: hci: Add NXP HCI driver
Add HCI driver generic to NXP platforms. Update west.yml to have ble support for rw61x Signed-off-by: Axel Le Bourhis <axel.lebourhis@nxp.com> Signed-off-by: Yassine El Aissaoui <yassine.elaissaoui@nxp.com>
This commit is contained in:
parent
39e126d6b3
commit
ee03123dd1
5 changed files with 452 additions and 8 deletions
|
@ -34,3 +34,4 @@ zephyr_library_sources_ifdef(CONFIG_BT_PSOC6_BLESS hci_psoc6_bless.c)
|
|||
zephyr_library_sources_ifdef(CONFIG_SOC_NRF5340_CPUAPP nrf53_support.c)
|
||||
zephyr_library_sources_ifdef(CONFIG_BT_AMBIQ_HCI hci_ambiq.c apollox_blue.c)
|
||||
zephyr_library_sources_ifdef(CONFIG_BT_DA1469X hci_da1469x.c)
|
||||
zephyr_library_sources_ifdef(CONFIG_BT_NXP hci_nxp.c)
|
||||
|
|
|
@ -115,6 +115,11 @@ config BT_DA1469X
|
|||
Bluetooth HCI driver for communication with CMAC core
|
||||
on DA1469x MCU.
|
||||
|
||||
config BT_NXP
|
||||
bool "NXP HCI driver"
|
||||
help
|
||||
NXP HCI bluetooth interface
|
||||
|
||||
config BT_NO_DRIVER
|
||||
bool "No default HCI driver"
|
||||
select BT_HAS_HCI_VS
|
||||
|
@ -149,16 +154,12 @@ config BT_BLUENRG_ACI
|
|||
|
||||
endif # BT_SPI
|
||||
|
||||
if BT_AMBIQ_HCI
|
||||
|
||||
config BT_HCI_INIT_PRIORITY
|
||||
int "BT HCI init priority"
|
||||
default 75
|
||||
default 75 if BT_AMBIQ_HCI
|
||||
default KERNEL_INIT_PRIORITY_DEVICE
|
||||
help
|
||||
The priority of BT HCI driver initialization needs to be lower than
|
||||
the SPI, GPIO, clock controller drivers initialization priorities.
|
||||
|
||||
endif # BT_AMBIQ_HCI
|
||||
The priority of BT HCI driver initialization.
|
||||
|
||||
config BT_STM32_IPM_RX_STACK_SIZE
|
||||
int "STM32 IPM stack size for RX thread"
|
||||
|
@ -178,6 +179,7 @@ menuconfig BT_AIROC
|
|||
IEEE 802.11a/b/g/n/ac/ax Wi-Fi and Bluetooth® 5.2 in a single-chip
|
||||
solution to enable small-form-factor IoT designs.
|
||||
source "drivers/bluetooth/hci/Kconfig.infineon"
|
||||
source "drivers/bluetooth/hci/Kconfig.nxp"
|
||||
|
||||
config BT_DRIVER_QUIRK_NO_AUTO_DLE
|
||||
bool "Host auto-initiated Data Length Update quirk"
|
||||
|
|
24
drivers/bluetooth/hci/Kconfig.nxp
Normal file
24
drivers/bluetooth/hci/Kconfig.nxp
Normal file
|
@ -0,0 +1,24 @@
|
|||
#
|
||||
# Copyright 2023 NXP
|
||||
#
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
#
|
||||
|
||||
config HCI_NXP_ENABLE_AUTO_SLEEP
|
||||
bool "BLE Controller auto sleep mode"
|
||||
help
|
||||
If enabled, the Controller auto sleep mode will be configured and enabled during HCI init.
|
||||
Auto sleep mode means the Controller will handle its low power state automatically.
|
||||
Enabling this feature will allow to save power at the cost of some latency when sending a HCI
|
||||
message to the Controller as the Host will need to wake it up.
|
||||
|
||||
config HCI_NXP_SET_CAL_DATA
|
||||
bool "BLE Controller calibration data"
|
||||
help
|
||||
If enabled, the Host will send calibration data to the BLE Controller during HCI init.
|
||||
|
||||
config HCI_NXP_SET_CAL_DATA_ANNEX100
|
||||
bool "BLE Controller calibration data annex 100"
|
||||
help
|
||||
If enabled, the Host will send calibration data annex 100 to the BLE Controller during HCI
|
||||
init.
|
417
drivers/bluetooth/hci/hci_nxp.c
Normal file
417
drivers/bluetooth/hci/hci_nxp.c
Normal file
|
@ -0,0 +1,417 @@
|
|||
/*
|
||||
* Copyright 2023 NXP
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
/* -------------------------------------------------------------------------- */
|
||||
/* Includes */
|
||||
/* -------------------------------------------------------------------------- */
|
||||
|
||||
#include <zephyr/init.h>
|
||||
#include <zephyr/drivers/bluetooth/hci_driver.h>
|
||||
#include <zephyr/logging/log.h>
|
||||
#include <zephyr/sys/byteorder.h>
|
||||
#include <zephyr/device.h>
|
||||
#include <zephyr/drivers/flash.h>
|
||||
#include <zephyr/bluetooth/hci_types.h>
|
||||
#include <soc.h>
|
||||
|
||||
#include <fwk_platform_ble.h>
|
||||
#include <fwk_platform.h>
|
||||
|
||||
/* -------------------------------------------------------------------------- */
|
||||
/* Definitions */
|
||||
/* -------------------------------------------------------------------------- */
|
||||
|
||||
#define DT_DRV_COMPAT nxp_hci_ble
|
||||
|
||||
#define LOG_LEVEL CONFIG_BT_HCI_DRIVER_LOG_LEVEL
|
||||
LOG_MODULE_REGISTER(bt_driver);
|
||||
|
||||
#define HCI_IRQ_N DT_INST_IRQ_BY_NAME(0, hci_int, irq)
|
||||
#define HCI_IRQ_P DT_INST_IRQ_BY_NAME(0, hci_int, priority)
|
||||
#define HCI_WAKEUP_IRQ_N DT_INST_IRQ_BY_NAME(0, wakeup_int, irq)
|
||||
#define HCI_WAKEUP_IRQ_P DT_INST_IRQ_BY_NAME(0, wakeup_int, priority)
|
||||
|
||||
/* Vendor specific commands */
|
||||
#define HCI_CMD_STORE_BT_CAL_DATA_OCF 0x61U
|
||||
#define HCI_CMD_STORE_BT_CAL_DATA_PARAM_LENGTH 32U
|
||||
#define HCI_CMD_STORE_BT_CAL_DATA_ANNEX100_OCF 0xFFU
|
||||
#define HCI_CMD_STORE_BT_CAL_DATA_PARAM_ANNEX100_LENGTH 16U
|
||||
#define HCI_CMD_SET_BT_SLEEP_MODE_OCF 0x23U
|
||||
#define HCI_CMD_SET_BT_SLEEP_MODE_PARAM_LENGTH 3U
|
||||
#define HCI_CMD_BT_HOST_SLEEP_CONFIG_OCF 0x59U
|
||||
#define HCI_CMD_BT_HOST_SLEEP_CONFIG_PARAM_LENGTH 2U
|
||||
|
||||
/* -------------------------------------------------------------------------- */
|
||||
/* Public prototypes */
|
||||
/* -------------------------------------------------------------------------- */
|
||||
|
||||
extern int32_t ble_hci_handler(void);
|
||||
extern int32_t ble_wakeup_done_handler(void);
|
||||
|
||||
/* -------------------------------------------------------------------------- */
|
||||
/* Private functions */
|
||||
/* -------------------------------------------------------------------------- */
|
||||
|
||||
#if CONFIG_HCI_NXP_ENABLE_AUTO_SLEEP || CONFIG_HCI_NXP_SET_CAL_DATA
|
||||
static int nxp_bt_send_vs_command(uint16_t opcode, const uint8_t *params, uint8_t params_len)
|
||||
{
|
||||
#if CONFIG_BT_HCI_HOST
|
||||
struct net_buf *buf;
|
||||
|
||||
/* Allocate buffer for the hci command */
|
||||
buf = bt_hci_cmd_create(opcode, params_len);
|
||||
if (buf == NULL) {
|
||||
LOG_ERR("Unable to allocate command buffer");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
/* Add data part of packet */
|
||||
net_buf_add_mem(buf, params, params_len);
|
||||
|
||||
/* Send the command */
|
||||
return bt_hci_cmd_send_sync(opcode, buf, NULL);
|
||||
#else
|
||||
return 0;
|
||||
#endif
|
||||
}
|
||||
#endif /* CONFIG_HCI_NXP_ENABLE_AUTO_SLEEP || CONFIG_HCI_NXP_SET_CAL_DATA */
|
||||
|
||||
#if CONFIG_HCI_NXP_ENABLE_AUTO_SLEEP
|
||||
static int nxp_bt_enable_controller_autosleep(void)
|
||||
{
|
||||
uint16_t opcode = BT_OP(BT_OGF_VS, HCI_CMD_SET_BT_SLEEP_MODE_OCF);
|
||||
const uint8_t params[HCI_CMD_SET_BT_SLEEP_MODE_PARAM_LENGTH] = {
|
||||
0x02U, /* Auto sleep enable */
|
||||
0x00U, /* Idle timeout LSB */
|
||||
0x00U /* Idle timeout MSB */
|
||||
};
|
||||
|
||||
/* Send the command */
|
||||
return nxp_bt_send_vs_command(opcode, params, HCI_CMD_SET_BT_SLEEP_MODE_PARAM_LENGTH);
|
||||
}
|
||||
|
||||
static int nxp_bt_set_host_sleep_config(void)
|
||||
{
|
||||
uint16_t opcode = BT_OP(BT_OGF_VS, HCI_CMD_BT_HOST_SLEEP_CONFIG_OCF);
|
||||
const uint8_t params[HCI_CMD_BT_HOST_SLEEP_CONFIG_PARAM_LENGTH] = {
|
||||
0xFFU, /* BT_HIU_WAKEUP_INBAND */
|
||||
0xFFU, /* BT_HIU_WAKE_GAP_WAIT_FOR_IRQ */
|
||||
};
|
||||
|
||||
/* Send the command */
|
||||
return nxp_bt_send_vs_command(opcode, params, HCI_CMD_BT_HOST_SLEEP_CONFIG_PARAM_LENGTH);
|
||||
}
|
||||
#endif /* CONFIG_HCI_NXP_ENABLE_AUTO_SLEEP */
|
||||
|
||||
#if CONFIG_HCI_NXP_SET_CAL_DATA
|
||||
static int bt_nxp_set_calibration_data(void)
|
||||
{
|
||||
uint16_t opcode = BT_OP(BT_OGF_VS, HCI_CMD_STORE_BT_CAL_DATA_OCF);
|
||||
extern const uint8_t hci_cal_data_params[HCI_CMD_STORE_BT_CAL_DATA_PARAM_LENGTH];
|
||||
|
||||
/* Send the command */
|
||||
return nxp_bt_send_vs_command(opcode, hci_cal_data_params,
|
||||
HCI_CMD_STORE_BT_CAL_DATA_PARAM_LENGTH);
|
||||
}
|
||||
|
||||
#if CONFIG_HCI_NXP_SET_CAL_DATA_ANNEX100
|
||||
static int bt_nxp_set_calibration_data_annex100(void)
|
||||
{
|
||||
uint16_t opcode = BT_OP(BT_OGF_VS, HCI_CMD_STORE_BT_CAL_DATA_ANNEX100_OCF);
|
||||
|
||||
extern const uint8_t
|
||||
hci_cal_data_annex100_params[HCI_CMD_STORE_BT_CAL_DATA_PARAM_ANNEX100_LENGTH];
|
||||
|
||||
/* Send the command */
|
||||
return nxp_bt_send_vs_command(opcode, hci_cal_data_annex100_params,
|
||||
HCI_CMD_STORE_BT_CAL_DATA_PARAM_ANNEX100_LENGTH);
|
||||
}
|
||||
#endif /* CONFIG_HCI_NXP_SET_CAL_DATA_ANNEX100 */
|
||||
|
||||
#endif /* CONFIG_HCI_NXP_SET_CAL_DATA */
|
||||
|
||||
static bool is_hci_event_discardable(const uint8_t *evt_data)
|
||||
{
|
||||
bool ret = false;
|
||||
uint8_t evt_type = evt_data[0];
|
||||
|
||||
switch (evt_type) {
|
||||
case BT_HCI_EVT_LE_META_EVENT: {
|
||||
uint8_t subevt_type = evt_data[sizeof(struct bt_hci_evt_hdr)];
|
||||
|
||||
switch (subevt_type) {
|
||||
case BT_HCI_EVT_LE_ADVERTISING_REPORT:
|
||||
case BT_HCI_EVT_LE_EXT_ADVERTISING_REPORT:
|
||||
ret = true;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
} break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static struct net_buf *bt_evt_recv(uint8_t *data, size_t len)
|
||||
{
|
||||
struct net_buf *buf;
|
||||
uint8_t payload_len;
|
||||
uint8_t evt_hdr;
|
||||
bool discardable_evt;
|
||||
size_t space_in_buffer;
|
||||
|
||||
payload_len = data[1];
|
||||
evt_hdr = data[0];
|
||||
discardable_evt = false;
|
||||
|
||||
/* Data Check */
|
||||
if (len < BT_HCI_EVT_HDR_SIZE) {
|
||||
LOG_ERR("Event header is missing");
|
||||
return NULL;
|
||||
}
|
||||
if ((len - BT_HCI_EVT_HDR_SIZE) != payload_len) {
|
||||
LOG_ERR("Event payload length is incorrect");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
discardable_evt = is_hci_event_discardable(data);
|
||||
|
||||
/* Allocate a buffer for the HCI Event */
|
||||
buf = bt_buf_get_evt(evt_hdr, discardable_evt, (discardable_evt ? K_NO_WAIT : K_FOREVER));
|
||||
|
||||
if (buf) {
|
||||
space_in_buffer = net_buf_tailroom(buf);
|
||||
if (len > space_in_buffer) {
|
||||
LOG_ERR("Buffer size error,INFO : evt_hdr=%d, data_len=%zu, buf_size=%zu",
|
||||
evt_hdr, len, space_in_buffer);
|
||||
net_buf_unref(buf);
|
||||
return NULL;
|
||||
}
|
||||
/* Copy the data to the buffer */
|
||||
net_buf_add_mem(buf, data, len);
|
||||
} else {
|
||||
if (discardable_evt) {
|
||||
LOG_DBG("Discardable buffer pool full, ignoring event");
|
||||
} else {
|
||||
LOG_ERR("No available event buffers!");
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return buf;
|
||||
}
|
||||
|
||||
static struct net_buf *bt_acl_recv(uint8_t *data, size_t len)
|
||||
{
|
||||
struct net_buf *buf;
|
||||
uint16_t payload_len;
|
||||
|
||||
/* Data Check */
|
||||
if (len < BT_HCI_ACL_HDR_SIZE) {
|
||||
LOG_ERR("ACL header is missing");
|
||||
return NULL;
|
||||
}
|
||||
memcpy((void *)&payload_len, (void *)&data[2], 2);
|
||||
if ((len - BT_HCI_ACL_HDR_SIZE) != payload_len) {
|
||||
LOG_ERR("ACL payload length is incorrect");
|
||||
return NULL;
|
||||
}
|
||||
/* Allocate a buffer for the received data */
|
||||
buf = bt_buf_get_rx(BT_BUF_ACL_IN, K_NO_WAIT);
|
||||
|
||||
if (buf) {
|
||||
if (len > net_buf_tailroom(buf)) {
|
||||
LOG_ERR("Buffer doesn't have enough space to store the data");
|
||||
net_buf_unref(buf);
|
||||
return NULL;
|
||||
}
|
||||
/* Copy the data to the buffer */
|
||||
net_buf_add_mem(buf, data, len);
|
||||
} else {
|
||||
LOG_ERR("ACL buffer is empty");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return buf;
|
||||
}
|
||||
|
||||
static void hci_rx_cb(uint8_t packetType, uint8_t *data, uint16_t len)
|
||||
{
|
||||
struct net_buf *buf;
|
||||
|
||||
switch (packetType) {
|
||||
case BT_HCI_H4_EVT:
|
||||
/* create a buffer and fill it out with event data */
|
||||
buf = bt_evt_recv(data, len);
|
||||
break;
|
||||
case BT_HCI_H4_ACL:
|
||||
/* create a buffer and fill it out with ACL data */
|
||||
buf = bt_acl_recv(data, len);
|
||||
break;
|
||||
default:
|
||||
buf = NULL;
|
||||
LOG_ERR("Unknown HCI type");
|
||||
}
|
||||
|
||||
if (buf) {
|
||||
/* Provide the buffer to the host */
|
||||
bt_recv(buf);
|
||||
}
|
||||
}
|
||||
|
||||
static int bt_nxp_send(struct net_buf *buf)
|
||||
{
|
||||
uint8_t packetType;
|
||||
|
||||
switch (bt_buf_get_type(buf)) {
|
||||
case BT_BUF_CMD:
|
||||
packetType = BT_HCI_H4_CMD;
|
||||
break;
|
||||
case BT_BUF_ACL_OUT:
|
||||
packetType = BT_HCI_H4_ACL;
|
||||
break;
|
||||
default:
|
||||
LOG_ERR("Not supported type");
|
||||
return -1;
|
||||
}
|
||||
|
||||
net_buf_push_u8(buf, packetType);
|
||||
PLATFORM_SendHciMessage(buf->data, buf->len);
|
||||
|
||||
net_buf_unref(buf);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int bt_nxp_open(void)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
do {
|
||||
ret = PLATFORM_InitBle();
|
||||
if (ret < 0) {
|
||||
LOG_ERR("Failed to initialize BLE controller");
|
||||
break;
|
||||
}
|
||||
|
||||
ret = PLATFORM_SetHciRxCallback(hci_rx_cb);
|
||||
if (ret < 0) {
|
||||
LOG_ERR("BLE HCI RX callback registration failed");
|
||||
break;
|
||||
}
|
||||
|
||||
ret = PLATFORM_StartHci();
|
||||
if (ret < 0) {
|
||||
LOG_ERR("HCI open failed");
|
||||
break;
|
||||
}
|
||||
|
||||
#if CONFIG_HCI_NXP_SET_CAL_DATA
|
||||
ret = bt_nxp_set_calibration_data();
|
||||
if (ret < 0) {
|
||||
LOG_ERR("Failed to set calibration data");
|
||||
break;
|
||||
}
|
||||
#if CONFIG_HCI_NXP_SET_CAL_DATA_ANNEX100
|
||||
/* After send annex55 to CPU2, CPU2 need reset,
|
||||
* a delay of at least 20ms is required to continue sending annex100
|
||||
*/
|
||||
k_sleep(Z_TIMEOUT_MS(20));
|
||||
|
||||
ret = bt_nxp_set_calibration_data_annex100();
|
||||
if (ret < 0) {
|
||||
LOG_ERR("Failed to set calibration data");
|
||||
break;
|
||||
}
|
||||
#endif /* CONFIG_HCI_NXP_SET_CAL_DATA_ANNEX100 */
|
||||
#endif /* CONFIG_HCI_NXP_SET_CAL_DATA */
|
||||
|
||||
#if CONFIG_HCI_NXP_ENABLE_AUTO_SLEEP
|
||||
ret = nxp_bt_set_host_sleep_config();
|
||||
if (ret < 0) {
|
||||
LOG_ERR("Failed to set host sleep config");
|
||||
break;
|
||||
}
|
||||
|
||||
ret = nxp_bt_enable_controller_autosleep();
|
||||
if (ret < 0) {
|
||||
LOG_ERR("Failed to configure controller autosleep");
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
} while (false);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int bt_nxp_close(void)
|
||||
{
|
||||
int ret = 0;
|
||||
/* Reset the Controller */
|
||||
#if CONFIG_BT_HCI_HOST
|
||||
ret = bt_hci_cmd_send_sync(BT_HCI_OP_RESET, NULL, NULL);
|
||||
if (ret) {
|
||||
LOG_ERR("Failed to reset BLE controller");
|
||||
}
|
||||
k_sleep(K_SECONDS(1));
|
||||
|
||||
ret = PLATFORM_TerminateBle();
|
||||
if (ret < 0) {
|
||||
LOG_ERR("Failed to shutdown BLE controller");
|
||||
}
|
||||
#endif
|
||||
return ret;
|
||||
}
|
||||
|
||||
static const struct bt_hci_driver drv = {
|
||||
.name = "BT NXP",
|
||||
.open = bt_nxp_open,
|
||||
.close = bt_nxp_close,
|
||||
.send = bt_nxp_send,
|
||||
.bus = BT_HCI_DRIVER_BUS_IPM,
|
||||
};
|
||||
|
||||
static int bt_nxp_init(void)
|
||||
{
|
||||
int status;
|
||||
int ret = 0;
|
||||
|
||||
/* HCI Interrupt */
|
||||
IRQ_CONNECT(HCI_IRQ_N, HCI_IRQ_P, ble_hci_handler, 0, 0);
|
||||
irq_enable(HCI_IRQ_N);
|
||||
|
||||
/* Wake up done interrupt */
|
||||
IRQ_CONNECT(HCI_WAKEUP_IRQ_N, HCI_WAKEUP_IRQ_P, ble_wakeup_done_handler, 0, 0);
|
||||
irq_enable(HCI_WAKEUP_IRQ_N);
|
||||
|
||||
#if (DT_INST_PROP(0, wakeup_source)) && CONFIG_PM
|
||||
EnableDeepSleepIRQ(HCI_IRQ_N);
|
||||
#endif
|
||||
|
||||
do {
|
||||
status = PLATFORM_InitBle();
|
||||
if (status < 0) {
|
||||
LOG_ERR("BLE Controller initialization failed");
|
||||
ret = status;
|
||||
break;
|
||||
}
|
||||
|
||||
status = bt_hci_driver_register(&drv);
|
||||
if (status < 0) {
|
||||
LOG_ERR("HCI driver registration failed");
|
||||
ret = status;
|
||||
break;
|
||||
}
|
||||
} while (0);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
SYS_INIT(bt_nxp_init, POST_KERNEL, CONFIG_BT_HCI_INIT_PRIORITY);
|
2
west.yml
2
west.yml
|
@ -193,7 +193,7 @@ manifest:
|
|||
groups:
|
||||
- hal
|
||||
- name: hal_nxp
|
||||
revision: 57d844b03ea545aa702ae99d9777481709ba8aa4
|
||||
revision: 5e84f1d2173e3c5057725d76f9dad6bfabad0b5f
|
||||
path: modules/hal/nxp
|
||||
groups:
|
||||
- hal
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue