From 35d2d474b84e2168ed58007d8bff170de2031708 Mon Sep 17 00:00:00 2001 From: Magdalena Kasenberg Date: Tue, 7 Jan 2025 11:17:59 +0100 Subject: [PATCH 1/3] nimble/drivers: Add initial support for nRF54L15 PHY --- nimble/drivers/nrf5x/pkg.yml | 22 +- nimble/drivers/nrf5x/src/ble_hw.c | 201 +++++++++++++++- nimble/drivers/nrf5x/src/ble_phy.c | 104 ++++---- nimble/drivers/nrf5x/src/nrf52/phy.c | 2 +- nimble/drivers/nrf5x/src/nrf52/phy_hw.h | 164 +++++++++++++ nimble/drivers/nrf5x/src/nrf53/phy.c | 2 +- nimble/drivers/nrf5x/src/nrf53/phy_hw.h | 20 ++ nimble/drivers/nrf5x/src/nrf54l15/phy.c | 251 ++++++++++++++++++++ nimble/drivers/nrf5x/src/nrf54l15/phy_hw.h | 172 ++++++++++++++ nimble/drivers/nrf5x/src/nrf54l15/phy_ppi.h | 167 +++++++++++++ nimble/drivers/nrf5x/src/phy_priv.h | 20 -- 11 files changed, 1033 insertions(+), 92 deletions(-) create mode 100644 nimble/drivers/nrf5x/src/nrf52/phy_hw.h create mode 100644 nimble/drivers/nrf5x/src/nrf53/phy_hw.h create mode 100644 nimble/drivers/nrf5x/src/nrf54l15/phy.c create mode 100644 nimble/drivers/nrf5x/src/nrf54l15/phy_hw.h create mode 100644 nimble/drivers/nrf5x/src/nrf54l15/phy_ppi.h diff --git a/nimble/drivers/nrf5x/pkg.yml b/nimble/drivers/nrf5x/pkg.yml index b99e669248..d7b7fcca5e 100644 --- a/nimble/drivers/nrf5x/pkg.yml +++ b/nimble/drivers/nrf5x/pkg.yml @@ -30,7 +30,21 @@ pkg.deps: - nimble - nimble/controller -pkg.ign_dirs.'MCU_TARGET=="nRF5340_NET"': - - nrf52 -pkg.ign_dirs.'MCU_TARGET!="nRF5340_NET"': - - nrf53 +pkg.source_files: + - "src/ble_hw.c" + - "src/ble_phy.c" + - "src/ble_phy_trace.c" + +pkg.source_files.'MCU_TARGET=="nRF52810" || MCU_TARGET=="nRF52811" || MCU_TARGET=="nRF52832" || MCU_TARGET=="nRF52840"': + - "src/nrf52/phy.c" +pkg.source_files.'MCU_TARGET=="nRF5340_NET"': + - "src/nrf53/phy.c" +pkg.source_files.'MCU_TARGET=="nRF54L15"': + - "src/nrf54l15/phy.c" + +pkg.include_dirs.'MCU_TARGET=="nRF52810" || MCU_TARGET=="nRF52811" || MCU_TARGET=="nRF52832" || MCU_TARGET=="nRF52840"': + - "src/nrf52/" +pkg.include_dirs.'MCU_TARGET=="nRF5340_NET"': + - "src/nrf53/" +pkg.include_dirs.'MCU_TARGET=="nRF54L15"': + - "src/nrf54l15/" diff --git a/nimble/drivers/nrf5x/src/ble_hw.c b/nimble/drivers/nrf5x/src/ble_hw.c index f8cd59f098..0089c21072 100644 --- a/nimble/drivers/nrf5x/src/ble_hw.c +++ b/nimble/drivers/nrf5x/src/ble_hw.c @@ -36,8 +36,8 @@ #include #endif #include "os/os_trace_api.h" -#include #include "hal/nrf_ecb.h" +#include "phy_hw.h" /* Total number of resolving list elements */ #define BLE_HW_RESOLV_LIST_SIZE (16) @@ -269,6 +269,7 @@ ble_hw_whitelist_match(void) } /* Encrypt data */ +#if NRF_ECB_HAS_ECBDATAPTR int ble_hw_encrypt_block(struct ble_encryption_block *ecb) { @@ -304,6 +305,68 @@ ble_hw_encrypt_block(struct ble_encryption_block *ecb) return rc; } +#else +#define NRF_ECB NRF_ECB00 +#define NRF_AAR NRF_AAR00 + +/* ECB data structure */ +struct ecb_job_entry { + uint8_t *ptr; + uint32_t attr_and_length; +}; + +static struct ecb_job_entry ecb_input_job_list[2]; +static struct ecb_job_entry ecb_output_job_list[2]; + +int +ble_hw_encrypt_block(struct ble_encryption_block *ecb) +{ + int rc; + uint32_t end; + uint32_t err; + + /* Stop ECB */ + nrf_ecb_task_trigger(NRF_ECB, NRF_ECB_TASK_STOP); + + ecb_input_job_list[0].ptr = ecb->plain_text; + ecb_input_job_list[0].attr_and_length = (11 << 24) | (16 & 0x00ffffff); + ecb_output_job_list[0].ptr = ecb->cipher_text; + ecb_output_job_list[0].attr_and_length = (11 << 24) | (16 & 0x00ffffff); + + /* The end of a job list shall be 0 */ + ecb_input_job_list[1].ptr = 0; + ecb_input_job_list[1].attr_and_length = 0; + ecb_output_job_list[1].ptr = 0; + ecb_output_job_list[1].attr_and_length = 0; + + NRF_ECB->EVENTS_END = 0; + NRF_ECB->EVENTS_ERROR = 0; + NRF_ECB->IN.PTR = (uint32_t)ecb_input_job_list; + NRF_ECB->OUT.PTR = (uint32_t)ecb_output_job_list; + memcpy((void *)NRF_ECB->KEY.VALUE, ecb->key, sizeof(uint32_t) * 4); + + /* Start ECB */ + nrf_ecb_task_trigger(NRF_ECB, NRF_ECB_TASK_START); + + /* Wait till error or done */ + rc = 0; + while (1) { + end = NRF_ECB->EVENTS_END; + err = NRF_ECB->EVENTS_ERROR; + if (end || err) { + if (err) { + rc = -1; + } + break; + } + } + + return rc; +} +#endif + +#ifdef RNG_PRESENT +#include /** * Random number generator ISR. @@ -431,6 +494,140 @@ ble_hw_rng_read(void) return rnum; } +#else +#include + +/** + * Random number generator ISR. + */ +static void +ble_rng_isr(void) +{ + os_trace_isr_enter(); + + /* No callback? Clear and disable interrupts */ + if (g_ble_rng_isr_cb == NULL) { + nrf_cracen_int_disable(NRF_CRACEN, NRF_CRACEN_INT_RNG_MASK); + NRF_CRACENCORE->RNGCONTROL.CONTROL &= ~CRACENCORE_RNGCONTROL_CONTROL_INTENFULL_Msk; + NRF_CRACEN->EVENTS_RNG = 0; + os_trace_isr_exit(); + return; + } + + if (g_ble_rng_isr_cb) { + /* If there is a value ready in the queue grab it */ + while ((NRF_CRACENCORE->RNGCONTROL.CONTROL & + CRACENCORE_RNGCONTROL_CONTROL_INTENFULL_Msk) && + (NRF_CRACENCORE->RNGCONTROL.FIFOLEVEL > 0)) { + g_ble_rng_isr_cb(ble_hw_rng_read()); + } + } + + os_trace_isr_exit(); +} + +/** + * Initialize the random number generator + * + * @param cb + * @param bias + * + * @return int + */ +int +ble_hw_rng_init(ble_rng_isr_cb_t cb, int bias) +{ + NRF_CRACEN->ENABLE = CRACEN_ENABLE_CRYPTOMASTER_Msk | + CRACEN_ENABLE_RNG_Msk | + CRACEN_ENABLE_PKEIKG_Msk; + + while (NRF_CRACENCORE->PK.STATUS & CRACENCORE_PK_STATUS_PKBUSY_Msk); + NRF_CRACENCORE->PK.CONTROL &= ~CRACENCORE_IKG_PKECONTROL_CLEARIRQ_Msk; + + NRF_CRACENCORE->RNGCONTROL.CONTROL = CRACENCORE_RNGCONTROL_CONTROL_ResetValue | + CRACENCORE_RNGCONTROL_CONTROL_ENABLE_Msk; + + /* If we were passed a function pointer we need to enable the interrupt */ + if (cb != NULL) { + NVIC_SetVector(CRACEN_IRQn, (uint32_t)ble_rng_isr); + NVIC_EnableIRQ(CRACEN_IRQn); + g_ble_rng_isr_cb = cb; + } + + return 0; +} + +/** + * Start the random number generator + * + * @return int + */ +int +ble_hw_rng_start(void) +{ + os_sr_t sr; + + /* No need for interrupt if there is no callback */ + OS_ENTER_CRITICAL(sr); + NRF_CRACEN->EVENTS_RNG = 0; + + if (g_ble_rng_isr_cb) { + nrf_cracen_int_enable(NRF_CRACEN, NRF_CRACEN_INT_RNG_MASK); + NRF_CRACENCORE->RNGCONTROL.CONTROL |= CRACENCORE_RNGCONTROL_CONTROL_INTENFULL_Msk; + /* Force regeneration of the samples */ + NRF_CRACENCORE->RNGCONTROL.FIFOLEVEL = 0; + } + OS_EXIT_CRITICAL(sr); + + return 0; +} + +/** + * Stop the random generator + * + * @return int + */ +int +ble_hw_rng_stop(void) +{ + os_sr_t sr; + + /* No need for interrupt if there is no callback */ + OS_ENTER_CRITICAL(sr); + nrf_cracen_int_disable(NRF_CRACEN, NRF_CRACEN_INT_RNG_MASK); + NRF_CRACENCORE->RNGCONTROL.CONTROL &= ~CRACENCORE_RNGCONTROL_CONTROL_INTENFULL_Msk; + NRF_CRACEN->EVENTS_RNG = 0; + OS_EXIT_CRITICAL(sr); + + return 0; +} + +/** + * Read the random number generator. + * + * @return uint8_t + */ +uint8_t +ble_hw_rng_read(void) +{ + uint8_t rnum; + uint8_t slot_id; + + /* Wait for a sample */ + while (NRF_CRACENCORE->RNGCONTROL.FIFOLEVEL == 0) { + assert((NRF_CRACENCORE->RNGCONTROL.STATUS & + CRACENCORE_RNGCONTROL_STATUS_STATE_Msk) != + (CRACENCORE_RNGCONTROL_STATUS_STATE_ERROR << + CRACENCORE_RNGCONTROL_STATUS_STATE_Pos)); + } + + NRF_CRACEN->EVENTS_RNG = 0; + slot_id = NRF_CRACENCORE->RNGCONTROL.FIFODEPTH - NRF_CRACENCORE->RNGCONTROL.FIFOLEVEL; + rnum = (uint8_t)NRF_CRACENCORE->RNGCONTROL.FIFO[slot_id]; + + return rnum; +} +#endif #if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_PRIVACY) /** @@ -511,7 +708,7 @@ int ble_hw_resolv_list_match(void) { if (NRF_AAR->ENABLE && NRF_AAR->EVENTS_END && NRF_AAR->EVENTS_RESOLVED) { - return (int)NRF_AAR->STATUS; + return (int)NRF_AAR_STATUS; } return -1; diff --git a/nimble/drivers/nrf5x/src/ble_phy.c b/nimble/drivers/nrf5x/src/ble_phy.c index 8be0771bd4..112febda23 100644 --- a/nimble/drivers/nrf5x/src/ble_phy.c +++ b/nimble/drivers/nrf5x/src/ble_phy.c @@ -55,6 +55,8 @@ #endif #include #include "phy_priv.h" +#include "phy_hw.h" +#include "phy_ppi.h" #ifndef min #define min(a, b) ((a) < (b) ? (a) : (b)) @@ -102,9 +104,6 @@ extern void tm_tick(void); extern uint8_t g_nrf_num_irks; extern uint32_t g_nrf_irk_list[]; -/* To disable all radio interrupts */ -#define NRF_RADIO_IRQ_MASK_ALL (0x34FF) - /* * We configure the nrf with a 1 byte S0 field, 8 bit length field, and * zero bit S1 field. The preamble is 8 bits long. @@ -121,7 +120,7 @@ extern uint32_t g_nrf_irk_list[]; /* NRF_RADIO->PCNF0 configuration values */ #define NRF_PCNF0 (NRF_LFLEN_BITS << RADIO_PCNF0_LFLEN_Pos) | \ - (RADIO_PCNF0_S1INCL_Msk) | \ + (RADIO_PCNF0_S1INCL_Include << RADIO_PCNF0_S1INCL_Pos) | \ (NRF_S0LEN << RADIO_PCNF0_S0LEN_Pos) | \ (NRF_S1LEN_BITS << RADIO_PCNF0_S1LEN_Pos) #define NRF_PCNF0_1M (NRF_PCNF0) | \ @@ -338,14 +337,6 @@ STATS_NAME_END(ble_phy_stats) static uint32_t g_nrf_encrypt_scratchpad[NRF_ENC_SCRATCH_WORDS]; -struct nrf_ccm_data -{ - uint8_t key[16]; - uint64_t pkt_counter; - uint8_t dir_bit; - uint8_t iv[8]; -} __attribute__((packed)); - struct nrf_ccm_data g_nrf_ccm_data; #endif @@ -937,7 +928,7 @@ ble_phy_wfr_enable(int txrx, uint8_t tx_phy_mode, uint32_t wfr_usecs) } #if MYNEWT_VAL(BLE_LL_CFG_FEAT_LE_ENCRYPTION) -static uint32_t +uint32_t ble_phy_get_ccm_datarate(void) { #if MYNEWT_VAL(BLE_LL_PHY) @@ -976,16 +967,11 @@ ble_phy_rx_xcvr_setup(void) #if MYNEWT_VAL(BLE_LL_CFG_FEAT_LE_ENCRYPTION) if (g_ble_phy_data.phy_encrypted) { NRF_RADIO->PACKETPTR = (uint32_t)&g_ble_phy_enc_buf[0]; - NRF_CCM->INPTR = (uint32_t)&g_ble_phy_enc_buf[0]; - NRF_CCM->OUTPTR = (uint32_t)dptr; - NRF_CCM->SCRATCHPTR = (uint32_t)&g_nrf_encrypt_scratchpad[0]; - NRF_CCM->MODE = CCM_MODE_LENGTH_Msk | CCM_MODE_MODE_Decryption | - ble_phy_get_ccm_datarate(); - NRF_CCM->CNFPTR = (uint32_t)&g_nrf_ccm_data; - NRF_CCM->SHORTS = 0; - NRF_CCM->EVENTS_ERROR = 0; - NRF_CCM->EVENTS_ENDCRYPT = 0; - nrf_ccm_task_trigger(NRF_CCM, NRF_CCM_TASK_KSGEN); + phy_hw_ccm_setup_rx((uint8_t *)&g_ble_phy_enc_buf[0], + (uint8_t *)&g_ble_phy_enc_buf[3], + (uint8_t *)&g_nrf_encrypt_scratchpad[0], + &g_nrf_ccm_data); + phy_hw_ccm_start(); phy_ppi_radio_address_to_ccm_crypt_enable(); } else { NRF_RADIO->PACKETPTR = (uint32_t)dptr; @@ -997,8 +983,7 @@ ble_phy_rx_xcvr_setup(void) #if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_PRIVACY) if (g_ble_phy_data.phy_privacy) { NRF_AAR->ENABLE = AAR_ENABLE_ENABLE_Enabled; - NRF_AAR->IRKPTR = (uint32_t)&g_nrf_irk_list[0]; - NRF_AAR->SCRATCHPTR = (uint32_t)&g_ble_phy_data.phy_aar_scratch; + phy_hw_aar_irk_setup(&g_nrf_irk_list[0], &g_ble_phy_data.phy_aar_scratch); NRF_AAR->EVENTS_END = 0; NRF_AAR->EVENTS_RESOLVED = 0; NRF_AAR->EVENTS_NOTRESOLVED = 0; @@ -1037,13 +1022,11 @@ ble_phy_rx_xcvr_setup(void) NRF_RADIO->EVENTS_ADDRESS = 0; NRF_RADIO->EVENTS_DEVMATCH = 0; NRF_RADIO->EVENTS_BCMATCH = 0; +#if defined(RADIO_INTENSET_RSSIEND_Msk) NRF_RADIO->EVENTS_RSSIEND = 0; +#endif NRF_RADIO->EVENTS_CRCOK = 0; - NRF_RADIO->SHORTS = RADIO_SHORTS_END_DISABLE_Msk | - RADIO_SHORTS_READY_START_Msk | - RADIO_SHORTS_ADDRESS_BCSTART_Msk | - RADIO_SHORTS_ADDRESS_RSSISTART_Msk | - RADIO_SHORTS_DISABLED_RSSISTOP_Msk; + phy_hw_radio_shorts_setup_rx(); nrf_radio_int_enable(NRF_RADIO, RADIO_INTENSET_ADDRESS_Msk | RADIO_INTENSET_DISABLED_Msk); @@ -1186,8 +1169,7 @@ ble_phy_tx_end_isr(void) * XXX: not sure we need to stop the timer here all the time. Or that * it should be stopped here. */ - nrf_timer_task_trigger(NRF_TIMER0, NRF_TIMER_TASK_STOP); - NRF_TIMER0->TASKS_SHUTDOWN = 1; + phy_hw_radio_timer_task_stop(); phy_ppi_wfr_disable(); phy_ppi_timer0_compare0_to_radio_txen_disable(); phy_ppi_rtc0_compare0_to_timer0_start_disable(); @@ -1240,7 +1222,9 @@ ble_phy_rx_end_isr(void) /* Set RSSI and CRC status flag in header */ ble_hdr = &g_ble_phy_data.rxhdr; +#if defined(RADIO_INTENSET_RSSIEND_Msk) assert(NRF_RADIO->EVENTS_RSSIEND != 0); +#endif ble_hdr->rxinfo.rssi = (-1 * NRF_RADIO->RSSISAMPLE); dptr = (uint8_t *)&g_ble_phy_rx_buf[0]; @@ -1255,12 +1239,12 @@ ble_phy_rx_end_isr(void) 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) { - while (NRF_CCM->EVENTS_ENDCRYPT == 0) { + while (NRF_CCM_EVENTS_END == 0) { /* Make sure CCM finished */ }; /* Only set MIC failure flag if frame is not zero length */ - if ((dptr[1] != 0) && (NRF_CCM->MICSTATUS == 0)) { + if ((dptr[1] != 0) && (NRF_CCM_STATUS == 0)) { ble_hdr->rxinfo.flags |= BLE_MBUF_HDR_F_MIC_FAILURE; } @@ -1453,7 +1437,7 @@ ble_phy_rx_start_isr(void) * octets for extended header. */ adva_offset = (dptr[3] & 0x0f) == 0x07 ? 2 : 0; - NRF_AAR->ADDRPTR = (uint32_t)(dptr + 3 + adva_offset); + phy_hw_aar_addrptr_set(dptr + 3 + adva_offset); /* Trigger AAR after last bit of AdvA is received */ NRF_RADIO->EVENTS_BCMATCH = 0; @@ -1490,7 +1474,7 @@ ble_phy_isr(void) os_trace_isr_enter(); /* Read irq register to determine which interrupts are enabled */ - irq_en = NRF_RADIO->INTENSET; + irq_en = NRF_RADIO_INTENSET; /* * NOTE: order of checking is important! Possible, if things get delayed, @@ -1597,9 +1581,11 @@ ble_phy_init(void) g_ble_phy_data.tifs = BLE_LL_IFS; #endif +#if defined(RADIO_POWER_POWER_Msk) /* Toggle peripheral power to reset (just in case) */ nrf_radio_power_set(NRF_RADIO, false); nrf_radio_power_set(NRF_RADIO, true); +#endif #ifdef NRF53_SERIES /* Errata 158: load trim values after toggling power */ @@ -1636,8 +1622,7 @@ ble_phy_init(void) RADIO_PCNF1_WHITEEN_Msk; /* Enable radio fast ramp-up */ - NRF_RADIO->MODECNF0 |= (RADIO_MODECNF0_RU_Fast << RADIO_MODECNF0_RU_Pos) & - RADIO_MODECNF0_RU_Msk; + phy_hw_radio_fast_ru_setup(); /* Set logical address 1 for TX and RX */ NRF_RADIO->TXADDRESS = 0; @@ -1654,7 +1639,7 @@ ble_phy_init(void) #if MYNEWT_VAL(BLE_LL_CFG_FEAT_LE_ENCRYPTION) nrf_ccm_int_disable(NRF_CCM, 0xffffffff); - NRF_CCM->SHORTS = CCM_SHORTS_ENDKSGEN_CRYPT_Msk; + phy_hw_ccm_init(); NRF_CCM->EVENTS_ERROR = 0; memset(g_nrf_encrypt_scratchpad, 0, sizeof(g_nrf_encrypt_scratchpad)); @@ -1667,20 +1652,19 @@ ble_phy_init(void) #if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_PRIVACY) g_ble_phy_data.phy_aar_scratch = 0; - NRF_AAR->IRKPTR = (uint32_t)&g_nrf_irk_list[0]; + phy_hw_aar_irk_setup(&g_nrf_irk_list[0], &g_ble_phy_data.phy_aar_scratch); nrf_aar_int_disable(NRF_AAR, 0xffffffff); NRF_AAR->EVENTS_END = 0; NRF_AAR->EVENTS_RESOLVED = 0; NRF_AAR->EVENTS_NOTRESOLVED = 0; - NRF_AAR->NIRK = 0; + NRF_AAR_NIRK = 0; #endif /* TIMER0 setup for PHY when using RTC */ - nrf_timer_task_trigger(NRF_TIMER0, NRF_TIMER_TASK_STOP); - NRF_TIMER0->TASKS_SHUTDOWN = 1; + phy_hw_radio_timer_task_stop(); NRF_TIMER0->BITMODE = 3; /* 32-bit timer */ NRF_TIMER0->MODE = 0; /* Timer mode */ - NRF_TIMER0->PRESCALER = 4; /* gives us 1 MHz */ + phy_hw_timer_configure(); phy_ppi_init(); @@ -1741,7 +1725,7 @@ ble_phy_rx(void) */ nrf_wait_disabled(); if ((NRF_RADIO->STATE != RADIO_STATE_STATE_Disabled) && - ((NRF_RADIO->STATE & 0x07) != RADIO_STATE_STATE_RxIdle)) { + ((NRF_RADIO->STATE & 0x07) != RADIO_STATE_STATE_RxIdle)) { ble_phy_disable(); STATS_INC(ble_phy_stats, radio_state_errs); return BLE_PHY_ERR_RADIO_STATE; @@ -1929,7 +1913,6 @@ ble_phy_tx(ble_phy_tx_pducb_t pducb, void *pducb_arg, uint8_t end_trans) uint8_t payload_len; uint8_t hdr_byte; uint32_t state; - uint32_t shortcuts; if (g_ble_phy_data.phy_transition_late) { ble_phy_disable(); @@ -1958,16 +1941,12 @@ ble_phy_tx(ble_phy_tx_pducb_t pducb, void *pducb_arg, uint8_t end_trans) if (g_ble_phy_data.phy_encrypted) { dptr = (uint8_t *)&g_ble_phy_enc_buf[0]; pktptr = (uint8_t *)&g_ble_phy_tx_buf[0]; - NRF_CCM->SHORTS = CCM_SHORTS_ENDKSGEN_CRYPT_Msk; - NRF_CCM->INPTR = (uint32_t)dptr; - NRF_CCM->OUTPTR = (uint32_t)pktptr; - NRF_CCM->SCRATCHPTR = (uint32_t)&g_nrf_encrypt_scratchpad[0]; - NRF_CCM->EVENTS_ERROR = 0; - NRF_CCM->MODE = CCM_MODE_LENGTH_Msk | ble_phy_get_ccm_datarate(); - NRF_CCM->CNFPTR = (uint32_t)&g_nrf_ccm_data; + phy_hw_ccm_setup_tx(dptr, pktptr, + (uint8_t *)&g_nrf_encrypt_scratchpad[0], + &g_nrf_ccm_data); } else { #if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_PRIVACY) - NRF_AAR->IRKPTR = (uint32_t)&g_nrf_irk_list[0]; + phy_hw_aar_irk_setup(&g_nrf_irk_list[0], &g_ble_phy_data.phy_aar_scratch); #endif dptr = (uint8_t *)&g_ble_phy_tx_buf[0]; pktptr = dptr; @@ -1997,20 +1976,17 @@ ble_phy_tx(ble_phy_tx_pducb_t pducb, void *pducb_arg, uint8_t end_trans) NRF_CCM->INTENSET = CCM_INTENSET_ENDKSGEN_Msk; } #endif - nrf_ccm_task_trigger(NRF_CCM, NRF_CCM_TASK_KSGEN); + phy_hw_ccm_start(); } #endif NRF_RADIO->PACKETPTR = (uint32_t)pktptr; /* Clear the ready, end and disabled events */ - NRF_RADIO->EVENTS_READY = 0; - NRF_RADIO->EVENTS_END = 0; - NRF_RADIO->EVENTS_DISABLED = 0; + phy_hw_radio_events_clear(); /* Enable shortcuts for transmit start/end. */ - shortcuts = RADIO_SHORTS_END_DISABLE_Msk | RADIO_SHORTS_READY_START_Msk; - NRF_RADIO->SHORTS = shortcuts; + phy_hw_radio_shorts_setup_tx(); nrf_radio_int_enable(NRF_RADIO, RADIO_INTENSET_DISABLED_Msk); /* Set the PHY transition */ @@ -2157,7 +2133,8 @@ ble_phy_setchan(uint8_t chan, uint32_t access_addr, uint32_t crcinit) /* Set the frequency and the data whitening initial value */ g_ble_phy_data.phy_chan = chan; NRF_RADIO->FREQUENCY = g_ble_phy_chan_freq[chan]; - NRF_RADIO->DATAWHITEIV = chan; + + phy_hw_radio_datawhite_set(chan); return 0; } @@ -2174,8 +2151,7 @@ ble_phy_chan_get(void) static void ble_phy_stop_usec_timer(void) { - nrf_timer_task_trigger(NRF_TIMER0, NRF_TIMER_TASK_STOP); - NRF_TIMER0->TASKS_SHUTDOWN = 1; + phy_hw_radio_timer_task_stop(); nrf_rtc_event_disable(NRF_RTC0, RTC_EVTENSET_COMPARE0_Msk); } @@ -2295,7 +2271,7 @@ ble_phy_max_data_pdu_pyld(void) void ble_phy_resolv_list_enable(void) { - NRF_AAR->NIRK = (uint32_t)g_nrf_num_irks; + NRF_AAR_NIRK = (uint32_t)g_nrf_num_irks; g_ble_phy_data.phy_privacy = 1; } diff --git a/nimble/drivers/nrf5x/src/nrf52/phy.c b/nimble/drivers/nrf5x/src/nrf52/phy.c index 30a19966b7..649c592743 100644 --- a/nimble/drivers/nrf5x/src/nrf52/phy.c +++ b/nimble/drivers/nrf5x/src/nrf52/phy.c @@ -20,7 +20,7 @@ #include #include #include -#include "../phy_priv.h" +#include "phy_ppi.h" #if PHY_USE_DEBUG void diff --git a/nimble/drivers/nrf5x/src/nrf52/phy_hw.h b/nimble/drivers/nrf5x/src/nrf52/phy_hw.h new file mode 100644 index 0000000000..a880979b58 --- /dev/null +++ b/nimble/drivers/nrf5x/src/nrf52/phy_hw.h @@ -0,0 +1,164 @@ +/* + * 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_PHY_HW_ +#define H_PHY_HW_ + +#include +#include +#include +#include + +struct nrf_ccm_data { + uint8_t key[16]; + uint64_t pkt_counter; + uint8_t dir_bit; + uint8_t iv[8]; +} __attribute__((packed)); + +/* To disable all radio interrupts */ +#define NRF_RADIO_IRQ_MASK_ALL (0x34FF) + +#define NRF_RADIO_INTENSET NRF_RADIO->INTENSET + +#define NRF_AAR_NIRK NRF_AAR->NIRK +#define NRF_AAR_IRKPTR NRF_AAR->IRKPTR +#define NRF_AAR_ADDRPTR NRF_AAR->ADDRPTR +#define NRF_AAR_STATUS NRF_AAR->STATUS + +#define NRF_CCM_STATUS NRF_CCM->MICSTATUS +#define NRF_CCM_EVENTS_END NRF_CCM->EVENTS_ENDCRYPT + +uint32_t ble_phy_get_ccm_datarate(void); + +static inline void +phy_hw_ccm_init(void) +{ + NRF_CCM->SHORTS = CCM_SHORTS_ENDKSGEN_CRYPT_Msk; +} + +static inline void +phy_hw_ccm_setup_tx(uint8_t *in_ptr, uint8_t *out_ptr, + uint8_t *scratch_ptr, struct nrf_ccm_data *ccm_data) +{ + NRF_CCM->SHORTS = CCM_SHORTS_ENDKSGEN_CRYPT_Msk; + NRF_CCM->INPTR = (uint32_t)in_ptr; + NRF_CCM->OUTPTR = (uint32_t)out_ptr; + NRF_CCM->SCRATCHPTR = (uint32_t)scratch_ptr; + NRF_CCM->EVENTS_ERROR = 0; + NRF_CCM->MODE = CCM_MODE_LENGTH_Msk | ble_phy_get_ccm_datarate(); + NRF_CCM->CNFPTR = (uint32_t)ccm_data; +} + +static inline void +phy_hw_ccm_setup_rx(uint8_t *in_ptr, uint8_t *out_ptr, + uint8_t *scratch_ptr, struct nrf_ccm_data *ccm_data) +{ + NRF_CCM->INPTR = (uint32_t)in_ptr; + NRF_CCM->OUTPTR = (uint32_t)out_ptr; + NRF_CCM->SCRATCHPTR = (uint32_t)scratch_ptr; + NRF_CCM->MODE = CCM_MODE_LENGTH_Msk | CCM_MODE_MODE_Decryption | + ble_phy_get_ccm_datarate(); + NRF_CCM->CNFPTR = (uint32_t)ccm_data; + NRF_CCM->SHORTS = 0; + NRF_CCM->EVENTS_ERROR = 0; + NRF_CCM->EVENTS_ENDCRYPT = 0; +} + +static inline void +phy_hw_ccm_start(void) +{ + nrf_ccm_task_trigger(NRF_CCM, NRF_CCM_TASK_KSGEN); +} + +static inline void +phy_hw_radio_fast_ru_setup(void) +{ + NRF_RADIO->MODECNF0 |= (RADIO_MODECNF0_RU_Fast << RADIO_MODECNF0_RU_Pos) & + RADIO_MODECNF0_RU_Msk; +} + +static inline void +phy_hw_radio_events_clear(void) +{ + NRF_RADIO->EVENTS_READY = 0; + NRF_RADIO->EVENTS_END = 0; + NRF_RADIO->EVENTS_DISABLED = 0; +} + +static inline void +phy_hw_radio_shorts_setup_tx(void) +{ + NRF_RADIO->SHORTS = RADIO_SHORTS_END_DISABLE_Msk | + RADIO_SHORTS_READY_START_Msk; +} + +static inline void +phy_hw_radio_shorts_setup_rx(void) +{ + NRF_RADIO->EVENTS_RSSIEND = 0; + NRF_RADIO->SHORTS = RADIO_SHORTS_END_DISABLE_Msk | + RADIO_SHORTS_READY_START_Msk | + RADIO_SHORTS_ADDRESS_BCSTART_Msk | + RADIO_SHORTS_ADDRESS_RSSISTART_Msk | + RADIO_SHORTS_DISABLED_RSSISTOP_Msk; +} + +static inline void +phy_hw_radio_datawhite_set(uint8_t chan) +{ + NRF_RADIO->DATAWHITEIV = chan; +} + +static inline void +phy_hw_timer_configure(void) +{ + NRF_TIMER0->PRESCALER = 4; +} + +static inline void +phy_hw_radio_timer_task_stop(void) +{ + nrf_timer_task_trigger(NRF_TIMER0, NRF_TIMER_TASK_STOP); + NRF_TIMER0->TASKS_SHUTDOWN = 1; +} + +static inline void +phy_hw_aar_irk_setup(uint32_t *irk_ptr, uint32_t *scratch_ptr) +{ + NRF_AAR->IRKPTR = (uint32_t)irk_ptr; + NRF_AAR->SCRATCHPTR = (uint32_t)scratch_ptr; +} + +static inline void +phy_hw_aar_addrptr_set(uint8_t *dptr) +{ + NRF_AAR->ADDRPTR = (uint32_t)dptr; +} + +static inline void +phy_gpiote_configure(int idx, int pin) +{ + nrf_gpio_cfg_output(pin); + nrf_gpiote_task_configure(NRF_GPIOTE, idx, pin, NRF_GPIOTE_POLARITY_NONE, + NRF_GPIOTE_INITIAL_VALUE_LOW); + nrf_gpiote_task_enable(NRF_GPIOTE, idx); +} + +#endif /* H_PHY_HW_ */ diff --git a/nimble/drivers/nrf5x/src/nrf53/phy.c b/nimble/drivers/nrf5x/src/nrf53/phy.c index ceda769b0f..37bd25983a 100644 --- a/nimble/drivers/nrf5x/src/nrf53/phy.c +++ b/nimble/drivers/nrf5x/src/nrf53/phy.c @@ -20,7 +20,7 @@ #include #include #include -#include "../phy_priv.h" +#include "phy_ppi.h" /* * When the radio is operated on high voltage (see VREQCTRL - Voltage request diff --git a/nimble/drivers/nrf5x/src/nrf53/phy_hw.h b/nimble/drivers/nrf5x/src/nrf53/phy_hw.h new file mode 100644 index 0000000000..4e4d95a87a --- /dev/null +++ b/nimble/drivers/nrf5x/src/nrf53/phy_hw.h @@ -0,0 +1,20 @@ +/* + * 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 "../nrf52/phy_hw.h" diff --git a/nimble/drivers/nrf5x/src/nrf54l15/phy.c b/nimble/drivers/nrf5x/src/nrf54l15/phy.c new file mode 100644 index 0000000000..0f67e0b657 --- /dev/null +++ b/nimble/drivers/nrf5x/src/nrf54l15/phy.c @@ -0,0 +1,251 @@ +/* + * 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 +#include +#include +#include "phy_ppi.h" + +/* Create PPIB links between RADIO and PERI power domain. */ +#define PPIB_RADIO_PERI(_ch, _src, _dst) \ + NRF_PPIB11->SUBSCRIBE_SEND[_ch] = DPPI_CH_SUB(_src); \ + NRF_PPIB21->PUBLISH_RECEIVE[_ch] = DPPI_CH_PUB(_dst); \ + NRF_DPPIC10->CHENSET |= 1 << DPPI_CH_ ## _src; \ + NRF_DPPIC20->CHENSET |= 1 << DPPI_CH_ ## _dst; + +/* Create PPIB links between RADIO and MCU power domain. */ +#define PPIB_RADIO_MCU(_ch, _src, _dst) \ + NRF_PPIB10->SUBSCRIBE_SEND[_ch] = DPPI_CH_SUB(_src); \ + NRF_PPIB00->PUBLISH_RECEIVE[_ch] = DPPI_CH_PUB(_dst); \ + NRF_DPPIC10->CHENSET |= 1 << DPPI_CH_ ## _src; \ + NRF_DPPIC00->CHENSET |= 1 << DPPI_CH_ ## _dst; + + +#define PPIB_RADIO_PERI_0(_src, _dst) PPIB_RADIO_PERI(0, _src, _dst) +#define PPIB_RADIO_PERI_1(_src, _dst) PPIB_RADIO_PERI(1, _src, _dst) +#define PPIB_RADIO_PERI_2(_src, _dst) PPIB_RADIO_PERI(2, _src, _dst) +#define PPIB_RADIO_PERI_3(_src, _dst) PPIB_RADIO_PERI(3, _src, _dst) + +#define PPIB_RADIO_MCU_0(_src, _dst) PPIB_RADIO_MCU(0, _src, _dst) +#define PPIB_RADIO_MCU_1(_src, _dst) PPIB_RADIO_MCU(1, _src, _dst) + +#if PHY_USE_DEBUG +void +phy_debug_init(void) +{ +#if PHY_USE_DEBUG_1 + nrf_gpio_cfg_output(MYNEWT_VAL(BLE_PHY_DBG_TIME_TXRXEN_READY_PIN)); + nrf_gpiote_task_configure(NRF_GPIOTE20, PHY_GPIOTE_DEBUG_1, + MYNEWT_VAL(BLE_PHY_DBG_TIME_TXRXEN_READY_PIN), + NRF_GPIOTE_POLARITY_NONE, + NRF_GPIOTE_INITIAL_VALUE_LOW); + nrf_gpiote_task_enable(NRF_GPIOTE20, PHY_GPIOTE_DEBUG_1); + + PPIB_RADIO_PERI_0(TIMER0_EVENTS_COMPARE_0, GPIOTE20_TASKS_SET_0); + NRF_GPIOTE20->SUBSCRIBE_SET[PHY_GPIOTE_DEBUG_1] = DPPI_CH_SUB(GPIOTE20_TASKS_SET_0); + + NRF_RADIO->PUBLISH_READY = DPPI_CH_PUB(RADIO_EVENTS_READY); + PPIB_RADIO_PERI_1(RADIO_EVENTS_READY, GPIOTE20_TASKS_CLR_0); + NRF_GPIOTE20->SUBSCRIBE_CLR[PHY_GPIOTE_DEBUG_1] = DPPI_CH_SUB(GPIOTE20_TASKS_CLR_0); +#endif + +#if PHY_USE_DEBUG_2 + nrf_gpio_cfg_output(MYNEWT_VAL(BLE_PHY_DBG_TIME_ADDRESS_END_PIN)); + nrf_gpiote_task_configure(NRF_GPIOTE20, PHY_GPIOTE_DEBUG_2, + MYNEWT_VAL(BLE_PHY_DBG_TIME_ADDRESS_END_PIN), + NRF_GPIOTE_POLARITY_NONE, + NRF_GPIOTE_INITIAL_VALUE_LOW); + nrf_gpiote_task_enable(NRF_GPIOTE20, PHY_GPIOTE_DEBUG_2); + + PPIB_RADIO_PERI_2(RADIO_EVENTS_ADDRESS, GPIOTE20_TASKS_SET_1); + NRF_GPIOTE20->SUBSCRIBE_SET[PHY_GPIOTE_DEBUG_2] = DPPI_CH_SUB(GPIOTE20_TASKS_SET_1); + + PPIB_RADIO_PERI_3(RADIO_EVENTS_END, GPIOTE20_TASKS_CLR_1); + NRF_GPIOTE20->SUBSCRIBE_CLR[PHY_GPIOTE_DEBUG_2] = DPPI_CH_SUB(GPIOTE20_TASKS_CLR_1); +#endif +} +#endif /* PHY_USE_DEBUG */ + +void +phy_ppi_init(void) +{ + /* Publish events */ + NRF_TIMER00->PUBLISH_COMPARE[0] = DPPI_CH_PUB(TIMER0_EVENTS_COMPARE_0); + NRF_TIMER00->PUBLISH_COMPARE[3] = DPPI_CH_PUB(TIMER0_EVENTS_COMPARE_3); + NRF_RADIO->PUBLISH_PHYEND = DPPI_CH_PUB(RADIO_EVENTS_END); + + NRF_RADIO->PUBLISH_BCMATCH = DPPI_CH_PUB(RADIO_EVENTS_BCMATCH); + NRF_RADIO->PUBLISH_ADDRESS = DPPI_CH_PUB(RADIO_EVENTS_ADDRESS); + NRF_RTC0->PUBLISH_COMPARE[0] = DPPI_CH_PUB(RTC0_EVENTS_COMPARE_0); + + /* Enable channels we publish on */ + NRF_DPPIC->CHENSET = DPPI_CH_ENABLE_ALL; + + /* radio_address_to_timer0_capture1 */ + NRF_TIMER00->SUBSCRIBE_CAPTURE[1] = DPPI_CH_SUB(RADIO_EVENTS_ADDRESS); + /* radio_end_to_timer0_capture2 */ + NRF_TIMER00->SUBSCRIBE_CAPTURE[2] = DPPI_CH_SUB(RADIO_EVENTS_END); +} + +void +phy_txpower_set(int8_t dbm) +{ + uint16_t val; + + switch (dbm) { + case 8: + val = RADIO_TXPOWER_TXPOWER_Pos8dBm; + break; + case 7: + val = RADIO_TXPOWER_TXPOWER_Pos7dBm; + break; + case 6: + val = RADIO_TXPOWER_TXPOWER_Pos6dBm; + break; + case 5: + val = RADIO_TXPOWER_TXPOWER_Pos5dBm; + break; + case 4: + val = RADIO_TXPOWER_TXPOWER_Pos4dBm; + break; + case 3: + val = RADIO_TXPOWER_TXPOWER_Pos3dBm; + break; + case 2: + val = RADIO_TXPOWER_TXPOWER_Pos2dBm; + break; + case 1: + val = RADIO_TXPOWER_TXPOWER_Pos1dBm; + break; + case 0: + val = RADIO_TXPOWER_TXPOWER_0dBm; + break; + case -1: + val = RADIO_TXPOWER_TXPOWER_Neg1dBm; + break; + case -2: + val = RADIO_TXPOWER_TXPOWER_Neg2dBm; + break; + case -3: + val = RADIO_TXPOWER_TXPOWER_Neg3dBm; + break; + case -4: + val = RADIO_TXPOWER_TXPOWER_Neg4dBm; + break; + case -5: + val = RADIO_TXPOWER_TXPOWER_Neg5dBm; + break; + case -6: + val = RADIO_TXPOWER_TXPOWER_Neg6dBm; + break; + case -7: + val = RADIO_TXPOWER_TXPOWER_Neg7dBm; + break; + case -8: + val = RADIO_TXPOWER_TXPOWER_Neg8dBm; + break; + case -9: + val = RADIO_TXPOWER_TXPOWER_Neg9dBm; + break; + case -10: + val = RADIO_TXPOWER_TXPOWER_Neg10dBm; + break; + case -12: + val = RADIO_TXPOWER_TXPOWER_Neg12dBm; + break; + case -14: + val = RADIO_TXPOWER_TXPOWER_Neg14dBm; + break; + case -16: + val = RADIO_TXPOWER_TXPOWER_Neg16dBm; + break; + case -18: + val = RADIO_TXPOWER_TXPOWER_Neg18dBm; + break; + case -20: + val = RADIO_TXPOWER_TXPOWER_Neg20dBm; + break; + case -22: + val = RADIO_TXPOWER_TXPOWER_Neg22dBm; + break; + case -28: + val = RADIO_TXPOWER_TXPOWER_Neg28dBm; + break; + case -40: + val = RADIO_TXPOWER_TXPOWER_Neg40dBm; + break; + case -46: + val = RADIO_TXPOWER_TXPOWER_Neg46dBm; + break; + default: + val = RADIO_TXPOWER_TXPOWER_0dBm; + } + + NRF_RADIO->TXPOWER = val; +} + +int8_t +phy_txpower_round(int8_t dbm) +{ + if (dbm >= (int8_t)8) { + return (int8_t)8; + } + + if (dbm >= (int8_t)-10) { + return (int8_t)dbm; + } + + if (dbm >= (int8_t)-12) { + return (int8_t)-12; + } + + if (dbm >= (int8_t)-14) { + return (int8_t)-14; + } + + if (dbm >= (int8_t)-16) { + return (int8_t)-16; + } + + if (dbm >= (int8_t)-18) { + return (int8_t)-18; + } + + if (dbm >= (int8_t)-20) { + return (int8_t)-20; + } + + if (dbm >= (int8_t)-22) { + return (int8_t)-22; + } + + if (dbm >= (int8_t)-28) { + return (int8_t)-28; + } + + if (dbm >= (int8_t)-40) { + return (int8_t)-40; + } + + return (int8_t)-46; +} diff --git a/nimble/drivers/nrf5x/src/nrf54l15/phy_hw.h b/nimble/drivers/nrf5x/src/nrf54l15/phy_hw.h new file mode 100644 index 0000000000..1989d7eefd --- /dev/null +++ b/nimble/drivers/nrf5x/src/nrf54l15/phy_hw.h @@ -0,0 +1,172 @@ +/* + * 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_PHY_HW_ +#define H_PHY_HW_ + +#include +#include + +struct nrf_ccm_data { + uint8_t key[16]; + uint64_t pkt_counter; + uint8_t dir_bit; + uint8_t iv[8]; +} __attribute__((packed)); + +#define NRF_TIMER0 NRF_TIMER10 +#define NRF_DPPIC NRF_DPPIC10 +#define NRF_RTC0 NRF_RTC10 +#define NRF_AAR NRF_AAR00 +#define NRF_CCM NRF_CCM00 +#define NRF_AAR NRF_AAR00 +#define NRF_GPIOTE NRF_GPIOTE20 + +#define RADIO_IRQn RADIO_0_IRQn +#define RADIO_INTENSET_ADDRESS_Msk RADIO_INTENSET00_ADDRESS_Msk +#define RADIO_INTENCLR_ADDRESS_Msk RADIO_INTENCLR00_ADDRESS_Msk +#define RADIO_INTENSET_DISABLED_Msk RADIO_INTENSET00_DISABLED_Msk +#define RADIO_INTENCLR_DISABLED_Msk RADIO_INTENCLR00_DISABLED_Msk + +#define NRF_RADIO_INTENSET NRF_RADIO->INTENSET00 + +/* To disable all radio interrupts */ +#define NRF_RADIO_IRQ_MASK_ALL (RADIO_INTENSET00_READY_Msk | \ + RADIO_INTENSET00_ADDRESS_Msk | \ + RADIO_INTENSET00_PAYLOAD_Msk | \ + RADIO_INTENSET00_PHYEND_Msk | \ + RADIO_INTENSET00_DISABLED_Msk | \ + RADIO_INTENSET00_DEVMATCH_Msk | \ + RADIO_INTENSET00_DEVMISS_Msk | \ + RADIO_INTENSET00_BCMATCH_Msk | \ + RADIO_INTENSET00_CRCOK_Msk | \ + RADIO_INTENSET00_CRCERROR_Msk) + +#define NRF_AAR_NIRK NRF_AAR->MAXRESOLVED +#define NRF_AAR_IRKPTR NRF_AAR->IN.PTR +#define NRF_AAR_ADDRPTR NRF_AAR->IN.PTR +#define NRF_AAR_STATUS NRF_AAR->ERRORSTATUS + +#define CCM_MODE_DATARATE_125Kbps CCM_MODE_DATARATE_125Kbit +#define CCM_MODE_DATARATE_500Kbps CCM_MODE_DATARATE_500Kbit + +#define NRF_CCM_STATUS NRF_CCM->MACSTATUS +#define NRF_CCM_EVENTS_END NRF_CCM->EVENTS_END + +uint32_t ble_phy_get_ccm_datarate(void); + +static inline void +phy_hw_ccm_init(void) +{ +} + +static inline void +phy_hw_ccm_setup_tx(uint8_t *in_ptr, uint8_t *out_ptr, + uint8_t *scratch_ptr, struct nrf_ccm_data *ccm_data) +{ + NRF_CCM->IN.PTR = (uint32_t)in_ptr; + NRF_CCM->OUT.PTR = (uint32_t)out_ptr; + NRF_CCM->EVENTS_ERROR = 0; + NRF_CCM->MODE = CCM_MODE_MACLEN_Pos | ble_phy_get_ccm_datarate(); + memcpy((uint8_t *) NRF_CCM->KEY.VALUE, &ccm_data->key, sizeof(ccm_data->key)); +} + +static inline void +phy_hw_ccm_setup_rx(uint8_t *in_ptr, uint8_t *out_ptr, + uint8_t *scratch_ptr, struct nrf_ccm_data *ccm_data) +{ + NRF_CCM->IN.PTR = (uint32_t)in_ptr; + NRF_CCM->OUT.PTR = (uint32_t)out_ptr; + NRF_CCM->MODE = CCM_MODE_MACLEN_Pos | CCM_MODE_MODE_Decryption | + ble_phy_get_ccm_datarate(); + memcpy((uint8_t *) NRF_CCM->KEY.VALUE, &ccm_data->key, + sizeof(ccm_data->key)); + NRF_CCM->EVENTS_ERROR = 0; + NRF_CCM->EVENTS_END = 0; +} + +static inline void +phy_hw_ccm_start(void) +{ + nrf_ccm_task_trigger(NRF_CCM, NRF_CCM_TASK_START); +} + +static inline void +phy_hw_radio_fast_ru_setup(void) +{ + NRF_RADIO->TIMING = (RADIO_TIMING_RU_Fast << RADIO_TIMING_RU_Pos) & + RADIO_TIMING_RU_Msk; +} + +static inline void +phy_hw_radio_events_clear(void) +{ + NRF_RADIO->EVENTS_READY = 0; + NRF_RADIO->EVENTS_PHYEND = 0; + NRF_RADIO->EVENTS_END = 0; + NRF_RADIO->EVENTS_DISABLED = 0; +} + +static inline void +phy_hw_radio_shorts_setup_tx(void) +{ + NRF_RADIO->SHORTS = RADIO_SHORTS_PHYEND_DISABLE_Msk | + RADIO_SHORTS_READY_START_Msk; +} + +static inline void +phy_hw_radio_shorts_setup_rx(void) +{ + NRF_RADIO->SHORTS = RADIO_SHORTS_PHYEND_DISABLE_Msk | + RADIO_SHORTS_READY_START_Msk | + RADIO_SHORTS_ADDRESS_BCSTART_Msk | + RADIO_SHORTS_ADDRESS_RSSISTART_Msk; +} + +static inline void +phy_hw_radio_datawhite_set(uint8_t chan) +{ + NRF_RADIO->DATAWHITE = RADIO_DATAWHITE_ResetValue | chan; +} + +static inline void +phy_hw_timer_configure(void) +{ + NRF_TIMER0->PRESCALER = 5; +} + +static inline void +phy_hw_radio_timer_task_stop(void) +{ + nrf_timer_task_trigger(NRF_TIMER0, NRF_TIMER_TASK_STOP); +} + +static inline void +phy_hw_aar_irk_setup(uint32_t *irk_ptr, uint32_t *scratch_ptr) +{ + /* TODO */ +} + +static inline void +phy_hw_aar_addrptr_set(uint8_t *dptr) +{ + /* TODO */ +} + +#endif /* H_PHY_HW_ */ diff --git a/nimble/drivers/nrf5x/src/nrf54l15/phy_ppi.h b/nimble/drivers/nrf5x/src/nrf54l15/phy_ppi.h new file mode 100644 index 0000000000..76bdd097c0 --- /dev/null +++ b/nimble/drivers/nrf5x/src/nrf54l15/phy_ppi.h @@ -0,0 +1,167 @@ +/* + * 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_PHY_PPI_ +#define H_PHY_PPI_ + +#include "phy_hw.h" + +#define DPPI_CH_PUB(_ch) (((DPPI_CH_ ## _ch) & 0xff) | (1 << 31)) +#define DPPI_CH_SUB(_ch) (((DPPI_CH_ ## _ch) & 0xff) | (1 << 31)) +#define DPPI_CH_UNSUB(_ch) (((DPPI_CH_ ## _ch) & 0xff) | (0 << 31)) +#define DPPI_CH_MASK(_ch) (1 << (DPPI_CH_ ## _ch)) + +/* DPPIC00 [0:7] */ + +/* DPPIC10 [0:23] */ +#define DPPI_CH_TIMER0_EVENTS_COMPARE_0 0 +#define DPPI_CH_TIMER0_EVENTS_COMPARE_3 1 +#define DPPI_CH_RADIO_EVENTS_END 2 +#define DPPI_CH_RADIO_EVENTS_BCMATCH 3 +#define DPPI_CH_RADIO_EVENTS_ADDRESS 4 +#define DPPI_CH_RTC0_EVENTS_COMPARE_0 5 +#define DPPI_CH_TIMER0_EVENTS_COMPARE_2 6 +#define DPPI_CH_RADIO_EVENTS_DISABLED 7 +#define DPPI_CH_RADIO_EVENTS_READY 8 +#define DPPI_CH_RADIO_EVENTS_RXREADY 9 + +/* DPPIC20 [0:15] */ +#define DPPI_CH_GPIOTE20_TASKS_SET_0 0 +#define DPPI_CH_GPIOTE20_TASKS_CLR_0 1 +#define DPPI_CH_GPIOTE20_TASKS_SET_1 2 +#define DPPI_CH_GPIOTE20_TASKS_CLR_1 3 + +/* DPPIC30 [0:3] */ + +#define DPPI_CH_ENABLE_ALL (DPPIC_CHEN_CH0_Msk | DPPIC_CHEN_CH1_Msk | \ + DPPIC_CHEN_CH2_Msk | DPPIC_CHEN_CH3_Msk | \ + DPPIC_CHEN_CH4_Msk | DPPIC_CHEN_CH5_Msk) + +#define DPPI_CH_MASK_FEM (DPPI_CH_MASK(TIMER0_EVENTS_COMPARE_2) | \ + DPPI_CH_MASK(RADIO_EVENTS_DISABLED)) + +static inline void +phy_ppi_rtc0_compare0_to_timer0_start_enable(void) +{ + NRF_TIMER0->SUBSCRIBE_START = DPPI_CH_SUB(RTC0_EVENTS_COMPARE_0); +} + +static inline void +phy_ppi_rtc0_compare0_to_timer0_start_disable(void) +{ + NRF_TIMER0->SUBSCRIBE_START = DPPI_CH_UNSUB(RTC0_EVENTS_COMPARE_0); + NRF_TIMER0->SUBSCRIBE_CAPTURE[3] = DPPI_CH_UNSUB(RADIO_EVENTS_ADDRESS); + NRF_RADIO->SUBSCRIBE_DISABLE = DPPI_CH_UNSUB(TIMER0_EVENTS_COMPARE_3); +} + +static inline void +phy_ppi_timer0_compare0_to_radio_txen_enable(void) +{ + NRF_RADIO->SUBSCRIBE_TXEN = DPPI_CH_SUB(TIMER0_EVENTS_COMPARE_0); +} + +static inline void +phy_ppi_timer0_compare0_to_radio_txen_disable(void) +{ + NRF_RADIO->SUBSCRIBE_TXEN = DPPI_CH_UNSUB(TIMER0_EVENTS_COMPARE_0); +} + +static inline void +phy_ppi_timer0_compare0_to_radio_rxen_enable(void) +{ + NRF_RADIO->SUBSCRIBE_RXEN = DPPI_CH_SUB(TIMER0_EVENTS_COMPARE_0); +} + +static inline void +phy_ppi_timer0_compare0_to_radio_rxen_disable(void) +{ + NRF_RADIO->SUBSCRIBE_RXEN = DPPI_CH_UNSUB(TIMER0_EVENTS_COMPARE_0); +} + +static inline void +phy_ppi_radio_address_to_ccm_crypt_enable(void) +{ + NRF_CCM->SUBSCRIBE_START = DPPI_CH_SUB(RADIO_EVENTS_ADDRESS); +} + +static inline void +phy_ppi_radio_address_to_ccm_crypt_disable(void) +{ + NRF_CCM->SUBSCRIBE_START = DPPI_CH_UNSUB(RADIO_EVENTS_ADDRESS); +} + +static inline void +phy_ppi_radio_bcmatch_to_aar_start_enable(void) +{ + NRF_AAR->SUBSCRIBE_START = DPPI_CH_SUB(RADIO_EVENTS_BCMATCH); +} + +static inline void +phy_ppi_radio_bcmatch_to_aar_start_disable(void) +{ + NRF_AAR->SUBSCRIBE_START = DPPI_CH_UNSUB(RADIO_EVENTS_BCMATCH); +} + +static inline void +phy_ppi_wfr_enable(void) +{ + NRF_TIMER0->SUBSCRIBE_CAPTURE[3] = DPPI_CH_SUB(RADIO_EVENTS_ADDRESS); + NRF_RADIO->SUBSCRIBE_DISABLE = DPPI_CH_SUB(TIMER0_EVENTS_COMPARE_3); +} + +static inline void +phy_ppi_wfr_disable(void) +{ + NRF_TIMER0->SUBSCRIBE_CAPTURE[3] = DPPI_CH_UNSUB(RADIO_EVENTS_ADDRESS); + NRF_RADIO->SUBSCRIBE_DISABLE = DPPI_CH_UNSUB(TIMER0_EVENTS_COMPARE_3); +} + +static inline void +phy_ppi_fem_disable(void) +{ +#if PHY_USE_FEM_SINGLE_GPIO + NRF_GPIOTE->SUBSCRIBE_SET[PHY_GPIOTE_FEM] = + DPPI_CH_UNSUB(TIMER0_EVENTS_COMPARE_3); +#else +#if PHY_USE_FEM_PA + NRF_GPIOTE->SUBSCRIBE_SET[PHY_GPIOTE_FEM_PA] = + DPPI_CH_UNSUB(TIMER0_EVENTS_COMPARE_2); +#endif +#if PHY_USE_FEM_LNA + NRF_GPIOTE->SUBSCRIBE_SET[PHY_GPIOTE_FEM_LNA] = + DPPI_CH_UNSUB(TIMER0_EVENTS_COMPARE_2); +#endif +#endif +} + +static inline void +phy_ppi_disable(void) +{ + NRF_TIMER0->SUBSCRIBE_START = DPPI_CH_UNSUB(RTC0_EVENTS_COMPARE_0); + NRF_TIMER0->SUBSCRIBE_CAPTURE[3] = DPPI_CH_UNSUB(RADIO_EVENTS_ADDRESS); + NRF_RADIO->SUBSCRIBE_DISABLE = DPPI_CH_UNSUB(TIMER0_EVENTS_COMPARE_3); + NRF_RADIO->SUBSCRIBE_TXEN = DPPI_CH_UNSUB(TIMER0_EVENTS_COMPARE_0); + NRF_RADIO->SUBSCRIBE_RXEN = DPPI_CH_UNSUB(TIMER0_EVENTS_COMPARE_0); + NRF_AAR->SUBSCRIBE_START = DPPI_CH_UNSUB(RADIO_EVENTS_BCMATCH); + NRF_CCM->SUBSCRIBE_START = DPPI_CH_UNSUB(RADIO_EVENTS_ADDRESS); + + phy_ppi_fem_disable(); +} + +#endif /* H_PHY_PPI_ */ diff --git a/nimble/drivers/nrf5x/src/phy_priv.h b/nimble/drivers/nrf5x/src/phy_priv.h index db0664da2b..cc02014b9c 100644 --- a/nimble/drivers/nrf5x/src/phy_priv.h +++ b/nimble/drivers/nrf5x/src/phy_priv.h @@ -53,15 +53,6 @@ #define PHY_GPIOTE_FEM_LNA (PHY_GPIOTE_FEM_PA - PHY_USE_FEM_LNA) #endif -static inline void -phy_gpiote_configure(int idx, int pin) -{ - nrf_gpio_cfg_output(pin); - nrf_gpiote_task_configure(NRF_GPIOTE, idx, pin, NRF_GPIOTE_POLARITY_NONE, - NRF_GPIOTE_INITIAL_VALUE_LOW); - nrf_gpiote_task_enable(NRF_GPIOTE, idx); -} - #if PHY_USE_DEBUG void phy_debug_init(void); #endif @@ -79,18 +70,7 @@ void phy_fem_disable(void); void phy_ppi_init(void); -#ifdef NRF52_SERIES -#include "nrf52/phy_ppi.h" -#endif - void phy_txpower_set(int8_t dbm); int8_t phy_txpower_round(int8_t dbm); -#ifdef NRF52_SERIES -#include "nrf52/phy_ppi.h" -#endif -#ifdef NRF53_SERIES -#include "nrf53/phy_ppi.h" -#endif - #endif /* H_PHY_PRIV_ */ From c8ea92d67b6b53e37a22f1d4d1e58fc125ed6589 Mon Sep 17 00:00:00 2001 From: Oleksandr Martynenko <30444314+nemethhh@users.noreply.github.com> Date: Mon, 4 May 2026 23:11:36 +0200 Subject: [PATCH 2/3] nrf5x: rename nrf54l15 driver directory to nrf54l --- nimble/drivers/nrf5x/pkg.yml | 4 ++-- nimble/drivers/nrf5x/src/{nrf54l15 => nrf54l}/phy.c | 0 nimble/drivers/nrf5x/src/{nrf54l15 => nrf54l}/phy_hw.h | 0 nimble/drivers/nrf5x/src/{nrf54l15 => nrf54l}/phy_ppi.h | 0 4 files changed, 2 insertions(+), 2 deletions(-) rename nimble/drivers/nrf5x/src/{nrf54l15 => nrf54l}/phy.c (100%) rename nimble/drivers/nrf5x/src/{nrf54l15 => nrf54l}/phy_hw.h (100%) rename nimble/drivers/nrf5x/src/{nrf54l15 => nrf54l}/phy_ppi.h (100%) diff --git a/nimble/drivers/nrf5x/pkg.yml b/nimble/drivers/nrf5x/pkg.yml index d7b7fcca5e..60eaa5993e 100644 --- a/nimble/drivers/nrf5x/pkg.yml +++ b/nimble/drivers/nrf5x/pkg.yml @@ -40,11 +40,11 @@ pkg.source_files.'MCU_TARGET=="nRF52810" || MCU_TARGET=="nRF52811" || MCU_TARGET pkg.source_files.'MCU_TARGET=="nRF5340_NET"': - "src/nrf53/phy.c" pkg.source_files.'MCU_TARGET=="nRF54L15"': - - "src/nrf54l15/phy.c" + - "src/nrf54l/phy.c" pkg.include_dirs.'MCU_TARGET=="nRF52810" || MCU_TARGET=="nRF52811" || MCU_TARGET=="nRF52832" || MCU_TARGET=="nRF52840"': - "src/nrf52/" pkg.include_dirs.'MCU_TARGET=="nRF5340_NET"': - "src/nrf53/" pkg.include_dirs.'MCU_TARGET=="nRF54L15"': - - "src/nrf54l15/" + - "src/nrf54l/" diff --git a/nimble/drivers/nrf5x/src/nrf54l15/phy.c b/nimble/drivers/nrf5x/src/nrf54l/phy.c similarity index 100% rename from nimble/drivers/nrf5x/src/nrf54l15/phy.c rename to nimble/drivers/nrf5x/src/nrf54l/phy.c diff --git a/nimble/drivers/nrf5x/src/nrf54l15/phy_hw.h b/nimble/drivers/nrf5x/src/nrf54l/phy_hw.h similarity index 100% rename from nimble/drivers/nrf5x/src/nrf54l15/phy_hw.h rename to nimble/drivers/nrf5x/src/nrf54l/phy_hw.h diff --git a/nimble/drivers/nrf5x/src/nrf54l15/phy_ppi.h b/nimble/drivers/nrf5x/src/nrf54l/phy_ppi.h similarity index 100% rename from nimble/drivers/nrf5x/src/nrf54l15/phy_ppi.h rename to nimble/drivers/nrf5x/src/nrf54l/phy_ppi.h From 8983c5e0677442d9fb67c9777564dc6bb08c7e37 Mon Sep 17 00:00:00 2001 From: Oleksandr Martynenko <30444314+nemethhh@users.noreply.github.com> Date: Mon, 4 May 2026 22:42:17 +0200 Subject: [PATCH 3/3] nrf5x: add nRF54L support Add PHY support for nRF54L15, nRF54L10, and nRF54L05 (PCA10156 DK tested). The nRF54L uses a fundamentally different peripheral architecture from nRF52/53: - DPPI cross-domain routing via PPIB bridges instead of PPI - Scatter/gather DMA job lists for CCM, AAR, and ECB instead of flat memory pointers (INPTR/OUTPTR) - TIMER10 as the PHY timer instead of RTC0 - CCM KEY and NONCE registers use reversed byte order This change adds: - nrf54l/phy.c: CCM SG-DMA encrypt/decrypt (FastDecryption mode), AAR SG-DMA address resolution, PPIB routing init, DPPI channel layout - nrf54l/phy_hw.h: peripheral aliases, inline hardware functions, CCM/AAR/ECB attribute constants - nrf54l/phy_ppi.h: DPPI channel definitions and subscribe/publish helpers - nrf52/phy_hw.h: extracted nRF52 inline hardware functions (pure lift-and-shift from ble_phy.c; no logic change) - src/phy_hw.h, src/phy_ppi.h: MCU dispatcher headers (route to nrf54l/, nrf53/, or nrf52/ based on MYNEWT_VAL_CHOICE(MCU_TARGET)) - ble_phy.c: four nRF54L conditional blocks; nRF52/53 behavior unchanged - ble_hw.c: ECB SG-DMA path guarded by NRF_ECB_HAS_ECBDATAPTR - pkg.yml: nRF54L MCU_TARGET conditions for source_files and include_dirs - .github/targets/nordic_pca10156_{blehci,btshell}/: CI targets BLE features enabled: 2M PHY, Coded PHY, LE encryption, LL privacy, extended advertising, periodic advertising, DLE, DTM. nRF52/53 behaviour is unchanged. --- .../targets/nordic_pca10156_blehci/pkg.yml | 24 + .../targets/nordic_pca10156_blehci/syscfg.yml | 38 ++ .../targets/nordic_pca10156_blehci/target.yml | 22 + .../targets/nordic_pca10156_btshell/pkg.yml | 24 + .../nordic_pca10156_btshell/syscfg.yml | 38 ++ .../nordic_pca10156_btshell/target.yml | 22 + README.md | 4 +- nimble/doc/transport.md | 4 +- nimble/drivers/nrf5x/pkg.yml | 6 +- nimble/drivers/nrf5x/src/ble_hw.c | 35 +- nimble/drivers/nrf5x/src/ble_phy.c | 148 +++-- nimble/drivers/nrf5x/src/nrf52/phy_hw.h | 108 +++- nimble/drivers/nrf5x/src/nrf54l/phy.c | 523 ++++++++++++++++-- nimble/drivers/nrf5x/src/nrf54l/phy_hw.h | 188 ++++--- nimble/drivers/nrf5x/src/nrf54l/phy_ppi.h | 60 +- nimble/drivers/nrf5x/src/phy_hw.h | 40 ++ nimble/drivers/nrf5x/src/phy_ppi.h | 40 ++ 17 files changed, 1083 insertions(+), 241 deletions(-) create mode 100644 .github/targets/nordic_pca10156_blehci/pkg.yml create mode 100644 .github/targets/nordic_pca10156_blehci/syscfg.yml create mode 100644 .github/targets/nordic_pca10156_blehci/target.yml create mode 100644 .github/targets/nordic_pca10156_btshell/pkg.yml create mode 100644 .github/targets/nordic_pca10156_btshell/syscfg.yml create mode 100644 .github/targets/nordic_pca10156_btshell/target.yml create mode 100644 nimble/drivers/nrf5x/src/phy_hw.h create mode 100644 nimble/drivers/nrf5x/src/phy_ppi.h diff --git a/.github/targets/nordic_pca10156_blehci/pkg.yml b/.github/targets/nordic_pca10156_blehci/pkg.yml new file mode 100644 index 0000000000..b8e1dcc809 --- /dev/null +++ b/.github/targets/nordic_pca10156_blehci/pkg.yml @@ -0,0 +1,24 @@ +# +# 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: "targets/nordic_pca10156_blehci" +pkg.type: "target" +pkg.description: +pkg.author: +pkg.homepage: diff --git a/.github/targets/nordic_pca10156_blehci/syscfg.yml b/.github/targets/nordic_pca10156_blehci/syscfg.yml new file mode 100644 index 0000000000..7c1b4b0f80 --- /dev/null +++ b/.github/targets/nordic_pca10156_blehci/syscfg.yml @@ -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. +# + +syscfg.vals: + BLE_PHY_2M: 1 + BLE_PHY_CODED: 1 + + BLE_LL_CFG_FEAT_DATA_LEN_EXT: 1 + BLE_LL_CFG_FEAT_LE_ENCRYPTION: 1 + BLE_LL_CFG_FEAT_LL_PRIVACY: 1 + BLE_LL_CONN_INIT_MAX_TX_BYTES: 251 + BLE_LL_CONN_INIT_SLOTS: 4 + BLE_LL_DTM: 1 + BLE_LL_DTM_EXTENSIONS: 1 + BLE_LL_HCI_VS_EVENT_ON_ASSERT: 1 + BLE_MAX_CONNECTIONS: 5 + BLE_MAX_PERIODIC_SYNCS: 5 + BLE_MULTI_ADV_INSTANCES: 5 + BLE_EXT_ADV: 1 + BLE_PERIODIC_ADV: 1 + BLE_PERIODIC_ADV_SYNC_TRANSFER: 1 + BLE_VERSION: 51 diff --git a/.github/targets/nordic_pca10156_blehci/target.yml b/.github/targets/nordic_pca10156_blehci/target.yml new file mode 100644 index 0000000000..e48b780d59 --- /dev/null +++ b/.github/targets/nordic_pca10156_blehci/target.yml @@ -0,0 +1,22 @@ +# +# 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. +# + +target.app: "@apache-mynewt-nimble/apps/blehci" +target.bsp: "@apache-mynewt-core/hw/bsp/nordic_pca10156" +target.build_profile: "debug" diff --git a/.github/targets/nordic_pca10156_btshell/pkg.yml b/.github/targets/nordic_pca10156_btshell/pkg.yml new file mode 100644 index 0000000000..542df5b511 --- /dev/null +++ b/.github/targets/nordic_pca10156_btshell/pkg.yml @@ -0,0 +1,24 @@ +# +# 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: "targets/nordic_pca10156_btshell" +pkg.type: "target" +pkg.description: +pkg.author: +pkg.homepage: diff --git a/.github/targets/nordic_pca10156_btshell/syscfg.yml b/.github/targets/nordic_pca10156_btshell/syscfg.yml new file mode 100644 index 0000000000..7c1b4b0f80 --- /dev/null +++ b/.github/targets/nordic_pca10156_btshell/syscfg.yml @@ -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. +# + +syscfg.vals: + BLE_PHY_2M: 1 + BLE_PHY_CODED: 1 + + BLE_LL_CFG_FEAT_DATA_LEN_EXT: 1 + BLE_LL_CFG_FEAT_LE_ENCRYPTION: 1 + BLE_LL_CFG_FEAT_LL_PRIVACY: 1 + BLE_LL_CONN_INIT_MAX_TX_BYTES: 251 + BLE_LL_CONN_INIT_SLOTS: 4 + BLE_LL_DTM: 1 + BLE_LL_DTM_EXTENSIONS: 1 + BLE_LL_HCI_VS_EVENT_ON_ASSERT: 1 + BLE_MAX_CONNECTIONS: 5 + BLE_MAX_PERIODIC_SYNCS: 5 + BLE_MULTI_ADV_INSTANCES: 5 + BLE_EXT_ADV: 1 + BLE_PERIODIC_ADV: 1 + BLE_PERIODIC_ADV_SYNC_TRANSFER: 1 + BLE_VERSION: 51 diff --git a/.github/targets/nordic_pca10156_btshell/target.yml b/.github/targets/nordic_pca10156_btshell/target.yml new file mode 100644 index 0000000000..b9162773f9 --- /dev/null +++ b/.github/targets/nordic_pca10156_btshell/target.yml @@ -0,0 +1,22 @@ +# +# 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. +# + +target.app: "@apache-mynewt-nimble/apps/btshell" +target.bsp: "@apache-mynewt-core/hw/bsp/nordic_pca10156" +target.build_profile: "debug" diff --git a/README.md b/README.md index e88acd7998..eeb6d29c8e 100644 --- a/README.md +++ b/README.md @@ -63,8 +63,8 @@ Feature highlight: ## Supported hardware -Controller supports Nordic nRF51, nRF52 and nRF5340 chipsets as well as DA1469x (cmac) -from Renesas. Host runs on any board and architecture +Controller supports Nordic nRF51, nRF52, nRF5340 and nRF54L chipsets as well as +DA1469x (cmac) from Renesas. Host runs on any board and architecture [supported](https://github.com/apache/mynewt-core#overview) by Apache Mynewt OS. diff --git a/nimble/doc/transport.md b/nimble/doc/transport.md index 35065197c9..f24e005489 100644 --- a/nimble/doc/transport.md +++ b/nimble/doc/transport.md @@ -120,7 +120,7 @@ assuming it's properly configured (see above). ### Combined build This setup runs both NimBLE host and controller on the same core. It's a typical -configuration when running application on SoCs like nRF51 or nRF52. +configuration when running application on SoCs like nRF51, nRF52 or nRF54L. Note: this is the default configuration, no need to set it explicitly. @@ -134,7 +134,7 @@ BLE_TRANSPORT_LL: native This setup makes NimBLE controller accessible to external host connected via e.g. UART or USB, so it can be used as an external Bluetooth LE controller. The controller runs on the same core as external interface. It's typically -used with `blehci` application running on SoCs like nRF51 or nRF52. +used with `blehci` application running on SoCs like nRF51, nRF52 or nRF54L. ```yaml BLE_TRANSPORT_HS: uart diff --git a/nimble/drivers/nrf5x/pkg.yml b/nimble/drivers/nrf5x/pkg.yml index 60eaa5993e..56daf8e5fb 100644 --- a/nimble/drivers/nrf5x/pkg.yml +++ b/nimble/drivers/nrf5x/pkg.yml @@ -18,7 +18,7 @@ # pkg.name: nimble/drivers/nrf5x -pkg.description: PHY for nRF52 and nRF53 series +pkg.description: PHY for nRF52, nRF53 and nRF54L series pkg.author: "Apache Mynewt " pkg.homepage: "https://mynewt.apache.org/" pkg.keywords: @@ -39,12 +39,12 @@ pkg.source_files.'MCU_TARGET=="nRF52810" || MCU_TARGET=="nRF52811" || MCU_TARGET - "src/nrf52/phy.c" pkg.source_files.'MCU_TARGET=="nRF5340_NET"': - "src/nrf53/phy.c" -pkg.source_files.'MCU_TARGET=="nRF54L15"': +pkg.source_files.'MCU_TARGET=="nRF54L15" || MCU_TARGET=="nRF54L10" || MCU_TARGET=="nRF54L05"': - "src/nrf54l/phy.c" pkg.include_dirs.'MCU_TARGET=="nRF52810" || MCU_TARGET=="nRF52811" || MCU_TARGET=="nRF52832" || MCU_TARGET=="nRF52840"': - "src/nrf52/" pkg.include_dirs.'MCU_TARGET=="nRF5340_NET"': - "src/nrf53/" -pkg.include_dirs.'MCU_TARGET=="nRF54L15"': +pkg.include_dirs.'MCU_TARGET=="nRF54L15" || MCU_TARGET=="nRF54L10" || MCU_TARGET=="nRF54L05"': - "src/nrf54l/" diff --git a/nimble/drivers/nrf5x/src/ble_hw.c b/nimble/drivers/nrf5x/src/ble_hw.c index 0089c21072..427c3e0487 100644 --- a/nimble/drivers/nrf5x/src/ble_hw.c +++ b/nimble/drivers/nrf5x/src/ble_hw.c @@ -306,9 +306,6 @@ ble_hw_encrypt_block(struct ble_encryption_block *ecb) return rc; } #else -#define NRF_ECB NRF_ECB00 -#define NRF_AAR NRF_AAR00 - /* ECB data structure */ struct ecb_job_entry { uint8_t *ptr; @@ -329,9 +326,9 @@ ble_hw_encrypt_block(struct ble_encryption_block *ecb) nrf_ecb_task_trigger(NRF_ECB, NRF_ECB_TASK_STOP); ecb_input_job_list[0].ptr = ecb->plain_text; - ecb_input_job_list[0].attr_and_length = (11 << 24) | (16 & 0x00ffffff); + ecb_input_job_list[0].attr_and_length = (ECB_ATTR_DATA << 24) | 16; ecb_output_job_list[0].ptr = ecb->cipher_text; - ecb_output_job_list[0].attr_and_length = (11 << 24) | (16 & 0x00ffffff); + ecb_output_job_list[0].attr_and_length = (ECB_ATTR_DATA << 24) | 16; /* The end of a job list shall be 0 */ ecb_input_job_list[1].ptr = 0; @@ -343,7 +340,14 @@ ble_hw_encrypt_block(struct ble_encryption_block *ecb) NRF_ECB->EVENTS_ERROR = 0; NRF_ECB->IN.PTR = (uint32_t)ecb_input_job_list; NRF_ECB->OUT.PTR = (uint32_t)ecb_output_job_list; - memcpy((void *)NRF_ECB->KEY.VALUE, ecb->key, sizeof(uint32_t) * 4); + /* nRF54L KEY.VALUE uses reversed byte order (same as CCM) */ + { + const uint32_t *kp = (const uint32_t *)ecb->key; + NRF_ECB->KEY.VALUE[0] = __builtin_bswap32(kp[3]); + NRF_ECB->KEY.VALUE[1] = __builtin_bswap32(kp[2]); + NRF_ECB->KEY.VALUE[2] = __builtin_bswap32(kp[1]); + NRF_ECB->KEY.VALUE[3] = __builtin_bswap32(kp[0]); + } /* Start ECB */ nrf_ecb_task_trigger(NRF_ECB, NRF_ECB_TASK_START); @@ -508,7 +512,8 @@ ble_rng_isr(void) /* No callback? Clear and disable interrupts */ if (g_ble_rng_isr_cb == NULL) { nrf_cracen_int_disable(NRF_CRACEN, NRF_CRACEN_INT_RNG_MASK); - NRF_CRACENCORE->RNGCONTROL.CONTROL &= ~CRACENCORE_RNGCONTROL_CONTROL_INTENFULL_Msk; + NRF_CRACENCORE->RNGCONTROL.CONTROL &= + ~CRACENCORE_RNGCONTROL_CONTROL_INTENFULL_Msk; NRF_CRACEN->EVENTS_RNG = 0; os_trace_isr_exit(); return; @@ -538,10 +543,10 @@ int ble_hw_rng_init(ble_rng_isr_cb_t cb, int bias) { NRF_CRACEN->ENABLE = CRACEN_ENABLE_CRYPTOMASTER_Msk | - CRACEN_ENABLE_RNG_Msk | - CRACEN_ENABLE_PKEIKG_Msk; + CRACEN_ENABLE_RNG_Msk | CRACEN_ENABLE_PKEIKG_Msk; - while (NRF_CRACENCORE->PK.STATUS & CRACENCORE_PK_STATUS_PKBUSY_Msk); + while (NRF_CRACENCORE->PK.STATUS & CRACENCORE_PK_STATUS_PKBUSY_Msk) + ; NRF_CRACENCORE->PK.CONTROL &= ~CRACENCORE_IKG_PKECONTROL_CLEARIRQ_Msk; NRF_CRACENCORE->RNGCONTROL.CONTROL = CRACENCORE_RNGCONTROL_CONTROL_ResetValue | @@ -573,7 +578,8 @@ ble_hw_rng_start(void) if (g_ble_rng_isr_cb) { nrf_cracen_int_enable(NRF_CRACEN, NRF_CRACEN_INT_RNG_MASK); - NRF_CRACENCORE->RNGCONTROL.CONTROL |= CRACENCORE_RNGCONTROL_CONTROL_INTENFULL_Msk; + NRF_CRACENCORE->RNGCONTROL.CONTROL |= + CRACENCORE_RNGCONTROL_CONTROL_INTENFULL_Msk; /* Force regeneration of the samples */ NRF_CRACENCORE->RNGCONTROL.FIFOLEVEL = 0; } @@ -615,10 +621,9 @@ ble_hw_rng_read(void) /* Wait for a sample */ while (NRF_CRACENCORE->RNGCONTROL.FIFOLEVEL == 0) { - assert((NRF_CRACENCORE->RNGCONTROL.STATUS & - CRACENCORE_RNGCONTROL_STATUS_STATE_Msk) != - (CRACENCORE_RNGCONTROL_STATUS_STATE_ERROR << - CRACENCORE_RNGCONTROL_STATUS_STATE_Pos)); + assert((NRF_CRACENCORE->RNGCONTROL.STATUS & CRACENCORE_RNGCONTROL_STATUS_STATE_Msk) != + (CRACENCORE_RNGCONTROL_STATUS_STATE_ERROR + << CRACENCORE_RNGCONTROL_STATUS_STATE_Pos)); } NRF_CRACEN->EVENTS_RNG = 0; diff --git a/nimble/drivers/nrf5x/src/ble_phy.c b/nimble/drivers/nrf5x/src/ble_phy.c index 112febda23..dfed7ad88b 100644 --- a/nimble/drivers/nrf5x/src/ble_phy.c +++ b/nimble/drivers/nrf5x/src/ble_phy.c @@ -25,7 +25,6 @@ #include #include #include -#include #include "syscfg/syscfg.h" #include "os/os.h" /* Keep os_cputime explicitly to enable build on non-Mynewt platforms */ @@ -63,10 +62,13 @@ #endif #if MYNEWT_VAL(BLE_LL_CFG_FEAT_LE_CODED_PHY) -#if !MYNEWT_VAL_CHOICE(MCU_TARGET, nRF52840) && \ - !MYNEWT_VAL_CHOICE(MCU_TARGET, nRF52811) && \ - !MYNEWT_VAL_CHOICE(MCU_TARGET, nRF5340_NET) -#error LE Coded PHY can only be enabled on nRF52811, nRF52840 or nRF5340 +#if !MYNEWT_VAL_CHOICE(MCU_TARGET, nRF52840) && \ + !MYNEWT_VAL_CHOICE(MCU_TARGET, nRF52811) && \ + !MYNEWT_VAL_CHOICE(MCU_TARGET, nRF5340_NET) && \ + !MYNEWT_VAL_CHOICE(MCU_TARGET, nRF54L15) && \ + !MYNEWT_VAL_CHOICE(MCU_TARGET, nRF54L10) && \ + !MYNEWT_VAL_CHOICE(MCU_TARGET, nRF54L05) +#error LE Coded PHY can only be enabled on nRF52811, nRF52840, nRF5340 or nRF54L series #endif #endif @@ -119,10 +121,11 @@ extern uint32_t g_nrf_irk_list[]; #define NRF_BALEN (3) /* For base address of 3 bytes */ /* NRF_RADIO->PCNF0 configuration values */ -#define NRF_PCNF0 (NRF_LFLEN_BITS << RADIO_PCNF0_LFLEN_Pos) | \ - (RADIO_PCNF0_S1INCL_Include << RADIO_PCNF0_S1INCL_Pos) | \ - (NRF_S0LEN << RADIO_PCNF0_S0LEN_Pos) | \ - (NRF_S1LEN_BITS << RADIO_PCNF0_S1LEN_Pos) +#define NRF_PCNF0 \ + (NRF_LFLEN_BITS << RADIO_PCNF0_LFLEN_Pos) | \ + (RADIO_PCNF0_S1INCL_Include << RADIO_PCNF0_S1INCL_Pos) | \ + (NRF_S0LEN << RADIO_PCNF0_S0LEN_Pos) | \ + (NRF_S1LEN_BITS << RADIO_PCNF0_S1LEN_Pos) #define NRF_PCNF0_1M (NRF_PCNF0) | \ (RADIO_PCNF0_PLEN_8bit << RADIO_PCNF0_PLEN_Pos) #define NRF_PCNF0_2M (NRF_PCNF0) | \ @@ -641,10 +644,6 @@ ble_phy_tifs_set(uint16_t tifs) static int ble_phy_set_start_time(uint32_t cputime, uint8_t rem_us, bool tx) { - uint32_t next_cc; - uint32_t cur_cc; - uint32_t cntr; - uint32_t delta; int radio_rem_us; #if PHY_USE_FEM int fem_rem_us = 0; @@ -700,26 +699,7 @@ ble_phy_set_start_time(uint32_t cputime, uint8_t rem_us, bool tx) rem_us_corr = 30; } - /* - * Can we set the RTC compare to start TIMER0? We can do it if: - * a) Current compare value is not N+1 or N+2 ticks from current - * counter. - * b) The value we want to set is not at least N+2 from current - * counter. - * - * NOTE: since the counter can tick 1 while we do these calculations we - * need to account for it. - */ - next_cc = cputime & 0xffffff; - cur_cc = NRF_RTC0->CC[0]; - cntr = NRF_RTC0->COUNTER; - - delta = (cur_cc - cntr) & 0xffffff; - if ((delta <= 3) && (delta != 0)) { - return -1; - } - delta = (next_cc - cntr) & 0xffffff; - if ((delta & 0x800000) || (delta < 3)) { + if (phy_hw_timer_start_trigger_configure(cputime) != 0) { return -1; } @@ -736,12 +716,7 @@ ble_phy_set_start_time(uint32_t cputime, uint8_t rem_us, bool tx) } #endif - /* Set RTC compare to start TIMER0 */ - NRF_RTC0->EVENTS_COMPARE[0] = 0; - nrf_rtc_cc_set(NRF_RTC0, 0, next_cc); - nrf_rtc_event_enable(NRF_RTC0, RTC_EVTENSET_COMPARE0_Msk); - - /* Enable PPI */ + /* Enable the start trigger feeding TIMER0/TIMER10. */ #if PHY_USE_FEM if (fem_rem_us) { if (tx) { @@ -757,7 +732,7 @@ ble_phy_set_start_time(uint32_t cputime, uint8_t rem_us, bool tx) #endif phy_ppi_rtc0_compare0_to_timer0_start_enable(); - /* Store the cputime at which we set the RTC */ + /* Store the cputime at which we armed the start trigger. */ g_ble_phy_data.phy_start_cputime = cputime; return 0; @@ -805,15 +780,12 @@ ble_phy_set_start_now(void) #endif /* - * Set RTC compare to start TIMER0. We need to set it to at least N+2 ticks - * from current value to guarantee triggering compare event, but let's set - * it to N+3 to account for possible extra tick on RTC0 during these - * operations. + * Arm the start trigger at N+3 ticks so the compare event fires after all + * registers and subscriptions are in place. */ now = os_cputime_get32(); - NRF_RTC0->EVENTS_COMPARE[0] = 0; - nrf_rtc_cc_set(NRF_RTC0, 0, (now + 3) & 0xffffff); - nrf_rtc_event_enable(NRF_RTC0, RTC_EVTENSET_COMPARE0_Msk); + now += 3; + phy_hw_timer_start_trigger_set(now); #if PHY_USE_FEM_LNA phy_fem_enable_lna(); @@ -823,15 +795,15 @@ ble_phy_set_start_now(void) phy_ppi_rtc0_compare0_to_timer0_start_enable(); /* - * Store the cputime at which we set the RTC + * Store the cputime at which we armed the start trigger. * - * XXX Compare event may be triggered on previous CC value (if it was set to - * less than N+2) so in rare cases actual start time may be 2 ticks earlier - * than what we expect. Since this is only used on RX, it may cause AUX scan - * to be scheduled 1 or 2 ticks too late so we'll miss it - it's acceptable - * for now. + * XXX Compare event may be triggered on previous CC value (if it was set + * to less than N+2) so in rare cases actual start time may be 2 ticks + * earlier than what we expect. Since this is only used on RX, it may cause + * AUX scan to be scheduled 1 or 2 ticks too late so we'll miss it - it's + * acceptable for now. */ - g_ble_phy_data.phy_start_cputime = now + 3; + g_ble_phy_data.phy_start_cputime = now; OS_EXIT_CRITICAL(sr); @@ -967,12 +939,14 @@ ble_phy_rx_xcvr_setup(void) #if MYNEWT_VAL(BLE_LL_CFG_FEAT_LE_ENCRYPTION) if (g_ble_phy_data.phy_encrypted) { NRF_RADIO->PACKETPTR = (uint32_t)&g_ble_phy_enc_buf[0]; - phy_hw_ccm_setup_rx((uint8_t *)&g_ble_phy_enc_buf[0], - (uint8_t *)&g_ble_phy_enc_buf[3], - (uint8_t *)&g_nrf_encrypt_scratchpad[0], - &g_nrf_ccm_data); + phy_hw_ccm_setup_rx((uint8_t *)&g_ble_phy_enc_buf[0], dptr, + (uint8_t *)&g_nrf_encrypt_scratchpad[0], &g_nrf_ccm_data); phy_hw_ccm_start(); +#if !MYNEWT_VAL_CHOICE(MCU_TARGET, nRF54L15) && \ + !MYNEWT_VAL_CHOICE(MCU_TARGET, nRF54L10) && \ + !MYNEWT_VAL_CHOICE(MCU_TARGET, nRF54L05) phy_ppi_radio_address_to_ccm_crypt_enable(); +#endif } else { NRF_RADIO->PACKETPTR = (uint32_t)dptr; } @@ -1239,12 +1213,10 @@ ble_phy_rx_end_isr(void) 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) { - while (NRF_CCM_EVENTS_END == 0) { - /* Make sure CCM finished */ - }; + uint8_t enc_len = ((uint8_t *)&g_ble_phy_enc_buf[0])[1]; - /* Only set MIC failure flag if frame is not zero length */ - if ((dptr[1] != 0) && (NRF_CCM_STATUS == 0)) { + if (phy_hw_ccm_finish_rx_decrypt((uint8_t *)&g_ble_phy_enc_buf[0], + dptr, enc_len)) { ble_hdr->rxinfo.flags |= BLE_MBUF_HDR_F_MIC_FAILURE; } @@ -1427,16 +1399,22 @@ ble_phy_rx_start_isr(void) #if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_PRIVACY) /* - * If privacy is enabled and received PDU has TxAdd bit set (i.e. random - * address) we try to resolve address using AAR. + * BCC is programmed to 8 bits and started at ADDRESS event via the + * ADDRESS_BCSTART short. BCMATCH fires after exactly one PDU byte — + * S0 = dptr[0] — has been written to DMA. Only dptr[0] is valid here; + * dptr[1..N] still hold stale data from the previous packet. + * Reading dptr[0] corrects a latent upstream nRF5x bug that read dptr[3]. + * + * If privacy is enabled and TxAdd (bit 6 of S0) is set, the peer uses + * a random address and we try to resolve it using AAR. */ - if (g_ble_phy_data.phy_privacy && (dptr[3] & 0x40)) { + if (g_ble_phy_data.phy_privacy && (dptr[0] & 0x40)) { /* * AdvA is located at 4th octet in RX buffer (after S0, length an S1 * fields). In case of extended advertising PDU we need to add 2 more * octets for extended header. */ - adva_offset = (dptr[3] & 0x0f) == 0x07 ? 2 : 0; + adva_offset = (dptr[0] & 0x0f) == 0x07 ? 2 : 0; phy_hw_aar_addrptr_set(dptr + 3 + adva_offset); /* Trigger AAR after last bit of AdvA is received */ @@ -1657,7 +1635,7 @@ ble_phy_init(void) NRF_AAR->EVENTS_END = 0; NRF_AAR->EVENTS_RESOLVED = 0; NRF_AAR->EVENTS_NOTRESOLVED = 0; - NRF_AAR_NIRK = 0; + phy_hw_aar_resolv_disable(); #endif /* TIMER0 setup for PHY when using RTC */ @@ -1725,7 +1703,7 @@ ble_phy_rx(void) */ nrf_wait_disabled(); if ((NRF_RADIO->STATE != RADIO_STATE_STATE_Disabled) && - ((NRF_RADIO->STATE & 0x07) != RADIO_STATE_STATE_RxIdle)) { + ((NRF_RADIO->STATE & 0x07) != RADIO_STATE_STATE_RxIdle)) { ble_phy_disable(); STATS_INC(ble_phy_stats, radio_state_errs); return BLE_PHY_ERR_RADIO_STATE; @@ -1913,6 +1891,9 @@ ble_phy_tx(ble_phy_tx_pducb_t pducb, void *pducb_arg, uint8_t end_trans) uint8_t payload_len; uint8_t hdr_byte; uint32_t state; +#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LE_ENCRYPTION) + uint8_t used_sw_ccm; +#endif if (g_ble_phy_data.phy_transition_late) { ble_phy_disable(); @@ -1941,8 +1922,7 @@ ble_phy_tx(ble_phy_tx_pducb_t pducb, void *pducb_arg, uint8_t end_trans) if (g_ble_phy_data.phy_encrypted) { dptr = (uint8_t *)&g_ble_phy_enc_buf[0]; pktptr = (uint8_t *)&g_ble_phy_tx_buf[0]; - phy_hw_ccm_setup_tx(dptr, pktptr, - (uint8_t *)&g_nrf_encrypt_scratchpad[0], + phy_hw_ccm_setup_tx(dptr, pktptr, (uint8_t *)&g_nrf_encrypt_scratchpad[0], &g_nrf_ccm_data); } else { #if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_PRIVACY) @@ -1967,8 +1947,15 @@ ble_phy_tx(ble_phy_tx_pducb_t pducb, void *pducb_arg, uint8_t end_trans) #if MYNEWT_VAL(BLE_LL_CFG_FEAT_LE_ENCRYPTION) /* Start key-stream generation and encryption (via short) */ if (g_ble_phy_data.phy_encrypted) { + used_sw_ccm = 0; + if (payload_len == 0) { + used_sw_ccm = 1; + pktptr[0] = hdr_byte; + pktptr[1] = 0; + pktptr[2] = 0; + } #if PHY_USE_HEADERMASK_WORKAROUND - if (g_ble_phy_data.phy_headermask != BLE_LL_PDU_HEADERMASK_DATA) { + if (!used_sw_ccm && g_ble_phy_data.phy_headermask != BLE_LL_PDU_HEADERMASK_DATA) { g_ble_phy_data.phy_headerbyte = dptr[0]; dptr[0] &= g_ble_phy_data.phy_headermask; g_ble_phy_tx_buf[0] = 0xffffffff; @@ -1976,7 +1963,14 @@ ble_phy_tx(ble_phy_tx_pducb_t pducb, void *pducb_arg, uint8_t end_trans) NRF_CCM->INTENSET = CCM_INTENSET_ENDKSGEN_Msk; } #endif - phy_hw_ccm_start(); + if (!used_sw_ccm) { + phy_hw_ccm_start(); + if (phy_hw_ccm_tx_wait_complete() != 0) { + STATS_INC(ble_phy_stats, tx_hw_err); + ble_phy_disable(); + return BLE_PHY_ERR_RADIO_STATE; + } + } } #endif @@ -2133,7 +2127,6 @@ ble_phy_setchan(uint8_t chan, uint32_t access_addr, uint32_t crcinit) /* Set the frequency and the data whitening initial value */ g_ble_phy_data.phy_chan = chan; NRF_RADIO->FREQUENCY = g_ble_phy_chan_freq[chan]; - phy_hw_radio_datawhite_set(chan); return 0; @@ -2146,13 +2139,13 @@ ble_phy_chan_get(void) } /** - * Stop the timer used to count microseconds when using RTC for cputime + * Stop the start-trigger source feeding the PHY timer. */ static void ble_phy_stop_usec_timer(void) { phy_hw_radio_timer_task_stop(); - nrf_rtc_event_disable(NRF_RTC0, RTC_EVTENSET_COMPARE0_Msk); + phy_hw_timer_start_trigger_disable(); } /** @@ -2271,13 +2264,14 @@ ble_phy_max_data_pdu_pyld(void) void ble_phy_resolv_list_enable(void) { - NRF_AAR_NIRK = (uint32_t)g_nrf_num_irks; + phy_hw_aar_resolv_enable(); g_ble_phy_data.phy_privacy = 1; } void ble_phy_resolv_list_disable(void) { + phy_hw_aar_resolv_disable(); g_ble_phy_data.phy_privacy = 0; } #endif diff --git a/nimble/drivers/nrf5x/src/nrf52/phy_hw.h b/nimble/drivers/nrf5x/src/nrf52/phy_hw.h index a880979b58..1be5cb699c 100644 --- a/nimble/drivers/nrf5x/src/nrf52/phy_hw.h +++ b/nimble/drivers/nrf5x/src/nrf52/phy_hw.h @@ -23,6 +23,7 @@ #include #include #include +#include #include struct nrf_ccm_data { @@ -33,16 +34,18 @@ struct nrf_ccm_data { } __attribute__((packed)); /* To disable all radio interrupts */ -#define NRF_RADIO_IRQ_MASK_ALL (0x34FF) +#define NRF_RADIO_IRQ_MASK_ALL (0x34FF) #define NRF_RADIO_INTENSET NRF_RADIO->INTENSET -#define NRF_AAR_NIRK NRF_AAR->NIRK -#define NRF_AAR_IRKPTR NRF_AAR->IRKPTR +extern uint8_t g_nrf_num_irks; + +#define NRF_AAR_NIRK NRF_AAR->NIRK +#define NRF_AAR_IRKPTR NRF_AAR->IRKPTR #define NRF_AAR_ADDRPTR NRF_AAR->ADDRPTR -#define NRF_AAR_STATUS NRF_AAR->STATUS +#define NRF_AAR_STATUS NRF_AAR->STATUS -#define NRF_CCM_STATUS NRF_CCM->MICSTATUS +#define NRF_CCM_STATUS NRF_CCM->MICSTATUS #define NRF_CCM_EVENTS_END NRF_CCM->EVENTS_ENDCRYPT uint32_t ble_phy_get_ccm_datarate(void); @@ -54,8 +57,8 @@ phy_hw_ccm_init(void) } static inline void -phy_hw_ccm_setup_tx(uint8_t *in_ptr, uint8_t *out_ptr, - uint8_t *scratch_ptr, struct nrf_ccm_data *ccm_data) +phy_hw_ccm_setup_tx(uint8_t *in_ptr, uint8_t *out_ptr, uint8_t *scratch_ptr, + struct nrf_ccm_data *ccm_data) { NRF_CCM->SHORTS = CCM_SHORTS_ENDKSGEN_CRYPT_Msk; NRF_CCM->INPTR = (uint32_t)in_ptr; @@ -67,8 +70,8 @@ phy_hw_ccm_setup_tx(uint8_t *in_ptr, uint8_t *out_ptr, } static inline void -phy_hw_ccm_setup_rx(uint8_t *in_ptr, uint8_t *out_ptr, - uint8_t *scratch_ptr, struct nrf_ccm_data *ccm_data) +phy_hw_ccm_setup_rx(uint8_t *in_ptr, uint8_t *out_ptr, uint8_t *scratch_ptr, + struct nrf_ccm_data *ccm_data) { NRF_CCM->INPTR = (uint32_t)in_ptr; NRF_CCM->OUTPTR = (uint32_t)out_ptr; @@ -87,6 +90,28 @@ phy_hw_ccm_start(void) nrf_ccm_task_trigger(NRF_CCM, NRF_CCM_TASK_KSGEN); } +static inline int +phy_hw_ccm_tx_wait_complete(void) +{ + /* nRF52/53 encrypts on-the-fly via KSGEN->CRYPT; no pre-TX wait needed. */ + return 0; +} + +/* Returns 1 if the LL should treat this as a MIC failure, else 0. */ +static inline int +phy_hw_ccm_finish_rx_decrypt(uint8_t *enc_buf, uint8_t *out_buf, uint8_t enc_len) +{ + (void)enc_buf; + (void)enc_len; + /* nRF52/53: decryption was triggered by ADDRESS->CCM_START PPI; the + * KSGEN->CRYPT short produced plaintext into out_buf in parallel + * with reception. Just wait for completion. */ + while (NRF_CCM_EVENTS_END == 0) { + /* spin */ + } + return ((out_buf[1] != 0) && (NRF_CCM_STATUS == 0)) ? 1 : 0; +} + static inline void phy_hw_radio_fast_ru_setup(void) { @@ -105,19 +130,17 @@ phy_hw_radio_events_clear(void) static inline void phy_hw_radio_shorts_setup_tx(void) { - NRF_RADIO->SHORTS = RADIO_SHORTS_END_DISABLE_Msk | - RADIO_SHORTS_READY_START_Msk; + NRF_RADIO->SHORTS = RADIO_SHORTS_END_DISABLE_Msk | RADIO_SHORTS_READY_START_Msk; } static inline void phy_hw_radio_shorts_setup_rx(void) { NRF_RADIO->EVENTS_RSSIEND = 0; - NRF_RADIO->SHORTS = RADIO_SHORTS_END_DISABLE_Msk | - RADIO_SHORTS_READY_START_Msk | - RADIO_SHORTS_ADDRESS_BCSTART_Msk | - RADIO_SHORTS_ADDRESS_RSSISTART_Msk | - RADIO_SHORTS_DISABLED_RSSISTOP_Msk; + NRF_RADIO->SHORTS = + RADIO_SHORTS_END_DISABLE_Msk | RADIO_SHORTS_READY_START_Msk | + RADIO_SHORTS_ADDRESS_BCSTART_Msk | RADIO_SHORTS_ADDRESS_RSSISTART_Msk | + RADIO_SHORTS_DISABLED_RSSISTOP_Msk; } static inline void @@ -152,6 +175,18 @@ phy_hw_aar_addrptr_set(uint8_t *dptr) NRF_AAR->ADDRPTR = (uint32_t)dptr; } +static inline void +phy_hw_aar_resolv_enable(void) +{ + NRF_AAR->NIRK = (uint32_t)g_nrf_num_irks; +} + +static inline void +phy_hw_aar_resolv_disable(void) +{ + NRF_AAR->NIRK = 0; +} + static inline void phy_gpiote_configure(int idx, int pin) { @@ -161,4 +196,45 @@ phy_gpiote_configure(int idx, int pin) nrf_gpiote_task_enable(NRF_GPIOTE, idx); } +static inline void +phy_hw_timer_start_trigger_set(uint32_t cputime) +{ + NRF_RTC0->EVENTS_COMPARE[0] = 0; + nrf_rtc_cc_set(NRF_RTC0, 0, cputime & 0xffffff); + nrf_rtc_event_enable(NRF_RTC0, RTC_EVTENSET_COMPARE0_Msk); +} + +static inline int +phy_hw_timer_start_trigger_configure(uint32_t cputime) +{ + uint32_t next_cc; + uint32_t cur_cc; + uint32_t cntr; + uint32_t delta; + + next_cc = cputime & 0xffffff; + cur_cc = NRF_RTC0->CC[0]; + cntr = NRF_RTC0->COUNTER; + + delta = (cur_cc - cntr) & 0xffffff; + if ((delta <= 3) && (delta != 0)) { + return -1; + } + + delta = (next_cc - cntr) & 0xffffff; + if ((delta & 0x800000) || (delta < 3)) { + return -1; + } + + phy_hw_timer_start_trigger_set(cputime); + + return 0; +} + +static inline void +phy_hw_timer_start_trigger_disable(void) +{ + nrf_rtc_event_disable(NRF_RTC0, RTC_EVTENSET_COMPARE0_Msk); +} + #endif /* H_PHY_HW_ */ diff --git a/nimble/drivers/nrf5x/src/nrf54l/phy.c b/nimble/drivers/nrf5x/src/nrf54l/phy.c index 0f67e0b657..c87857014a 100644 --- a/nimble/drivers/nrf5x/src/nrf54l/phy.c +++ b/nimble/drivers/nrf5x/src/nrf54l/phy.c @@ -23,31 +23,66 @@ #include #include +#include #include +#include #include "phy_ppi.h" +/* AAR output status — resolved IRK index written by the nRF54L AAR output + * job list. Width must match what the AAR EasyDMA writes per resolved IRK. + * Zephyr's nRF54L driver allocates 4 bytes (radio.c:2570); the datasheet + * (§8.3.5.15) lists PrematureOutptrEnd as the failure mode if the OUT + * entry is shorter than the chip wants to write. UINT32_MAX is an + * out-of-range sentinel for "no resolution". */ +static uint32_t g_nrf_aar_out_status; + +/* CCM scatter/gather job lists and state — private to this file */ +static struct sg_job_entry g_ccm_in_jl[5]; +static struct sg_job_entry g_ccm_out_jl[5]; +static uint16_t g_ccm_alen; +static uint16_t g_ccm_mlen; +static uint16_t g_ccm_out_alen; +static uint8_t g_ccm_out_adata; +static uint8_t g_ccm_adata_in; +static uint16_t g_ccm_out_mlen; +static uint8_t *g_ccm_in_ptr; +static uint8_t *g_ccm_out_ptr; +static uint8_t g_ccm_decrypt; +static struct nrf_ccm_data *g_ccm_data_ptr; + +/* AAR scatter/gather job lists */ +static struct sg_job_entry g_aar_in_jl[19]; +static struct sg_job_entry g_aar_out_jl[2]; + /* Create PPIB links between RADIO and PERI power domain. */ -#define PPIB_RADIO_PERI(_ch, _src, _dst) \ - NRF_PPIB11->SUBSCRIBE_SEND[_ch] = DPPI_CH_SUB(_src); \ - NRF_PPIB21->PUBLISH_RECEIVE[_ch] = DPPI_CH_PUB(_dst); \ - NRF_DPPIC10->CHENSET |= 1 << DPPI_CH_ ## _src; \ - NRF_DPPIC20->CHENSET |= 1 << DPPI_CH_ ## _dst; +#define PPIB_RADIO_PERI(_ch, _src, _dst) \ + NRF_PPIB11->SUBSCRIBE_SEND[_ch] = DPPI_CH_SUB(_src); \ + NRF_PPIB21->PUBLISH_RECEIVE[_ch] = DPPI_CH_PUB(_dst); \ + NRF_DPPIC10->CHENSET |= 1 << DPPI_CH_##_src; \ + NRF_DPPIC20->CHENSET |= 1 << DPPI_CH_##_dst; /* Create PPIB links between RADIO and MCU power domain. */ -#define PPIB_RADIO_MCU(_ch, _src, _dst) \ - NRF_PPIB10->SUBSCRIBE_SEND[_ch] = DPPI_CH_SUB(_src); \ - NRF_PPIB00->PUBLISH_RECEIVE[_ch] = DPPI_CH_PUB(_dst); \ - NRF_DPPIC10->CHENSET |= 1 << DPPI_CH_ ## _src; \ - NRF_DPPIC00->CHENSET |= 1 << DPPI_CH_ ## _dst; +#define PPIB_RADIO_MCU(_ch, _src, _dst) \ + NRF_PPIB10->SUBSCRIBE_SEND[_ch] = DPPI_CH_SUB(_src); \ + NRF_PPIB00->PUBLISH_RECEIVE[_ch] = DPPI_CH_PUB(_dst); \ + NRF_DPPIC10->CHENSET |= 1 << DPPI_CH_##_src; \ + NRF_DPPIC00->CHENSET |= 1 << DPPI_CH_##_dst; +/* Create PPIB links between PERI and RADIO power domain. */ +#define PPIB_PERI_RADIO(_ch, _src, _dst) \ + NRF_PPIB21->SUBSCRIBE_SEND[_ch] = DPPI_CH_SUB(_src); \ + NRF_PPIB11->PUBLISH_RECEIVE[_ch] = DPPI_CH_PUB(_dst); \ + NRF_DPPIC20->CHENSET |= 1 << DPPI_CH_##_src; \ + NRF_DPPIC10->CHENSET |= 1 << DPPI_CH_##_dst; #define PPIB_RADIO_PERI_0(_src, _dst) PPIB_RADIO_PERI(0, _src, _dst) #define PPIB_RADIO_PERI_1(_src, _dst) PPIB_RADIO_PERI(1, _src, _dst) #define PPIB_RADIO_PERI_2(_src, _dst) PPIB_RADIO_PERI(2, _src, _dst) #define PPIB_RADIO_PERI_3(_src, _dst) PPIB_RADIO_PERI(3, _src, _dst) -#define PPIB_RADIO_MCU_0(_src, _dst) PPIB_RADIO_MCU(0, _src, _dst) -#define PPIB_RADIO_MCU_1(_src, _dst) PPIB_RADIO_MCU(1, _src, _dst) +#define PPIB_RADIO_MCU_0(_src, _dst) PPIB_RADIO_MCU(0, _src, _dst) +#define PPIB_RADIO_MCU_1(_src, _dst) PPIB_RADIO_MCU(1, _src, _dst) +#define PPIB_PERI_RADIO_4(_src, _dst) PPIB_PERI_RADIO(4, _src, _dst) #if PHY_USE_DEBUG void @@ -57,54 +92,475 @@ phy_debug_init(void) nrf_gpio_cfg_output(MYNEWT_VAL(BLE_PHY_DBG_TIME_TXRXEN_READY_PIN)); nrf_gpiote_task_configure(NRF_GPIOTE20, PHY_GPIOTE_DEBUG_1, MYNEWT_VAL(BLE_PHY_DBG_TIME_TXRXEN_READY_PIN), - NRF_GPIOTE_POLARITY_NONE, - NRF_GPIOTE_INITIAL_VALUE_LOW); + NRF_GPIOTE_POLARITY_NONE, NRF_GPIOTE_INITIAL_VALUE_LOW); nrf_gpiote_task_enable(NRF_GPIOTE20, PHY_GPIOTE_DEBUG_1); PPIB_RADIO_PERI_0(TIMER0_EVENTS_COMPARE_0, GPIOTE20_TASKS_SET_0); - NRF_GPIOTE20->SUBSCRIBE_SET[PHY_GPIOTE_DEBUG_1] = DPPI_CH_SUB(GPIOTE20_TASKS_SET_0); + NRF_GPIOTE20->SUBSCRIBE_SET[PHY_GPIOTE_DEBUG_1] = + DPPI_CH_SUB(GPIOTE20_TASKS_SET_0); NRF_RADIO->PUBLISH_READY = DPPI_CH_PUB(RADIO_EVENTS_READY); PPIB_RADIO_PERI_1(RADIO_EVENTS_READY, GPIOTE20_TASKS_CLR_0); - NRF_GPIOTE20->SUBSCRIBE_CLR[PHY_GPIOTE_DEBUG_1] = DPPI_CH_SUB(GPIOTE20_TASKS_CLR_0); + NRF_GPIOTE20->SUBSCRIBE_CLR[PHY_GPIOTE_DEBUG_1] = + DPPI_CH_SUB(GPIOTE20_TASKS_CLR_0); #endif #if PHY_USE_DEBUG_2 nrf_gpio_cfg_output(MYNEWT_VAL(BLE_PHY_DBG_TIME_ADDRESS_END_PIN)); nrf_gpiote_task_configure(NRF_GPIOTE20, PHY_GPIOTE_DEBUG_2, MYNEWT_VAL(BLE_PHY_DBG_TIME_ADDRESS_END_PIN), - NRF_GPIOTE_POLARITY_NONE, - NRF_GPIOTE_INITIAL_VALUE_LOW); + NRF_GPIOTE_POLARITY_NONE, NRF_GPIOTE_INITIAL_VALUE_LOW); nrf_gpiote_task_enable(NRF_GPIOTE20, PHY_GPIOTE_DEBUG_2); PPIB_RADIO_PERI_2(RADIO_EVENTS_ADDRESS, GPIOTE20_TASKS_SET_1); - NRF_GPIOTE20->SUBSCRIBE_SET[PHY_GPIOTE_DEBUG_2] = DPPI_CH_SUB(GPIOTE20_TASKS_SET_1); + NRF_GPIOTE20->SUBSCRIBE_SET[PHY_GPIOTE_DEBUG_2] = + DPPI_CH_SUB(GPIOTE20_TASKS_SET_1); - PPIB_RADIO_PERI_3(RADIO_EVENTS_END, GPIOTE20_TASKS_CLR_1); - NRF_GPIOTE20->SUBSCRIBE_CLR[PHY_GPIOTE_DEBUG_2] = DPPI_CH_SUB(GPIOTE20_TASKS_CLR_1); + PPIB_RADIO_PERI_3(RADIO_EVENTS_PHYEND, GPIOTE20_TASKS_CLR_1); + NRF_GPIOTE20->SUBSCRIBE_CLR[PHY_GPIOTE_DEBUG_2] = + DPPI_CH_SUB(GPIOTE20_TASKS_CLR_1); #endif } #endif /* PHY_USE_DEBUG */ +/* + * nRF54L KEY.VALUE storage convention (datasheet section 8.4.2): + * - Word index is reversed: KEY.VALUE[0] holds bytes 12..15 of the + * 16-byte AES key, KEY.VALUE[3] holds bytes 0..3. + * - Within each word, bytes are big-endian: byte 12 occupies bits + * [31:24] of KEY.VALUE[0], byte 15 occupies bits [7:0]. + * On a little-endian Cortex-M33 this is equivalent to reading the key + * bytes as native uint32_t and applying bswap32 to each word. + * + * Verified against Zephyr's nRF54L driver (radio.c:2225-2228), which + * uses sys_get_be32(&key[12..0]) — the same byte arrangement. + */ +static void +phy_hw_ccm_set_key(const uint8_t *key) +{ + const uint32_t *kp = (const uint32_t *)key; + + NRF_CCM->KEY.VALUE[0] = __builtin_bswap32(kp[3]); + NRF_CCM->KEY.VALUE[1] = __builtin_bswap32(kp[2]); + NRF_CCM->KEY.VALUE[2] = __builtin_bswap32(kp[1]); + NRF_CCM->KEY.VALUE[3] = __builtin_bswap32(kp[0]); +} + +/* + * Build BLE CCM nonce and write to NONCE.VALUE[0..3] (datasheet section + * 8.4.2, v0.8 page 234 example: IV=DEAFBABEBADCAB24, dir=1, + * packet_counter=1). + * + * The 13-byte BLE nonce is laid out in memory in reversed byte order: + * nonce[0..7] = IV[7..0] + * nonce[8] = dir << 7 | counter_msb_7bits + * nonce[9..12] = counter[3..0] (big-endian) + * nonce[13..15] = 0 (pad to 16 bytes) + * Then NONCE.VALUE[i] = the i-th 32-bit LE word of nonce[]. No bswap is + * applied because the reversal is already in the byte arrangement. + * + * The KEY (above) uses bswap32 of native LE words; NONCE uses a manual + * byte permutation followed by plain LE word writes. Both store the + * same logical "reversed-byte-order" data, but the C-level expressions + * differ — pick whichever form is cheaper given the source layout. + * + * Verified against Zephyr's nRF54L driver (radio.c:2230-2234): + * NRF_CCM->NONCE.VALUE[3] = ((uint8_t*)&counter)[0]; + * NRF_CCM->NONCE.VALUE[2] = sys_get_be32(&counter_bytes[1]) | (dir<<7); + * NRF_CCM->NONCE.VALUE[1] = sys_get_be32(&iv[0]); + * NRF_CCM->NONCE.VALUE[0] = sys_get_be32(&iv[4]); + * The arithmetic in this function produces an equivalent bit pattern + * via the nonce[] intermediate. + */ +static void +phy_hw_ccm_set_nonce(struct nrf_ccm_data *ccm_data) +{ + uint8_t nonce[16]; + const uint32_t *np; + + /* + * IV bytes must be reversed — the nRF54L nonce register stores the + * entire 13-byte BLE nonce in reversed byte order, packed as LE words. + * The counter and dir fields are already in the correct (reversed = + * big-endian) order below; the IV must also be byte-reversed. + */ + nonce[0] = ccm_data->iv[7]; + nonce[1] = ccm_data->iv[6]; + nonce[2] = ccm_data->iv[5]; + nonce[3] = ccm_data->iv[4]; + nonce[4] = ccm_data->iv[3]; + nonce[5] = ccm_data->iv[2]; + nonce[6] = ccm_data->iv[1]; + nonce[7] = ccm_data->iv[0]; + /* + * The controller already passes the BLE packet direction bit in the + * convention used by the other NimBLE PHY drivers: + * - TX uses CONN_IS_CENTRAL(connsm) + * - RX uses !CONN_IS_CENTRAL(connsm) + * + * The nRF54L datasheet documents a plain nonce direction bit and the + * working upstream drivers use the controller-provided value directly, so + * do not apply any nRF54L-specific inversion here. + */ + nonce[8] = (ccm_data->dir_bit & 1) << 7 | ((ccm_data->pkt_counter >> 32) & 0x7F); + nonce[9] = (ccm_data->pkt_counter >> 24) & 0xFF; + nonce[10] = (ccm_data->pkt_counter >> 16) & 0xFF; + nonce[11] = (ccm_data->pkt_counter >> 8) & 0xFF; + nonce[12] = ccm_data->pkt_counter & 0xFF; + nonce[13] = 0; + nonce[14] = 0; + nonce[15] = 0; + + np = (const uint32_t *)nonce; + + NRF_CCM->NONCE.VALUE[0] = np[0]; + NRF_CCM->NONCE.VALUE[1] = np[1]; + NRF_CCM->NONCE.VALUE[2] = np[2]; + NRF_CCM->NONCE.VALUE[3] = np[3]; +} + +/* + * Build CCM scatter/gather job lists for BLE packet format. + * BLE RADIO packet in RAM: [S0=hdr][LEN][S1=0][payload...][MIC if encrypted] + * CCM Adata = S0 (1 byte), Mdata starts at offset 3 (after S0/LEN/S1). + */ +static void +phy_hw_ccm_build_ble_job_lists(uint8_t *in_buf, uint8_t *out_buf, + uint16_t payload_len, uint8_t decrypt) +{ + uint16_t mdata_in_len; + uint16_t mdata_out_len; + + if (decrypt) { + mdata_in_len = payload_len + 4; /* ciphertext + MIC */ + mdata_out_len = payload_len; /* plaintext only */ + } else { + mdata_in_len = payload_len; /* plaintext only */ + mdata_out_len = payload_len + 4; /* ciphertext + MIC */ + } + + g_ccm_alen = 1; + /* + * Per datasheet Fig. 45/46: input MLEN = l(m) for encrypt, l(c) for + * decrypt. MLEN tells the CCM how many bytes are in the MDATA field. + */ + g_ccm_mlen = mdata_in_len; + + /* + * BLE Data Channel PDU header mask (0xE3) zeroes NESN, SN, MD bits + * for CCM authentication. These bits change hop-to-hop and must not + * be included in the MIC calculation. The CCM ADATAMASK register + * should do this automatically (reset value 0xE3), but we pre-mask + * here to be safe — double-masking is idempotent. + */ + g_ccm_adata_in = in_buf[0] & 0xE3; + + /* Input job list */ + g_ccm_in_jl[0].ptr = (uint8_t *)&g_ccm_alen; + g_ccm_in_jl[0].attr_and_length = (CCM_ATTR_ALEN << 24) | 2; + g_ccm_in_jl[1].ptr = (uint8_t *)&g_ccm_mlen; + g_ccm_in_jl[1].attr_and_length = (CCM_ATTR_MLEN << 24) | 2; + g_ccm_in_jl[2].ptr = &g_ccm_adata_in; /* pre-masked S0 byte */ + g_ccm_in_jl[2].attr_and_length = (CCM_ATTR_ADATA << 24) | 1; + g_ccm_in_jl[3].ptr = in_buf + 3; /* payload after S0/LEN/S1 */ + /* + * RX (decrypt) MDATA buffer length follows Zephyr's nRF54L pattern in + * radio.c:2260: declare PCNF1.MAXLEN + 2 (mitigates sporadic MIC failures + * on small encrypted PDUs — Zephyr's NRF_CCM_WORKAROUND_XXXX_MDATA_EXTRA) + * regardless of the actual PDU size. Read MAXLEN from PCNF1 live so any + * future change to NRF_MAXLEN in ble_phy.c flows through automatically. + * Per-PDU mdata_in_len + 2 over-reads stale buffer content for small PDUs + * and corrupts MIC; TX (encrypt) keeps the actual length. + */ + if (decrypt) { + uint32_t maxlen = (NRF_RADIO->PCNF1 & RADIO_PCNF1_MAXLEN_Msk) >> + RADIO_PCNF1_MAXLEN_Pos; + g_ccm_in_jl[3].attr_and_length = (CCM_ATTR_MDATA << 24) | (maxlen + 2U); + } else { + g_ccm_in_jl[3].attr_and_length = (CCM_ATTR_MDATA << 24) | mdata_in_len; + } + g_ccm_in_jl[4].ptr = NULL; + g_ccm_in_jl[4].attr_and_length = 0; + + /* Output job list — must include ALEN + MLEN per datasheet Fig. 45/46 */ + g_ccm_out_jl[0].ptr = (uint8_t *)&g_ccm_out_alen; + g_ccm_out_jl[0].attr_and_length = (CCM_ATTR_ALEN << 24) | 2; + /* + * MLEN output must NOT point at out_buf[1] (BLE LENGTH byte). + * The CCM hardware writes the plaintext message length here, which + * overwrites the BLE LENGTH field. Redirect to a dummy so the + * caller-set LENGTH is preserved. + */ + g_ccm_out_jl[1].ptr = (uint8_t *)&g_ccm_out_mlen; + g_ccm_out_jl[1].attr_and_length = (CCM_ATTR_MLEN << 24) | 2; + /* + * CCM ADATA output may be masked by ADATAMASK (0xE3), which zeroes + * NESN/SN/MD bits. Write it to a dummy so the pre-copied original + * header in out_buf[0] is preserved (see phy_hw_ccm_post_rx_decrypt). + */ + g_ccm_out_jl[2].ptr = &g_ccm_out_adata; + g_ccm_out_jl[2].attr_and_length = (CCM_ATTR_ADATA << 24) | 1; + g_ccm_out_jl[3].ptr = out_buf + 3; /* payload after header slot */ + g_ccm_out_jl[3].attr_and_length = (CCM_ATTR_MDATA << 24) | mdata_out_len; + g_ccm_out_jl[4].ptr = NULL; + g_ccm_out_jl[4].attr_and_length = 0; + + NRF_CCM->IN.PTR = (uint32_t)g_ccm_in_jl; + NRF_CCM->OUT.PTR = (uint32_t)g_ccm_out_jl; +} + +void +phy_hw_ccm_setup_tx(uint8_t *in_ptr, uint8_t *out_ptr, uint8_t *scratch_ptr, + struct nrf_ccm_data *ccm_data) +{ + g_ccm_in_ptr = in_ptr; + g_ccm_out_ptr = out_ptr; + g_ccm_decrypt = 0; + + /* + * Re-arm CCM explicitly for each TX packet. On nRF54L the RX path + * toggles ENABLE around deferred decrypt setup, so relying on stale + * peripheral state here can leave the next encrypted TX packet using an + * undefined configuration window. + */ + NRF_CCM->ENABLE = CCM_ENABLE_ENABLE_Disabled; + NRF_CCM->EVENTS_ERROR = 0; + NRF_CCM->EVENTS_END = 0; + NRF_CCM->MODE = (CCM_MODE_MACLEN_M4 << CCM_MODE_MACLEN_Pos) | + ble_phy_get_ccm_datarate(); + NRF_CCM->ENABLE = CCM_ENABLE_ENABLE_Enabled; + phy_hw_ccm_set_key(ccm_data->key); + phy_hw_ccm_set_nonce(ccm_data); +} + +void +phy_hw_ccm_setup_rx(uint8_t *in_ptr, uint8_t *out_ptr, uint8_t *scratch_ptr, + struct nrf_ccm_data *ccm_data) +{ + g_ccm_in_ptr = in_ptr; + g_ccm_out_ptr = out_ptr; + g_ccm_decrypt = 1; + g_ccm_data_ptr = ccm_data; + + /* Defer all CCM setup to post_rx_decrypt — writing registers + * hundreds of µs before START causes MIC failure on nRF54L. */ +} + +void +phy_hw_ccm_start(void) +{ + if (g_ccm_decrypt) { + /* RX: don't start yet — triggered post-receive in ISR */ + return; + } + + /* TX: build job lists now (payload is filled) and start encryption. + * Set output header — CCM MLEN/ADATA outputs go to dummies, so the + * RADIO-visible S0/LENGTH/S1 must be set explicitly here. */ + phy_hw_ccm_build_ble_job_lists(g_ccm_in_ptr, g_ccm_out_ptr, g_ccm_in_ptr[1], 0); + g_ccm_out_ptr[0] = g_ccm_in_ptr[0]; /* S0 (header byte) */ + g_ccm_out_ptr[1] = g_ccm_in_ptr[1] + 4; /* LENGTH = plaintext + MIC */ + g_ccm_out_ptr[2] = 0; /* S1 */ + __DSB(); + nrf_ccm_task_trigger(NRF_CCM, NRF_CCM_TASK_START); +} + +/* + * Post-receive CCM FastDecryption for nRF54L. + * Called from rx_end_isr after full packet received. + * + * ALL CCM register setup (MODE, ENABLE, KEY, NONCE, job lists) is done + * here, immediately before START, to avoid MIC failure caused by a + * hundreds-of-µs gap between register writes and CCM START on nRF54L. + * + * BLE LENGTH field (enc_buf[1]) includes the 4-byte MIC for encrypted + * packets, so plaintext_len = LENGTH - 4. + */ +void +phy_hw_ccm_post_rx_decrypt(uint8_t *enc_buf, uint8_t *out_buf) +{ + uint16_t ble_len = enc_buf[1]; + uint16_t plaintext_len = (ble_len >= 4) ? (ble_len - 4) : 0; + + /* Copy S0/S1 from encrypted buffer; set LENGTH to plaintext size. */ + out_buf[0] = enc_buf[0]; + out_buf[1] = plaintext_len; + out_buf[2] = enc_buf[2]; + + /* Full CCM setup in required order: + * ENABLE=0 → MODE → ENABLE=2 → events → KEY → NONCE → + * job lists → IN.PTR/OUT.PTR → DSB → START + */ + NRF_CCM->ENABLE = CCM_ENABLE_ENABLE_Disabled; + NRF_CCM->MODE = CCM_MODE_MODE_FastDecryption | + (CCM_MODE_MACLEN_M4 << CCM_MODE_MACLEN_Pos) | + ble_phy_get_ccm_datarate(); + NRF_CCM->ENABLE = CCM_ENABLE_ENABLE_Enabled; + NRF_CCM->EVENTS_END = 0; + NRF_CCM->EVENTS_ERROR = 0; + + phy_hw_ccm_set_key(g_ccm_data_ptr->key); + phy_hw_ccm_set_nonce(g_ccm_data_ptr); + + phy_hw_ccm_build_ble_job_lists(enc_buf, out_buf, plaintext_len, 1); + + __DSB(); + nrf_ccm_task_trigger(NRF_CCM, NRF_CCM_TASK_START); +} + +#if BABBLESIM +extern void tm_tick(void); +#endif + +/* + * Bounded busy-wait for CCM completion. CCM at 32 MHz core encrypts a + * worst-case 251-byte BLE PDU in well under 100 us. Set the bound to + * ~250 us (50000 cycles at 32 MHz/2 per loop iter) to give margin while + * keeping us comfortably inside the pre-T_IFS budget. + */ +#define PHY_CCM_TX_WAIT_MAX_LOOPS 50000 + +int +phy_hw_ccm_tx_wait_complete(void) +{ + uint32_t loops = 0; + + while ((NRF_CCM_EVENTS_END == 0) && (NRF_CCM->EVENTS_ERROR == 0)) { + if (++loops > PHY_CCM_TX_WAIT_MAX_LOOPS) { + /* Abort: disable CCM so the next packet starts clean. */ + NRF_CCM->ENABLE = CCM_ENABLE_ENABLE_Disabled; + return -1; + } +#if BABBLESIM + tm_tick(); +#endif + } + return 0; +} + +/* + * Post-RX-decrypt finish: nRF54L FastDecryption is triggered after the + * full PDU is received, so we kick off CCM and wait, then set S0/LEN/S1 + * in the plaintext output. Returns 1 if LL should treat this as a MIC + * failure, else 0. + */ +int +phy_hw_ccm_finish_rx_decrypt(uint8_t *enc_buf, uint8_t *out_buf, uint8_t enc_len) +{ + /* Zero-length encrypted payload: skip CCM, mirror header bytes. */ + if (enc_len == 0) { + out_buf[0] = enc_buf[0]; + out_buf[1] = 0; + out_buf[2] = enc_buf[2]; + return 0; + } + + phy_hw_ccm_post_rx_decrypt(enc_buf, out_buf); + + while (NRF_CCM_EVENTS_END == 0) { + /* spin */ + } + + /* + * After CCM completes, set S0/LEN/S1 in the plaintext output. S0/S1 + * copied verbatim from the encrypted frame; LENGTH set to plaintext + * size (encrypted LENGTH minus the 4-byte MIC). See audit F4 — the + * OUT job list redirects MLEN/ADATA outputs to dummy sinks, but + * empirical confirmation that CCM never touches out_buf[0..2] is + * deferred, so we rewrite defensively. + */ + out_buf[0] = enc_buf[0]; + out_buf[1] = (enc_len >= 4) ? (enc_len - 4) : 0; + out_buf[2] = enc_buf[2]; + + return ((out_buf[1] != 0) && (NRF_CCM_STATUS == 0)) ? 1 : 0; +} + +/* + * nRF54L AAR has no STATUS register for the resolved IRK index. + * The resolved index is written to the output job list buffer (2 bytes LE). + */ +uint32_t +phy_hw_aar_get_resolved_index(void) +{ + /* Caller (ble_hw_resolv_list_match) already gates on EVENTS_RESOLVED, + * so reaching here implies AAR wrote the index. Return the sentinel + * if the chip wrote nothing (defensive). */ + if (NRF_AAR->OUT.AMOUNT >= sizeof(g_nrf_aar_out_status)) { + return g_nrf_aar_out_status; + } + return UINT32_MAX; +} + +void +phy_hw_aar_irk_setup(uint32_t *irk_ptr, uint32_t *scratch_ptr) +{ + int i; + int num_irks = g_nrf_num_irks; + struct sg_job_entry *entry; + + /* IRK entries in the input job list define how many IRKs AAR scans. */ + entry = &g_aar_in_jl[2]; + for (i = 0; i < num_irks; i++) { + entry->ptr = (uint8_t *)&irk_ptr[i * 4]; + entry->attr_and_length = (AAR_ATTR_IRK << 24) | 16; + entry++; + } + entry->ptr = NULL; + entry->attr_and_length = 0; + + /* Output job list stores the resolved IRK index. Width matches what + * AAR EasyDMA writes per entry (4 bytes per Zephyr radio.c:2570). */ + g_nrf_aar_out_status = UINT32_MAX; + g_aar_out_jl[0].ptr = (uint8_t *)&g_nrf_aar_out_status; + g_aar_out_jl[0].attr_and_length = + (AAR_ATTR_OUTPUT << 24) | sizeof(g_nrf_aar_out_status); + g_aar_out_jl[1].ptr = NULL; + g_aar_out_jl[1].attr_and_length = 0; + + NRF_AAR->IN.PTR = (uint32_t)g_aar_in_jl; + NRF_AAR->OUT.PTR = (uint32_t)g_aar_out_jl; +} + +void +phy_hw_aar_addrptr_set(uint8_t *dptr) +{ + /* + * dptr points to start of device address (6 bytes): + * bytes [0..2] = hash (3 bytes, LSB first) + * bytes [3..5] = prand (3 bytes, LSB first) + */ + g_aar_in_jl[0].ptr = dptr; + g_aar_in_jl[0].attr_and_length = (AAR_ATTR_HASH << 24) | 3; + g_aar_in_jl[1].ptr = dptr + 3; + g_aar_in_jl[1].attr_and_length = (AAR_ATTR_PRAND << 24) | 3; +} + void phy_ppi_init(void) { /* Publish events */ - NRF_TIMER00->PUBLISH_COMPARE[0] = DPPI_CH_PUB(TIMER0_EVENTS_COMPARE_0); - NRF_TIMER00->PUBLISH_COMPARE[3] = DPPI_CH_PUB(TIMER0_EVENTS_COMPARE_3); - NRF_RADIO->PUBLISH_PHYEND = DPPI_CH_PUB(RADIO_EVENTS_END); + NRF_TIMER0->PUBLISH_COMPARE[0] = DPPI_CH_PUB(TIMER0_EVENTS_COMPARE_0); + NRF_TIMER0->PUBLISH_COMPARE[3] = DPPI_CH_PUB(TIMER0_EVENTS_COMPARE_3); + NRF_RADIO->PUBLISH_PHYEND = DPPI_CH_PUB(RADIO_EVENTS_PHYEND); NRF_RADIO->PUBLISH_BCMATCH = DPPI_CH_PUB(RADIO_EVENTS_BCMATCH); NRF_RADIO->PUBLISH_ADDRESS = DPPI_CH_PUB(RADIO_EVENTS_ADDRESS); - NRF_RTC0->PUBLISH_COMPARE[0] = DPPI_CH_PUB(RTC0_EVENTS_COMPARE_0); + NRF_CPUTIME_TIMER->PUBLISH_COMPARE[0] = DPPI_CH_PUB(RTC0_EVENTS_COMPARE_0); + + /* Cross-domain RADIO -> MCU routes for CCM00 and AAR00. */ + PPIB_RADIO_MCU_0(RADIO_EVENTS_ADDRESS, CCM00_SUBSCRIBE_START); + PPIB_RADIO_MCU_1(RADIO_EVENTS_BCMATCH, AAR00_SUBSCRIBE_START); + /* Cross-domain PERI -> RADIO route for scheduled start trigger. */ + PPIB_PERI_RADIO_4(RTC0_EVENTS_COMPARE_0, RTC0_EVENTS_COMPARE_0); /* Enable channels we publish on */ NRF_DPPIC->CHENSET = DPPI_CH_ENABLE_ALL; /* radio_address_to_timer0_capture1 */ - NRF_TIMER00->SUBSCRIBE_CAPTURE[1] = DPPI_CH_SUB(RADIO_EVENTS_ADDRESS); + NRF_TIMER0->SUBSCRIBE_CAPTURE[1] = DPPI_CH_SUB(RADIO_EVENTS_ADDRESS); /* radio_end_to_timer0_capture2 */ - NRF_TIMER00->SUBSCRIBE_CAPTURE[2] = DPPI_CH_SUB(RADIO_EVENTS_END); + NRF_TIMER0->SUBSCRIBE_CAPTURE[2] = DPPI_CH_SUB(RADIO_EVENTS_PHYEND); } void @@ -182,12 +638,11 @@ phy_txpower_set(int8_t dbm) case -18: val = RADIO_TXPOWER_TXPOWER_Neg18dBm; break; + case -22: case -20: + /* MDK maps Neg22dBm and Neg20dBm to identical register value 0x002. */ val = RADIO_TXPOWER_TXPOWER_Neg20dBm; break; - case -22: - val = RADIO_TXPOWER_TXPOWER_Neg22dBm; - break; case -28: val = RADIO_TXPOWER_TXPOWER_Neg28dBm; break; @@ -198,6 +653,10 @@ phy_txpower_set(int8_t dbm) val = RADIO_TXPOWER_TXPOWER_Neg46dBm; break; default: + /* Caller must round to a valid level via phy_txpower_round() first. + * Hitting this branch means a logic bug, not user input — assert + * rather than silently produce a 22+ dB radio swing. */ + BLE_LL_ASSERT(0); val = RADIO_TXPOWER_TXPOWER_0dBm; } @@ -235,10 +694,6 @@ phy_txpower_round(int8_t dbm) return (int8_t)-20; } - if (dbm >= (int8_t)-22) { - return (int8_t)-22; - } - if (dbm >= (int8_t)-28) { return (int8_t)-28; } diff --git a/nimble/drivers/nrf5x/src/nrf54l/phy_hw.h b/nimble/drivers/nrf5x/src/nrf54l/phy_hw.h index 1989d7eefd..94bd2a88ff 100644 --- a/nimble/drivers/nrf5x/src/nrf54l/phy_hw.h +++ b/nimble/drivers/nrf5x/src/nrf54l/phy_hw.h @@ -20,6 +20,9 @@ #ifndef H_PHY_HW_ #define H_PHY_HW_ +#include +#include "syscfg/syscfg.h" +#include "os/os_cputime.h" #include #include @@ -30,88 +33,155 @@ struct nrf_ccm_data { uint8_t iv[8]; } __attribute__((packed)); +/* + * Scatter/gather DMA job list entry — shared by AAR, CCM, ECB. + * attr_and_length: attr[31:24] | length[23:0] + */ +struct sg_job_entry { + uint8_t *ptr; + uint32_t attr_and_length; +}; + +#define AAR_ATTR_HASH 11 /* 3-byte hash from BLE resolvable address */ +#define AAR_ATTR_PRAND 12 /* 3-byte prand from BLE resolvable address */ +#define AAR_ATTR_IRK 13 /* 16-byte Identity Resolving Key */ +/* AAR_ATTR_OUTPUT uses attr ID 11 on OUT.PTR (same numeric value as + * AAR_ATTR_HASH on IN.PTR; in/out lists are independent channels per + * datasheet §8.3 Fig. 41-42). The resolved IRK index is written as a + * 4-byte value matching Zephyr's reference; see nrf54l/phy.c. */ +#define AAR_ATTR_OUTPUT 11 + +extern uint8_t g_nrf_num_irks; + #define NRF_TIMER0 NRF_TIMER10 -#define NRF_DPPIC NRF_DPPIC10 -#define NRF_RTC0 NRF_RTC10 -#define NRF_AAR NRF_AAR00 -#define NRF_CCM NRF_CCM00 -#define NRF_AAR NRF_AAR00 +#define NRF_DPPIC NRF_DPPIC10 +#define NRF_AAR NRF_AAR00 +#define NRF_CCM NRF_CCM00 +#define NRF_ECB NRF_ECB00 #define NRF_GPIOTE NRF_GPIOTE20 -#define RADIO_IRQn RADIO_0_IRQn -#define RADIO_INTENSET_ADDRESS_Msk RADIO_INTENSET00_ADDRESS_Msk -#define RADIO_INTENCLR_ADDRESS_Msk RADIO_INTENCLR00_ADDRESS_Msk +#if MYNEWT_VAL(OS_CPUTIME_TIMER_NUM) == 0 +#define NRF_CPUTIME_TIMER NRF_TIMER20 +#elif MYNEWT_VAL(OS_CPUTIME_TIMER_NUM) == 1 +#define NRF_CPUTIME_TIMER NRF_TIMER21 +#elif MYNEWT_VAL(OS_CPUTIME_TIMER_NUM) == 2 +#define NRF_CPUTIME_TIMER NRF_TIMER22 +#elif MYNEWT_VAL(OS_CPUTIME_TIMER_NUM) == 3 +#define NRF_CPUTIME_TIMER NRF_TIMER23 +#elif MYNEWT_VAL(OS_CPUTIME_TIMER_NUM) == 4 +#define NRF_CPUTIME_TIMER NRF_TIMER24 +#else +#error Unsupported OS_CPUTIME_TIMER_NUM for nRF54L PHY +#endif + +#define RADIO_IRQn RADIO_0_IRQn +#define RADIO_INTENSET_ADDRESS_Msk RADIO_INTENSET00_ADDRESS_Msk +#define RADIO_INTENCLR_ADDRESS_Msk RADIO_INTENCLR00_ADDRESS_Msk #define RADIO_INTENSET_DISABLED_Msk RADIO_INTENSET00_DISABLED_Msk #define RADIO_INTENCLR_DISABLED_Msk RADIO_INTENCLR00_DISABLED_Msk #define NRF_RADIO_INTENSET NRF_RADIO->INTENSET00 /* To disable all radio interrupts */ -#define NRF_RADIO_IRQ_MASK_ALL (RADIO_INTENSET00_READY_Msk | \ - RADIO_INTENSET00_ADDRESS_Msk | \ - RADIO_INTENSET00_PAYLOAD_Msk | \ - RADIO_INTENSET00_PHYEND_Msk | \ - RADIO_INTENSET00_DISABLED_Msk | \ - RADIO_INTENSET00_DEVMATCH_Msk | \ - RADIO_INTENSET00_DEVMISS_Msk | \ - RADIO_INTENSET00_BCMATCH_Msk | \ - RADIO_INTENSET00_CRCOK_Msk | \ - RADIO_INTENSET00_CRCERROR_Msk) - -#define NRF_AAR_NIRK NRF_AAR->MAXRESOLVED -#define NRF_AAR_IRKPTR NRF_AAR->IN.PTR -#define NRF_AAR_ADDRPTR NRF_AAR->IN.PTR -#define NRF_AAR_STATUS NRF_AAR->ERRORSTATUS +#define NRF_RADIO_IRQ_MASK_ALL \ + (RADIO_INTENSET00_READY_Msk | RADIO_INTENSET00_ADDRESS_Msk | \ + RADIO_INTENSET00_PAYLOAD_Msk | RADIO_INTENSET00_PHYEND_Msk | \ + RADIO_INTENSET00_DISABLED_Msk | RADIO_INTENSET00_DEVMATCH_Msk | \ + RADIO_INTENSET00_DEVMISS_Msk | RADIO_INTENSET00_BCMATCH_Msk | \ + RADIO_INTENSET00_CRCOK_Msk | RADIO_INTENSET00_CRCERROR_Msk) + +/* Resolved IRK index from the last AAR run — state is private in nrf54l/phy.c */ +uint32_t phy_hw_aar_get_resolved_index(void); +#define NRF_AAR_STATUS phy_hw_aar_get_resolved_index() + +static inline void +phy_hw_aar_resolv_enable(void) +{ + /* IRK scan count comes from the input job list; stop after first match. */ + NRF_AAR->MAXRESOLVED = 1; +} + +static inline void +phy_hw_aar_resolv_disable(void) +{ + NRF_AAR->MAXRESOLVED = 0; +} #define CCM_MODE_DATARATE_125Kbps CCM_MODE_DATARATE_125Kbit #define CCM_MODE_DATARATE_500Kbps CCM_MODE_DATARATE_500Kbit -#define NRF_CCM_STATUS NRF_CCM->MACSTATUS +#define NRF_CCM_STATUS NRF_CCM->MACSTATUS #define NRF_CCM_EVENTS_END NRF_CCM->EVENTS_END +#define CCM_ATTR_ALEN 11 +#define CCM_ATTR_MLEN 12 +#define CCM_ATTR_ADATA 13 +#define CCM_ATTR_MDATA 14 + +/* ECB job list data attribute (per nRF54L Product Specification) */ +#define ECB_ATTR_DATA 11 + uint32_t ble_phy_get_ccm_datarate(void); static inline void phy_hw_ccm_init(void) { + /* CCM initialization is a no-op; hardware is configured per-packet */ } +/* CCM functions — implemented in nrf54l/phy.c */ +void phy_hw_ccm_setup_tx(uint8_t *in_ptr, uint8_t *out_ptr, + uint8_t *scratch_ptr, struct nrf_ccm_data *ccm_data); +void phy_hw_ccm_setup_rx(uint8_t *in_ptr, uint8_t *out_ptr, + uint8_t *scratch_ptr, struct nrf_ccm_data *ccm_data); +void phy_hw_ccm_start(void); +int phy_hw_ccm_tx_wait_complete(void); /* returns 0 on success, -1 on timeout */ +int phy_hw_ccm_finish_rx_decrypt(uint8_t *enc_buf, uint8_t *out_buf, uint8_t enc_len); +void phy_hw_ccm_post_rx_decrypt(uint8_t *enc_buf, uint8_t *out_buf); + static inline void -phy_hw_ccm_setup_tx(uint8_t *in_ptr, uint8_t *out_ptr, - uint8_t *scratch_ptr, struct nrf_ccm_data *ccm_data) +phy_hw_timer_start_trigger_set(uint32_t cputime) { - NRF_CCM->IN.PTR = (uint32_t)in_ptr; - NRF_CCM->OUT.PTR = (uint32_t)out_ptr; - NRF_CCM->EVENTS_ERROR = 0; - NRF_CCM->MODE = CCM_MODE_MACLEN_Pos | ble_phy_get_ccm_datarate(); - memcpy((uint8_t *) NRF_CCM->KEY.VALUE, &ccm_data->key, sizeof(ccm_data->key)); + NRF_CPUTIME_TIMER->EVENTS_COMPARE[0] = 0; + nrf_timer_cc_set(NRF_CPUTIME_TIMER, 0, cputime); } -static inline void -phy_hw_ccm_setup_rx(uint8_t *in_ptr, uint8_t *out_ptr, - uint8_t *scratch_ptr, struct nrf_ccm_data *ccm_data) +static inline int +phy_hw_timer_start_trigger_configure(uint32_t cputime) { - NRF_CCM->IN.PTR = (uint32_t)in_ptr; - NRF_CCM->OUT.PTR = (uint32_t)out_ptr; - NRF_CCM->MODE = CCM_MODE_MACLEN_Pos | CCM_MODE_MODE_Decryption | - ble_phy_get_ccm_datarate(); - memcpy((uint8_t *) NRF_CCM->KEY.VALUE, &ccm_data->key, - sizeof(ccm_data->key)); - NRF_CCM->EVENTS_ERROR = 0; - NRF_CCM->EVENTS_END = 0; + uint32_t cur_cc; + uint32_t cntr; + uint32_t delta; + + cur_cc = NRF_CPUTIME_TIMER->CC[0]; + cntr = os_cputime_get32(); + + delta = cur_cc - cntr; + if ((delta <= 3) && (delta != 0)) { + return -1; + } + + delta = cputime - cntr; + if (((int32_t)delta < 0) || (delta < 3)) { + return -1; + } + + phy_hw_timer_start_trigger_set(cputime); + + return 0; } static inline void -phy_hw_ccm_start(void) +phy_hw_timer_start_trigger_disable(void) { - nrf_ccm_task_trigger(NRF_CCM, NRF_CCM_TASK_START); + NRF_CPUTIME_TIMER->EVENTS_COMPARE[0] = 0; } static inline void phy_hw_radio_fast_ru_setup(void) { - NRF_RADIO->TIMING = (RADIO_TIMING_RU_Fast << RADIO_TIMING_RU_Pos) & - RADIO_TIMING_RU_Msk; + NRF_RADIO->TIMING = + (RADIO_TIMING_RU_Fast << RADIO_TIMING_RU_Pos) & RADIO_TIMING_RU_Msk; } static inline void @@ -126,17 +196,16 @@ phy_hw_radio_events_clear(void) static inline void phy_hw_radio_shorts_setup_tx(void) { - NRF_RADIO->SHORTS = RADIO_SHORTS_PHYEND_DISABLE_Msk | - RADIO_SHORTS_READY_START_Msk; + NRF_RADIO->SHORTS = RADIO_SHORTS_PHYEND_DISABLE_Msk | RADIO_SHORTS_READY_START_Msk; } static inline void phy_hw_radio_shorts_setup_rx(void) { - NRF_RADIO->SHORTS = RADIO_SHORTS_PHYEND_DISABLE_Msk | - RADIO_SHORTS_READY_START_Msk | - RADIO_SHORTS_ADDRESS_BCSTART_Msk | - RADIO_SHORTS_ADDRESS_RSSISTART_Msk; + /* nRF54L RADIO has no DISABLED_RSSISTOP shortcut */ + NRF_RADIO->SHORTS = + RADIO_SHORTS_PHYEND_DISABLE_Msk | RADIO_SHORTS_READY_START_Msk | + RADIO_SHORTS_ADDRESS_BCSTART_Msk | RADIO_SHORTS_ADDRESS_RSSISTART_Msk; } static inline void @@ -148,6 +217,7 @@ phy_hw_radio_datawhite_set(uint8_t chan) static inline void phy_hw_timer_configure(void) { + /* nRF54L TIMER10 runs at 32MHz; prescaler 5 → 32MHz/32 = 1MHz */ NRF_TIMER0->PRESCALER = 5; } @@ -157,16 +227,8 @@ phy_hw_radio_timer_task_stop(void) nrf_timer_task_trigger(NRF_TIMER0, NRF_TIMER_TASK_STOP); } -static inline void -phy_hw_aar_irk_setup(uint32_t *irk_ptr, uint32_t *scratch_ptr) -{ - /* TODO */ -} - -static inline void -phy_hw_aar_addrptr_set(uint8_t *dptr) -{ - /* TODO */ -} +/* AAR functions — implemented in nrf54l/phy.c */ +void phy_hw_aar_irk_setup(uint32_t *irk_ptr, uint32_t *scratch_ptr); +void phy_hw_aar_addrptr_set(uint8_t *dptr); #endif /* H_PHY_HW_ */ diff --git a/nimble/drivers/nrf5x/src/nrf54l/phy_ppi.h b/nimble/drivers/nrf5x/src/nrf54l/phy_ppi.h index 76bdd097c0..670807e21a 100644 --- a/nimble/drivers/nrf5x/src/nrf54l/phy_ppi.h +++ b/nimble/drivers/nrf5x/src/nrf54l/phy_ppi.h @@ -22,39 +22,41 @@ #include "phy_hw.h" -#define DPPI_CH_PUB(_ch) (((DPPI_CH_ ## _ch) & 0xff) | (1 << 31)) -#define DPPI_CH_SUB(_ch) (((DPPI_CH_ ## _ch) & 0xff) | (1 << 31)) -#define DPPI_CH_UNSUB(_ch) (((DPPI_CH_ ## _ch) & 0xff) | (0 << 31)) -#define DPPI_CH_MASK(_ch) (1 << (DPPI_CH_ ## _ch)) +#define DPPI_CH_PUB(_ch) (((DPPI_CH_##_ch) & 0xff) | (1 << 31)) +#define DPPI_CH_SUB(_ch) (((DPPI_CH_##_ch) & 0xff) | (1 << 31)) +#define DPPI_CH_UNSUB(_ch) (((DPPI_CH_##_ch) & 0xff) | (0 << 31)) +#define DPPI_CH_MASK(_ch) (1 << (DPPI_CH_##_ch)) /* DPPIC00 [0:7] */ +#define DPPI_CH_CCM00_SUBSCRIBE_START 0 +#define DPPI_CH_AAR00_SUBSCRIBE_START 1 /* DPPIC10 [0:23] */ -#define DPPI_CH_TIMER0_EVENTS_COMPARE_0 0 -#define DPPI_CH_TIMER0_EVENTS_COMPARE_3 1 -#define DPPI_CH_RADIO_EVENTS_END 2 -#define DPPI_CH_RADIO_EVENTS_BCMATCH 3 -#define DPPI_CH_RADIO_EVENTS_ADDRESS 4 -#define DPPI_CH_RTC0_EVENTS_COMPARE_0 5 -#define DPPI_CH_TIMER0_EVENTS_COMPARE_2 6 -#define DPPI_CH_RADIO_EVENTS_DISABLED 7 -#define DPPI_CH_RADIO_EVENTS_READY 8 -#define DPPI_CH_RADIO_EVENTS_RXREADY 9 +#define DPPI_CH_TIMER0_EVENTS_COMPARE_0 0 +#define DPPI_CH_TIMER0_EVENTS_COMPARE_3 1 +#define DPPI_CH_RADIO_EVENTS_PHYEND 2 +#define DPPI_CH_RADIO_EVENTS_BCMATCH 3 +#define DPPI_CH_RADIO_EVENTS_ADDRESS 4 +#define DPPI_CH_RTC0_EVENTS_COMPARE_0 5 +#define DPPI_CH_TIMER0_EVENTS_COMPARE_2 6 +#define DPPI_CH_RADIO_EVENTS_DISABLED 7 +#define DPPI_CH_RADIO_EVENTS_READY 8 +#define DPPI_CH_RADIO_EVENTS_RXREADY 9 /* reserved; not currently used */ /* DPPIC20 [0:15] */ -#define DPPI_CH_GPIOTE20_TASKS_SET_0 0 -#define DPPI_CH_GPIOTE20_TASKS_CLR_0 1 -#define DPPI_CH_GPIOTE20_TASKS_SET_1 2 -#define DPPI_CH_GPIOTE20_TASKS_CLR_1 3 +#define DPPI_CH_GPIOTE20_TASKS_SET_0 0 +#define DPPI_CH_GPIOTE20_TASKS_CLR_0 1 +#define DPPI_CH_GPIOTE20_TASKS_SET_1 2 +#define DPPI_CH_GPIOTE20_TASKS_CLR_1 3 /* DPPIC30 [0:3] */ -#define DPPI_CH_ENABLE_ALL (DPPIC_CHEN_CH0_Msk | DPPIC_CHEN_CH1_Msk | \ - DPPIC_CHEN_CH2_Msk | DPPIC_CHEN_CH3_Msk | \ - DPPIC_CHEN_CH4_Msk | DPPIC_CHEN_CH5_Msk) +#define DPPI_CH_ENABLE_ALL \ + (DPPIC_CHEN_CH0_Msk | DPPIC_CHEN_CH1_Msk | DPPIC_CHEN_CH2_Msk | \ + DPPIC_CHEN_CH3_Msk | DPPIC_CHEN_CH4_Msk | DPPIC_CHEN_CH5_Msk) -#define DPPI_CH_MASK_FEM (DPPI_CH_MASK(TIMER0_EVENTS_COMPARE_2) | \ - DPPI_CH_MASK(RADIO_EVENTS_DISABLED)) +#define DPPI_CH_MASK_FEM \ + (DPPI_CH_MASK(TIMER0_EVENTS_COMPARE_2) | DPPI_CH_MASK(RADIO_EVENTS_DISABLED)) static inline void phy_ppi_rtc0_compare0_to_timer0_start_enable(void) @@ -97,25 +99,25 @@ phy_ppi_timer0_compare0_to_radio_rxen_disable(void) static inline void phy_ppi_radio_address_to_ccm_crypt_enable(void) { - NRF_CCM->SUBSCRIBE_START = DPPI_CH_SUB(RADIO_EVENTS_ADDRESS); + NRF_CCM->SUBSCRIBE_START = DPPI_CH_SUB(CCM00_SUBSCRIBE_START); } static inline void phy_ppi_radio_address_to_ccm_crypt_disable(void) { - NRF_CCM->SUBSCRIBE_START = DPPI_CH_UNSUB(RADIO_EVENTS_ADDRESS); + NRF_CCM->SUBSCRIBE_START = DPPI_CH_UNSUB(CCM00_SUBSCRIBE_START); } static inline void phy_ppi_radio_bcmatch_to_aar_start_enable(void) { - NRF_AAR->SUBSCRIBE_START = DPPI_CH_SUB(RADIO_EVENTS_BCMATCH); + NRF_AAR->SUBSCRIBE_START = DPPI_CH_SUB(AAR00_SUBSCRIBE_START); } static inline void phy_ppi_radio_bcmatch_to_aar_start_disable(void) { - NRF_AAR->SUBSCRIBE_START = DPPI_CH_UNSUB(RADIO_EVENTS_BCMATCH); + NRF_AAR->SUBSCRIBE_START = DPPI_CH_UNSUB(AAR00_SUBSCRIBE_START); } static inline void @@ -158,8 +160,8 @@ phy_ppi_disable(void) NRF_RADIO->SUBSCRIBE_DISABLE = DPPI_CH_UNSUB(TIMER0_EVENTS_COMPARE_3); NRF_RADIO->SUBSCRIBE_TXEN = DPPI_CH_UNSUB(TIMER0_EVENTS_COMPARE_0); NRF_RADIO->SUBSCRIBE_RXEN = DPPI_CH_UNSUB(TIMER0_EVENTS_COMPARE_0); - NRF_AAR->SUBSCRIBE_START = DPPI_CH_UNSUB(RADIO_EVENTS_BCMATCH); - NRF_CCM->SUBSCRIBE_START = DPPI_CH_UNSUB(RADIO_EVENTS_ADDRESS); + NRF_AAR->SUBSCRIBE_START = DPPI_CH_UNSUB(AAR00_SUBSCRIBE_START); + NRF_CCM->SUBSCRIBE_START = DPPI_CH_UNSUB(CCM00_SUBSCRIBE_START); phy_ppi_fem_disable(); } diff --git a/nimble/drivers/nrf5x/src/phy_hw.h b/nimble/drivers/nrf5x/src/phy_hw.h new file mode 100644 index 0000000000..92bcfdca79 --- /dev/null +++ b/nimble/drivers/nrf5x/src/phy_hw.h @@ -0,0 +1,40 @@ +/* + * 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. + */ + +/* + * MCU-specific PHY hardware abstraction dispatcher. + * Routes to the correct chip-specific phy_hw.h based on MCU_TARGET. + */ + +#ifndef H_PHY_HW_DISPATCH_ +#define H_PHY_HW_DISPATCH_ + +#include "syscfg/syscfg.h" + +#if MYNEWT_VAL_CHOICE(MCU_TARGET, nRF54L15) || \ + MYNEWT_VAL_CHOICE(MCU_TARGET, nRF54L10) || \ + MYNEWT_VAL_CHOICE(MCU_TARGET, nRF54L05) +#include "nrf54l/phy_hw.h" +#elif MYNEWT_VAL_CHOICE(MCU_TARGET, nRF5340_NET) +#include "nrf53/phy_hw.h" +#else +#include "nrf52/phy_hw.h" +#endif + +#endif /* H_PHY_HW_DISPATCH_ */ diff --git a/nimble/drivers/nrf5x/src/phy_ppi.h b/nimble/drivers/nrf5x/src/phy_ppi.h new file mode 100644 index 0000000000..112eb409c4 --- /dev/null +++ b/nimble/drivers/nrf5x/src/phy_ppi.h @@ -0,0 +1,40 @@ +/* + * 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. + */ + +/* + * MCU-specific PPI abstraction dispatcher. + * Routes to the correct chip-specific phy_ppi.h based on MCU_TARGET. + */ + +#ifndef H_PHY_PPI_DISPATCH_ +#define H_PHY_PPI_DISPATCH_ + +#include "syscfg/syscfg.h" + +#if MYNEWT_VAL_CHOICE(MCU_TARGET, nRF54L15) || \ + MYNEWT_VAL_CHOICE(MCU_TARGET, nRF54L10) || \ + MYNEWT_VAL_CHOICE(MCU_TARGET, nRF54L05) +#include "nrf54l/phy_ppi.h" +#elif MYNEWT_VAL_CHOICE(MCU_TARGET, nRF5340_NET) +#include "nrf53/phy_ppi.h" +#else +#include "nrf52/phy_ppi.h" +#endif + +#endif /* H_PHY_PPI_DISPATCH_ */