1 /*
2  * Copyright (c) 2020 Nordic Semiconductor ASA
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  */
6 
7 #include <stdint.h>
8 #include <errno.h>
9 #include <soc.h>
10 #include <zephyr/devicetree.h>
11 #include <zephyr/sys/util_macro.h>
12 #include <hal/nrf_radio.h>
13 #include <hal/nrf_gpio.h>
14 #include <hal/ccm.h>
15 
16 #include "pdu_df.h"
17 #include "lll/pdu_vendor.h"
18 #include "pdu.h"
19 
20 #include "radio_nrf5.h"
21 #include "radio.h"
22 #include "radio_df.h"
23 #include "radio_internal.h"
24 
25 /* Devicetree node identifier for the radio node. */
26 #define RADIO_NODE DT_NODELABEL(radio)
27 
28 /* Value to set for unconnected antenna GPIO pins. */
29 #define DFE_PSEL_NOT_SET 0xFF
30 /* Number of PSEL_DFEGPIO[n] registers in the radio peripheral. */
31 #define MAX_DFE_GPIO 8
32 /* Run a macro 'fn' on each available DFE GPIO index, from 0 to
33  * MAX_DFE_GPIO-1, with the given parenthesized separator.
34  */
35 #define FOR_EACH_DFE_GPIO(fn, sep) \
36 	FOR_EACH(fn, sep, 0, 1, 2, 3, 4, 5, 6, 7)
37 
38 /* Index of antenna id in antenna switching pattern used for GUARD and REFERENCE period */
39 #define GUARD_REF_ANTENNA_PATTERN_IDX 0U
40 
41 /* Direction Finding antenna matrix configuration */
42 struct df_ant_cfg {
43 	uint8_t ant_num;
44 	/* Selection of GPIOs to be used to switch antennas by Radio */
45 	uint8_t dfe_gpio[MAX_DFE_GPIO];
46 };
47 
48 #define DFE_GPIO_PSEL(idx)					  \
49 	NRF_DT_GPIOS_TO_PSEL_OR(RADIO_NODE, dfegpio##idx##_gpios, \
50 				DFE_PSEL_NOT_SET)
51 
52 #define DFE_GPIO_PIN_DISCONNECT (RADIO_PSEL_DFEGPIO_CONNECT_Disconnected << \
53 				 RADIO_PSEL_DFEGPIO_CONNECT_Pos)
54 
55 #define HAS_DFE_GPIO(idx) DT_NODE_HAS_PROP(RADIO_NODE, dfegpio##idx##_gpios)
56 
57 /* The number of dfegpio[n]-gpios properties which are set. */
58 #define DFE_GPIO_NUM (FOR_EACH_DFE_GPIO(HAS_DFE_GPIO, (+)))
59 
60 /* The minimum number of antennas required to enable antenna switching. */
61 #define MIN_ANTENNA_NUM 2
62 
63 /* The maximum number of antennas supported by the number of
64  * dfegpio[n]-gpios properties which are set.
65  */
66 #if (DFE_GPIO_NUM > 0)
67 #define MAX_ANTENNA_NUM BIT(DFE_GPIO_NUM)
68 #else
69 #define MAX_ANTENNA_NUM 0
70 #endif
71 
radio_df_pdu_antenna_switch_pattern_get(void)72 uint8_t radio_df_pdu_antenna_switch_pattern_get(void)
73 {
74 	return PDU_ANTENNA;
75 }
76 
77 #if defined(CONFIG_BT_CTLR_DF_ANT_SWITCH_TX) || \
78 	defined(CONFIG_BT_CTLR_DF_ANT_SWITCH_RX)
79 
80 /*
81  * Check that we have an antenna switch pattern for the DFE idle
82  * state. (In DFE idle state, the radio peripheral transmits or
83  * receives PDUs.)
84  */
85 
86 #define HAS_PDU_ANTENNA DT_NODE_HAS_PROP(RADIO_NODE, dfe_pdu_antenna)
87 
88 BUILD_ASSERT(HAS_PDU_ANTENNA,
89 	     "Missing antenna pattern used to select antenna for PDU Tx "
90 	     "during the DFE Idle state. "
91 	     "Set the dfe-pdu-antenna devicetree property.");
92 
radio_df_ant_switch_pattern_set(const uint8_t * patterns,uint8_t len)93 void radio_df_ant_switch_pattern_set(const uint8_t *patterns, uint8_t len)
94 {
95 	/* SWITCHPATTERN is like a moving pointer to an underlying buffer.
96 	 * Each write stores a value and moves the pointer to new free position.
97 	 * When read it returns number of stored elements since last write to
98 	 * CLEARPATTERN. There is no need to use a subscript operator.
99 	 *
100 	 * Some storage entries in the buffer has special purpose for DFE
101 	 * extension in radio:
102 	 * - SWITCHPATTERN[0] for idle period (PDU Tx/Rx),
103 	 * - SWITCHPATTERN[1] for guard and reference period,
104 	 * - SWITCHPATTERN[2] and following for switch-sampling slots.
105 	 * Due to that in SWITCHPATTER[0] there is stored a pattern provided by
106 	 * DTS property dfe_pdu_antenna. This limits number of supported antenna
107 	 * switch patterns by one.
108 	 */
109 	NRF_RADIO->SWITCHPATTERN = PDU_ANTENNA;
110 	for (uint8_t idx = 0; idx < len; ++idx) {
111 		NRF_RADIO->SWITCHPATTERN = patterns[idx];
112 	}
113 
114 	/* Store antenna id used for GUARD and REFERENCE period at the end of SWITCHPATTERN buffer.
115 	 * It is required to apply reference antenna id when user provided switchpattern is
116 	 * exhausted.
117 	 * Maximum length of the switch pattern provided to this function is at maximum lower by one
118 	 * than capacity of SWITCHPATTERN buffer. Hence there is always space for reference antenna
119 	 * id after end of switch pattern.
120 	 */
121 	NRF_RADIO->SWITCHPATTERN = patterns[GUARD_REF_ANTENNA_PATTERN_IDX];
122 }
123 
124 /*
125  * Check that the number of antennas has been set, and that enough
126  * pins are configured to represent each pattern for the given number
127  * of antennas.
128  */
129 
130 #define HAS_ANTENNA_NUM DT_NODE_HAS_PROP(RADIO_NODE, dfe_antenna_num)
131 
132 BUILD_ASSERT(HAS_ANTENNA_NUM,
133 	     "You must set the dfe-antenna-num property in the radio node "
134 	     "to enable antenna switching.");
135 
136 #define ANTENNA_NUM DT_PROP_OR(RADIO_NODE, dfe_antenna_num, 0)
137 
138 BUILD_ASSERT(!HAS_ANTENNA_NUM || (ANTENNA_NUM <= MAX_ANTENNA_NUM),
139 	     "Insufficient number of GPIO pins configured. "
140 	     "Set more dfegpio[n]-gpios properties.");
141 BUILD_ASSERT(!HAS_ANTENNA_NUM || (ANTENNA_NUM >= MIN_ANTENNA_NUM),
142 	     "Insufficient number of antennas provided. "
143 	     "Increase the dfe-antenna-num property.");
144 
145 /*
146  * Check that each dfegpio[n]-gpios property has a zero flags cell.
147  */
148 
149 #define ASSERT_DFE_GPIO_FLAGS_ARE_ZERO(idx)				   \
150 	BUILD_ASSERT(DT_GPIO_FLAGS(RADIO_NODE, dfegpio##idx##_gpios) == 0, \
151 		     "The flags cell in each dfegpio[n]-gpios "		   \
152 		     "property must be zero.")
153 
154 FOR_EACH_DFE_GPIO(ASSERT_DFE_GPIO_FLAGS_ARE_ZERO, (;));
155 
156 /* Stores the dfegpio[n]-gpios property values.
157  */
158 const static struct df_ant_cfg ant_cfg = {
159 	.ant_num = ANTENNA_NUM,
160 	.dfe_gpio = { FOR_EACH_DFE_GPIO(DFE_GPIO_PSEL, (,)) }
161 };
162 
163 /* @brief Function configures Radio with information about GPIO pins that may be
164  *        used to drive antenna switching during CTE Tx/RX.
165  *
166  * Sets up DF related PSEL.DFEGPIO registers to give possibility to Radio
167  * to drive antennas switches.
168  *
169  */
radio_df_ant_switching_pin_sel_cfg(void)170 void radio_df_ant_switching_pin_sel_cfg(void)
171 {
172 	uint8_t pin_sel;
173 
174 	for (uint8_t idx = 0; idx < MAX_DFE_GPIO; ++idx) {
175 		pin_sel = ant_cfg.dfe_gpio[idx];
176 
177 		if (pin_sel != DFE_PSEL_NOT_SET) {
178 			nrf_radio_dfe_pattern_pin_set(NRF_RADIO,
179 						      pin_sel,
180 						      idx);
181 		} else {
182 			nrf_radio_dfe_pattern_pin_set(NRF_RADIO,
183 						      DFE_GPIO_PIN_DISCONNECT,
184 						      idx);
185 		}
186 	}
187 }
188 
189 #if defined(CONFIG_BT_CTLR_DF_INIT_ANT_SEL_GPIOS)
190 /* @brief Function configures GPIO pins that will be used by Direction Finding
191  *        Extension for antenna switching.
192  *
193  * Function configures antenna selection GPIO pins in GPIO peripheral.
194  * Also sets pin outputs to match state in SWITCHPATTERN[0] that is used
195  * to enable antenna for PDU Rx/Tx. That has to prevent glitches when
196  * DFE is powered off after end of transmission.
197  *
198  * @return	Zero in case of success, other value in case of failure.
199  */
radio_df_ant_switching_gpios_cfg(void)200 void radio_df_ant_switching_gpios_cfg(void)
201 {
202 	uint8_t pin_sel;
203 
204 	for (uint8_t idx = 0; idx < MAX_DFE_GPIO; ++idx) {
205 		pin_sel = ant_cfg.dfe_gpio[idx];
206 		if (pin_sel != DFE_PSEL_NOT_SET) {
207 			nrf_gpio_cfg_output(pin_sel);
208 
209 			if (BIT(idx) & PDU_ANTENNA) {
210 				nrf_gpio_pin_set(pin_sel);
211 			} else {
212 				nrf_gpio_pin_clear(pin_sel);
213 			}
214 		}
215 	}
216 }
217 #endif /* CONFIG_BT_CTLR_DF_INIT_ANT_SEL_GPIOS */
218 #endif /* CONFIG_BT_CTLR_DF_ANT_SWITCH_TX || CONFIG_BT_CTLR_DF_ANT_SWITCH_RX */
219 
220 /* @brief Function provides number of available antennas for Direction Finding.
221  *
222  * The number of antennas is hardware defined. It is provided via devicetree.
223  *
224  * If antenna switching is not enabled then there must be a single antenna
225  * responsible for PDU reception and transmission.
226  *
227  * @return	Number of available antennas.
228  */
radio_df_ant_num_get(void)229 uint8_t radio_df_ant_num_get(void)
230 {
231 #if defined(CONFIG_BT_CTLR_DF_ANT_SWITCH_TX) || \
232 	defined(CONFIG_BT_CTLR_DF_ANT_SWITCH_RX)
233 	return ant_cfg.ant_num;
234 #else
235 	return 1U;
236 #endif
237 }
238 
radio_df_mode_set(uint8_t mode)239 static inline void radio_df_mode_set(uint8_t mode)
240 {
241 	NRF_RADIO->DFEMODE &= ~RADIO_DFEMODE_DFEOPMODE_Msk;
242 	NRF_RADIO->DFEMODE |= ((mode << RADIO_DFEMODE_DFEOPMODE_Pos)
243 			       & RADIO_DFEMODE_DFEOPMODE_Msk);
244 }
245 
radio_df_mode_set_aoa(void)246 void radio_df_mode_set_aoa(void)
247 {
248 	radio_df_mode_set(NRF_RADIO_DFE_OP_MODE_AOA);
249 }
250 
radio_df_mode_set_aod(void)251 void radio_df_mode_set_aod(void)
252 {
253 	radio_df_mode_set(NRF_RADIO_DFE_OP_MODE_AOD);
254 }
255 
radio_df_ctrl_set(uint8_t cte_len,uint8_t switch_spacing,uint8_t sample_spacing,uint8_t phy)256 static inline void radio_df_ctrl_set(uint8_t cte_len,
257 				     uint8_t switch_spacing,
258 				     uint8_t sample_spacing,
259 				     uint8_t phy)
260 {
261 	uint16_t sample_offset;
262 	uint32_t conf;
263 
264 	/* Complete setup is done on purpose, to be sure that there isn't left
265 	 * any unexpected state in the register.
266 	 */
267 	conf = ((((uint32_t)cte_len << RADIO_DFECTRL1_NUMBEROF8US_Pos) &
268 				       RADIO_DFECTRL1_NUMBEROF8US_Msk) |
269 		((uint32_t)RADIO_DFECTRL1_DFEINEXTENSION_CRC <<
270 			RADIO_DFECTRL1_DFEINEXTENSION_Pos) |
271 		((uint32_t)switch_spacing << RADIO_DFECTRL1_TSWITCHSPACING_Pos) |
272 		((uint32_t)NRF_RADIO_DFECTRL_SAMPLE_SPACING_1US <<
273 			RADIO_DFECTRL1_TSAMPLESPACINGREF_Pos) |
274 		((uint32_t)NRF_RADIO_DFECTRL_SAMPLE_TYPE_IQ <<
275 			RADIO_DFECTRL1_SAMPLETYPE_Pos) |
276 		((uint32_t)sample_spacing << RADIO_DFECTRL1_TSAMPLESPACING_Pos) |
277 		(((uint32_t)0 << RADIO_DFECTRL1_AGCBACKOFFGAIN_Pos) &
278 				 RADIO_DFECTRL1_AGCBACKOFFGAIN_Msk));
279 
280 	NRF_RADIO->DFECTRL1 = conf;
281 
282 	switch (phy) {
283 	case PHY_1M:
284 		if (switch_spacing == RADIO_DFECTRL1_TSWITCHSPACING_2us) {
285 			sample_offset = CONFIG_BT_CTLR_DF_SAMPLE_OFFSET_PHY_1M_SAMPLING_1US;
286 		} else if (switch_spacing == RADIO_DFECTRL1_TSWITCHSPACING_4us) {
287 			sample_offset = CONFIG_BT_CTLR_DF_SAMPLE_OFFSET_PHY_1M_SAMPLING_2US;
288 		} else {
289 			sample_offset = 0;
290 		}
291 		break;
292 	case PHY_2M:
293 		if (switch_spacing == RADIO_DFECTRL1_TSWITCHSPACING_2us) {
294 			sample_offset = CONFIG_BT_CTLR_DF_SAMPLE_OFFSET_PHY_2M_SAMPLING_1US;
295 		} else if (switch_spacing == RADIO_DFECTRL1_TSWITCHSPACING_4us) {
296 			sample_offset = CONFIG_BT_CTLR_DF_SAMPLE_OFFSET_PHY_2M_SAMPLING_2US;
297 		} else {
298 			sample_offset = 0;
299 		}
300 		break;
301 	case PHY_LEGACY:
302 	default:
303 		/* If phy is set to legacy, the function is called in TX context and actual value
304 		 * does not matter, hence it is set to default zero.
305 		 */
306 		sample_offset = 0;
307 	}
308 
309 	conf = ((((uint32_t)sample_offset << RADIO_DFECTRL2_TSAMPLEOFFSET_Pos) &
310 				       RADIO_DFECTRL2_TSAMPLEOFFSET_Msk) |
311 		(((uint32_t)CONFIG_BT_CTLR_DF_SWITCH_OFFSET << RADIO_DFECTRL2_TSWITCHOFFSET_Pos) &
312 				       RADIO_DFECTRL2_TSWITCHOFFSET_Msk));
313 
314 	NRF_RADIO->DFECTRL2 = conf;
315 }
316 
radio_df_cte_tx_aod_2us_set(uint8_t cte_len)317 void radio_df_cte_tx_aod_2us_set(uint8_t cte_len)
318 {
319 	/* Sample spacing does not matter for AoD Tx. It is set to value
320 	 * that is in DFECTRL1 register after reset. That is done instead of
321 	 * adding conditions on the value and masking of the field before
322 	 * storing configuration in the register. Also values in DFECTRL2,
323 	 * that depend on PHY, are irrelevant for AoD Tx, hence use of
324 	 * PHY_LEGACY here.
325 	 */
326 	radio_df_ctrl_set(cte_len, RADIO_DFECTRL1_TSWITCHSPACING_2us,
327 			  RADIO_DFECTRL1_TSAMPLESPACING_2us, PHY_LEGACY);
328 }
329 
radio_df_cte_tx_aod_4us_set(uint8_t cte_len)330 void radio_df_cte_tx_aod_4us_set(uint8_t cte_len)
331 {
332 	/* Sample spacing does not matter for AoD Tx. It is set to value
333 	 * that is in DFECTRL1 register after reset. That is done instead of
334 	 * adding conditions on the value and masking of the field before
335 	 * storing configuration in the register. Also values in DFECTRL2,
336 	 * that depend on PHY, are irrelevant for AoD Tx, hence use of
337 	 * PHY_LEGACY here.
338 	 */
339 	radio_df_ctrl_set(cte_len, RADIO_DFECTRL1_TSWITCHSPACING_4us,
340 			  RADIO_DFECTRL1_TSAMPLESPACING_2us, PHY_LEGACY);
341 }
342 
radio_df_cte_tx_aoa_set(uint8_t cte_len)343 void radio_df_cte_tx_aoa_set(uint8_t cte_len)
344 {
345 	/* Switch and sample spacing does not matter for AoA Tx. It is set to
346 	 * value that is in DFECTRL1 register after reset. That is done instead
347 	 * of adding conditions on the value and masking of the field before
348 	 * storing configuration in the register. Also values in DFECTRL2,
349 	 * that depend on PHY, are irrelevant for AoA Tx, hence use of
350 	 * PHY_LEGACY here.
351 	 */
352 	radio_df_ctrl_set(cte_len, RADIO_DFECTRL1_TSWITCHSPACING_4us,
353 			  RADIO_DFECTRL1_TSAMPLESPACING_2us, PHY_LEGACY);
354 }
355 
radio_df_cte_rx_2us_switching(bool cte_info_in_s1,uint8_t phy)356 void radio_df_cte_rx_2us_switching(bool cte_info_in_s1, uint8_t phy)
357 {
358 	/* BT spec requires single sample for a single switching slot, so
359 	 * spacing for slot and samples is the same.
360 	 * CTE duration is used only when CTEINLINE config is disabled.
361 	 */
362 	radio_df_ctrl_set(0, RADIO_DFECTRL1_TSWITCHSPACING_2us,
363 			  RADIO_DFECTRL1_TSAMPLESPACING_2us, phy);
364 	radio_df_cte_inline_set_enabled(cte_info_in_s1);
365 }
366 
radio_df_cte_rx_4us_switching(bool cte_info_in_s1,uint8_t phy)367 void radio_df_cte_rx_4us_switching(bool cte_info_in_s1, uint8_t phy)
368 {
369 	/* BT spec requires single sample for a single switching slot, so
370 	 * spacing for slot and samples is the same.
371 	 * CTE duration is used only when CTEINLINE config is disabled.
372 	 */
373 	radio_df_ctrl_set(0, RADIO_DFECTRL1_TSWITCHSPACING_4us,
374 			  RADIO_DFECTRL1_TSAMPLESPACING_4us, phy);
375 	radio_df_cte_inline_set_enabled(cte_info_in_s1);
376 }
377 
radio_df_ant_switch_pattern_clear(void)378 void radio_df_ant_switch_pattern_clear(void)
379 {
380 	NRF_RADIO->CLEARPATTERN = (HAL_RADIO_CLEARPATTERN_CLEARPATTERN_Clear <<
381 				   RADIO_CLEARPATTERN_CLEARPATTERN_Pos) &
382 				  RADIO_CLEARPATTERN_CLEARPATTERN_Msk;
383 }
384 
radio_df_reset(void)385 void radio_df_reset(void)
386 {
387 	/* Initialize to NRF_RADIO reset values
388 	 * Note: Only registers that turn off the DF feature and those
389 	 *       registers whose bits are partially modified across functions
390 	 *       are assigned back the power-on reset values.
391 	 */
392 	NRF_RADIO->DFEMODE = HAL_RADIO_RESET_VALUE_DFEMODE;
393 	NRF_RADIO->CTEINLINECONF = HAL_RADIO_RESET_VALUE_CTEINLINECONF;
394 
395 	radio_df_ant_switch_pattern_clear();
396 }
397 
radio_switch_complete_and_phy_end_b2b_tx(uint8_t phy_curr,uint8_t flags_curr,uint8_t phy_next,uint8_t flags_next)398 void radio_switch_complete_and_phy_end_b2b_tx(uint8_t phy_curr, uint8_t flags_curr,
399 					      uint8_t phy_next, uint8_t flags_next)
400 {
401 #if defined(CONFIG_BT_CTLR_TIFS_HW)
402 	NRF_RADIO->SHORTS = RADIO_SHORTS_READY_START_Msk | RADIO_SHORTS_END_DISABLE_Msk |
403 			    RADIO_SHORTS_DISABLED_TXEN_Msk;
404 #else /* !CONFIG_BT_CTLR_TIFS_HW */
405 	NRF_RADIO->SHORTS = RADIO_SHORTS_READY_START_Msk | NRF_RADIO_SHORTS_TRX_END_DISABLE_Msk;
406 	sw_switch(SW_SWITCH_TX, SW_SWITCH_TX, phy_curr, flags_curr, phy_next, flags_next,
407 		  END_EVT_DELAY_DISABLED);
408 #endif /* !CONFIG_BT_CTLR_TIFS_HW */
409 }
410 
radio_df_iq_data_packet_set(uint8_t * buffer,size_t len)411 void radio_df_iq_data_packet_set(uint8_t *buffer, size_t len)
412 {
413 	nrf_radio_dfe_buffer_set(NRF_RADIO, (uint32_t *)buffer, len);
414 }
415 
radio_df_iq_samples_amount_get(void)416 uint32_t radio_df_iq_samples_amount_get(void)
417 {
418 	return nrf_radio_dfe_amount_get(NRF_RADIO);
419 }
420 
radio_df_cte_status_get(void)421 uint8_t radio_df_cte_status_get(void)
422 {
423 	return NRF_RADIO->CTESTATUS;
424 }
425 
radio_df_cte_ready(void)426 bool radio_df_cte_ready(void)
427 {
428 	return (NRF_RADIO->EVENTS_CTEPRESENT != 0);
429 }
430