Low frequency and high frequency clocks had separate devices while they are actually handled by single peripheral with single interrupt. The split was done probably because opaque subsys argument in the API was used for other purposes and there was no way to pass the information which clock should be controlled. Implementation changes some time ago and subsys parameter was no longer used. It now can be used to indicate which clock should be controlled. Change become necessary when nrf5340 is taken into account where there are more clocks and current approach would lead to create multiple devices - mess. Signed-off-by: Krzysztof Chruscinski <krzysztof.chruscinski@nordicsemi.no>
355 lines
7.7 KiB
C
355 lines
7.7 KiB
C
/*
|
|
* Copyright (c) 2017 Nordic Semiconductor ASA
|
|
*
|
|
* SPDX-License-Identifier: Apache-2.0
|
|
*/
|
|
|
|
#include <stddef.h>
|
|
#include <string.h>
|
|
|
|
#include <toolchain.h>
|
|
#include <zephyr/types.h>
|
|
#include <soc.h>
|
|
#include <drivers/clock_control.h>
|
|
#include <drivers/clock_control/nrf_clock_control.h>
|
|
|
|
#include "hal/cpu.h"
|
|
#include "hal/cntr.h"
|
|
#include "hal/ccm.h"
|
|
#include "hal/radio.h"
|
|
|
|
#if defined(CONFIG_BT_LL_SW_LEGACY)
|
|
#include "util/util.h"
|
|
#include "util/memq.h"
|
|
#include "pdu.h"
|
|
#include "lll.h"
|
|
#include "ctrl.h"
|
|
#endif
|
|
|
|
#include "ll_test.h"
|
|
|
|
#define CNTR_MIN_DELTA 3
|
|
|
|
static const u32_t test_sync_word = 0x71764129;
|
|
static u8_t test_phy;
|
|
static u8_t test_phy_flags;
|
|
static u16_t test_num_rx;
|
|
static bool started;
|
|
|
|
/* NOTE: The PRBS9 sequence used as packet payload.
|
|
* The bytes in the sequence are in the right order, but the bits of each byte
|
|
* in the array are reverse from that found by running the PRBS9 algorithm. This
|
|
* is done to transmit MSbit first on air.
|
|
*/
|
|
|
|
static const u8_t prbs9[] = {
|
|
0xFF, 0xC1, 0xFB, 0xE8, 0x4C, 0x90, 0x72, 0x8B,
|
|
0xE7, 0xB3, 0x51, 0x89, 0x63, 0xAB, 0x23, 0x23,
|
|
0x02, 0x84, 0x18, 0x72, 0xAA, 0x61, 0x2F, 0x3B,
|
|
0x51, 0xA8, 0xE5, 0x37, 0x49, 0xFB, 0xC9, 0xCA,
|
|
0x0C, 0x18, 0x53, 0x2C, 0xFD, 0x45, 0xE3, 0x9A,
|
|
0xE6, 0xF1, 0x5D, 0xB0, 0xB6, 0x1B, 0xB4, 0xBE,
|
|
0x2A, 0x50, 0xEA, 0xE9, 0x0E, 0x9C, 0x4B, 0x5E,
|
|
0x57, 0x24, 0xCC, 0xA1, 0xB7, 0x59, 0xB8, 0x87,
|
|
0xFF, 0xE0, 0x7D, 0x74, 0x26, 0x48, 0xB9, 0xC5,
|
|
0xF3, 0xD9, 0xA8, 0xC4, 0xB1, 0xD5, 0x91, 0x11,
|
|
0x01, 0x42, 0x0C, 0x39, 0xD5, 0xB0, 0x97, 0x9D,
|
|
0x28, 0xD4, 0xF2, 0x9B, 0xA4, 0xFD, 0x64, 0x65,
|
|
0x06, 0x8C, 0x29, 0x96, 0xFE, 0xA2, 0x71, 0x4D,
|
|
0xF3, 0xF8, 0x2E, 0x58, 0xDB, 0x0D, 0x5A, 0x5F,
|
|
0x15, 0x28, 0xF5, 0x74, 0x07, 0xCE, 0x25, 0xAF,
|
|
0x2B, 0x12, 0xE6, 0xD0, 0xDB, 0x2C, 0xDC, 0xC3,
|
|
0x7F, 0xF0, 0x3E, 0x3A, 0x13, 0xA4, 0xDC, 0xE2,
|
|
0xF9, 0x6C, 0x54, 0xE2, 0xD8, 0xEA, 0xC8, 0x88,
|
|
0x00, 0x21, 0x86, 0x9C, 0x6A, 0xD8, 0xCB, 0x4E,
|
|
0x14, 0x6A, 0xF9, 0x4D, 0xD2, 0x7E, 0xB2, 0x32,
|
|
0x03, 0xC6, 0x14, 0x4B, 0x7F, 0xD1, 0xB8, 0xA6,
|
|
0x79, 0x7C, 0x17, 0xAC, 0xED, 0x06, 0xAD, 0xAF,
|
|
0x0A, 0x94, 0x7A, 0xBA, 0x03, 0xE7, 0x92, 0xD7,
|
|
0x15, 0x09, 0x73, 0xE8, 0x6D, 0x16, 0xEE, 0xE1,
|
|
0x3F, 0x78, 0x1F, 0x9D, 0x09, 0x52, 0x6E, 0xF1,
|
|
0x7C, 0x36, 0x2A, 0x71, 0x6C, 0x75, 0x64, 0x44,
|
|
0x80, 0x10, 0x43, 0x4E, 0x35, 0xEC, 0x65, 0x27,
|
|
0x0A, 0xB5, 0xFC, 0x26, 0x69, 0x3F, 0x59, 0x99,
|
|
0x01, 0x63, 0x8A, 0xA5, 0xBF, 0x68, 0x5C, 0xD3,
|
|
0x3C, 0xBE, 0x0B, 0xD6, 0x76, 0x83, 0xD6, 0x57,
|
|
0x05, 0x4A, 0x3D, 0xDD, 0x81, 0x73, 0xC9, 0xEB,
|
|
0x8A, 0x84, 0x39, 0xF4, 0x36, 0x0B, 0xF7};
|
|
|
|
/* TODO: fill correct prbs15 */
|
|
static const u8_t prbs15[255] = { 0x00, };
|
|
|
|
static u8_t tx_req;
|
|
static u8_t volatile tx_ack;
|
|
|
|
static void isr_tx(void *param)
|
|
{
|
|
u32_t l, i, s, t;
|
|
|
|
/* Clear radio status and events */
|
|
radio_status_reset();
|
|
radio_tmr_status_reset();
|
|
|
|
#if defined(CONFIG_BT_CTLR_GPIO_PA_PIN)
|
|
radio_gpio_pa_lna_disable();
|
|
#endif /* CONFIG_BT_CTLR_GPIO_PA_PIN */
|
|
|
|
/* Exit if radio disabled */
|
|
if (((tx_req - tx_ack) & 0x01) == 0U) {
|
|
tx_ack = tx_req;
|
|
|
|
return;
|
|
}
|
|
|
|
/* LE Test Packet Interval */
|
|
l = radio_tmr_end_get() - radio_tmr_ready_get();
|
|
i = ((l + 249 + 624) / 625) * 625U;
|
|
t = radio_tmr_end_get() - l + i;
|
|
t -= radio_tx_ready_delay_get(test_phy, test_phy_flags);
|
|
|
|
/* Set timer capture in the future. */
|
|
radio_tmr_sample();
|
|
s = radio_tmr_sample_get();
|
|
while (t < s) {
|
|
t += 625U;
|
|
}
|
|
|
|
/* Setup next Tx */
|
|
radio_switch_complete_and_disable();
|
|
radio_tmr_start_us(1, t);
|
|
radio_tmr_aa_capture();
|
|
radio_tmr_end_capture();
|
|
|
|
/* TODO: check for probable stale timer capture being set */
|
|
|
|
#if defined(CONFIG_BT_CTLR_GPIO_PA_PIN)
|
|
radio_gpio_pa_setup();
|
|
radio_gpio_pa_lna_enable(t + radio_tx_ready_delay_get(test_phy,
|
|
test_phy_flags) -
|
|
CONFIG_BT_CTLR_GPIO_PA_OFFSET);
|
|
#endif /* CONFIG_BT_CTLR_GPIO_PA_PIN */
|
|
}
|
|
|
|
static void isr_rx(void *param)
|
|
{
|
|
u8_t crc_ok = 0U;
|
|
u8_t trx_done;
|
|
|
|
/* Read radio status and events */
|
|
trx_done = radio_is_done();
|
|
if (trx_done) {
|
|
crc_ok = radio_crc_is_valid();
|
|
}
|
|
|
|
/* Clear radio status and events */
|
|
radio_status_reset();
|
|
radio_tmr_status_reset();
|
|
|
|
/* Exit if radio disabled */
|
|
if (!trx_done) {
|
|
return;
|
|
}
|
|
|
|
/* Setup next Rx */
|
|
radio_switch_complete_and_rx(test_phy);
|
|
|
|
/* Count Rx-ed packets */
|
|
if (crc_ok) {
|
|
test_num_rx++;
|
|
}
|
|
}
|
|
|
|
static u32_t init(u8_t chan, u8_t phy, void (*isr)(void *param))
|
|
{
|
|
struct device *hf_clock;
|
|
|
|
if (started) {
|
|
return 1;
|
|
}
|
|
|
|
/* start coarse timer */
|
|
cntr_start();
|
|
|
|
/* Setup resources required by Radio */
|
|
hf_clock = radio_hf_clock_get();
|
|
clock_control_on(hf_clock, NULL); /* start clock, blocking. */
|
|
|
|
while (clock_control_get_status(hf_clock, NULL) !=
|
|
CLOCK_CONTROL_STATUS_ON) {
|
|
}
|
|
|
|
/* Reset Radio h/w */
|
|
radio_reset();
|
|
radio_isr_set(isr, NULL);
|
|
|
|
/* Store value needed in Tx/Rx ISR */
|
|
if (phy < 0x04) {
|
|
test_phy = BIT(phy - 1);
|
|
test_phy_flags = 1U;
|
|
} else {
|
|
test_phy = BIT(2);
|
|
test_phy_flags = 0U;
|
|
}
|
|
|
|
/* Setup Radio in Tx/Rx */
|
|
/* NOTE: No whitening in test mode. */
|
|
radio_phy_set(test_phy, test_phy_flags);
|
|
radio_tmr_tifs_set(150);
|
|
radio_tx_power_max_set();
|
|
radio_freq_chan_set((chan << 1) + 2);
|
|
radio_aa_set((u8_t *)&test_sync_word);
|
|
radio_crc_configure(0x65b, 0x555555);
|
|
radio_pkt_configure(8, 255, (test_phy << 1));
|
|
|
|
return 0;
|
|
}
|
|
|
|
u32_t ll_test_tx(u8_t chan, u8_t len, u8_t type, u8_t phy)
|
|
{
|
|
u32_t start_us;
|
|
u8_t *payload;
|
|
u8_t *pdu;
|
|
u32_t err;
|
|
|
|
if ((type > 0x07) || !phy || (phy > 0x04)) {
|
|
return 1;
|
|
}
|
|
|
|
err = init(chan, phy, isr_tx);
|
|
if (err) {
|
|
return err;
|
|
}
|
|
|
|
tx_req++;
|
|
|
|
pdu = radio_pkt_scratch_get();
|
|
payload = &pdu[2];
|
|
|
|
switch (type) {
|
|
case 0x00:
|
|
memcpy(payload, prbs9, len);
|
|
break;
|
|
|
|
case 0x01:
|
|
(void)memset(payload, 0x0f, len);
|
|
break;
|
|
|
|
case 0x02:
|
|
(void)memset(payload, 0x55, len);
|
|
break;
|
|
|
|
case 0x03:
|
|
memcpy(payload, prbs15, len);
|
|
break;
|
|
|
|
case 0x04:
|
|
(void)memset(payload, 0xff, len);
|
|
break;
|
|
|
|
case 0x05:
|
|
(void)memset(payload, 0x00, len);
|
|
break;
|
|
|
|
case 0x06:
|
|
(void)memset(payload, 0xf0, len);
|
|
break;
|
|
|
|
case 0x07:
|
|
(void)memset(payload, 0xaa, len);
|
|
break;
|
|
}
|
|
|
|
pdu[0] = type;
|
|
pdu[1] = len;
|
|
|
|
radio_pkt_tx_set(pdu);
|
|
radio_switch_complete_and_disable();
|
|
start_us = radio_tmr_start(1, cntr_cnt_get() + CNTR_MIN_DELTA, 0);
|
|
radio_tmr_aa_capture();
|
|
radio_tmr_end_capture();
|
|
|
|
#if defined(CONFIG_BT_CTLR_GPIO_PA_PIN)
|
|
radio_gpio_pa_setup();
|
|
radio_gpio_pa_lna_enable(start_us +
|
|
radio_tx_ready_delay_get(test_phy,
|
|
test_phy_flags) -
|
|
CONFIG_BT_CTLR_GPIO_PA_OFFSET);
|
|
#else /* !CONFIG_BT_CTLR_GPIO_PA_PIN */
|
|
ARG_UNUSED(start_us);
|
|
#endif /* !CONFIG_BT_CTLR_GPIO_PA_PIN */
|
|
|
|
started = true;
|
|
|
|
return 0;
|
|
}
|
|
|
|
u32_t ll_test_rx(u8_t chan, u8_t phy, u8_t mod_idx)
|
|
{
|
|
u32_t err;
|
|
|
|
if (!phy || (phy > 0x03)) {
|
|
return 1;
|
|
}
|
|
|
|
err = init(chan, phy, isr_rx);
|
|
if (err) {
|
|
return err;
|
|
}
|
|
|
|
radio_pkt_rx_set(radio_pkt_scratch_get());
|
|
radio_switch_complete_and_rx(test_phy);
|
|
radio_tmr_start(0, cntr_cnt_get() + CNTR_MIN_DELTA, 0);
|
|
|
|
#if defined(CONFIG_BT_CTLR_GPIO_LNA_PIN)
|
|
radio_gpio_lna_on();
|
|
#endif /* !CONFIG_BT_CTLR_GPIO_LNA_PIN */
|
|
|
|
started = true;
|
|
|
|
return 0;
|
|
}
|
|
|
|
u32_t ll_test_end(u16_t *num_rx)
|
|
{
|
|
struct device *clock;
|
|
u8_t ack;
|
|
|
|
if (!started) {
|
|
return 1;
|
|
}
|
|
|
|
/* Return packets Rx-ed/Completed */
|
|
*num_rx = test_num_rx;
|
|
test_num_rx = 0U;
|
|
|
|
/* Disable Radio, if in Rx test */
|
|
ack = tx_ack;
|
|
if (tx_req == ack) {
|
|
radio_disable();
|
|
} else {
|
|
/* Wait for Tx to complete */
|
|
tx_req = ack + 2;
|
|
while (tx_req != tx_ack) {
|
|
cpu_sleep();
|
|
}
|
|
}
|
|
|
|
/* Stop packet timer */
|
|
radio_tmr_stop();
|
|
|
|
/* Release resources acquired for Radio */
|
|
clock = radio_hf_clock_get();
|
|
clock_control_off(clock, CLOCK_CONTROL_NRF_SUBSYS_HF);
|
|
|
|
/* Stop coarse timer */
|
|
cntr_stop();
|
|
|
|
#if defined(CONFIG_BT_CTLR_GPIO_LNA_PIN)
|
|
radio_gpio_lna_off();
|
|
#endif /* !CONFIG_BT_CTLR_GPIO_LNA_PIN */
|
|
|
|
started = false;
|
|
|
|
return 0;
|
|
}
|