From d90b7274fa8bbfa09f79660b45b550d91f7b0125 Mon Sep 17 00:00:00 2001 From: Jean-François Milants Date: Tue, 2 Feb 2021 22:09:00 +0100 Subject: Update to nimble 1.3 master branch commit 82153e744833821e20e9a8b0d61c38b2b0dbcfe1 WARNING : heartbeat task is disabled! --- .../nimble/drivers/dialog_cmac/README.md | 68 + .../nimble/drivers/dialog_cmac/include/ble/xcvr.h | 39 + .../nimble/drivers/dialog_cmac/pkg.yml | 33 + .../nimble/drivers/dialog_cmac/src/ble_hw.c | 340 ++++ .../nimble/drivers/dialog_cmac/src/ble_hw_priv.h | 29 + .../nimble/drivers/dialog_cmac/src/ble_phy.c | 1798 ++++++++++++++++++++ .../nimble/drivers/dialog_cmac/src/ble_rf.c | 747 ++++++++ .../nimble/drivers/dialog_cmac/src/ble_rf_priv.h | 38 + .../nimble/drivers/dialog_cmac/syscfg.yml | 29 + 9 files changed, 3121 insertions(+) create mode 100644 src/libs/mynewt-nimble/nimble/drivers/dialog_cmac/README.md create mode 100644 src/libs/mynewt-nimble/nimble/drivers/dialog_cmac/include/ble/xcvr.h create mode 100644 src/libs/mynewt-nimble/nimble/drivers/dialog_cmac/pkg.yml create mode 100644 src/libs/mynewt-nimble/nimble/drivers/dialog_cmac/src/ble_hw.c create mode 100644 src/libs/mynewt-nimble/nimble/drivers/dialog_cmac/src/ble_hw_priv.h create mode 100644 src/libs/mynewt-nimble/nimble/drivers/dialog_cmac/src/ble_phy.c create mode 100644 src/libs/mynewt-nimble/nimble/drivers/dialog_cmac/src/ble_rf.c create mode 100644 src/libs/mynewt-nimble/nimble/drivers/dialog_cmac/src/ble_rf_priv.h create mode 100644 src/libs/mynewt-nimble/nimble/drivers/dialog_cmac/syscfg.yml (limited to 'src/libs/mynewt-nimble/nimble/drivers/dialog_cmac') diff --git a/src/libs/mynewt-nimble/nimble/drivers/dialog_cmac/README.md b/src/libs/mynewt-nimble/nimble/drivers/dialog_cmac/README.md new file mode 100644 index 00000000..2e76e2c1 --- /dev/null +++ b/src/libs/mynewt-nimble/nimble/drivers/dialog_cmac/README.md @@ -0,0 +1,68 @@ + + +## How to run NimBLE controller on Dialog DA1469x + +Dialog DA1469x has separate Cortex-M0+ core inside CMAC hw block which can run +NimBLE controller. This means DA1469x can run full NimBLE stack: host is running +on M33 core while controller is running on M0+ core. Both communicate using +standard HCI H4 protocol exchanged over mailboxes located in shared memory. + +### Basic setup + +In order to run full NimBLE stack on DA1469x you will need two newt targets: one +for M33 (e.g. `dialog_da1469x-dk-pro` BSP) and one for M0+ (`dialog_cmac` BSP). + +Once everything is configured properly, you only need to build target for M33. +Target configured for M0+ will be build automatically and image is linked with +M33 image so everything can be flashed at once just as if there is only single +target used. + +Target for M33 should be set and configured as any other BLE application. In +order to use NimBLE controller on CMAC, set proper HCI transport via syscfg: + + BLE_HCI_TRANSPORT: dialog_cmac + +This will include proper transport, driver and add M0+ target to build process. + +For M0+, there is sample target provided in `targets/dialog_cmac` and it's used +by default unless overrided by syscfg in M33 target: + + CMAC_IMAGE_TARGET_NAME: "@apache-mynewt-nimble/targets/dialog_cmac" + +If you wish to create own target for M0+, make sure your target is set the same +way (`app`, `bsp` and `build_profile`) as sample. Also it is recommended to use +syscfg settings from sample target in new target. + +### NimBLE configuration + +Since host and controller are running on different cores, they both use separate +configuration: host configuration is in M33 target, controller configuration is +in M0+ target. There is currently no way to automatically synchronize both, so +care needs to be taken when enabling features in either of targets. + +A possible workaround is to use separate `.yml` file with all the NimBLE syscfg +values settings and include it in both targets using `$import` directive which +is supported by recent versions of `newt` tool. + +### Advanced settings + +(tbd) diff --git a/src/libs/mynewt-nimble/nimble/drivers/dialog_cmac/include/ble/xcvr.h b/src/libs/mynewt-nimble/nimble/drivers/dialog_cmac/include/ble/xcvr.h new file mode 100644 index 00000000..e4e741c5 --- /dev/null +++ b/src/libs/mynewt-nimble/nimble/drivers/dialog_cmac/include/ble/xcvr.h @@ -0,0 +1,39 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +#ifndef H_BLE_XCVR_ +#define H_BLE_XCVR_ + +#ifdef __cplusplus +extern "C" { +#endif + +#define XCVR_TX_SCHED_DELAY_USECS (250) + +/* + * Define HW whitelist size. This is the total possible whitelist size; + * not necessarily the size that will be used (may be smaller) + */ +#define BLE_HW_WHITE_LIST_SIZE (8) + +#ifdef __cplusplus +} +#endif + +#endif /* H_BLE_XCVR_ */ diff --git a/src/libs/mynewt-nimble/nimble/drivers/dialog_cmac/pkg.yml b/src/libs/mynewt-nimble/nimble/drivers/dialog_cmac/pkg.yml new file mode 100644 index 00000000..9cf63ffc --- /dev/null +++ b/src/libs/mynewt-nimble/nimble/drivers/dialog_cmac/pkg.yml @@ -0,0 +1,33 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# + +pkg.name: nimble/drivers/dialog_cmac +pkg.description: BLE driver for Dialog CMAC +pkg.author: "Apache Mynewt " +pkg.homepage: "http://mynewt.apache.org/" +pkg.keywords: + - ble + - bluetooth +pkg.deps: + - "@apache-mynewt-nimble/nimble/controller" + - "@apache-mynewt-core/crypto/tinycrypt" +pkg.apis: + - ble_driver +pkg.req_apis: + - ble_transport diff --git a/src/libs/mynewt-nimble/nimble/drivers/dialog_cmac/src/ble_hw.c b/src/libs/mynewt-nimble/nimble/drivers/dialog_cmac/src/ble_hw.c new file mode 100644 index 00000000..98c8144b --- /dev/null +++ b/src/libs/mynewt-nimble/nimble/drivers/dialog_cmac/src/ble_hw.c @@ -0,0 +1,340 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +#include +#include +#include "mcu/mcu.h" +#include "nimble/ble.h" +#include "controller/ble_hw.h" +#include "CMAC.h" +#include "cmac_driver/cmac_shared.h" +#include "mcu/mcu.h" +#include "tinycrypt/aes.h" + +static struct tc_aes_key_sched_struct g_ctx; + +int +ble_hw_rng_init(ble_rng_isr_cb_t cb, int bias) +{ + cmac_rand_set_isr_cb(cb); + return 0; +} + +int +ble_hw_rng_start(void) +{ + /* Chime the M33 in case we need random numbers generated */ + cmac_rand_start(); + CMAC->CM_EV_SET_REG = CMAC_CM_EV_SET_REG_EV1C_CMAC2SYS_IRQ_SET_Msk; + return 0; +} + +int +ble_hw_rng_stop(void) +{ + cmac_rand_stop(); + return 0; +} + +#define BLE_HW_RESOLV_LIST_SIZE (MYNEWT_VAL(BLE_LL_RESOLV_LIST_SIZE)) + +struct ble_hw_resolv_irk { + uint32_t key[4]; +}; + +struct ble_hw_resolv_list { + uint8_t count; + struct ble_hw_resolv_irk irk[BLE_HW_RESOLV_LIST_SIZE]; +}; + +struct ble_hw_resolv_proc { + uint32_t hash; + uint8_t f_configured; + uint8_t f_active; + uint8_t f_match; + uint8_t f_done; + struct ble_hw_resolv_irk *irk; + struct ble_hw_resolv_irk *irk_end; + uint32_t crypto_prand_in[4]; + uint32_t crypto_e_out[4]; +}; + +static struct ble_hw_resolv_list g_ble_hw_resolv_list; +static struct ble_hw_resolv_proc g_ble_hw_resolv_proc; + +int +ble_hw_get_public_addr(ble_addr_t *addr) +{ + return -1; +} + +int +ble_hw_get_static_addr(ble_addr_t *addr) +{ + return -1; +} + +void +ble_hw_whitelist_clear(void) +{ +} + +int +ble_hw_whitelist_add(const uint8_t *addr, uint8_t addr_type) +{ + return 0; +} + +void +ble_hw_whitelist_rmv(const uint8_t *addr, uint8_t addr_type) +{ +} + +uint8_t +ble_hw_whitelist_size(void) +{ + return 0; +} + +void +ble_hw_whitelist_enable(void) +{ +} + + +void +ble_hw_whitelist_disable(void) +{ +} + +int +ble_hw_whitelist_match(void) +{ + return 0; +} + +int +ble_hw_encrypt_block(struct ble_encryption_block *ecb) +{ + uint32_t in_addr; + uint32_t out_addr; + + /* + * The following code bears some explanation. This function is called by + * the LL task to encrypt blocks and calculate session keys. Address + * resolution also calls this function. Furthermore, during connections, + * the M0 crypto accelerator is used but this function is not called when + * using it. During the entire connection event, the M0 crypto block cannot + * be used as the crypto state (some of it) needs to remain un-changed. + * Note that this is also true when address resolution is enabled: the + * HW crypto block is set up and cannot be modified. + * + * Rather than attempt to share the M0 crypto block between the various + * controller features which require it, we decided to use software to + * perform the encryption task for anything being done at the link-layer + * (outside of an ISR). If this function is called inside an ISR, and it + * is when resolving addresses, the crypto accelerator is not being used + * by a connection event. Thus, we check to see if we are inside of an ISR. + * If so, we use the M0 crypto block. If outside of an ISR, we use the M33 + */ + if (!os_arch_in_isr()) { + tc_aes128_set_encrypt_key(&g_ctx, ecb->key); + tc_aes_encrypt(ecb->cipher_text, ecb->plain_text, &g_ctx); + return 0; + } + + /* Need to retain state of in/out pointers */ + in_addr = CMAC->CM_CRYPTO_IN_ADR2_REG; + out_addr = CMAC->CM_CRYPTO_OUT_ADR_REG; + + while (CMAC->CM_CRYPTO_STAT_REG & CMAC_CM_CRYPTO_STAT_REG_CM_CRYPTO_BUSY_Msk); + + /* RECB, memory in/out, encryption */ + CMAC->CM_CRYPTO_CTRL_REG = CMAC_CM_CRYPTO_CTRL_REG_CM_CRYPTO_ECB_ENC_EN_Msk | + CMAC_CM_CRYPTO_CTRL_REG_CM_CRYPTO_IN_SEL_Msk | + CMAC_CM_CRYPTO_CTRL_REG_CM_CRYPTO_OUT_SEL_Msk | + CMAC_CM_CRYPTO_CTRL_REG_CM_CRYPTO_ENC_DECN_Msk; + + CMAC->CM_CRYPTO_KEY_31_0_REG = get_le32(&ecb->key[0]); + CMAC->CM_CRYPTO_KEY_63_32_REG = get_le32(&ecb->key[4]); + CMAC->CM_CRYPTO_KEY_95_64_REG = get_le32(&ecb->key[8]); + CMAC->CM_CRYPTO_KEY_127_96_REG = get_le32(&ecb->key[12]); + CMAC->CM_CRYPTO_IN_ADR2_REG = (uint32_t)ecb->plain_text; + CMAC->CM_CRYPTO_OUT_ADR_REG = (uint32_t)ecb->cipher_text; + + CMAC->CM_EXC_STAT_REG = CMAC_CM_EXC_STAT_REG_EXC_CRYPTO_Msk; + CMAC->CM_EV_SET_REG = CMAC_CM_EV_SET_REG_EV_CRYPTO_START_Msk; + while (!(CMAC->CM_EXC_STAT_REG & CMAC_CM_EXC_STAT_REG_EXC_CRYPTO_Msk)); + CMAC->CM_EXC_STAT_REG = CMAC_CM_EXC_STAT_REG_EXC_CRYPTO_Msk; + + CMAC->CM_CRYPTO_IN_ADR2_REG = in_addr; + CMAC->CM_CRYPTO_OUT_ADR_REG = out_addr; + + return 0; +} + +void +ble_hw_resolv_list_clear(void) +{ + g_ble_hw_resolv_list.count = 0; +} + +int +ble_hw_resolv_list_add(uint8_t *irk) +{ + struct ble_hw_resolv_irk *e; + + if (g_ble_hw_resolv_list.count == BLE_HW_RESOLV_LIST_SIZE) { + return BLE_ERR_MEM_CAPACITY; + } + + e = &g_ble_hw_resolv_list.irk[g_ble_hw_resolv_list.count]; + /* Prepare key here so we do not need to do it during resolving */ + e->key[0] = get_le32(&irk[0]); + e->key[1] = get_le32(&irk[4]); + e->key[2] = get_le32(&irk[8]); + e->key[3] = get_le32(&irk[12]); + + g_ble_hw_resolv_list.count++; + + return BLE_ERR_SUCCESS; +} + +void +ble_hw_resolv_list_rmv(int index) +{ + struct ble_hw_resolv_irk *e; + + if (index < g_ble_hw_resolv_list.count) { + g_ble_hw_resolv_list.count--; + + e = &g_ble_hw_resolv_list.irk[index]; + memmove(e, e + 1, (g_ble_hw_resolv_list.count - index) * sizeof(e->key)); + } +} + +uint8_t +ble_hw_resolv_list_size(void) +{ + return BLE_HW_RESOLV_LIST_SIZE; +} + +int +ble_hw_resolv_list_match(void) +{ + return g_ble_hw_resolv_proc.f_match ? + g_ble_hw_resolv_proc.irk - g_ble_hw_resolv_list.irk : -1; +} + +static void +ble_hw_resolv_proc_next(void) +{ + void *src = &g_ble_hw_resolv_proc.irk->key; + + if (g_ble_hw_resolv_proc.irk == g_ble_hw_resolv_proc.irk_end) { + g_ble_hw_resolv_proc.f_done = 1; + g_ble_hw_resolv_proc.f_active = 0; + } else { + __asm__ volatile (".syntax unified \n" + " ldm %[ptr]!, {r1, r2, r3, r4} \n" + " ldr %[ptr], =%[reg] \n" + " stm %[ptr]!, {r1, r2, r3, r4} \n" + : [ptr] "+l" (src) + : [reg] "i" (&CMAC->CM_CRYPTO_KEY_31_0_REG) + : "r1", "r2", "r3", "r4", "memory"); + + CMAC->CM_EV_SET_REG = CMAC_CM_EV_SET_REG_EV_CRYPTO_START_Msk; + } +} + +void +ble_hw_resolv_proc_enable(void) +{ + assert(!g_ble_hw_resolv_proc.f_active); + + CMAC->CM_CRYPTO_CTRL_REG = CMAC_CM_CRYPTO_CTRL_REG_CM_CRYPTO_SW_REQ_ABORT_Msk; + + CMAC->CM_CRYPTO_CTRL_REG = CMAC_CM_CRYPTO_CTRL_REG_CM_CRYPTO_ECB_ENC_EN_Msk | + CMAC_CM_CRYPTO_CTRL_REG_CM_CRYPTO_IN_SEL_Msk | + CMAC_CM_CRYPTO_CTRL_REG_CM_CRYPTO_OUT_SEL_Msk | + CMAC_CM_CRYPTO_CTRL_REG_CM_CRYPTO_ENC_DECN_Msk; + + CMAC->CM_CRYPTO_IN_ADR2_REG = (uint32_t)g_ble_hw_resolv_proc.crypto_prand_in; + CMAC->CM_CRYPTO_OUT_ADR_REG = (uint32_t)g_ble_hw_resolv_proc.crypto_e_out; + + g_ble_hw_resolv_proc.irk = g_ble_hw_resolv_list.irk; + g_ble_hw_resolv_proc.irk_end = g_ble_hw_resolv_list.irk + + g_ble_hw_resolv_list.count; + g_ble_hw_resolv_proc.f_configured = 1; + g_ble_hw_resolv_proc.f_active = 0; + + /* + * It would be better to enable IRQ in ble_hw_resolv_proc_start, but this + * would introduce a bit of latency when starting resolving procedure and + * we need to save every us possible there in order to be able to resolve + * RPA on time. + */ + NVIC_ClearPendingIRQ(CRYPTO_IRQn); + NVIC_EnableIRQ(CRYPTO_IRQn); +} + +void +ble_hw_resolv_proc_disable(void) +{ + g_ble_hw_resolv_proc.f_configured = 0; + g_ble_hw_resolv_proc.f_active = 0; + g_ble_hw_resolv_proc.f_match = 0; + g_ble_hw_resolv_proc.f_done = 1; + + NVIC_DisableIRQ(CRYPTO_IRQn); +} + +void +ble_hw_resolv_proc_start(const uint8_t *addr) +{ + assert(g_ble_hw_resolv_proc.f_configured); + + /* crypto_prand_in is already zeroed so prand is properly padded */ + g_ble_hw_resolv_proc.crypto_prand_in[3] = get_be24(&addr[3]) << 8; + g_ble_hw_resolv_proc.hash = get_be24(&addr[0]); + + g_ble_hw_resolv_proc.f_match = 0; + g_ble_hw_resolv_proc.f_done = 0; + g_ble_hw_resolv_proc.f_active = 1; + + ble_hw_resolv_proc_next(); +} + +void +CRYPTO_IRQHandler(void) +{ + uint32_t hash; + + CMAC->CM_EXC_STAT_REG = CMAC_CM_EXC_STAT_REG_EXC_CRYPTO_Msk; + + hash = g_ble_hw_resolv_proc.crypto_e_out[3] >> 8; + if (g_ble_hw_resolv_proc.hash == hash) { + g_ble_hw_resolv_proc.f_active = 0; + g_ble_hw_resolv_proc.f_match = 1; + g_ble_hw_resolv_proc.f_done = 1; + } else { + g_ble_hw_resolv_proc.irk++; + ble_hw_resolv_proc_next(); + } +} diff --git a/src/libs/mynewt-nimble/nimble/drivers/dialog_cmac/src/ble_hw_priv.h b/src/libs/mynewt-nimble/nimble/drivers/dialog_cmac/src/ble_hw_priv.h new file mode 100644 index 00000000..627994ff --- /dev/null +++ b/src/libs/mynewt-nimble/nimble/drivers/dialog_cmac/src/ble_hw_priv.h @@ -0,0 +1,29 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +#ifndef _BLE_HW_PRIV_H_ +#define _BLE_HW_PRIV_H_ + +#include + +void ble_hw_resolv_proc_enable(void); +void ble_hw_resolv_proc_disable(void); +void ble_hw_resolv_proc_start(const uint8_t *addr); + +#endif /* _BLE_HW_PRIV_H_ */ diff --git a/src/libs/mynewt-nimble/nimble/drivers/dialog_cmac/src/ble_phy.c b/src/libs/mynewt-nimble/nimble/drivers/dialog_cmac/src/ble_phy.c new file mode 100644 index 00000000..d5767c56 --- /dev/null +++ b/src/libs/mynewt-nimble/nimble/drivers/dialog_cmac/src/ble_phy.c @@ -0,0 +1,1798 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +#include "syscfg/syscfg.h" +#if !MYNEWT_VAL(BLE_PHY_DEBUG_DSER) +#define MCU_DIAG_SER_DISABLE +#endif + +#include +#include +#include +#include "nimble/ble.h" +#include "mcu/mcu.h" +#include "mcu/cmac_timer.h" +#include "cmac_driver/cmac_shared.h" +#include "controller/ble_phy.h" +#include "controller/ble_ll.h" +#include "stats/stats.h" +#include "CMAC.h" +#include "ble_hw_priv.h" +#include "ble_rf_priv.h" + +#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LE_CODED_PHY) +#error LE Coded PHY cannot be enabled on DA1469x +#endif + +/* Statistics */ +STATS_SECT_START(ble_phy_stats) + STATS_SECT_ENTRY(phy_isrs) + STATS_SECT_ENTRY(tx_good) + STATS_SECT_ENTRY(tx_fail) + STATS_SECT_ENTRY(tx_late) + STATS_SECT_ENTRY(tx_bytes) + STATS_SECT_ENTRY(rx_starts) + STATS_SECT_ENTRY(rx_aborts) + STATS_SECT_ENTRY(rx_valid) + STATS_SECT_ENTRY(rx_crc_err) + STATS_SECT_ENTRY(rx_late) + STATS_SECT_ENTRY(radio_state_errs) + STATS_SECT_ENTRY(rx_hw_err) + STATS_SECT_ENTRY(tx_hw_err) +STATS_SECT_END +STATS_SECT_DECL(ble_phy_stats) ble_phy_stats; + +STATS_NAME_START(ble_phy_stats) + STATS_NAME(ble_phy_stats, phy_isrs) + STATS_NAME(ble_phy_stats, tx_good) + STATS_NAME(ble_phy_stats, tx_fail) + STATS_NAME(ble_phy_stats, tx_late) + STATS_NAME(ble_phy_stats, tx_bytes) + STATS_NAME(ble_phy_stats, rx_starts) + STATS_NAME(ble_phy_stats, rx_aborts) + STATS_NAME(ble_phy_stats, rx_valid) + STATS_NAME(ble_phy_stats, rx_crc_err) + STATS_NAME(ble_phy_stats, rx_late) + STATS_NAME(ble_phy_stats, radio_state_errs) + STATS_NAME(ble_phy_stats, rx_hw_err) + STATS_NAME(ble_phy_stats, tx_hw_err) +STATS_NAME_END(ble_phy_stats) + +#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LE_CODED_PHY) +#error LE Coded PHY is not supported +#endif + +/* An easy way to get and set bit field value in CMAC registers */ +#define CMAC_SETREGF(_reg, _field, _val) \ + CMAC->_reg = (CMAC->_reg & ~(CMAC_ ## _reg ## _ ## _field ## _Msk)) | \ + ((_val) << (CMAC_ ## _reg ## _ ## _field ## _Pos)); +#define CMAC_GETREGF(_reg, _field) \ + (CMAC->_reg & (CMAC_ ## _reg ## _ ## _field ## _Msk)) >> \ + (CMAC_ ## _reg ## _ ## _field ## _Pos) + +/* Definitions for fields queue */ +#define FIELD_DATA_REG_DMA_TX(_offset, _len) \ + ((uint32_t)&g_ble_phy_tx_buf[(_offset)] & 0x3ffff) | ((_len) << 20) +#define FIELD_DATA_REG_DMA_RX(_offset, _len) \ + ((uint32_t)&g_ble_phy_rx_buf[(_offset)] & 0x3ffff) | ((_len) << 20) + +#if MYNEWT_VAL(BLE_LL_DTM) +#define PHY_WHITENING (g_ble_phy_data.phy_whitening) +#else +#define PHY_WHITENING (1) +#endif + +#define FIELD_CTRL_REG_TX_PREAMBLE \ + (0 << CMAC_CM_FIELD_PUSH_CTRL_REG_FIELD_XL_CRC_Pos) | \ + (0 << CMAC_CM_FIELD_PUSH_CTRL_REG_FIELD_XL_WHITENING_Pos) | \ + (0 << CMAC_CM_FIELD_PUSH_CTRL_REG_FIELD_TX_DATA_SRC_Pos) | \ + (g_ble_phy_data.phy_mode_evpsym << \ + CMAC_CM_FIELD_PUSH_CTRL_REG_FIELD_EVPSYMBOL_LUT_Pos) | \ + (1 << CMAC_CM_FIELD_PUSH_CTRL_REG_FIELD_VALID_Pos) | \ + (g_ble_phy_data.phy_mode_pre_len << \ + CMAC_CM_FIELD_PUSH_CTRL_REG_FIELD_SIZE_M1_Pos) +#define FIELD_CTRL_REG_TX_ACCESS_ADDR \ + (1 << CMAC_CM_FIELD_PUSH_CTRL_REG_FIELD_EXC_ON_EXP_Pos) | \ + (0 << CMAC_CM_FIELD_PUSH_CTRL_REG_FIELD_XL_CRC_Pos) | \ + (0 << CMAC_CM_FIELD_PUSH_CTRL_REG_FIELD_XL_WHITENING_Pos) | \ + (0 << CMAC_CM_FIELD_PUSH_CTRL_REG_FIELD_TX_DATA_SRC_Pos) | \ + (g_ble_phy_data.phy_mode_evpsym << \ + CMAC_CM_FIELD_PUSH_CTRL_REG_FIELD_EVPSYMBOL_LUT_Pos) | \ + (1 << CMAC_CM_FIELD_PUSH_CTRL_REG_FIELD_VALID_Pos) | \ + (31 << CMAC_CM_FIELD_PUSH_CTRL_REG_FIELD_SIZE_M1_Pos) +#define FIELD_CTRL_REG_TX_PAYLOAD \ + (1 << CMAC_CM_FIELD_PUSH_CTRL_REG_FIELD_XL_CRC_Pos) | \ + (PHY_WHITENING << \ + CMAC_CM_FIELD_PUSH_CTRL_REG_FIELD_XL_WHITENING_Pos) | \ + (1 << CMAC_CM_FIELD_PUSH_CTRL_REG_FIELD_XL_DMA_MEM_Pos) | \ + (g_ble_phy_data.phy_mode_evpsym << \ + CMAC_CM_FIELD_PUSH_CTRL_REG_FIELD_EVPSYMBOL_LUT_Pos) | \ + (1 << CMAC_CM_FIELD_PUSH_CTRL_REG_FIELD_VALID_Pos); +#define FIELD_CTRL_REG_TX_ENC_PAYLOAD \ + (1 << CMAC_CM_FIELD_PUSH_CTRL_REG_FIELD_XL_CRC_Pos) | \ + (1 << CMAC_CM_FIELD_PUSH_CTRL_REG_FIELD_XL_WHITENING_Pos) | \ + (1 << CMAC_CM_FIELD_PUSH_CTRL_REG_FIELD_XL_DMA_CRYPTO_Pos) | \ + (g_ble_phy_data.phy_mode_evpsym << \ + CMAC_CM_FIELD_PUSH_CTRL_REG_FIELD_EVPSYMBOL_LUT_Pos) | \ + (1 << CMAC_CM_FIELD_PUSH_CTRL_REG_FIELD_VALID_Pos) +#define FIELD_CTRL_REG_TX_MIC \ + (1 << CMAC_CM_FIELD_PUSH_CTRL_REG_FIELD_XL_CRC_Pos) | \ + (1 << CMAC_CM_FIELD_PUSH_CTRL_REG_FIELD_XL_WHITENING_Pos) | \ + (1 << CMAC_CM_FIELD_PUSH_CTRL_REG_FIELD_XL_DMA_CRYPTO_Pos) | \ + (g_ble_phy_data.phy_mode_evpsym << \ + CMAC_CM_FIELD_PUSH_CTRL_REG_FIELD_EVPSYMBOL_LUT_Pos) | \ + (1 << CMAC_CM_FIELD_PUSH_CTRL_REG_FIELD_VALID_Pos) +#define FIELD_CTRL_REG_TX_CRC \ + (0 << CMAC_CM_FIELD_PUSH_CTRL_REG_FIELD_XL_CRC_Pos) | \ + (PHY_WHITENING << \ + CMAC_CM_FIELD_PUSH_CTRL_REG_FIELD_XL_WHITENING_Pos) | \ + (1 << CMAC_CM_FIELD_PUSH_CTRL_REG_FIELD_TX_DATA_SRC_Pos) | \ + (1 << CMAC_CM_FIELD_PUSH_CTRL_REG_FIELD_LAST_Pos) | \ + (1 << CMAC_CM_FIELD_PUSH_CTRL_REG_FIELD_MSB_FIRST_Pos) | \ + (g_ble_phy_data.phy_mode_evpsym << \ + CMAC_CM_FIELD_PUSH_CTRL_REG_FIELD_EVPSYMBOL_LUT_Pos) | \ + (1 << CMAC_CM_FIELD_PUSH_CTRL_REG_FIELD_VALID_Pos) | \ + (23 << CMAC_CM_FIELD_PUSH_CTRL_REG_FIELD_SIZE_M1_Pos) +#define FIELD_CTRL_REG_RX_ACCESS_ADDR \ + (0 << CMAC_CM_FIELD_PUSH_CTRL_REG_FIELD_XL_CRC_Pos) | \ + (0 << CMAC_CM_FIELD_PUSH_CTRL_REG_FIELD_XL_WHITENING_Pos) | \ + (0 << CMAC_CM_FIELD_PUSH_CTRL_REG_FIELD_TX_DATA_SRC_Pos) | \ + (g_ble_phy_data.phy_mode_evpsym << \ + CMAC_CM_FIELD_PUSH_CTRL_REG_FIELD_EVPSYMBOL_LUT_Pos) | \ + (1 << CMAC_CM_FIELD_PUSH_CTRL_REG_FIELD_XL_CORR_Pos) | \ + (1 << CMAC_CM_FIELD_PUSH_CTRL_REG_FIELD_VALID_Pos) | \ + (31 << CMAC_CM_FIELD_PUSH_CTRL_REG_FIELD_SIZE_M1_Pos) +#define FIELD_CTRL_REG_RX_HEADER \ + (1 << CMAC_CM_FIELD_PUSH_CTRL_REG_FIELD_EXC_ON_EXP_Pos) | \ + (1 << CMAC_CM_FIELD_PUSH_CTRL_REG_FIELD_XL_CRC_Pos) | \ + (PHY_WHITENING << \ + CMAC_CM_FIELD_PUSH_CTRL_REG_FIELD_XL_WHITENING_Pos) | \ + (1 << CMAC_CM_FIELD_PUSH_CTRL_REG_FIELD_XL_DMA_MEM_Pos) | \ + (g_ble_phy_data.phy_mode_evpsym << \ + CMAC_CM_FIELD_PUSH_CTRL_REG_FIELD_EVPSYMBOL_LUT_Pos) | \ + (1 << CMAC_CM_FIELD_PUSH_CTRL_REG_FIELD_VALID_Pos) +#define FIELD_CTRL_REG_RX_CRC \ + (1 << CMAC_CM_FIELD_PUSH_CTRL_REG_FIELD_XL_CRC_Pos) | \ + (PHY_WHITENING << \ + CMAC_CM_FIELD_PUSH_CTRL_REG_FIELD_XL_WHITENING_Pos) | \ + (1 << CMAC_CM_FIELD_PUSH_CTRL_REG_FIELD_LAST_Pos) | \ + (1 << CMAC_CM_FIELD_PUSH_CTRL_REG_FIELD_MSB_FIRST_Pos) | \ + (g_ble_phy_data.phy_mode_evpsym << \ + CMAC_CM_FIELD_PUSH_CTRL_REG_FIELD_EVPSYMBOL_LUT_Pos) | \ + (1 << CMAC_CM_FIELD_PUSH_CTRL_REG_FIELD_VALID_Pos) | \ + (1 << CMAC_CM_FIELD_PUSH_CTRL_REG_FIELD_XL_DMA_MEM_Pos) +#define FIELD_CTRL_REG_RX_PAYLOAD \ + (1 << CMAC_CM_FIELD_PUSH_CTRL_REG_FIELD_XL_CRC_Pos) | \ + (PHY_WHITENING << \ + CMAC_CM_FIELD_PUSH_CTRL_REG_FIELD_XL_WHITENING_Pos) | \ + (1 << CMAC_CM_FIELD_PUSH_CTRL_REG_FIELD_XL_DMA_MEM_Pos) | \ + (g_ble_phy_data.phy_mode_evpsym << \ + CMAC_CM_FIELD_PUSH_CTRL_REG_FIELD_EVPSYMBOL_LUT_Pos) | \ + (1 << CMAC_CM_FIELD_PUSH_CTRL_REG_FIELD_VALID_Pos) +#define FIELD_CTRL_REG_RX_PAYLOAD_WITH_EXC \ + FIELD_CTRL_REG_RX_PAYLOAD | \ + (1 << CMAC_CM_FIELD_PUSH_CTRL_REG_FIELD_EXC_ON_EXP_Pos) +#define FIELD_CTRL_REG_RX_ENC_PAYLOAD \ + (1 << CMAC_CM_FIELD_PUSH_CTRL_REG_FIELD_XL_CRC_Pos) | \ + (1 << CMAC_CM_FIELD_PUSH_CTRL_REG_FIELD_XL_WHITENING_Pos) | \ + (1 << CMAC_CM_FIELD_PUSH_CTRL_REG_FIELD_XL_DMA_CRYPTO_Pos) | \ + (g_ble_phy_data.phy_mode_evpsym << \ + CMAC_CM_FIELD_PUSH_CTRL_REG_FIELD_EVPSYMBOL_LUT_Pos) | \ + (1 << CMAC_CM_FIELD_PUSH_CTRL_REG_FIELD_VALID_Pos) + +/* RF power up/down delays */ +#define PHY_DELAY_POWER_DN_RX (23) +#define PHY_DELAY_POWER_DN_TX (23) +#define PHY_DELAY_POWER_UP_RX (90) +#define PHY_DELAY_POWER_UP_TX (75) +#define PHY_DELAY_TX_RX ((PHY_DELAY_POWER_DN_TX) + (PHY_DELAY_POWER_UP_RX)) +#define PHY_DELAY_RX_TX ((PHY_DELAY_POWER_DN_RX) + (PHY_DELAY_POWER_UP_TX)) + +/* RF TX/RX path delays */ +static const uint8_t g_ble_phy_path_delay_tx[2] = { + 4, /* 1M = 3.8us */ + 0, /* 2M = 0.2us */ +}; +static const uint8_t g_ble_phy_path_delay_rx[2] = { + 2, /* 1M = 2.2us */ + 1, /* 2M = 0.8us */ +}; + +/* Measured and pre-calculated offsets for transitions */ +static const uint8_t g_ble_phy_frame_offset_txrx[4] = { + ((BLE_LL_IFS) - (PHY_DELAY_TX_RX) + (4)), /* 2M/1M */ + ((BLE_LL_IFS) - (PHY_DELAY_TX_RX) + (5)), /* 1M/1M */ + ((BLE_LL_IFS) - (PHY_DELAY_TX_RX) + (4)), /* 2M/2M */ + ((BLE_LL_IFS) - (PHY_DELAY_TX_RX) + (5)), /* 1M/2M */ +}; +static const uint8_t g_ble_phy_frame_offset_rxtx[4] = { + ((BLE_LL_IFS) - (PHY_DELAY_RX_TX) - (5)), /* 2M/1M */ + ((BLE_LL_IFS) - (PHY_DELAY_RX_TX) - (6)), /* 1M/1M */ + ((BLE_LL_IFS) - (PHY_DELAY_RX_TX) - (3)), /* 2M/2M */ + ((BLE_LL_IFS) - (PHY_DELAY_RX_TX) - (5)), /* 1M/2M */ +}; + +/* packet start offsets (in usecs) */ +static const uint16_t g_ble_phy_mode_pkt_start_off[BLE_PHY_NUM_MODE] = { 376, 40, 24, 376 }; + +struct ble_phy_data { + uint8_t phy_state; /* Current state */ + uint8_t channel; /* Current PHY channel */ + uint8_t phy_mode_cur; /* Current PHY mode */ + uint8_t phy_mode_tx; /* TX PHY mode */ + uint8_t phy_mode_rx; /* RX PHY mode */ + uint8_t phy_mode_pre_len; /* Preamble length - 1 */ + uint8_t phy_mode_evpsym; /* EVPSYMBOL_LUT value for fields */ + uint8_t end_transition; /* Scheduled transition */ + uint8_t path_delay_tx; + uint8_t path_delay_rx; + uint8_t frame_offset_txrx; + uint8_t frame_offset_rxtx; + uint8_t phy_rx_started; + uint8_t phy_encrypted; + uint8_t phy_privacy; +#if MYNEWT_VAL(BLE_LL_DTM) + uint8_t phy_whitening; /* Whitening state (disabled for DTM) */ +#endif + uint32_t access_addr; /* Current access address */ + uint32_t crc_init; + uint32_t llt_at_cputime; + uint32_t cputime_at_llt; + uint64_t start_llt; + struct ble_mbuf_hdr rxhdr; + ble_phy_tx_end_func txend_cb; + void *txend_arg; +}; + +static struct ble_phy_data g_ble_phy_data; + +#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LE_ENCRYPTION) +/* Encryption related variables */ +struct ble_phy_encrypt_obj { + uint8_t key[16]; + uint8_t b0[16]; + uint8_t b1[16]; + uint8_t ai[16]; +}; + +struct ble_phy_encrypt_obj g_ble_phy_encrypt_data; + +static void ble_phy_tx_enc_start(void); +static void ble_phy_rx_enc_start(uint8_t len); +#endif + +#define SW_MAC_EXC_NONE (0) +#define SW_MAC_EXC_LL_RX_END (1) +#define SW_MAC_EXC_TXEND_CB (2) +#define SW_MAC_EXC_LL_RX_START (3) +#define SW_MAC_EXC_WFR_TIMER_EXP (4) + +static volatile uint8_t g_sw_mac_exc; + +/* Channel index to RF channel mapping */ +static const uint8_t g_ble_phy_chan_to_rf[BLE_PHY_NUM_CHANS] = { + 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, /* 0-9 */ + 11, 13, 14, 15, 16, 17, 18, 19, 20, 21, /* 10-19 */ + 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, /* 20-29 */ + 32, 33, 34, 35, 36, 37, 38, 0, 12, 39, /* 30-39 */ +}; + +__attribute__((aligned(4))) +static uint8_t g_ble_phy_tx_buf[BLE_PHY_MAX_PDU_LEN + 3]; +__attribute__((aligned(4))) +static uint8_t g_ble_phy_rx_buf[BLE_PHY_MAX_PDU_LEN + 3]; + +static void ble_phy_irq_field_tx(void); +static void ble_phy_irq_field_rx(void); +static void ble_phy_irq_frame_tx(void); +static void ble_phy_irq_frame_rx(void); +static bool ble_phy_rx_start_isr(void); +static void ble_phy_rx_setup_fields(void); +static void ble_phy_rx_setup_xcvr(void); +static void ble_phy_mode_apply(uint8_t phy_mode); + +void +FIELD_IRQHandler(void) +{ + MCU_DIAG_SER('E'); + + switch (g_ble_phy_data.phy_state) { + case BLE_PHY_STATE_TX: + ble_phy_irq_field_tx(); + break; + case BLE_PHY_STATE_RX: + ble_phy_irq_field_rx(); + break; + default: + STATS_INC(ble_phy_stats, radio_state_errs); + CMAC->CM_EXC_STAT_REG = 0xfffffffe; + break; + } + + MCU_DIAG_SER('e'); +} + +void +CALLBACK_IRQHandler(void) +{ + MCU_DIAG_SER('C'); + + /* XXX: clear these for now. */ + (void)CMAC->CM_BS_SMPL_D_REG; + + /* Clear IRQ*/ + CMAC->CM_EV_SET_REG = CMAC_CM_EV_SET_REG_EV1C_CALLBACK_VALID_CLR_Msk; + CMAC->CM_EXC_STAT_REG = CMAC_CM_EXC_STAT_REG_EXC_FIELD_ON_THR_EXP_Msk; + + /* + * Program next frame for transition to TX. CM_EV_LINKUP_REG register to + * enable actual transition can be set later, we just need to make sure 2nd + * frame is already set before current frame is finished - this guarantees + * that frame for transition will be moved to 1st frame once current frame + * is popped off the queue. + */ + CMAC->CM_FRAME_2_REG = CMAC_CM_FRAME_2_REG_FRAME_VALID_Msk | + CMAC_CM_FRAME_2_REG_FRAME_TX_Msk | + CMAC_CM_FRAME_2_REG_FRAME_EXC_ON_BS_START_Msk | + ((g_ble_phy_data.frame_offset_rxtx) << + CMAC_CM_FRAME_2_REG_FRAME_START_OFFSET_Pos); + + /* + * We just got an access address match so do this as early as possible + * to save time in the field rx isr. + */ + ble_phy_rx_start_isr(); + + MCU_DIAG_SER('c'); +} + +void +FRAME_IRQHandler(void) +{ + MCU_DIAG_SER('F'); + + switch (g_ble_phy_data.phy_state) { + case BLE_PHY_STATE_TX: + ble_phy_irq_frame_tx(); + break; + case BLE_PHY_STATE_RX: + ble_phy_irq_frame_rx(); + break; + default: + STATS_INC(ble_phy_stats, radio_state_errs); + CMAC->CM_EXC_STAT_REG = 0xfffffffe; + break; + } + + MCU_DIAG_SER('f'); +} + +void +SW_MAC_IRQHandler(void) +{ + uint8_t exc; + int rc; + + MCU_DIAG_SER('S'); + + CMAC->CM_EXC_STAT_REG = CMAC_CM_EXC_STAT_REG_EXC_SW_MAC_Msk; + assert(g_sw_mac_exc); + + exc = g_sw_mac_exc; + g_sw_mac_exc = 0; + + MCU_DIAG_SER('0' + exc); + + /* Next SW_MAC handover can now be queued */ + os_arch_cmac_bs_ctrl_irq_unblock(); + + switch (exc) { + case SW_MAC_EXC_TXEND_CB: + assert(g_ble_phy_data.txend_cb); + g_ble_phy_data.txend_cb(g_ble_phy_data.txend_arg); + break; + case SW_MAC_EXC_WFR_TIMER_EXP: + ble_ll_wfr_timer_exp(NULL); + break; + case SW_MAC_EXC_LL_RX_START: + /* Call Link Layer receive start function */ + rc = ble_ll_rx_start(&g_ble_phy_rx_buf[0], g_ble_phy_data.channel, + &g_ble_phy_data.rxhdr); + if (rc == 0) { + /* Set rx started flag and enable rx end ISR */ + g_ble_phy_data.phy_rx_started = 1; + + /* No transition */ + CMAC->CM_EV_LINKUP_REG = CMAC_CM_EV_LINKUP_REG_LU_PHY_TO_IDLE_2_NONE_Msk | + CMAC_CM_EV_LINKUP_REG_LU_FRAME_START_2_NONE_Msk; + } else if (rc > 0) { + g_ble_phy_data.phy_rx_started = 1; + + /* Setup transition */ + CMAC->CM_EV_LINKUP_REG = CMAC_CM_EV_LINKUP_REG_LU_PHY_TO_IDLE_2_EXC_Msk | + CMAC_CM_EV_LINKUP_REG_LU_FRAME_START_2_PHY_TO_IDLE_Msk; + } else { + /* Disable PHY */ + ble_phy_disable(); + STATS_INC(ble_phy_stats, rx_aborts); + } + break; + case SW_MAC_EXC_LL_RX_END: + /* Call LL end processing */ + rc = ble_ll_rx_end(&g_ble_phy_rx_buf[0], &g_ble_phy_data.rxhdr); + if (rc < 0) { + ble_phy_disable(); + } + break; + default: + assert(0); + break; + } + + MCU_DIAG_SER('s'); +} + +static inline uint32_t +ble_phy_convert_and_record_start_time(uint32_t cputime, uint8_t rem_usecs) +{ + uint64_t ll_val; + + ll_val = cmac_timer_convert_hal2llt(cputime); + + /* + * Since we just converted cputime to the LL timer, record both these + * values as they will be used to calculate packet reception start time. + */ + g_ble_phy_data.cputime_at_llt = cputime; + g_ble_phy_data.llt_at_cputime = ll_val; + g_ble_phy_data.start_llt = ll_val + rem_usecs; + + return ll_val; +} + +static inline void +ble_phy_sw_mac_handover(uint8_t exc) +{ + assert(!g_sw_mac_exc); + + g_sw_mac_exc = exc; + + CMAC->CM_EV_SET_REG = CMAC_CM_EV_SET_REG_EV1C_SW_MAC_Msk; + + /* + * We want SW_MAC to be fired just after BS_CTRL interrupt so we block + * BS_CTRL temporarily and SW_MAC is next in order of interrupts priority. + */ + os_arch_cmac_bs_ctrl_irq_block(); +} + +static void +ble_phy_rx_end_isr(void) +{ + struct ble_mbuf_hdr *ble_hdr; + + /* XXX just clear captured timer for now. Handle rx end time */ + (void)CMAC->CM_TS1_REG; + + /* Set RSSI and CRC status flag in header */ + ble_hdr = &g_ble_phy_data.rxhdr; + + /* Count PHY crc errors and valid packets */ + if (CMAC->CM_CRC_REG != 0) { + STATS_INC(ble_phy_stats, rx_crc_err); + } else { + STATS_INC(ble_phy_stats, rx_valid); + ble_hdr->rxinfo.flags |= BLE_MBUF_HDR_F_CRC_OK; +#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LE_ENCRYPTION) + if (g_ble_phy_data.phy_encrypted) { + /* Only set MIC failure flag if frame is not zero length */ + if (g_ble_phy_rx_buf[1] != 0) { + if (CMAC->CM_CRYPTO_STAT_REG != 0) { + ble_hdr->rxinfo.flags |= BLE_MBUF_HDR_F_MIC_FAILURE; + } else { + g_ble_phy_rx_buf[1] = g_ble_phy_rx_buf[1] - 4; + } + } + } +#endif + } + + ble_phy_sw_mac_handover(SW_MAC_EXC_LL_RX_END); +} + +static bool +ble_phy_rx_start_isr(void) +{ + uint32_t llt32; + uint32_t llt_10_0; + uint32_t llt_10_0_mask; + uint32_t timestamp; + uint32_t ticks; + uint32_t usecs; + struct ble_mbuf_hdr *ble_hdr; + + /* Initialize the ble mbuf header */ + ble_hdr = &g_ble_phy_data.rxhdr; + ble_hdr->rxinfo.flags = ble_ll_state_get(); + ble_hdr->rxinfo.channel = g_ble_phy_data.channel; + ble_hdr->rxinfo.handle = 0; + ble_hdr->rxinfo.phy = ble_phy_get_cur_phy(); + ble_hdr->rxinfo.phy_mode = g_ble_phy_data.phy_mode_rx; +#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_EXT_ADV) + ble_hdr->rxinfo.user_data = NULL; +#endif + + /* Read the latched RSSI value */ + ble_hdr->rxinfo.rssi = ble_rf_get_rssi(); +#if MYNEWT_VAL(CMAC_DEBUG_DATA_ENABLE) + g_cmac_shared_data.debug.last_rx_rssi = ble_hdr->rxinfo.rssi; +#endif + + /* Count rx starts */ + STATS_INC(ble_phy_stats, rx_starts); + + /* + * Calculate packet start time. Note that we have only received the + * access address at this point but we should have the 1st symbol and + * thus the timestamp should be set (this is based on looking at the diag + * signals). For now, lets make sure that the dirty bit is set. The + * dirty bit means that the timestamp was set since the last time cleared. + * Note that we need to read the timestamp first to guarantee it was set + * before reading the LL timer. + */ + timestamp = CMAC->CM_TS1_REG; + assert((timestamp & CMAC_CM_TS1_REG_TS1_DIRTY_Msk) != 0); + + /* Get the LL timer (only need 32 bits) */ + llt32 = cmac_timer_read32(); + + /* + * We assume that the timestamp was set within 11 bits, or 2047 usecs, of + * when we read the ll timer. We assume this because we need to calculate + * the LL timer value at the timestamp. If the low 11 bits of the LL timer + * are greater than the timestamp, it means that the upper bits of the + * timestamp are correct. If the timestamp value is greater, it means the + * timer wrapped the 11 bits and we need to adjust the LL timer value. + */ + llt_10_0_mask = (CMAC_CM_TS1_REG_TS1_TIMER1_9_0_Msk | + CMAC_CM_TS1_REG_TS1_TIMER1_10_Msk); + timestamp &= llt_10_0_mask; + llt_10_0 = llt32 & llt_10_0_mask; + llt32 &= ~llt_10_0_mask; + if (timestamp > llt_10_0) { + llt32 -= 2048; + } + llt32 |= timestamp; + + /* Actual RX start time needs to account for preamble and access address */ + llt32 -= g_ble_phy_mode_pkt_start_off[g_ble_phy_data.phy_mode_rx] + + g_ble_phy_data.path_delay_rx; + + if (llt32 < g_ble_phy_data.llt_at_cputime) { + g_ble_phy_data.llt_at_cputime -= 31; + g_ble_phy_data.cputime_at_llt--; + } + + /* + * We now have the LL timer when the packet was received. Get the cputime + * and the leftover usecs. + */ + usecs = llt32 - g_ble_phy_data.llt_at_cputime; + ticks = os_cputime_usecs_to_ticks(usecs); + ble_hdr->beg_cputime = g_ble_phy_data.cputime_at_llt + ticks; + ble_hdr->rem_usecs = usecs - os_cputime_ticks_to_usecs(ticks); + + return true; +} + +static void +ble_phy_irq_field_tx_exc_bs_start_4this(void) +{ + CMAC->CM_EXC_STAT_REG = CMAC_CM_EXC_STAT_REG_EXC_BS_START_4THIS_Msk; + + if (g_ble_phy_data.end_transition == BLE_PHY_TRANSITION_TX_RX) { + /* + * Setup 2nd frame that will start after current one. + * -2us offset to adjust for allowed active clock accuracy. + */ + CMAC->CM_FRAME_2_REG = CMAC_CM_FRAME_2_REG_FRAME_VALID_Msk | + (((g_ble_phy_data.frame_offset_txrx) - 2) << + CMAC_CM_FRAME_2_REG_FRAME_START_OFFSET_Pos); + + /* Next frame starts automatically on phy2idle */ + CMAC->CM_EV_LINKUP_REG = CMAC_CM_EV_LINKUP_REG_LU_PHY_TO_IDLE_2_EXC_Msk | + CMAC_CM_EV_LINKUP_REG_LU_FRAME_START_2_PHY_TO_IDLE_Msk; + + ble_phy_wfr_enable(BLE_PHY_WFR_ENABLE_TXRX, g_ble_phy_data.phy_mode_rx, 0); + } else { + CMAC->CM_EV_LINKUP_REG = CMAC_CM_EV_LINKUP_REG_LU_PHY_TO_IDLE_2_EXC_Msk | + CMAC_CM_EV_LINKUP_REG_LU_FRAME_START_2_NONE_Msk; + } + +#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LE_ENCRYPTION) + if (g_ble_phy_data.phy_encrypted && (g_ble_phy_tx_buf[1] != 0)) { + ble_phy_tx_enc_start(); + } +#endif +} + +static void +ble_phy_irq_field_tx_exc_field_on_thr_exp(void) +{ + CMAC->CM_EXC_STAT_REG = CMAC_CM_EXC_STAT_REG_EXC_FIELD_ON_THR_EXP_Msk; + (void)CMAC->CM_TS1_REG; + + /* Set up remaining field (CRC) */ + CMAC->CM_FIELD_PUSH_CTRL_REG = FIELD_CTRL_REG_TX_CRC; +} + +static void +ble_phy_irq_field_tx(void) +{ + uint32_t stat; + + stat = CMAC->CM_EXC_STAT_REG; + + if (stat & CMAC_CM_EXC_STAT_REG_EXC_BS_START_4THIS_Msk) { + MCU_DIAG_SER('6'); + ble_phy_irq_field_tx_exc_bs_start_4this(); + } + + if (stat & CMAC_CM_EXC_STAT_REG_EXC_FIELD_ON_THR_EXP_Msk) { + MCU_DIAG_SER('7'); + ble_phy_irq_field_tx_exc_field_on_thr_exp(); + } +} + +static void +ble_phy_irq_frame_tx_exc_bs_stop(void) +{ + CMAC->CM_EXC_STAT_REG = CMAC_CM_EXC_STAT_REG_EXC_BS_STOP_Msk; + + /* Clear latched timestamp so we do not have error on next frame */ + (void)CMAC->CM_TS1_REG; + + if (g_ble_phy_data.end_transition == BLE_PHY_TRANSITION_TX_RX) { +#if (BLE_LL_BT5_PHY_SUPPORTED == 1) + ble_phy_mode_apply(g_ble_phy_data.phy_mode_rx); +#endif + ble_phy_rx_setup_fields(); + } else { + CMAC->CM_EV_LINKUP_REG = CMAC_CM_EV_LINKUP_REG_LU_PHY_TO_IDLE_2_EXC_Msk | + CMAC_CM_EV_LINKUP_REG_LU_FRAME_START_2_NONE_Msk; + } + + if (g_ble_phy_data.txend_cb) { + ble_phy_sw_mac_handover(SW_MAC_EXC_TXEND_CB); + return; + } +} + +static void +ble_phy_irq_frame_tx_exc_phy_to_idle_4this(void) +{ + CMAC->CM_EXC_STAT_REG = CMAC_CM_EXC_STAT_REG_EXC_PHY_TO_IDLE_4THIS_Msk; + + if (g_ble_phy_data.end_transition == BLE_PHY_TRANSITION_TX_RX) { + ble_phy_rx_setup_xcvr(); + + g_ble_phy_data.phy_state = BLE_PHY_STATE_RX; + } else { + /* + * Disable explicitly in case RX-TX was done (we cannot setup for auto + * disable in such case) */ + ble_rf_stop(); + + g_ble_phy_data.phy_state = BLE_PHY_STATE_IDLE; + } + + g_ble_phy_data.end_transition = BLE_PHY_TRANSITION_NONE; +} + +static void +ble_phy_irq_frame_tx(void) +{ + uint32_t stat; + + stat = CMAC->CM_EXC_STAT_REG; + + /* + * In case of phy2idle this should be first and only exception we handle + * here. This is because in case of TX-RX transition frame_start will occur + * at the same as phy2idle so we will have 2 exceptions here. To handle this + * properly we first need to handle phy2idle in TX state and keep frame_start + * pending so it will be called again in RX state. + */ + if (stat & CMAC_CM_EXC_STAT_REG_EXC_PHY_TO_IDLE_4THIS_Msk) { + MCU_DIAG_SER('6'); + ble_phy_irq_frame_tx_exc_phy_to_idle_4this(); + return; + } + + if (stat & CMAC_CM_EXC_STAT_REG_EXC_FRAME_START_Msk) { + MCU_DIAG_SER('7'); + CMAC->CM_EXC_STAT_REG = CMAC_CM_EXC_STAT_REG_EXC_FRAME_START_Msk; + } + + if (stat & CMAC_CM_EXC_STAT_REG_EXC_BS_STOP_Msk) { + MCU_DIAG_SER('8'); + ble_phy_irq_frame_tx_exc_bs_stop(); + } +} + +#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LE_ENCRYPTION) +static void +ble_phy_field_rx_encrypted(uint32_t len) +{ + if (len) { + /* + * An encrypted frame should have a minimum length of 5 + * bytes (at least one for payload and 4 for MIC). If the + * length is less than 5 this frame is bogus and will most + * likely fail CRC. We still need to process this frame + * though as we need to call the handover function with + * the frame. If this happens we will not bother to + * run the remaining bytes through the accelerator; just + * process them like normal and generate (a hopefully + * incorrect) CRC. + */ + if (len >= 5) { + /* Start the crypto accelerator */ + ble_phy_rx_enc_start(len); + + /* + * We have already processed one byte; process remaining + * payload and MIC. Note: length contains MIC. + */ + len -= 2; + CMAC->CM_FIELD_PUSH_DATA_REG = FIELD_DATA_REG_DMA_RX(4, len); + CMAC->CM_FIELD_PUSH_CTRL_REG = FIELD_CTRL_REG_RX_ENC_PAYLOAD; + + /* CRC */ + CMAC->CM_FIELD_PUSH_DATA_REG = FIELD_DATA_REG_DMA_RX(4 + len, 3); + CMAC->CM_FIELD_PUSH_CTRL_REG = FIELD_CTRL_REG_RX_CRC; + } else { + /* We have processed one byte so far. Send remaining + payload bytes to normal rx payload processing */ + len -= 2; + if (len) { + CMAC->CM_FIELD_PUSH_DATA_REG = FIELD_DATA_REG_DMA_RX(4, len); + CMAC->CM_FIELD_PUSH_CTRL_REG = FIELD_CTRL_REG_RX_PAYLOAD; + } + + CMAC->CM_FIELD_PUSH_DATA_REG = FIELD_DATA_REG_DMA_RX(4 + len, 3); + CMAC->CM_FIELD_PUSH_CTRL_REG = FIELD_CTRL_REG_RX_CRC; + + /* Clear crypto pre-buffer */ + CMAC->CM_CRYPTO_CTRL_REG = CMAC_CM_CRYPTO_CTRL_REG_CM_CRYPTO_SW_REQ_PBUF_CLR_Msk; + } + } else { + /* We programmed one byte, so get next two bytes for CRC */ + CMAC->CM_FIELD_PUSH_DATA_REG = FIELD_DATA_REG_DMA_RX(4, 1); + CMAC->CM_FIELD_PUSH_CTRL_REG = FIELD_CTRL_REG_RX_CRC; + } +} +#endif + +static void +ble_phy_field_rx_unencrypted(uint32_t len) +{ + uint8_t pduhdr; + uint8_t adva_thr; + + if (len) { + pduhdr = g_ble_phy_rx_buf[0]; + adva_thr = 0; + + /* + * Setup interrupt after AdvA to start address resolving if + * privacy is enabled and TxAdd bit is set. + */ + if (MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_PRIVACY) && + g_ble_phy_data.phy_privacy && (pduhdr & 0x40)) { + + /* + * For legacy advertising AdvA ends at 6th byte. + * For extended advertising AdvA ends at 8th byte. + * We already programmed 2 bytes of payload so need + * to adjust threshold accordingly or just reset it + * in case there is not enough bytes in PDU to fit AdvA. + */ + adva_thr = (pduhdr & 0x0f) == 0x07 ? 6 : 4; + if (len >= adva_thr + 2) { + CMAC->CM_FIELD_PUSH_DATA_REG = FIELD_DATA_REG_DMA_RX(4, adva_thr); + CMAC->CM_FIELD_PUSH_CTRL_REG = FIELD_CTRL_REG_RX_PAYLOAD_WITH_EXC; + } else { + adva_thr = 0; + } + } + + CMAC->CM_FIELD_PUSH_DATA_REG = FIELD_DATA_REG_DMA_RX(4 + adva_thr, + len - adva_thr); + CMAC->CM_FIELD_PUSH_CTRL_REG = FIELD_CTRL_REG_RX_PAYLOAD; + } + + CMAC->CM_FIELD_PUSH_DATA_REG = FIELD_DATA_REG_DMA_RX(4 + len, 1); + CMAC->CM_FIELD_PUSH_CTRL_REG = FIELD_CTRL_REG_RX_CRC; +} + +static void +ble_phy_irq_field_rx_exc_field_on_thr_exp(void) +{ + uint32_t len; + uint32_t smpl; + + CMAC->CM_EXC_STAT_REG = CMAC_CM_EXC_STAT_REG_EXC_FIELD_ON_THR_EXP_Msk; + + smpl = CMAC->CM_BS_SMPL_ST_REG; + + if ((smpl & CMAC_CM_BS_SMPL_ST_REG_FIELD_CNT_LATCHED_Msk) == 1) { + assert(g_ble_phy_data.phy_rx_started == 0); + + /* Clear this */ + (void)CMAC->CM_TS1_REG; + + /* Read length of frame */ + len = CMAC->CM_BS_SMPL_D_REG; + len = len & 0xFF; + +#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LE_ENCRYPTION) + if (g_ble_phy_data.phy_encrypted) { + ble_phy_field_rx_encrypted(len); + } else { + ble_phy_field_rx_unencrypted(len); + } +#else + ble_phy_field_rx_unencrypted(len); +#endif + + ble_phy_sw_mac_handover(SW_MAC_EXC_LL_RX_START); + } else if ((smpl & CMAC_CM_BS_SMPL_ST_REG_FIELD_CNT_LATCHED_Msk) == 3) { + (void)CMAC->CM_BS_SMPL_D_REG; + + assert(g_ble_phy_data.phy_privacy); + + /* + * Resolve only if RPA is received. AdvA is at different offset + * in ExtAdv PDU. TxAdd was already checked before programming + * field threshold. + */ + if ((g_ble_phy_rx_buf[0] & 0x0f) == 0x07) { + if ((g_ble_phy_rx_buf[9] & 0xc0) == 0x40) { + ble_hw_resolv_proc_start(&g_ble_phy_rx_buf[4]); + } + } else { + if ((g_ble_phy_rx_buf[7] & 0xc0) == 0x40) { + ble_hw_resolv_proc_start(&g_ble_phy_rx_buf[2]); + } + } + } else { + assert(0); + } +} + +static void +ble_phy_irq_field_rx_exc_stat_reg_exc_corr_timeout(void) +{ + CMAC->CM_EXC_STAT_REG = CMAC_CM_EXC_STAT_REG_EXC_CORR_TIMEOUT_Msk; + CMAC->CM_EXC_STAT_REG = CMAC_CM_EXC_STAT_REG_EXC_BS_STOP_Msk; + CMAC->CM_EV_LINKUP_REG = CMAC_CM_EV_LINKUP_REG_LU_FRAME_START_2_NONE_Msk; + + ble_phy_sw_mac_handover(SW_MAC_EXC_WFR_TIMER_EXP); +} + +static void +ble_phy_irq_field_rx(void) +{ + uint32_t stat; + + stat = CMAC->CM_EXC_STAT_REG; + + if (stat & CMAC_CM_EXC_STAT_REG_EXC_FIELD_ON_THR_EXP_Msk) { + MCU_DIAG_SER('1'); + ble_phy_irq_field_rx_exc_field_on_thr_exp(); + } + + if (stat & CMAC_CM_EXC_STAT_REG_EXC_CORR_TIMEOUT_Msk) { + MCU_DIAG_SER('2'); + ble_phy_irq_field_rx_exc_stat_reg_exc_corr_timeout(); + } +} + +static void +ble_phy_irq_frame_rx_exc_phy_to_idle_4this(void) +{ + uint8_t rf_chan; + + CMAC->CM_EXC_STAT_REG = CMAC_CM_EXC_STAT_REG_EXC_PHY_TO_IDLE_4THIS_Msk; + + /* We are here only on transition, so switch to TX */ +#if (BLE_LL_BT5_PHY_SUPPORTED == 1) + ble_phy_mode_apply(g_ble_phy_data.phy_mode_tx); +#endif + rf_chan = g_ble_phy_chan_to_rf[g_ble_phy_data.channel]; + ble_rf_setup_tx(rf_chan, g_ble_phy_data.phy_mode_tx); + g_ble_phy_data.phy_state = BLE_PHY_STATE_TX; +} + +static void +ble_phy_irq_frame_rx_exc_frame_start(void) +{ + CMAC->CM_EXC_STAT_REG = CMAC_CM_EXC_STAT_REG_EXC_FRAME_START_Msk; +} + +static void +ble_phy_irq_frame_rx_exc_bs_stop(void) +{ + CMAC->CM_EXC_STAT_REG = CMAC_CM_EXC_STAT_REG_EXC_BS_STOP_Msk; + ble_phy_rx_end_isr(); +} + +static void +ble_phy_irq_frame_rx(void) +{ + uint32_t stat; + + stat = CMAC->CM_EXC_STAT_REG; + + if (stat & CMAC_CM_EXC_STAT_REG_EXC_PHY_TO_IDLE_4THIS_Msk) { + MCU_DIAG_SER('3'); + ble_phy_irq_frame_rx_exc_phy_to_idle_4this(); + return; + } + + if (stat & CMAC_CM_EXC_STAT_REG_EXC_FRAME_START_Msk) { + MCU_DIAG_SER('1'); + ble_phy_irq_frame_rx_exc_frame_start(); + } + + if (stat & CMAC_CM_EXC_STAT_REG_EXC_BS_STOP_Msk) { + MCU_DIAG_SER('2'); + ble_phy_irq_frame_rx_exc_bs_stop(); + } +} + +static void +ble_phy_mode_apply(uint8_t phy_mode) +{ + if (phy_mode == g_ble_phy_data.phy_mode_cur) { + return; + } + + switch (phy_mode) { + case BLE_PHY_MODE_1M: +#if MYNEWT_VAL(BLE_PHY_RF_HP_MODE) + CMAC_SETREGF(CM_PHY_CTRL2_REG, CORR_CLK_MODE, 3); +#endif + CMAC_SETREGF(CM_PHY_CTRL2_REG, PHY_MODE, 1); + g_ble_phy_data.phy_mode_evpsym = 1; /* 1000 ns per symbol */ + g_ble_phy_data.phy_mode_pre_len = 7; + break; +#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LE_2M_PHY) + case BLE_PHY_MODE_2M: +#if MYNEWT_VAL(BLE_PHY_RF_HP_MODE) + CMAC_SETREGF(CM_PHY_CTRL2_REG, CORR_CLK_MODE, 2); +#endif + CMAC_SETREGF(CM_PHY_CTRL2_REG, PHY_MODE, 0); + g_ble_phy_data.phy_mode_evpsym = 0; /* 500 ns per symbol */ + g_ble_phy_data.phy_mode_pre_len = 15; + break; +#endif + default: + assert(0); + return; + } + + g_ble_phy_data.phy_mode_cur = phy_mode; +} + +void +ble_phy_mode_set(uint8_t tx_phy_mode, uint8_t rx_phy_mode) +{ + uint8_t txrx; + uint8_t rxtx; + + g_ble_phy_data.phy_mode_tx = tx_phy_mode; + g_ble_phy_data.phy_mode_rx = rx_phy_mode; + + g_ble_phy_data.path_delay_tx = g_ble_phy_path_delay_tx[tx_phy_mode - 1]; + g_ble_phy_data.path_delay_rx = g_ble_phy_path_delay_rx[rx_phy_mode - 1]; + + /* + * Calculate index of transition in frame offset array without tons of + * branches. Note that transitions have to be in specific order in array. + * + * phy_mode 1M = 01b + * phy_mode 2M = 10b + * + * 1M/1M = 01b | 00b = 01b + * 1M/2M = 01b | 10b = 11b + * 2M/1M = 00b | 00b = 00b + * 2M/2M = 00b | 10b = 10b + */ + txrx = (tx_phy_mode & 0x01) | (rx_phy_mode & 0x02); + rxtx = (rx_phy_mode & 0x01) | (tx_phy_mode & 0x02); + g_ble_phy_data.frame_offset_txrx = g_ble_phy_frame_offset_txrx[txrx]; + g_ble_phy_data.frame_offset_rxtx = g_ble_phy_frame_offset_rxtx[rxtx]; +} + +int +ble_phy_get_cur_phy(void) +{ +#if (BLE_LL_BT5_PHY_SUPPORTED == 1) + switch (g_ble_phy_data.phy_mode_cur) { + case BLE_PHY_MODE_1M: + return BLE_PHY_1M; + case BLE_PHY_MODE_2M: + return BLE_PHY_2M; + default: + assert(0); + return -1; + } +#else + return BLE_PHY_1M; +#endif +} + +/** + * Copies the data from the phy receive buffer into a mbuf chain. + * + * @param dptr Pointer to receive buffer + * @param rxpdu Pointer to already allocated mbuf chain + * + * NOTE: the packet header already has the total mbuf length in it. The + * lengths of the individual mbufs are not set prior to calling. + * + */ +void +ble_phy_rxpdu_copy(uint8_t *dptr, struct os_mbuf *rxpdu) +{ + uint32_t rem_len; + uint32_t copy_len; + uint32_t block_len; + uint32_t block_rem_len; + void *dst; + void *src; + struct os_mbuf * om; + + /* Better be aligned */ + assert(((uint32_t)dptr & 3) == 0); + + block_len = rxpdu->om_omp->omp_databuf_len; + rem_len = OS_MBUF_PKTHDR(rxpdu)->omp_len; + src = dptr; + + /* + * Setup for copying from first mbuf which is shorter due to packet header + * and extra leading space + */ + copy_len = block_len - rxpdu->om_pkthdr_len - 4; + om = rxpdu; + dst = om->om_data; + + while (om) { + /* + * Always copy blocks of length aligned to word size, only last mbuf + * will have remaining non-word size bytes appended. + */ + block_rem_len = copy_len; + copy_len = min(copy_len, rem_len); + copy_len &= ~3; + + dst = om->om_data; + om->om_len = copy_len; + rem_len -= copy_len; + block_rem_len -= copy_len; + + __asm__ volatile (".syntax unified \n" + " mov r4, %[len] \n" + " b 2f \n" + "1: ldr r3, [%[src], %[len]] \n" + " str r3, [%[dst], %[len]] \n" + "2: subs %[len], #4 \n" + " bpl 1b \n" + " adds %[src], %[src], r4 \n" + " adds %[dst], %[dst], r4 \n" + : [dst] "+l" (dst), [src] "+l" (src), + [len] "+l" (copy_len) + : + : "r3", "r4", "memory"); + + if ((rem_len < 4) && (block_rem_len >= rem_len)) { + break; + } + + /* Move to next mbuf */ + om = SLIST_NEXT(om, om_next); + copy_len = block_len; + } + + /* Copy remaining bytes, if any, to last mbuf */ + om->om_len += rem_len; + __asm__ volatile (".syntax unified \n" + " b 2f \n" + "1: ldrb r3, [%[src], %[len]] \n" + " strb r3, [%[dst], %[len]] \n" + "2: subs %[len], #1 \n" + " bpl 1b \n" + : [len] "+l" (rem_len) + : [dst] "l" (dst), [src] "l" (src) + : "r3", "memory"); + + /* Copy header */ + memcpy(BLE_MBUF_HDR_PTR(rxpdu), &g_ble_phy_data.rxhdr, + sizeof(struct ble_mbuf_hdr)); +} + +void +ble_phy_wfr_enable(int txrx, uint8_t tx_phy_mode, uint32_t wfr_usecs) +{ + uint64_t llt; + uint32_t corr_window; + uint32_t llt_z_ticks; + uint32_t aa_time; + + /* + * RX is started 2us earlier due to allowed clock accuracy and it should end + * 2us later for the same reason. Preamble is always 8us (8 symbols on 1M, + * 16 symbols on 2M) and Access Address is 32us on 1M and 16us on 2M. Add + * 1us just in case... + */ +#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LE_2M_PHY) + aa_time = 2 + (g_ble_phy_data.phy_mode_rx == BLE_PHY_MODE_1M ? 40 : 24) + 2 + 1; +#else + aa_time = 45; +#endif + + if (txrx == BLE_PHY_WFR_ENABLE_TXRX) { + CMAC_SETREGF(CM_PHY_CTRL2_REG, CORR_WINDOW, aa_time); + CMAC->CM_EV_LINKUP_REG = CMAC_CM_EV_LINKUP_REG_LU_CORR_TMR_LD_2_CORR_START_Msk; + } else if (wfr_usecs < 16384) { + CMAC_SETREGF(CM_PHY_CTRL2_REG, CORR_WINDOW, wfr_usecs + aa_time); + CMAC->CM_EV_LINKUP_REG = CMAC_CM_EV_LINKUP_REG_LU_CORR_TMR_LD_2_CORR_START_Msk; + } else { + wfr_usecs += aa_time; + llt = g_ble_phy_data.start_llt; + + /* + * wfr is outside range of CORR_WINDOW so we need to use LLT to start + * correlator timeout with some delay. Let's use ~10ms as new CORR_WINDOW + * value (does not really matter, just had to pick something) so need to + * calculate how many hi-Z ticks of delay we need. + */ + llt_z_ticks = (wfr_usecs - 10000) / 1024; + + /* New CORR_WINDOW is wfr adjusted by hi-Z ticks and remainder of 1st tick. */ + corr_window = wfr_usecs; + corr_window -= llt_z_ticks * 1024; + corr_window -= 1024 - (llt & 0x3ff); + + CMAC->CM_LL_TIMER1_36_10_EQ_Z_REG = (llt >> 10) + llt_z_ticks; + CMAC_SETREGF(CM_PHY_CTRL2_REG, CORR_WINDOW, corr_window); + CMAC->CM_EV_LINKUP_REG = CMAC_CM_EV_LINKUP_REG_LU_CORR_TMR_LD_2_TMR1_36_10_EQ_Z_Msk; + } +} + +int +ble_phy_init(void) +{ + g_ble_phy_data.phy_state = BLE_PHY_STATE_IDLE; +#if MYNEWT_VAL(BLE_LL_DTM) + g_ble_phy_data.phy_whitening = 1; +#endif + + ble_rf_init(); + + /* + * 9_0_EQ_X can be linked to start RX/TX so we'll use this one for + * scheduling TX/RX start - make sure it's not linked to LL_TIMER2LLC + */ + CMAC->CM_LL_INT_SEL_REG &= ~CMAC_CM_LL_INT_SEL_REG_LL_TIMER1_9_0_EQ_X_SEL_Msk; + + CMAC->CM_PHY_CTRL_REG = ((PHY_DELAY_POWER_DN_RX - 1) << 24) | + ((PHY_DELAY_POWER_DN_TX - 1) << 16) | + ((PHY_DELAY_POWER_UP_RX - 1) << 8) | + ((PHY_DELAY_POWER_UP_TX - 1)); + +#if MYNEWT_VAL(BLE_PHY_RF_HP_MODE) + CMAC->CM_PHY_CTRL2_REG = CMAC_CM_PHY_CTRL2_REG_PHY_MODE_Msk | + (3 << CMAC_CM_PHY_CTRL2_REG_CORR_CLK_MODE_Pos); +#else + CMAC->CM_PHY_CTRL2_REG = CMAC_CM_PHY_CTRL2_REG_PHY_MODE_Msk | + (2 << CMAC_CM_PHY_CTRL2_REG_CORR_CLK_MODE_Pos); +#endif + + CMAC_SETREGF(CM_CTRL2_REG, WHITENING_MODE, 0); + CMAC_SETREGF(CM_CTRL2_REG, CRC_MODE, 0); + + /* Setup for 1M by default */ + ble_phy_mode_set(BLE_PHY_MODE_1M, BLE_PHY_MODE_1M); + ble_phy_mode_apply(BLE_PHY_MODE_1M); + + NVIC_SetPriority(FIELD_IRQn, 0); + NVIC_SetPriority(CALLBACK_IRQn, 0); + NVIC_SetPriority(FRAME_IRQn, 0); + NVIC_SetPriority(CRYPTO_IRQn, 1); + NVIC_SetPriority(SW_MAC_IRQn, 1); + NVIC_EnableIRQ(FIELD_IRQn); + NVIC_EnableIRQ(CALLBACK_IRQn); + NVIC_EnableIRQ(FRAME_IRQn); + NVIC_EnableIRQ(SW_MAC_IRQn); + +#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LE_ENCRYPTION) + /* Initialize non-zero fixed values in CCM blocks */ + g_ble_phy_encrypt_data.b0[0] = 0x49; + g_ble_phy_encrypt_data.b1[1] = 0x01; + g_ble_phy_encrypt_data.ai[0] = 0x01; +#endif + + return 0; +} + +void +ble_phy_disable(void) +{ + MCU_DIAG_SER('D'); + + __disable_irq(); + + CMAC->CM_EV_SET_REG = CMAC_CM_EV_SET_REG_EV1C_BS_CLEAR_Msk; + + __NOP(); + __NOP(); + __NOP(); + __NOP(); + __NOP(); + + CMAC->CM_EXC_STAT_REG = CMAC_CM_EXC_STAT_REG_EXC_FIELD_ON_THR_EXP_Msk | + CMAC_CM_EXC_STAT_REG_EXC_BS_START_4THIS_Msk | + CMAC_CM_EXC_STAT_REG_EXC_CORR_TIMEOUT_Msk | + CMAC_CM_EXC_STAT_REG_EXC_BS_STOP_Msk | + CMAC_CM_EXC_STAT_REG_EXC_FRAME_START_Msk | + CMAC_CM_EXC_STAT_REG_EXC_PHY_TO_IDLE_4THIS_Msk | + CMAC_CM_EXC_STAT_REG_EXC_SW_MAC_Msk; + + NVIC->ICPR[0] = (1 << FIELD_IRQn) | (1 << CALLBACK_IRQn) | + (1 << FRAME_IRQn) | (1 << SW_MAC_IRQn); + + os_arch_cmac_bs_ctrl_irq_unblock(); + g_sw_mac_exc = 0; + + CMAC->CM_ERROR_DIS_REG = 0; + + ble_rf_stop(); + + /* + * If ble_phy_disable is called precisely when access address was matched, + * ts1_dirty may not be cleared properly. This is because bs_clear will + * cause bitstream controller to be stopped and we won't get callback_irq, + * but seems like demodulator is still active for a while and will trigger + * ev1c_ts1_trigger on 1st symbol which will set ts1_dirty. We do not expect + * ts1_dirty to be set after bs_clear so we won't clear it. To workaround + * his, we can just clear it explicitly here after everything is already + * disabled. + */ + (void)CMAC->CM_TS1_REG; + + __enable_irq(); + + g_ble_phy_data.phy_state = BLE_PHY_STATE_IDLE; +} + +static void +ble_phy_rx_setup_fields(void) +{ + /* Make sure CRC LFSR initial value is set */ + CMAC_SETREGF(CM_CRC_REG, CRC_INIT_VAL, g_ble_phy_data.crc_init); + + CMAC->CM_FIELD_PUSH_DATA_REG = g_ble_phy_data.access_addr; + CMAC->CM_FIELD_PUSH_CTRL_REG = FIELD_CTRL_REG_RX_ACCESS_ADDR; + CMAC->CM_FIELD_PUSH_DATA_REG = FIELD_DATA_REG_DMA_RX(0, 2); + CMAC->CM_FIELD_PUSH_CTRL_REG = FIELD_CTRL_REG_RX_HEADER; +#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LE_ENCRYPTION) + if (g_ble_phy_data.phy_encrypted) { + /* Only program one byte for encrypted payloads */ + CMAC->CM_FIELD_PUSH_DATA_REG = FIELD_DATA_REG_DMA_RX(2, 2); + CMAC->CM_FIELD_PUSH_CTRL_REG = FIELD_CTRL_REG_RX_ENC_PAYLOAD; + } else { + CMAC->CM_FIELD_PUSH_DATA_REG = FIELD_DATA_REG_DMA_RX(2, 2); + CMAC->CM_FIELD_PUSH_CTRL_REG = FIELD_CTRL_REG_RX_PAYLOAD; + } +#else + CMAC->CM_FIELD_PUSH_DATA_REG = FIELD_DATA_REG_DMA_RX(2, 2); + CMAC->CM_FIELD_PUSH_CTRL_REG = FIELD_CTRL_REG_RX_PAYLOAD; +#endif +} + +static void +ble_phy_rx_setup_xcvr(void) +{ + uint8_t rf_chan = g_ble_phy_chan_to_rf[g_ble_phy_data.channel]; + + CMAC->CM_EV_SET_REG = CMAC_CM_EV_SET_REG_EV1C_CALLBACK_VALID_SET_Msk; + + ble_rf_setup_rx(rf_chan, g_ble_phy_data.phy_mode_rx); + + g_ble_phy_data.phy_rx_started = 0; +} + +int +ble_phy_rx(void) +{ + MCU_DIAG_SER('R'); + + ble_rf_configure(); + ble_phy_rx_setup_xcvr(); + + CMAC->CM_FRAME_1_REG = CMAC_CM_FRAME_1_REG_FRAME_VALID_Msk; + + CMAC_SETREGF(CM_PHY_CTRL2_REG, CORR_WINDOW, 0); + + ble_phy_rx_setup_fields(); + + g_ble_phy_data.phy_state = BLE_PHY_STATE_RX; + + return 0; +} + +int +ble_phy_rx_set_start_time(uint32_t cputime, uint8_t rem_usecs) +{ + uint32_t ll_val32; + int32_t time_till_start; + + MCU_DIAG_SER('r'); + +#if (BLE_LL_BT5_PHY_SUPPORTED == 1) + ble_phy_mode_apply(g_ble_phy_data.phy_mode_rx); +#endif + + assert(ble_rf_is_enabled()); + + ble_phy_rx(); + + /* Get LL timer at cputime */ + ll_val32 = ble_phy_convert_and_record_start_time(cputime, rem_usecs); + + /* Add remaining usecs to get exact receive start time */ + ll_val32 += rem_usecs; + + /* Adjust start time for rx delays */ + ll_val32 -= PHY_DELAY_POWER_UP_RX - g_ble_phy_data.path_delay_rx; + + __disable_irq(); + CMAC->CM_LL_TIMER1_9_0_EQ_X_REG = ll_val32; + CMAC->CM_EV_LINKUP_REG = + CMAC_CM_EV_LINKUP_REG_LU_FRAME_START_2_TMR1_9_0_EQ_X_Msk; + + time_till_start = (int32_t)(ll_val32 - cmac_timer_read32()); + if (time_till_start <= 0) { + /* + * Possible we missed the frame start! If we have, we need to start + * ASAP. + */ + if ((CMAC->CM_EXC_STAT_REG & CMAC_CM_EXC_STAT_REG_EXC_FRAME_START_Msk) == 0) { + /* We missed start. Start now */ + CMAC->CM_EV_LINKUP_REG = + CMAC_CM_EV_LINKUP_REG_LU_FRAME_START_2_ASAP_Msk; + } + } + __enable_irq(); + + return 0; +} + +int +ble_phy_tx(ble_phy_tx_pducb_t pducb, void *pducb_arg, uint8_t end_trans) +{ + uint8_t *txbuf = g_ble_phy_tx_buf; + int rc; + + MCU_DIAG_SER('T'); + + assert(CMAC->CM_FRAME_1_REG & CMAC_CM_FRAME_1_REG_FRAME_TX_Msk); + + g_ble_phy_data.end_transition = end_trans; + + /* + * Program required fields now so in worst case TX can continue while we + * are still preparing header and payload. + */ + CMAC->CM_FIELD_PUSH_DATA_REG = g_ble_phy_data.access_addr & 1 ? 0x5555 : 0xaaaa; + CMAC->CM_FIELD_PUSH_CTRL_REG = FIELD_CTRL_REG_TX_PREAMBLE; + CMAC->CM_FIELD_PUSH_DATA_REG = g_ble_phy_data.access_addr; + CMAC->CM_FIELD_PUSH_CTRL_REG = FIELD_CTRL_REG_TX_ACCESS_ADDR; + + /* Make sure CRC LFSR initial value is set */ + CMAC_SETREGF(CM_CRC_REG, CRC_INIT_VAL, g_ble_phy_data.crc_init); + + /* txbuf[0] is hdr_byte, txbuf[1] is pkt_len */ + txbuf[1] = pducb(&txbuf[2], pducb_arg, &txbuf[0]); + +#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LE_ENCRYPTION) + if (g_ble_phy_data.phy_encrypted && (txbuf[1] != 0)) { + /* We have to add the MIC to the length */ + txbuf[1] += BLE_LL_DATA_MIC_LEN; + + /* Program header field */ + CMAC->CM_FIELD_PUSH_DATA_REG = FIELD_DATA_REG_DMA_TX(0, 2); + CMAC->CM_FIELD_PUSH_CTRL_REG = FIELD_CTRL_REG_TX_PAYLOAD; + + /* Program payload (and MIC) */ + CMAC->CM_FIELD_PUSH_DATA_REG = FIELD_DATA_REG_DMA_TX(2, txbuf[1]); + CMAC->CM_FIELD_PUSH_CTRL_REG = FIELD_CTRL_REG_TX_ENC_PAYLOAD; + } else { + /* Program header and payload fields */ + CMAC->CM_FIELD_PUSH_DATA_REG = FIELD_DATA_REG_DMA_TX(0, txbuf[1] + 2); + CMAC->CM_FIELD_PUSH_CTRL_REG = FIELD_CTRL_REG_TX_PAYLOAD; + } +#else + /* Program header and payload fields */ + CMAC->CM_FIELD_PUSH_DATA_REG = FIELD_DATA_REG_DMA_TX(0, txbuf[1] + 2); + CMAC->CM_FIELD_PUSH_CTRL_REG = FIELD_CTRL_REG_TX_PAYLOAD; +#endif + + /* + * If there was FIELD_ON_THR exception it means access address was already + * sent and we are likely too late here - abort. + */ + if (CMAC->CM_EXC_STAT_REG & CMAC_CM_EXC_STAT_REG_EXC_FIELD_ON_THR_EXP_Msk) { + ble_phy_disable(); + g_ble_phy_data.end_transition = BLE_PHY_TRANSITION_NONE; + STATS_INC(ble_phy_stats, tx_late); + rc = BLE_PHY_ERR_RADIO_STATE; + } else { + if (g_ble_phy_data.phy_state == BLE_PHY_STATE_IDLE) { + g_ble_phy_data.phy_state = BLE_PHY_STATE_TX; + } + STATS_INC(ble_phy_stats, tx_good); + STATS_INCN(ble_phy_stats, tx_bytes, txbuf[1] + 2); + rc = BLE_ERR_SUCCESS; + } + + /* Now we can handle BS_CTRL */ + CMAC->CM_ERROR_DIS_REG &= ~CMAC_CM_ERROR_DIS_REG_CM_FIELD1_ERR_Msk; + NVIC_EnableIRQ(FRAME_IRQn); + NVIC_EnableIRQ(FIELD_IRQn); + + return rc; +} + +int +ble_phy_tx_set_start_time(uint32_t cputime, uint8_t rem_usecs) +{ + uint8_t rf_chan = g_ble_phy_chan_to_rf[g_ble_phy_data.channel]; + uint32_t ll_val32; + int rc; + + MCU_DIAG_SER('t'); + +#if (BLE_LL_BT5_PHY_SUPPORTED == 1) + ble_phy_mode_apply(g_ble_phy_data.phy_mode_tx); +#endif + + assert(ble_rf_is_enabled()); + + ble_rf_configure(); + ble_rf_setup_tx(rf_chan, g_ble_phy_data.phy_mode_tx); + + ll_val32 = ble_phy_convert_and_record_start_time(cputime, rem_usecs); + ll_val32 += rem_usecs; + ll_val32 -= PHY_DELAY_POWER_UP_TX + g_ble_phy_data.path_delay_tx; + /* we can schedule TX only up to 1023us in advance */ + assert((int32_t)(ll_val32 - cmac_timer_read32()) < 1024); + + /* + * We do not want FIELD/FRAME interrupts or FIELD1_ERR until ble_phy_tx() + * has finished pushing all the fields. Also we do not want premature + * FRAME_ERR so disable it until we program FRAME1 properly. If we won't + * make configuration on time, assume tx_late and abort TX. + */ + NVIC_DisableIRQ(FRAME_IRQn); + NVIC_DisableIRQ(FIELD_IRQn); + CMAC->CM_ERROR_DIS_REG |= CMAC_CM_ERROR_DIS_REG_CM_FIELD1_ERR_Msk | + CMAC_CM_ERROR_DIS_REG_CM_FRAME_ERR_Msk; + + CMAC->CM_LL_TIMER1_9_0_EQ_X_REG = ll_val32; + CMAC->CM_EV_LINKUP_REG = CMAC_CM_EV_LINKUP_REG_LU_FRAME_START_2_TMR1_9_0_EQ_X_Msk; + + if ((int32_t)(ll_val32 - cmac_timer_read32()) < 0) { + goto tx_late; + } + + /* + * Program frame now since it needs to be ready for FRAME_START, we can + * push fields later + */ + CMAC->CM_FRAME_1_REG = CMAC_CM_FRAME_1_REG_FRAME_VALID_Msk | + CMAC_CM_FRAME_1_REG_FRAME_TX_Msk | + CMAC_CM_FRAME_1_REG_FRAME_EXC_ON_BS_START_Msk; + + /* + * There should be no EXC_FRAME_START here so if it already happened we + * need to assume tx_late and abort. + */ + if (CMAC->CM_EXC_STAT_REG & CMAC_CM_EXC_STAT_REG_EXC_FRAME_START_Msk) { + goto tx_late; + } + + CMAC->CM_ERROR_DIS_REG &= ~CMAC_CM_ERROR_DIS_REG_CM_FRAME_ERR_Msk; + rc = 0; + + goto done; + +tx_late: + STATS_INC(ble_phy_stats, tx_late); + ble_phy_disable(); + NVIC_EnableIRQ(FRAME_IRQn); + NVIC_EnableIRQ(FIELD_IRQn); + rc = BLE_PHY_ERR_TX_LATE; + +done: + return rc; +} + +void +ble_phy_set_txend_cb(ble_phy_tx_end_func txend_cb, void *arg) +{ + g_ble_phy_data.txend_cb = txend_cb; + g_ble_phy_data.txend_arg = arg; +} + +#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LE_ENCRYPTION) +void +ble_phy_tx_enc_start(void) +{ + struct ble_phy_encrypt_obj *enc; + + enc = &g_ble_phy_encrypt_data; + enc->b0[15] = g_ble_phy_tx_buf[1] - 4; + enc->b1[2] = g_ble_phy_tx_buf[0] & BLE_LL_DATA_HDR_LLID_MASK; + + /* XXX: should we check for busy? */ + /* XXX: might not be needed, but for now terminate any crypto operations. */ + CMAC->CM_CRYPTO_CTRL_REG = CMAC_CM_CRYPTO_CTRL_REG_CM_CRYPTO_SW_REQ_ABORT_Msk; + + CMAC->CM_CRYPTO_CTRL_REG = CMAC_CM_CRYPTO_CTRL_REG_CM_CRYPTO_IN_SEL_Msk | + CMAC_CM_CRYPTO_CTRL_REG_CM_CRYPTO_CTR_MAC_EN_Msk | + CMAC_CM_CRYPTO_CTRL_REG_CM_CRYPTO_CTR_PLD_EN_Msk | + CMAC_CM_CRYPTO_CTRL_REG_CM_CRYPTO_AUTH_EN_Msk | + CMAC_CM_CRYPTO_CTRL_REG_CM_CRYPTO_ENC_DECN_Msk; + + /* Start crypto */ + CMAC->CM_EV_SET_REG = CMAC_CM_EV_SET_REG_EV_CRYPTO_START_Msk; +} + +void +ble_phy_rx_enc_start(uint8_t len) +{ + struct ble_phy_encrypt_obj *enc; + + enc = &g_ble_phy_encrypt_data; + enc->b0[15] = len - 4; /* length without MIC as length includes MIC */ + enc->b1[2] = g_ble_phy_rx_buf[0] & BLE_LL_DATA_HDR_LLID_MASK; + + /* XXX: should we check for busy? */ + /* XXX: might not be needed, but for now terminate any crypto operations. */ + //CMAC->CM_CRYPTO_CTRL_REG = CMAC_CM_CRYPTO_CTRL_REG_CM_CRYPTO_SW_REQ_ABORT_Msk; + + CMAC->CM_CRYPTO_CTRL_REG = CMAC_CM_CRYPTO_CTRL_REG_CM_CRYPTO_OUT_SEL_Msk | + CMAC_CM_CRYPTO_CTRL_REG_CM_CRYPTO_CTR_MAC_EN_Msk | + CMAC_CM_CRYPTO_CTRL_REG_CM_CRYPTO_CTR_PLD_EN_Msk | + CMAC_CM_CRYPTO_CTRL_REG_CM_CRYPTO_AUTH_EN_Msk; + + /* Start crypto */ + CMAC->CM_EV_SET_REG = CMAC_CM_EV_SET_REG_EV_CRYPTO_START_Msk; +} + +void +ble_phy_encrypt_enable(uint64_t pkt_counter, uint8_t *iv, uint8_t *key, + uint8_t is_master) +{ + struct ble_phy_encrypt_obj *enc; + + enc = &g_ble_phy_encrypt_data; + memcpy(enc->key, key, 16); + memcpy(&enc->b0[6], iv, 8); + put_le32(&enc->b0[1], pkt_counter); + enc->b0[5] = is_master ? 0x80 : 0; + memcpy(&enc->ai[6], iv, 8); + put_le32(&enc->ai[1], pkt_counter); + enc->ai[5] = enc->b0[5]; + + g_ble_phy_data.phy_encrypted = 1; + + /* Program key registers */ + CMAC->CM_CRYPTO_KEY_31_0_REG = get_le32(&enc->key[0]); + CMAC->CM_CRYPTO_KEY_63_32_REG = get_le32(&enc->key[4]); + CMAC->CM_CRYPTO_KEY_95_64_REG = get_le32(&enc->key[8]); + CMAC->CM_CRYPTO_KEY_127_96_REG = get_le32(&enc->key[12]); + + /* Program ADRx registers */ + CMAC->CM_CRYPTO_IN_ADR0_REG = (uint32_t)enc->b1; + CMAC->CM_CRYPTO_IN_ADR1_REG = (uint32_t)enc->b0; + CMAC->CM_CRYPTO_IN_ADR2_REG = (uint32_t)&g_ble_phy_tx_buf[2]; + CMAC->CM_CRYPTO_IN_ADR3_REG = (uint32_t)enc->ai; + CMAC->CM_CRYPTO_OUT_ADR_REG = (uint32_t)&g_ble_phy_rx_buf[2]; + + CMAC->CM_CRYPTO_CTRL_REG = CMAC_CM_CRYPTO_CTRL_REG_CM_CRYPTO_SW_REQ_PBUF_CLR_Msk; +} + +void +ble_phy_encrypt_set_pkt_cntr(uint64_t pkt_counter, int dir) +{ + struct ble_phy_encrypt_obj *enc; + + enc = &g_ble_phy_encrypt_data; + put_le32(&enc->b0[1], pkt_counter); + enc->b0[5] = dir ? 0x80 : 0; + put_le32(&enc->ai[1], pkt_counter); + enc->ai[5] = enc->b0[5]; + + CMAC->CM_CRYPTO_CTRL_REG = CMAC_CM_CRYPTO_CTRL_REG_CM_CRYPTO_SW_REQ_PBUF_CLR_Msk; +} + +void +ble_phy_encrypt_disable(void) +{ + g_ble_phy_data.phy_encrypted = 0; +} +#endif + +int +ble_phy_txpwr_set(int dbm) +{ +#if MYNEWT_VAL(CMAC_DEBUG_DATA_ENABLE) + if (g_cmac_shared_data.debug.tx_power_override != INT8_MAX) { + ble_rf_set_tx_power(g_cmac_shared_data.debug.tx_power_override); + } else { + ble_rf_set_tx_power(dbm); + } +#else + ble_rf_set_tx_power(dbm); +#endif + + return 0; +} + +int +ble_phy_txpower_round(int dbm) +{ + return 0; +} + +void +ble_phy_set_rx_pwr_compensation(int8_t compensation) +{ +} + +int +ble_phy_setchan(uint8_t chan, uint32_t access_addr, uint32_t crc_init) +{ + uint8_t rf_chan = g_ble_phy_chan_to_rf[chan]; + + assert(chan < BLE_PHY_NUM_CHANS); + + if (chan >= BLE_PHY_NUM_CHANS) { + return BLE_PHY_ERR_INV_PARAM; + } + + g_ble_phy_data.channel = chan; + g_ble_phy_data.access_addr = access_addr; + g_ble_phy_data.crc_init = crc_init; + + CMAC_SETREGF(CM_PHY_CTRL2_REG, PHY_RF_CHANNEL, rf_chan); + + return 0; +} + +void +ble_phy_restart_rx(void) +{ + /* XXX: for now, we will disable the phy using ble_phy_disable and then + re-enable it + */ + ble_phy_disable(); + + /* Apply mode before starting RX */ +#if (BLE_LL_BT5_PHY_SUPPORTED == 1) + ble_phy_mode_apply(g_ble_phy_data.phy_mode_rx); +#endif + + /* Setup phy to rx again */ + ble_phy_rx(); + + /* Start reception now */ + CMAC->CM_EV_LINKUP_REG = CMAC_CM_EV_LINKUP_REG_LU_FRAME_START_2_ASAP_Msk; +} + +uint32_t +ble_phy_access_addr_get(void) +{ + return g_ble_phy_data.access_addr; +} + +int +ble_phy_state_get(void) +{ + return g_ble_phy_data.phy_state; +} + +int +ble_phy_rx_started(void) +{ + return g_ble_phy_data.phy_rx_started; +} + +uint8_t +ble_phy_xcvr_state_get(void) +{ + return ble_rf_is_enabled(); +} + +uint8_t +ble_phy_max_data_pdu_pyld(void) +{ + return BLE_LL_DATA_PDU_MAX_PYLD; +} + +void +ble_phy_resolv_list_enable(void) +{ + g_ble_phy_data.phy_privacy = 1; + + ble_hw_resolv_proc_enable(); +} + +void +ble_phy_resolv_list_disable(void) +{ + ble_hw_resolv_proc_disable(); + + g_ble_phy_data.phy_privacy = 0; +} + +void +ble_phy_rfclk_enable(void) +{ + ble_rf_enable(); +} + +void +ble_phy_rfclk_disable(void) +{ + /* XXX We can't disable RF while PHY_BUSY is asserted so let's wait a bit */ + while (CMAC->CM_DIAG_WORD2_REG & CMAC_CM_DIAG_WORD2_REG_DIAG2_PHY_BUSY_BUF_Msk); + + ble_rf_disable(); +} + +#if MYNEWT_VAL(BLE_LL_DTM) +void +ble_phy_enable_dtm(void) +{ + g_ble_phy_data.phy_whitening = 0; +} + +void +ble_phy_disable_dtm(void) +{ + g_ble_phy_data.phy_whitening = 1; +} +#endif diff --git a/src/libs/mynewt-nimble/nimble/drivers/dialog_cmac/src/ble_rf.c b/src/libs/mynewt-nimble/nimble/drivers/dialog_cmac/src/ble_rf.c new file mode 100644 index 00000000..806f4ea8 --- /dev/null +++ b/src/libs/mynewt-nimble/nimble/drivers/dialog_cmac/src/ble_rf.c @@ -0,0 +1,747 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +#include +#include +#include +#include "mcu/mcu.h" +#include "mcu/cmac_timer.h" +#include "controller/ble_phy.h" +#include "cmac_driver/cmac_shared.h" +#include "ble_rf_priv.h" + +#define RF_CALIBRATION_0 (0x01) +#define RF_CALIBRATION_1 (0x02) +#define RF_CALIBRATION_2 (0x04) + +static const int8_t g_ble_rf_power_lvls[] = { + -18, -12, -8, -6, -3, -2, -1, 0, 1, 2, 3, 4, 4, 5, 6 +}; + +struct ble_phy_rf_data { + uint8_t tx_power_cfg0; + uint8_t tx_power_cfg1; + uint8_t tx_power_cfg2; + uint8_t tx_power_cfg3; + uint32_t cal_res_1; + uint32_t cal_res_2; + uint32_t trim_val1_tx_1; + uint32_t trim_val1_tx_2; + uint32_t trim_val2_tx; + uint32_t trim_val2_rx; + uint8_t calibrate_req; +}; + +static struct ble_phy_rf_data g_ble_phy_rf_data; + +static inline uint32_t +get_reg32(uint32_t addr) +{ + volatile uint32_t *reg = (volatile uint32_t *)addr; + + return *reg; +} + +static inline uint32_t +get_reg32_bits(uint32_t addr, uint32_t mask) +{ + volatile uint32_t *reg = (volatile uint32_t *)addr; + + return (*reg & mask) >> __builtin_ctz(mask); +} + +static inline void +set_reg8(uint32_t addr, uint8_t val) +{ + volatile uint8_t *reg = (volatile uint8_t *)addr; + + *reg = val; +} + +static inline void +set_reg16(uint32_t addr, uint16_t val) +{ + volatile uint16_t *reg = (volatile uint16_t *)addr; + + *reg = val; +} + +static inline void +set_reg32(uint32_t addr, uint32_t val) +{ + volatile uint32_t *reg = (volatile uint32_t *)addr; + + *reg = val; +} + +static inline void +set_reg32_bits(uint32_t addr, uint32_t mask, uint32_t val) +{ + volatile uint32_t *reg = (volatile uint32_t *)addr; + + *reg = (*reg & (~mask)) | (val << __builtin_ctz(mask)); +} + +static inline void +set_reg32_mask(uint32_t addr, uint32_t mask, uint32_t val) +{ + volatile uint32_t *reg = (volatile uint32_t *)addr; + + *reg = (*reg & (~mask)) | (val & mask); +} + +static inline void +set_reg16_mask(uint32_t addr, uint16_t mask, uint16_t val) +{ + volatile uint16_t *reg = (volatile uint16_t *)addr; + + *reg = (*reg & (~mask)) | (val & mask); +} + +static void +delay_us(uint32_t delay_us) +{ + while (delay_us--) { + __NOP(); __NOP(); __NOP(); __NOP(); __NOP(); __NOP(); __NOP(); __NOP(); + __NOP(); __NOP(); __NOP(); __NOP(); __NOP(); __NOP(); __NOP(); __NOP(); + __NOP(); __NOP(); __NOP(); __NOP(); __NOP(); __NOP(); __NOP(); __NOP(); + __NOP(); __NOP(); __NOP(); __NOP(); + } +} + +static void +ble_rf_apply_trim(volatile uint32_t *tv, unsigned len) +{ + while (len) { + *(volatile uint32_t *)tv[0] = tv[1]; + len -= 2; + tv += 2; + } +} + +static void +ble_rf_apply_calibration(void) +{ + set_reg32(0x40020094, g_ble_phy_rf_data.cal_res_1); + if (g_ble_phy_rf_data.cal_res_2) { + set_reg32_bits(0x40022018, 0xff800000, g_ble_phy_rf_data.cal_res_2); + set_reg32_bits(0x40022018, 0x00007fc0, g_ble_phy_rf_data.cal_res_2); + } +} + +static inline void +ble_rf_ldo_on(void) +{ + set_reg8(0x40020004, 9); +} + +static inline void +ble_rf_ldo_off(void) +{ + set_reg8(0x40020004, 0); +} + +static inline void +ble_rf_rfcu_enable(void) +{ + set_reg32_bits(0x50000010, 0x00000020, 1); +} + +static inline void +ble_rf_rfcu_disable(void) +{ + set_reg32_bits(0x50000010, 0x00000020, 0); +} + +static void +ble_rf_rfcu_apply_recommended_settings(void) +{ + set_reg16_mask(0x400200a0, 0x0001, 0x0001); + set_reg16_mask(0x40021020, 0x03f0, 0x02f5); + set_reg32_mask(0x40021018, 0x001fffff, 0x005a5809); + set_reg32_mask(0x4002101c, 0x00001e01, 0x0040128c); + set_reg32_mask(0x40021004, 0xffffff1f, 0x64442404); + set_reg32_mask(0x40021008, 0xfcfcffff, 0x6b676665); + set_reg32_mask(0x4002100c, 0x00fcfcfc, 0x9793736f); + set_reg32_mask(0x40021010, 0x1f1f1c1f, 0x04072646); + set_reg32_mask(0x40020000, 0x001ff000, 0x0f099820); + set_reg16_mask(0x40020348, 0x00ff, 0x0855); + set_reg16(0x40020350, 0x0234); + set_reg16(0x40020354, 0x0a34); + set_reg16(0x40020358, 0x0851); + set_reg16(0x4002035c, 0x0a26); + set_reg16(0x40020360, 0x0858); + set_reg16(0x4002102c, 0xdfe7); + set_reg32_mask(0x4002103c, 0x00c00000, 0x0024a19f); + set_reg16_mask(0x40021000, 0x0008, 0x000b); + set_reg16_mask(0x40020238, 0x03e0, 0x02c0); + set_reg16_mask(0x4002023c, 0x03e0, 0x02c0); + set_reg16_mask(0x40020244, 0x03e0, 0x0250); + set_reg16_mask(0x40020248, 0x03e0, 0x02a0); + set_reg16_mask(0x4002024c, 0x03e0, 0x02c0); + set_reg16_mask(0x40020288, 0x03e0, 0x0300); + set_reg16_mask(0x4002029c, 0x001f, 0x0019); + set_reg16_mask(0x4002003c, 0x6000, 0x0788); + set_reg16_mask(0x40020074, 0x7f00, 0x2007); + set_reg32_mask(0x40020080, 0x00333330, 0x00222224); + set_reg32_mask(0x40020068, 0x00000f0f, 0x00000f0d); +} + +static void +ble_rf_rfcu_apply_settings(void) +{ + ble_rf_apply_trim(g_cmac_shared_data.trim.rfcu, + g_cmac_shared_data.trim.rfcu_len); + ble_rf_rfcu_apply_recommended_settings(); +} + +static inline void +ble_rf_synth_enable(void) +{ + set_reg8(0x40020005, 3); +} + +static inline void +ble_rf_synth_disable(void) +{ + set_reg8(0x40020005, 0); + __NOP(); + __NOP(); +} + +static bool +ble_rf_synth_is_enabled(void) +{ + return get_reg32_bits(0x40020004, 256); +} + +static void +ble_rf_synth_apply_recommended_settings(void) +{ + set_reg32_mask(0x40022048, 0x0000000c, 0x000000d5); + set_reg32_mask(0x40022050, 0x00000300, 0x00000300); + set_reg16_mask(0x40022024, 0x0001, 0x0001); +} + +static void +ble_rf_synth_apply_settings(void) +{ + ble_rf_apply_trim(g_cmac_shared_data.trim.synth, + g_cmac_shared_data.trim.synth_len); + ble_rf_synth_apply_recommended_settings(); +} + +static void +ble_rf_calibration_0(void) +{ + uint32_t bkp[10]; + + bkp[0] = get_reg32(0x40020208); + bkp[1] = get_reg32(0x40020250); + bkp[2] = get_reg32(0x40020254); + bkp[3] = get_reg32(0x40021028); + bkp[4] = get_reg32(0x40020020); + bkp[5] = get_reg32(0x40020294); + bkp[6] = get_reg32(0x4002103C); + bkp[7] = get_reg32(0x400200A8); + bkp[8] = get_reg32(0x40020000); + bkp[9] = get_reg32(0x40022000); + + set_reg32_bits(0x40020000, 0x00000002, 0); + set_reg32_bits(0x40022000, 0x00000001, 0); + set_reg32_mask(0x4002103C, 0x00201c00, 0x00001c00); + set_reg32_bits(0x400200A8, 0x00000001, 1); + set_reg8(0x40020006, 1); + set_reg32(0x40020208, 0); + set_reg32(0x40020250, 0); + set_reg32(0x40020254, 0); + set_reg32(0x40021028, 0x00F8A494); + set_reg32(0x40020020, 8); + set_reg32(0x40020294, 0); + set_reg32(0x40020024, 0); + + delay_us(5); + if (get_reg32_bits(0x40020020, 0x00000002)) { + goto done; + } + + set_reg32_bits(0x40020020, 0x00000001, 1); + delay_us(15); + if (!get_reg32_bits(0x40020020, 0x00000001)) { + goto done; + } + + delay_us(300); + if (get_reg32_bits(0x40020020, 0x00000001)) { + goto done; + } + +done: + set_reg32(0x40020024, 0); + set_reg32(0x40020208, bkp[0]); + set_reg32(0x40020250, bkp[1]); + set_reg32(0x40020254, bkp[2]); + set_reg32(0x40021028, bkp[3]); + set_reg32(0x40020020, bkp[4]); + set_reg32(0x40020294, bkp[5]); + set_reg32(0x4002103C, bkp[6]); + set_reg32(0x400200A8, bkp[7]); + set_reg32(0x40020000, bkp[8]); + set_reg32(0x40022000, bkp[9]); +} + +static void +ble_rf_calibration_1(void) +{ + uint32_t bkp[12]; + uint32_t val; + + bkp[0] = get_reg32(0x40020020); + bkp[1] = get_reg32(0x40020208); + bkp[2] = get_reg32(0x40020250); + bkp[3] = get_reg32(0x40020254); + bkp[4] = get_reg32(0x40020218); + bkp[5] = get_reg32(0x4002021c); + bkp[6] = get_reg32(0x40020220); + bkp[7] = get_reg32(0x40020270); + bkp[8] = get_reg32(0x4002027c); + bkp[9] = get_reg32(0x4002101c); + bkp[10] = get_reg32(0x40020000); + bkp[11] = get_reg32(0x40022000); + + set_reg32(0x4002103c, 0x0124a21f); + set_reg32(0x40020208, 0); + set_reg32(0x40020250, 0); + set_reg32(0x40020254, 0); + set_reg32(0x40020218, 0); + set_reg32(0x4002021c, 0); + set_reg32(0x40020220, 0); + set_reg32(0x40020270, 0); + set_reg32(0x4002027c, 0); + set_reg32(0x40020000, 0x0f168820); + set_reg32_bits(0x40022000, 0x00000001, 0); + set_reg32_bits(0x4002101c, 0x00001e00, 0); + set_reg32_bits(0x4002001c, 0x0000003f, 47); + set_reg8(0x40020006, 1); + set_reg32(0x40020020, 16); + set_reg32_bits(0x4002003c, 0x00000800, 1); + set_reg32(0x40020024, 0); + + delay_us(5); + if (get_reg32_bits(0x40020020, 0x00000002)) { + goto done; + } + + set_reg32_bits(0x40020020, 0x00000001, 1); + delay_us(15); + if (!get_reg32_bits(0x40020020, 0x00000001)) { + goto done; + } + + delay_us(300); + if (get_reg32_bits(0x40020020, 0x00000001)) { + goto done; + } + + val = get_reg32(0x40020090); + set_reg32_bits(0x40020094, 0x0000000f, val); + set_reg32_bits(0x40020094, 0x00000f00, val); + set_reg32_bits(0x40020094, 0x000f0000, val); + set_reg32_bits(0x40020094, 0x0f000000, val); + g_ble_phy_rf_data.cal_res_1 = get_reg32(0x40020094); + +done: + set_reg32(0x40020024, 0); + set_reg32(0x40020020, bkp[0]); + set_reg32(0x40020208, bkp[1]); + set_reg32(0x40020250, bkp[2]); + set_reg32(0x40020254, bkp[3]); + set_reg32(0x40020218, bkp[4]); + set_reg32(0x4002021c, bkp[5]); + set_reg32(0x40020220, bkp[6]); + set_reg32(0x40020270, bkp[7]); + set_reg32(0x4002027c, bkp[8]); + set_reg32(0x4002101c, bkp[9]); + set_reg32(0x40020000, bkp[10]); + set_reg32(0x40022000, bkp[11]); + set_reg32_bits(0x4002003c, 0x00000800, 0); +} + +static void +ble_rf_calibration_2(void) +{ + uint32_t bkp[2]; + uint32_t k1; + + set_reg8(0x40020005, 3); + set_reg32(0x40022000, 0x00000300); + set_reg32_bits(0x40022004, 0x0000007f, 20); + bkp[0] = get_reg32(0x40022040); + set_reg32(0x40022040, 0xffffffff); + set_reg32_bits(0x40022018, 0x0000003f, 0); + set_reg32_bits(0x40022018, 0x00008000, 0); + set_reg32_bits(0x4002201c, 0x00000600, 2); + set_reg32_bits(0x4002201c, 0x00000070, 4); + set_reg32_bits(0x40022030, 0x3f000000, 22); + set_reg32_bits(0x40022030, 0x00000fc0, 24); + set_reg32_bits(0x40022030, 0x0000003f, 24); + set_reg8(0x4002201c, 0x43); + set_reg8(0x40020006, 2); + delay_us(2); + bkp[1] = get_reg32_bits(0x4002024c, 0x000003e0); + set_reg32_bits(0x4002024c, 0x000003e0, 0); + set_reg8(0x40020006, 1); + set_reg32_bits(0x400200ac, 0x00000003, 3); + delay_us(30); + delay_us(100); + set_reg8(0x40020005, 3); + k1 = get_reg32_bits(0x40022088, 0x000001ff); + set_reg32(0x400200ac, 0); + delay_us(20); + set_reg32_bits(0x4002024c, 0x000003e0, bkp[1]); + delay_us(10); + + set_reg32_bits(0x40022018, 0xff800000, k1); + set_reg32_bits(0x40022018, 0x00007fc0, k1); + set_reg8(0x4002201c, 0x41); + set_reg32_bits(0x4002201c, 0x00000600, 2); + set_reg8(0x40020006, 2); + delay_us(2); + bkp[1] = get_reg32_bits(0x4002024c, 0x000003e0); + set_reg32_bits(0x4002024c, 0x000003e0, 0); + set_reg8(0x40020006, 1); + set_reg32_bits(0x400200ac, 0x00000003, 3); + delay_us(30); + delay_us(100); + set_reg8(0x40020005, 3); + k1 = get_reg32_bits(0x40022088, 0x1ff); + set_reg32(0x400200ac, 0); + delay_us(20); + set_reg32_bits(0x4002024c, 0x000003e0, bkp[1]); + delay_us(10); + + set_reg32_bits(0x40022018, 0xff800000, k1); + set_reg32_bits(0x40022018, 0x00007fc0, k1); + set_reg8(0x4002201c, 0x41); + set_reg32_bits(0x4002201c, 0x00000600, 2); + set_reg8(0x40020006, 2); + delay_us(2); + bkp[1] = get_reg32_bits(0x4002024c, 0x000003e0); + set_reg32_bits(0x4002024c, 0x000003e0, 0); + set_reg8(0x40020006, 1); + set_reg32_bits(0x400200ac, 0x00000003, 3); + delay_us(30); + delay_us(100); + set_reg8(0x40020005, 3); + k1 = get_reg32_bits(0x40022088, 0x000001ff); + set_reg32_bits(0x40022018, 0xff800000, k1); + set_reg32_bits(0x40022018, 0x00007fc0, k1); + set_reg32_bits(0x4002201c, 0x00000001, 0); + set_reg32(0x40022040, bkp[0]); + set_reg32_bits(0x40022018, 0x0000003f, 0x1c); + set_reg32_bits(0x40022018, 0x00008000, 0); + set_reg32_bits(0x40022030, 0x3f000000, 28); + set_reg32_bits(0x40022030, 0x00000fc0, 30); + set_reg32_bits(0x40022030, 0x0000003f, 30); + set_reg32(0x400200ac, 0); + delay_us(20); + set_reg32_bits(0x4002024c, 0x000003e0, bkp[1]); + delay_us(10); + + g_ble_phy_rf_data.cal_res_2 = k1; +} + +static void +ble_rf_calibrate_int(uint8_t mask) +{ + __disable_irq(); + + ble_rf_enable(); + delay_us(20); + + ble_rf_synth_disable(); + ble_rf_synth_enable(); + ble_rf_synth_apply_settings(); + set_reg8(0x40020005, 1); + + if (mask & RF_CALIBRATION_0) { + ble_rf_calibration_0(); + } + if (mask & RF_CALIBRATION_1) { + ble_rf_calibration_1(); + } + if (mask & RF_CALIBRATION_2) { + ble_rf_calibration_2(); + } + + ble_rf_disable(); + + __enable_irq(); + +#if MYNEWT_VAL(CMAC_DEBUG_DATA_ENABLE) + g_cmac_shared_data.debug.cal_res_1 = g_ble_phy_rf_data.cal_res_1; + g_cmac_shared_data.debug.cal_res_2 = g_ble_phy_rf_data.cal_res_2; +#endif +} + +bool +ble_rf_try_recalibrate(uint32_t idle_time_us) +{ + /* Run recalibration if we have at least 1ms of time to spare and RF is + * currently disabled. Calibration is much shorter than 1ms, but that gives + * us good margin to make sure we can finish before next event. + */ + if (!g_ble_phy_rf_data.calibrate_req || (idle_time_us < 1000) || + ble_rf_is_enabled()) { + return false; + } + + ble_rf_calibrate_int(RF_CALIBRATION_2); + + g_ble_phy_rf_data.calibrate_req = 0; + + return true; +} + +static uint32_t +ble_rf_find_trim_reg(volatile uint32_t *tv, unsigned len, uint32_t reg) +{ + while (len) { + if (tv[0] == reg) { + return tv[1]; + } + len -= 2; + tv += 2; + } + + return 0; +} + +void +ble_rf_init(void) +{ + static bool done = false; + uint32_t val; + + ble_rf_disable(); + + if (done) { + return; + } + + val = ble_rf_find_trim_reg(g_cmac_shared_data.trim.rfcu_mode1, + g_cmac_shared_data.trim.rfcu_mode1_len, + 0x4002004c); + g_ble_phy_rf_data.trim_val1_tx_1 = val; + + val = ble_rf_find_trim_reg(g_cmac_shared_data.trim.rfcu_mode2, + g_cmac_shared_data.trim.rfcu_mode2_len, + 0x4002004c); + g_ble_phy_rf_data.trim_val1_tx_2 = val; + + if (!g_ble_phy_rf_data.trim_val1_tx_1 || !g_ble_phy_rf_data.trim_val1_tx_2) { + val = ble_rf_find_trim_reg(g_cmac_shared_data.trim.rfcu, + g_cmac_shared_data.trim.rfcu_len, + 0x4002004c); + if (!val) { + val = 0x0300; + } + g_ble_phy_rf_data.trim_val1_tx_1 = val; + g_ble_phy_rf_data.trim_val1_tx_2 = val; + } + + val = ble_rf_find_trim_reg(g_cmac_shared_data.trim.synth, + g_cmac_shared_data.trim.synth_len, + 0x40022038); + if (!val) { + val = 0x0198ff03; + } + g_ble_phy_rf_data.trim_val2_rx = val; + g_ble_phy_rf_data.trim_val2_tx = val; + set_reg32_bits((uint32_t)&g_ble_phy_rf_data.trim_val2_tx, 0x0001ff00, 0x87); + +#if MYNEWT_VAL(CMAC_DEBUG_DATA_ENABLE) + g_cmac_shared_data.debug.trim_val1_tx_1 = g_ble_phy_rf_data.trim_val1_tx_1; + g_cmac_shared_data.debug.trim_val1_tx_2 = g_ble_phy_rf_data.trim_val1_tx_2; + g_cmac_shared_data.debug.trim_val2_tx = g_ble_phy_rf_data.trim_val2_tx; + g_cmac_shared_data.debug.trim_val2_rx = g_ble_phy_rf_data.trim_val2_rx; +#endif + + ble_rf_rfcu_enable(); + ble_rf_rfcu_apply_settings(); + g_ble_phy_rf_data.tx_power_cfg1 = get_reg32_bits(0x500000a4, 0xf0); + g_ble_phy_rf_data.tx_power_cfg2 = get_reg32_bits(0x40020238, 0x000003e0); + g_ble_phy_rf_data.tx_power_cfg3 = 0; + ble_rf_rfcu_disable(); + + ble_rf_calibrate_int(RF_CALIBRATION_0 | RF_CALIBRATION_1 | RF_CALIBRATION_2); + + done = true; +} + +void +ble_rf_enable(void) +{ + if (ble_rf_is_enabled()) { + return; + } + + ble_rf_rfcu_enable(); + ble_rf_rfcu_apply_settings(); + ble_rf_ldo_on(); +} + +void +ble_rf_configure(void) +{ + if (ble_rf_synth_is_enabled()) { + return; + } + + ble_rf_synth_enable(); + ble_rf_synth_apply_settings(); +} + +void +ble_rf_stop(void) +{ + ble_rf_synth_disable(); + set_reg8(0x40020006, 0); +} + +void +ble_rf_disable(void) +{ + ble_rf_stop(); + ble_rf_ldo_off(); + ble_rf_rfcu_disable(); +} + +bool +ble_rf_is_enabled(void) +{ + return get_reg32_bits(0x40020008, 5) == 5; +} + +void +ble_rf_calibrate_req(void) +{ + g_ble_phy_rf_data.calibrate_req = 1; +} + +void +ble_rf_setup_tx(uint8_t rf_chan, uint8_t phy_mode) +{ + set_reg32_bits(0x40020000, 0x0f000000, g_ble_phy_rf_data.tx_power_cfg0); + set_reg32_bits(0x500000a4, 0x000000f0, g_ble_phy_rf_data.tx_power_cfg1); + set_reg32_bits(0x40020238, 0x000003e0, g_ble_phy_rf_data.tx_power_cfg2); + set_reg32_bits(0x40020234, 0x000003e0, g_ble_phy_rf_data.tx_power_cfg3); + + if (g_ble_phy_rf_data.tx_power_cfg0 < 13) { + set_reg32(0x4002004c, g_ble_phy_rf_data.trim_val1_tx_1); + } else { + set_reg32(0x4002004c, g_ble_phy_rf_data.trim_val1_tx_2); + } + set_reg8(0x40020005, 3); + set_reg8(0x40022004, rf_chan); + if (phy_mode == BLE_PHY_MODE_2M) { +#if MYNEWT_VAL(BLE_PHY_RF_HP_MODE) + set_reg32(0x40022000, 0x00000303); +#else + set_reg32(0x40022000, 0x00000003); +#endif + } else { +#if MYNEWT_VAL(BLE_PHY_RF_HP_MODE) + set_reg32(0x40022000, 0x00000300); +#else + set_reg32(0x40022000, 0x00000000); +#endif + } + + ble_rf_apply_calibration(); + + set_reg32_bits(0x40022050, 0x00000200, 1); + set_reg32_bits(0x40022050, 0x00000100, 0); + set_reg32_bits(0x40022048, 0x01ffff00, 0x7700); + set_reg32(0x40022038, g_ble_phy_rf_data.trim_val2_tx); + + set_reg8(0x40020006, 3); +} + +void +ble_rf_setup_rx(uint8_t rf_chan, uint8_t phy_mode) +{ + set_reg32_bits(0x500000a4, 0x000000f0, g_ble_phy_rf_data.tx_power_cfg1); + set_reg8(0x40020005, 3); + set_reg8(0x40022004, rf_chan); + if (phy_mode == BLE_PHY_MODE_2M) { +#if MYNEWT_VAL(BLE_PHY_RF_HP_MODE) + set_reg32(0x40022000, 0x00000303); + set_reg32(0x40020000, 0x0f11b823); + set_reg32(0x4002103c, 0x0125261b); +#else + set_reg32(0x40022000, 0x00000003); + set_reg32(0x40020000, 0x0f0c2803); + set_reg32(0x4002103c, 0x0125a61b); +#endif + set_reg32(0x40021020, 0x000002f5); + set_reg32(0x4002102c, 0x0000d1d5); + } else { +#if MYNEWT_VAL(BLE_PHY_RF_HP_MODE) + set_reg32(0x40022000, 0x00000300); + set_reg32(0x40020000, 0x0f099820); + set_reg32(0x4002103c, 0x0124a21f); +#else + set_reg32(0x40022000, 0x00000000); + set_reg32(0x40020000, 0x0f062800); + set_reg32(0x4002103c, 0x01051e1f); +#endif + set_reg32(0x40021020, 0x000002f5); + set_reg32(0x4002102c, 0x0000dfe7); + } + + ble_rf_apply_calibration(); + + set_reg32_bits(0x40022050, 0x00000200, 1); + set_reg32_bits(0x40022050, 0x00000100, 1); + set_reg32_bits(0x40022048, 0x01ffff00, 0); + set_reg32(0x40022038, g_ble_phy_rf_data.trim_val2_rx); + + set_reg8(0x40020006, 3); +} + +void +ble_rf_set_tx_power(int dbm) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(g_ble_rf_power_lvls); i++) { + if (g_ble_rf_power_lvls[i] >= dbm) { + break; + } + } + + g_ble_phy_rf_data.tx_power_cfg0 = i + 1; +} + +int8_t +ble_rf_get_rssi(void) +{ + return (501 * get_reg32_bits(0x40021038, 0x000003ff) - 493000) / 4096; +} diff --git a/src/libs/mynewt-nimble/nimble/drivers/dialog_cmac/src/ble_rf_priv.h b/src/libs/mynewt-nimble/nimble/drivers/dialog_cmac/src/ble_rf_priv.h new file mode 100644 index 00000000..50c2e4a7 --- /dev/null +++ b/src/libs/mynewt-nimble/nimble/drivers/dialog_cmac/src/ble_rf_priv.h @@ -0,0 +1,38 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +#ifndef _BLE_RF_PRIV_H_ +#define _BLE_RF_PRIV_H_ + +void ble_rf_init(void); +void ble_rf_enable(void); +void ble_rf_stop(void); +void ble_rf_disable(void); +bool ble_rf_is_enabled(void); +void ble_rf_configure(void); + +void ble_rf_calibrate(void); + +void ble_rf_setup_tx(uint8_t rf_chan, uint8_t mode); +void ble_rf_setup_rx(uint8_t rf_chan, uint8_t mode); + +void ble_rf_set_tx_power(int dbm); +int8_t ble_rf_get_rssi(void); + +#endif /* _BLE_RF_PRIV_H_ */ diff --git a/src/libs/mynewt-nimble/nimble/drivers/dialog_cmac/syscfg.yml b/src/libs/mynewt-nimble/nimble/drivers/dialog_cmac/syscfg.yml new file mode 100644 index 00000000..a82b62e5 --- /dev/null +++ b/src/libs/mynewt-nimble/nimble/drivers/dialog_cmac/syscfg.yml @@ -0,0 +1,29 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# + +syscfg.defs: + BLE_PHY_RF_HP_MODE: + description: Enable high-performance RF mode. + value: 1 + + BLE_PHY_DEBUG_DSER: + description: Enable DSER output from PHY + value: 0 + +syscfg.restrictions: + - BLE_LL_RFMGMT_ENABLE_TIME == 0 || BLE_LL_RFMGMT_ENABLE_TIME >= 20 -- cgit v1.2.3