From ee03123dd1cd3c9b12c1644ef9a8a8d7392e8e02 Mon Sep 17 00:00:00 2001 From: Axel Le Bourhis Date: Fri, 25 Aug 2023 14:29:39 +0200 Subject: [PATCH] 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 Signed-off-by: Yassine El Aissaoui --- drivers/bluetooth/hci/CMakeLists.txt | 1 + drivers/bluetooth/hci/Kconfig | 16 +- drivers/bluetooth/hci/Kconfig.nxp | 24 ++ drivers/bluetooth/hci/hci_nxp.c | 417 +++++++++++++++++++++++++++ west.yml | 2 +- 5 files changed, 452 insertions(+), 8 deletions(-) create mode 100644 drivers/bluetooth/hci/Kconfig.nxp create mode 100644 drivers/bluetooth/hci/hci_nxp.c diff --git a/drivers/bluetooth/hci/CMakeLists.txt b/drivers/bluetooth/hci/CMakeLists.txt index ab1df047ce1..ca3f62f9e59 100644 --- a/drivers/bluetooth/hci/CMakeLists.txt +++ b/drivers/bluetooth/hci/CMakeLists.txt @@ -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) diff --git a/drivers/bluetooth/hci/Kconfig b/drivers/bluetooth/hci/Kconfig index 1b4bc9fdb25..7a2f79fd85a 100644 --- a/drivers/bluetooth/hci/Kconfig +++ b/drivers/bluetooth/hci/Kconfig @@ -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" diff --git a/drivers/bluetooth/hci/Kconfig.nxp b/drivers/bluetooth/hci/Kconfig.nxp new file mode 100644 index 00000000000..a5eb8648c8a --- /dev/null +++ b/drivers/bluetooth/hci/Kconfig.nxp @@ -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. diff --git a/drivers/bluetooth/hci/hci_nxp.c b/drivers/bluetooth/hci/hci_nxp.c new file mode 100644 index 00000000000..78016a794b5 --- /dev/null +++ b/drivers/bluetooth/hci/hci_nxp.c @@ -0,0 +1,417 @@ +/* + * Copyright 2023 NXP + * + * SPDX-License-Identifier: Apache-2.0 + */ + +/* -------------------------------------------------------------------------- */ +/* Includes */ +/* -------------------------------------------------------------------------- */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +/* -------------------------------------------------------------------------- */ +/* 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); diff --git a/west.yml b/west.yml index c6ff13bd6bf..177d1715856 100644 --- a/west.yml +++ b/west.yml @@ -193,7 +193,7 @@ manifest: groups: - hal - name: hal_nxp - revision: 57d844b03ea545aa702ae99d9777481709ba8aa4 + revision: 5e84f1d2173e3c5057725d76f9dad6bfabad0b5f path: modules/hal/nxp groups: - hal