diff --git a/boards/common/kw41z/Makefile.dep b/boards/common/kw41z/Makefile.dep index 0688b61ce600..1027516a9c1e 100644 --- a/boards/common/kw41z/Makefile.dep +++ b/boards/common/kw41z/Makefile.dep @@ -2,3 +2,7 @@ ifneq (,$(filter saul_default,$(USEMODULE))) USEMODULE += saul_adc USEMODULE += saul_gpio endif + +ifneq (,$(filter netdev_default gnrc_netdev_default,$(USEMODULE))) + USEMODULE += kw41zrf +endif diff --git a/boards/openlabs-kw41z-mini/Makefile.dep b/boards/openlabs-kw41z-mini/Makefile.dep index f578666f71dd..1027516a9c1e 100644 --- a/boards/openlabs-kw41z-mini/Makefile.dep +++ b/boards/openlabs-kw41z-mini/Makefile.dep @@ -3,7 +3,6 @@ ifneq (,$(filter saul_default,$(USEMODULE))) USEMODULE += saul_gpio endif -# TODO uncomment after #12277 (add support for kw41zrf) is merged -# ifneq (,$(filter netdev_default gnrc_netdev_default,$(USEMODULE))) -# USEMODULE += kw41zrf -# endif +ifneq (,$(filter netdev_default gnrc_netdev_default,$(USEMODULE))) + USEMODULE += kw41zrf +endif diff --git a/drivers/Makefile.dep b/drivers/Makefile.dep index 4f9870345271..1e5b0a20edd3 100644 --- a/drivers/Makefile.dep +++ b/drivers/Makefile.dep @@ -340,6 +340,16 @@ ifneq (,$(filter kw2xrf,$(USEMODULE))) FEATURES_REQUIRED += periph_gpio_irq endif +ifneq (,$(filter kw41zrf,$(USEMODULE))) + USEMODULE += luid + USEMODULE += netif + USEMODULE += ieee802154 + USEMODULE += netdev_ieee802154 + USEMODULE += core_thread_flags + USEMODULE += random + USEMODULE += mcux_xcvr_mkw41z +endif + ifneq (,$(filter l3g4200d,$(USEMODULE))) FEATURES_REQUIRED += periph_i2c endif diff --git a/drivers/Makefile.include b/drivers/Makefile.include index dcdfd33e6f4a..4300c8b8122e 100644 --- a/drivers/Makefile.include +++ b/drivers/Makefile.include @@ -180,6 +180,10 @@ ifneq (,$(filter kw2xrf,$(USEMODULE))) USEMODULE_INCLUDES += $(RIOTBASE)/drivers/kw2xrf/include endif +ifneq (,$(filter kw41zrf,$(USEMODULE))) + USEMODULE_INCLUDES += $(RIOTBASE)/drivers/kw41zrf/include +endif + ifneq (,$(filter l3g4200d,$(USEMODULE))) USEMODULE_INCLUDES += $(RIOTBASE)/drivers/l3g4200d/include endif diff --git a/drivers/include/kw41zrf.h b/drivers/include/kw41zrf.h new file mode 100644 index 000000000000..8cf669c939d4 --- /dev/null +++ b/drivers/include/kw41zrf.h @@ -0,0 +1,170 @@ +/* + * Copyright (C) 2017 SKF AB + * + * This file is subject to the terms and conditions of the GNU Lesser + * General Public License v2.1. See the file LICENSE in the top level + * directory for more details. + */ + +/** + * @defgroup drivers_kw41zrf KW41Z radio-driver + * @ingroup drivers_netdev + * @brief Device driver for the NXP KW41Z, KW21Z in-cpu transceiver + * @{ + * + * @file + * @brief Interface definition for the kw41zrf driver + * + * @author Joakim Nohlgård + */ + +#ifndef KW41ZRF_H +#define KW41ZRF_H + +#include + +#include "mutex.h" +#include "board.h" +#include "net/netdev.h" +#include "net/netdev/ieee802154.h" +#include "net/gnrc/nettype.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief Maximum packet length + */ +#define KW41ZRF_MAX_PKT_LENGTH (IEEE802154_FRAME_LEN_MAX) + +/** + * @brief Default channel used after initialization + * + * @{ + */ +#ifndef KW41ZRF_DEFAULT_CHANNEL +#define KW41ZRF_DEFAULT_CHANNEL (IEEE802154_DEFAULT_CHANNEL) +#endif +/** @} */ + +/** + * @brief Default CCA threshold + * + * @{ + */ +#ifndef KW41ZRF_DEFAULT_CCA_THRESHOLD +#define KW41ZRF_DEFAULT_CCA_THRESHOLD (-60) +#endif +/** @} */ + +/** + * @brief Default LQI compensation + * + * @{ + */ +#ifndef KW41ZRF_DEFAULT_LQI_COMPENSATION +#define KW41ZRF_DEFAULT_LQI_COMPENSATION (102) +#endif +/** @} */ + +/** + * @brief Allowed range of channels + * + * @{ + */ +#define KW41ZRF_MIN_CHANNEL (11U) +#define KW41ZRF_MAX_CHANNEL (26U) +/** @} */ + +/** + * @brief Default TX_POWER in dbm used after initialization + */ +#define KW41ZRF_DEFAULT_TX_POWER (IEEE802154_DEFAULT_TXPOWER) + +/** + * @brief Maximum output power of the kw41z device in dBm + */ +#define KW41ZRF_OUTPUT_POWER_MAX (4) + +/** + * @brief Minimum output power of the kw41z device in dBm + */ +#define KW41ZRF_OUTPUT_POWER_MIN (-19) + +/** + * @brief ISR callback function type + */ +typedef void (*kw41zrf_cb_t)(void *arg); + +/** + * @brief Device descriptor for KW41ZRF radio devices + * + * @extends netdev_ieee802154_t + */ +typedef struct { + netdev_ieee802154_t netdev; /**< netdev parent struct */ + /** + * @name device specific fields + * @{ + */ + thread_t *thread; /**< Network driver thread, for providing feedback from IRQ handler */ + uint32_t tx_warmup_time; /**< TX warmup time, in event timer ticks */ + uint32_t rx_warmup_time; /**< RX warmup time, in event timer ticks */ + uint32_t rf_osc_en_idle; /**< RF_OSC_EN bits setting when RF module is in standby */ + int16_t tx_power; /**< The current tx-power setting of the device */ + uint8_t flags; /**< Internal driver option flags */ + uint8_t max_retrans; /**< Maximum number of frame retransmissions + * when no Ack frame is received (macMaxFrameRetries) */ + uint8_t csma_max_backoffs; /**< Maximum number of CSMA backoffs when + * waiting for channel clear (macMaxCsmaBackoffs) */ + uint8_t csma_min_be; /**< Minimum backoff exponent (macMinBe) */ + uint8_t csma_max_be; /**< Maximum backoff exponent (macMaxBe) */ + uint8_t idle_seq; /**< state to return to after sending */ + uint8_t cca_result; /**< Used for passing CCA result from ISR to user */ + uint8_t csma_be; /**< Counter used internally by send implementation */ + uint8_t csma_num_backoffs; /**< Counter used internally by send implementation */ + uint8_t num_retrans; /**< Counter used internally by send implementation */ + uint32_t backoff_delay; /**< CSMA delay for the current TX operation */ + uint32_t tx_timeout; /**< Used to timeout waiting for ACK during TRX */ + uint8_t pm_blocked; /**< true if we have blocked a low power mode in the CPU */ + uint8_t recv_blocked; /**< blocks moving to XCVSEQ_RECEIVE to prevent + * overwriting the RX buffer before the higher + * layers have copied it to system RAM */ + /** @} */ +} kw41zrf_t; + +/** + * @brief Setup an KW41ZRF based device state + * + * @param[out] dev device descriptor + */ +void kw41zrf_setup(kw41zrf_t *dev); + +/** + * @brief Initialize the given KW41ZRF device + * + * @param[out] dev device descriptor + * @param[in] cb irq callback + * + * @return 0 on success + * @return <0 on error + */ +int kw41zrf_init(kw41zrf_t *dev, kw41zrf_cb_t cb); + +/** + * @brief Reset radio hardware and restore default settings + * + * @param[in] dev device to reset + * + * @return 0 on success + * @return <0 on initialization failure + */ +int kw41zrf_reset(kw41zrf_t *dev); + +#ifdef __cplusplus +} +#endif + +#endif /* KW41ZRF_H */ +/** @} */ diff --git a/drivers/kw41zrf/Makefile b/drivers/kw41zrf/Makefile new file mode 100644 index 000000000000..4f09d6a0e290 --- /dev/null +++ b/drivers/kw41zrf/Makefile @@ -0,0 +1,4 @@ +# Use vendor-supplied low level XCVR hardware initialization +DIRS += vendor/XCVR/MKW41Z4 + +include $(RIOTBASE)/Makefile.base diff --git a/drivers/kw41zrf/include/kw41zrf_getset.h b/drivers/kw41zrf/include/kw41zrf_getset.h new file mode 100644 index 000000000000..514894191226 --- /dev/null +++ b/drivers/kw41zrf/include/kw41zrf_getset.h @@ -0,0 +1,212 @@ +/* + * Copyright (C) 2017 SKF AB + * Copyright (C) 2016 Phytec Messtechnik GmbH + * + * This file is subject to the terms and conditions of the GNU Lesser + * General Public License v2.1. See the file LICENSE in the top level + * directory for more details. + */ + +/** + * @ingroup drivers_kw41zrf + * @{ + * + * @file + * @brief get/set interfaces for kw41zrf driver + * + * @author Joakim Nohlgård + */ + +#ifndef KW41ZRF_GETSET_H +#define KW41ZRF_GETSET_H + +#include "kw41zrf.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @name Internal device option flags + * @{ + */ +#define KW41ZRF_OPT_CSMA (0x01u) /**< use CSMA/CA algorithm for sending */ +#define KW41ZRF_OPT_PROMISCUOUS (0x02u) /**< promiscuous mode active */ +#define KW41ZRF_OPT_PRELOADING (0x04u) /**< preloading enabled */ +#define KW41ZRF_OPT_TELL_TX_START (0x08u) /**< notify MAC layer on TX start */ +#define KW41ZRF_OPT_TELL_TX_END (0x10u) /**< notify MAC layer on TX finished */ +#define KW41ZRF_OPT_TELL_RX_START (0x20u) /**< notify MAC layer on RX start */ +#define KW41ZRF_OPT_TELL_RX_END (0x40u) /**< notify MAC layer on RX finished */ +#define KW41ZRF_OPT_AUTOACK (0x80u) /**< automatic sending of ACKs */ +#define KW41ZRF_OPT_ACK_PENDING (0x81u) /**< set pending bit on auto ACKs */ +/** @} */ + +/** @brief Transceiver sequence identifiers */ +enum kw41zrf_xcvseq { + XCVSEQ_IDLE = 0b000, + XCVSEQ_RECEIVE = 0b001, + XCVSEQ_TRANSMIT = 0b010, + XCVSEQ_CCA = 0b011, + XCVSEQ_TX_RX = 0b100, + XCVSEQ_CONTINUOUS_CCA = 0b101, + /* Other values are reserved */ + /* Special value for idle_seq when sleeping */ + XCVSEQ_DSM_IDLE = 0b1000, +}; + +/** + * @brief Set tx power of given device + * + * @param[in] dev kw41zrf device descriptor + * @param[in] txpower transmit power in dBm + */ +void kw41zrf_set_tx_power(kw41zrf_t *dev, int16_t txpower); + +/** + * @brief Get tx power value of given device + * + * @param[in] dev kw41zrf device descriptor + * + * @return current tx power value + */ +int16_t kw41zrf_get_txpower(kw41zrf_t *dev); + +/** + * @brief Set channel of given device + * + * @param[in] dev kw41zrf device descriptor + * @param[in] val channel + */ +int kw41zrf_set_channel(kw41zrf_t *dev, uint8_t val); + +/** + * @brief Get channel of given device + * + * @param[in] dev kw41zrf device descriptor + * + * @return current channel + */ +uint8_t kw41zrf_get_channel(kw41zrf_t *dev); + +/** + * @brief Set PAN ID of a given device + * + * @param[in] dev kw41zrf device descriptor + * @param[in] pan PAN ID value + */ +void kw41zrf_set_pan(kw41zrf_t *dev, uint16_t pan); + +/** + * @brief Get PAN ID of given device + * + * @param[in] dev kw41zrf device descriptor + * + * @return current PAN ID + */ +uint16_t kw41zrf_get_pan(kw41zrf_t *dev); + +/** + * @brief Set short address of a given device + * + * @param[in] dev kw41zrf device descriptor + * @param[in] addr short address + */ +void kw41zrf_set_addr_short(kw41zrf_t *dev, const network_uint16_t *addr); + +/** + * @brief Set long address of a given device + * + * @param[in] dev kw41zrf device descriptor + * @param[in] addr long address + */ +void kw41zrf_set_addr_long(kw41zrf_t *dev, const eui64_t *addr); + +/** + * @brief Get short address of a given device + * + * @param[in] dev kw41zrf device descriptor + * @param[out] addr current short address + */ +void kw41zrf_get_addr_short(kw41zrf_t *dev, network_uint16_t *addr); + +/** + * @brief Get long address of a given device + * + * @param[in] dev kw41zrf device descriptor + * @param[out] addr current long address + */ +void kw41zrf_get_addr_long(kw41zrf_t *dev, eui64_t *addr); + +/** + * @brief Get CCA threshold of a given device + * + * @param[in] dev kw41zrf device descriptor + * @return current CCA threshold + */ +int8_t kw41zrf_get_cca_threshold(kw41zrf_t *dev); + +/** + * @brief Set CCA threshold of a given device + * + * @param[in] dev kw41zrf device descriptor + * @param[in] value CCA threshold + */ +void kw41zrf_set_cca_threshold(kw41zrf_t *dev, int8_t value); + +/** + * @brief Set CCA mode of a given device + * + * @param[in] dev kw41zrf device descriptor + * @param[in] mode CCA mode + */ +void kw41zrf_set_cca_mode(kw41zrf_t *dev, uint8_t mode); + +/** + * @brief Get CCA mode of a given device + * + * @param[in] dev kw41zrf device descriptor + * @return current CCA mode + */ +uint8_t kw41zrf_get_cca_mode(kw41zrf_t *dev); + +/** + * @brief Get latest ED measurement from the device + * + * @param[in] dev kw41zrf device descriptor + * @return most recent ED level + */ +int8_t kw41zrf_get_ed_level(kw41zrf_t *dev); + +/** + * @brief Perform one CCA measurement and return the result + * + * @param[in] dev kw41zrf device descriptor + * + * @return 0 if channel is idle + * @return 1 if channel is busy + */ +int kw41zrf_cca(kw41zrf_t *dev); + +/** + * @brief Set receive watermark to signal when the packet buffer is part full + * + * @param[in] dev kw41zrf device descriptor + * @param[in] value watermark + */ +void kw41zrf_set_rx_watermark(kw41zrf_t *dev, uint8_t value); + +/** + * @brief Set netopt a given device + * + * @param[in] dev kw41zrf device descriptor + * @param[in] option Netopt option type + * @param[in] state state + */ +void kw41zrf_set_option(kw41zrf_t *dev, uint8_t option, uint8_t state); + +#ifdef __cplusplus +} +#endif + +#endif /* KW41ZRF_GETSET_H */ +/** @} */ diff --git a/drivers/kw41zrf/include/kw41zrf_intern.h b/drivers/kw41zrf/include/kw41zrf_intern.h new file mode 100644 index 000000000000..5f66f2df0a2a --- /dev/null +++ b/drivers/kw41zrf/include/kw41zrf_intern.h @@ -0,0 +1,293 @@ +/* + * Copyright (C) 2017 SKF AB + * Copyright (C) 2016 Phytec Messtechnik GmbH + * + * This file is subject to the terms and conditions of the GNU Lesser + * General Public License v2.1. See the file LICENSE in the top level + * directory for more details. + */ + +/** + * @ingroup drivers_kw41zrf + * @{ + * + * @file + * @brief Internal function interfaces for kw41zrf driver + * + * @author Joakim Nohlgård + */ + +#ifndef KW41ZRF_INTERN_H +#define KW41ZRF_INTERN_H + +#include +#include "kw41zrf.h" +/* For XCVSEQ_IDLE */ +#include "kw41zrf_getset.h" +/* For ZLL CPU registers */ +#include "cpu.h" + +#ifdef MODULE_PM_LAYERED +#include "pm_layered.h" +#define PM_BLOCK(x) pm_block(x) +#define PM_UNBLOCK(x) pm_unblock(x) +/* When the transceiver is not in DSM, this power mode will be blocked. + * TODO: Change this to symbolic name KINETIS_PM_VLPS when Kinetis power + * management is merged (https://github.com/RIOT-OS/RIOT/pull/7897) */ +#define KW41ZRF_PM_BLOCKER 1 +#else +#define PM_BLOCK(x) +#define PM_UNBLOCK(x) +#endif + +/* Set to 1 to use on board LEDs to show RX/TX activity */ +#ifndef KW41ZRF_ENABLE_LEDS +#define KW41ZRF_ENABLE_LEDS (0) +#endif + +#if KW41ZRF_ENABLE_LEDS +/* For LED macros */ +#include "board.h" +#if !defined(KW41ZRF_LED_RX_ON) +#if defined(LED0_ON) +#define KW41ZRF_LED_RX_ON LED0_ON +#define KW41ZRF_LED_RX_OFF LED0_OFF +#else /* defined(LED0_ON) */ +#define KW41ZRF_LED_RX_ON +#define KW41ZRF_LED_RX_OFF +#endif /* defined(LED0_ON) */ +#endif /* !defined(KW41ZRF_LED_RX_ON) */ +#if !defined(KW41ZRF_LED_TX_ON) +#if defined(LED1_ON) +/* Separate TX LED */ +#define KW41ZRF_LED_TX_ON LED1_ON +#define KW41ZRF_LED_TX_OFF LED1_OFF +#elif defined(LED0_ON) +/* Combined RX+TX in one LED */ +#define KW41ZRF_LED_TX_ON LED0_ON +#define KW41ZRF_LED_TX_OFF LED0_OFF +#else /* defined(LEDx_ON) */ +#define KW41ZRF_LED_TX_ON +#define KW41ZRF_LED_TX_OFF +#endif /* defined(LEDx_ON) */ +#endif /* !defined(KW41ZRF_LED_TX_ON) */ +#if !defined(KW41ZRF_LED_NDSM_ON) +#if defined(LED2_ON) +#define KW41ZRF_LED_NDSM_ON LED2_ON +#define KW41ZRF_LED_NDSM_OFF LED2_OFF +#else /* defined(LEDx_ON) */ +#define KW41ZRF_LED_NDSM_ON +#define KW41ZRF_LED_NDSM_OFF +#endif /* defined(LEDx_ON) */ +#endif /* !defined(KW41ZRF_LED_NDSM_ON) */ +#if !defined(KW41ZRF_LED_IRQ_ON) +#if defined(LED3_ON) +#define KW41ZRF_LED_IRQ_ON LED3_ON +#define KW41ZRF_LED_IRQ_OFF LED3_OFF +#else /* defined(LEDx_ON) */ +#define KW41ZRF_LED_IRQ_ON +#define KW41ZRF_LED_IRQ_OFF +#endif /* defined(LEDx_ON) */ +#endif /* !defined(KW41ZRF_LED_IRQ_ON) */ +#else /* KW41ZRF_ENABLE_LEDS */ +#define KW41ZRF_LED_NDSM_ON +#define KW41ZRF_LED_NDSM_OFF +#define KW41ZRF_LED_TX_ON +#define KW41ZRF_LED_TX_OFF +#define KW41ZRF_LED_RX_ON +#define KW41ZRF_LED_RX_OFF +#define KW41ZRF_LED_IRQ_ON +#define KW41ZRF_LED_IRQ_OFF +#endif /* KW41ZRF_ENABLE_LEDS */ + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief KW41Z transceiver power modes + */ +typedef enum { + KW41ZRF_POWER_IDLE = 0, /**< All parts powered */ + KW41ZRF_POWER_DSM, /**< Deep sleep mode */ +} kw41zrf_powermode_t; + +/** + * @brief Timebase settings + */ +typedef enum kw41zrf_timer_timebase { + KW41ZRF_TIMEBASE_500000HZ = 0b010, + KW41ZRF_TIMEBASE_250000HZ = 0b011, + KW41ZRF_TIMEBASE_125000HZ = 0b100, + KW41ZRF_TIMEBASE_62500HZ = 0b101, + KW41ZRF_TIMEBASE_31250HZ = 0b110, + KW41ZRF_TIMEBASE_15625HZ = 0b111, +} kw41zrf_timer_timebase_t; + +/** + * @brief Mask all transceiver interrupts + */ +static inline void kw41zrf_mask_irqs(void) +{ + NVIC_DisableIRQ(Radio_1_IRQn); + NVIC_ClearPendingIRQ(Radio_1_IRQn); +} + +/** + * @brief Unmask all transceiver interrupts + */ +static inline void kw41zrf_unmask_irqs(void) +{ + KW41ZRF_LED_IRQ_OFF; + NVIC_EnableIRQ(Radio_1_IRQn); +} + +/** + * @brief Set the callback function for the radio ISR + * + * This callback will be called from ISR context when a radio_1 interrupt occurs + * + * @param[in] cb Pointer to callback function + * @param[in] arg Argument that will be passed to the callback + */ +void kw41zrf_set_irq_callback(void (*cb)(void *arg), void *arg); + +/** + * @brief Set power mode for device + * + * @param[in] dev kw41zrf device descriptor + * @param[in] pm power mode value + */ +void kw41zrf_set_power_mode(kw41zrf_t *dev, kw41zrf_powermode_t pm); + +/** + * @brief Determine if the transceiver is busy doing TX or RX + * + * @param[in] dev kw41zrf device descriptor + * + * @return 0 if transceiver is in progress transmitting a packet + * @return 1 otherwise + */ +int kw41zrf_can_switch_to_idle(kw41zrf_t *dev); + +/** + * @brief Set sequence state of device + * + * @param[in] dev kw41zrf device descriptor + * @param[in] seq sequence + */ +void kw41zrf_set_sequence(kw41zrf_t *dev, uint32_t seq); + +/** + * @brief Abort the current autosequence + * + * @param[in] dev kw41zrf device descriptor + */ +static inline void kw41zrf_abort_sequence(kw41zrf_t *dev) +{ + (void) dev; + /* Writing IDLE to XCVSEQ aborts any ongoing sequence */ + ZLL->PHY_CTRL = (ZLL->PHY_CTRL & + ~(ZLL_PHY_CTRL_XCVSEQ_MASK | + ZLL_PHY_CTRL_TC3TMOUT_MASK | ZLL_PHY_CTRL_TMRTRIGEN_MASK)) | + ZLL_PHY_CTRL_XCVSEQ(XCVSEQ_IDLE) | ZLL_PHY_CTRL_SEQMSK_MASK; + /* Spin until the sequence manager has acknowledged the sequence abort, this + * should not take many cycles */ + while (!(ZLL->SEQ_CTRL_STS & ZLL_SEQ_CTRL_STS_SEQ_IDLE_MASK)) {} + + /* Clear interrupt flags */ + uint32_t irqsts = ZLL->IRQSTS; + ZLL->IRQSTS = irqsts; +} + +/** + * @brief Check if the radio is in deep sleep mode + * + * @return non-zero if radio is in deep sleep + * @return 0 if radio is not in deep sleep mode + */ +static inline uint32_t kw41zrf_is_dsm(void) +{ + return (RSIM->DSM_CONTROL & RSIM_DSM_CONTROL_ZIG_DEEP_SLEEP_STATUS_MASK); +} + +/** + * @brief Set event timer counter value + * + * @param[in] dev kw41zrf device descriptor + * @param[in] value new time + */ +static inline void kw41zrf_timer_load(kw41zrf_t *dev, uint32_t value) +{ + (void) dev; + ZLL->EVENT_TMR = ZLL_EVENT_TMR_EVENT_TMR(value) | ZLL_EVENT_TMR_EVENT_TMR_LD_MASK; +} + +/** + * @brief Get current event timer counter value + * + * @param[in] dev kw41zrf device descriptor + * + * @return Current timer value + */ +static inline uint32_t kw41zrf_timer_get(kw41zrf_t *dev) +{ + (void) dev; + return (ZLL->EVENT_TMR & ZLL_EVENT_TMR_EVENT_TMR_MASK) >> ZLL_EVENT_TMR_EVENT_TMR_SHIFT; +} + +/** + * @brief Set a timeout value for the given compare register of the Event Timer + * + * @param[in] dev kw41zrf device descriptor + * @param[out] cmp_reg pointer to timer compare register, &ZLL->TxCMP + * @param[in] timeout timer offset from current time + */ +static inline void kw41zrf_timer_set(kw41zrf_t *dev, volatile uint32_t *cmp_reg, uint32_t timeout) +{ + uint32_t now = kw41zrf_timer_get(dev); + + *cmp_reg = now + timeout; +} + +/** + * @brief Initialize the Event Timer Block (up counter) + * + * The Event Timer Block provides: + * - Abort an RX and CCA sequence at pre-determined time + * - Latches "timestamp" value during packet reception + * - Initiates timer-triggered sequences + * + * @param[in] dev kw41zrf device descriptor + * @param[in] tb timer base value + */ +static inline void kw41zrf_timer_init(kw41zrf_t *dev, kw41zrf_timer_timebase_t tb) +{ + ZLL->TMR_PRESCALE = (ZLL->TMR_PRESCALE & ~ZLL_TMR_PRESCALE_TMR_PRESCALE_MASK) | + ZLL_TMR_PRESCALE_TMR_PRESCALE(tb); + kw41zrf_timer_load(dev, 0); +} + +/** + * @brief Returns timestamp of the beginning of the most recently received packet + * + * The latched timestamp corresponds to the point where the SFD detection was + * triggered for the most recent packet, i.e. right before the first byte of the + * packet. + * + * @param[in] dev kw41zrf device descriptor + * + * @return timestamp value + */ +static inline uint32_t kw41zrf_get_timestamp(kw41zrf_t *dev) +{ + (void) dev; + return ZLL->TIMESTAMP; +} + +#ifdef __cplusplus +} +#endif + +#endif /* KW41ZRF_INTERN_H */ +/** @} */ diff --git a/drivers/kw41zrf/include/kw41zrf_netdev.h b/drivers/kw41zrf/include/kw41zrf_netdev.h new file mode 100644 index 000000000000..59af06d740d1 --- /dev/null +++ b/drivers/kw41zrf/include/kw41zrf_netdev.h @@ -0,0 +1,36 @@ +/* + * Copyright (C) 2017 SKF AB + * + * This file is subject to the terms and conditions of the GNU Lesser + * General Public License v2.1. See the file LICENSE in the top level + * directory for more details. + */ + +/** + * @ingroup drivers_kw41zrf + * @{ + * + * @file + * @brief Netdev interface for kw41zrf driver + * + * @author Joakim Nohlgård + */ + +#ifndef KW41ZRF_NETDEV_H +#define KW41ZRF_NETDEV_H + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief Reference to the netdev device driver struct + */ +extern const netdev_driver_t kw41zrf_driver; + +#ifdef __cplusplus +} +#endif + +#endif /* KW41ZRF_NETDEV_H */ +/** @} */ diff --git a/drivers/kw41zrf/kw41zrf.c b/drivers/kw41zrf/kw41zrf.c new file mode 100644 index 000000000000..04953c1e1112 --- /dev/null +++ b/drivers/kw41zrf/kw41zrf.c @@ -0,0 +1,263 @@ +/* + * Copyright (C) 2017 SKF AB + * + * This file is subject to the terms and conditions of the GNU Lesser + * General Public License v2.1. See the file LICENSE in the top level + * directory for more details. + */ + +/** + * @ingroup drivers_kw41zrf + * @{ + * @file + * @brief Basic functionality of kw41zrf driver + * + * @author Joakim Nohlgård + * @} + */ +#include +#include +#include + +#include "log.h" +#include "msg.h" +#include "luid.h" +#include "net/gnrc.h" +#include "net/ieee802154.h" + +#include "kw41zrf.h" +#include "kw41zrf_netdev.h" +#include "kw41zrf_getset.h" +#include "kw41zrf_intern.h" +#include "vendor/XCVR/MKW41Z4/fsl_xcvr.h" +#include "vendor/XCVR/MKW41Z4/ifr_radio.h" +#include "vendor/MKW41Z4.h" + +#define ENABLE_DEBUG (0) +#include "debug.h" + +static void kw41zrf_set_address(kw41zrf_t *dev) +{ + DEBUG("[kw41zrf] Set MAC address\n"); + eui64_t addr_long; + network_uint16_t addr_short; + + /* get unique IDs to use as hardware addresses */ + luid_get_eui64(&addr_long); + luid_get_short(&addr_short); + + /* set short and long address */ + kw41zrf_set_addr_long(dev, &addr_long); + kw41zrf_set_addr_short(dev, &addr_short); +} + +void kw41zrf_setup(kw41zrf_t *dev) +{ + netdev_t *netdev = (netdev_t *)dev; + + netdev->driver = &kw41zrf_driver; + /* initialize device descriptor */ + dev->idle_seq = XCVSEQ_RECEIVE; + dev->pm_blocked = 0; + dev->recv_blocked = 0; + /* Set default parameters according to STD IEEE802.15.4-2015 */ + dev->csma_max_be = 5; + dev->csma_min_be = 3; + dev->max_retrans = 3; + dev->csma_max_backoffs = 4; + + DEBUG("[kw41zrf] setup finished\n"); +} + +/* vendor routine to initialize the radio core */ +int kw41zrf_xcvr_init(kw41zrf_t *dev); + +int kw41zrf_init(kw41zrf_t *dev, kw41zrf_cb_t cb) +{ + if (dev == NULL) { + return -EINVAL; + } + + /* Save a copy of the RF_OSC_EN setting to use when the radio is in deep sleep */ + dev->rf_osc_en_idle = RSIM->CONTROL & RSIM_CONTROL_RF_OSC_EN_MASK; + kw41zrf_mask_irqs(); + kw41zrf_set_irq_callback(cb, dev); + + /* Perform clean reset of the radio modules. */ + int res = kw41zrf_reset(dev); + if (res < 0) { + /* initialization error signaled from vendor driver */ + /* Restore saved RF_OSC_EN setting */ + RSIM->CONTROL = (RSIM->CONTROL & ~RSIM_CONTROL_RF_OSC_EN_MASK) | dev->rf_osc_en_idle; + return res; + } + /* Radio is now on and idle */ + + /* Allow radio interrupts */ + kw41zrf_unmask_irqs(); + + DEBUG("[kw41zrf] init finished\n"); + + return 0; +} + +int kw41zrf_reset_hardware(kw41zrf_t *dev) +{ + /* Enable RSIM oscillator in RUN and WAIT modes, in order to be able to + * access the XCVR and ZLL registers when using the internal reference clock + * for the CPU core */ + RSIM->CONTROL |= RSIM_CONTROL_RF_OSC_EN(1); + + /* Wait for oscillator ready signal */ + while ((RSIM->CONTROL & RSIM_CONTROL_RF_OSC_READY_MASK) == 0) {} + + /* Assert radio software reset */ + RSIM->CONTROL |= RSIM_CONTROL_RADIO_RESET_BIT_MASK; + /* De-assert radio software reset twice to follow recommendations in the + * reference manual */ + RSIM->CONTROL &= ~RSIM_CONTROL_RADIO_RESET_BIT_MASK; + RSIM->CONTROL &= ~RSIM_CONTROL_RADIO_RESET_BIT_MASK; + + DEBUG("[kw41zrf] start xcvr init\n"); + int res = kw41zrf_xcvr_init(dev); + if (res < 0) { + return res; + } + + /* Configure DSM exit oscillator stabilization delay */ + uint32_t tmp = (RSIM->RF_OSC_CTRL & RSIM_RF_OSC_CTRL_BB_XTAL_READY_COUNT_SEL_MASK) >> + RSIM_RF_OSC_CTRL_BB_XTAL_READY_COUNT_SEL_SHIFT; + /* Stabilization time is 1024 * 2^x radio crystal clocks, 0 <= x <= 3 */ + RSIM->DSM_OSC_OFFSET = (1024ul << tmp) / (CLOCK_RADIOXTAL / 32768u) + 1u; /* round up */ + + /* Clear and disable all interrupts */ + /* Reset PHY_CTRL to the default values, mask all interrupts, + * enable RXACKRQD, we only use TR mode for receiving acknowledgements */ + ZLL->PHY_CTRL = + ZLL_PHY_CTRL_CCATYPE(1) | + ZLL_PHY_CTRL_TSM_MSK_MASK | + ZLL_PHY_CTRL_WAKE_MSK_MASK | + ZLL_PHY_CTRL_CRC_MSK_MASK | + ZLL_PHY_CTRL_PLL_UNLOCK_MSK_MASK | + ZLL_PHY_CTRL_FILTERFAIL_MSK_MASK | + ZLL_PHY_CTRL_RX_WMRK_MSK_MASK | + ZLL_PHY_CTRL_CCAMSK_MASK | + ZLL_PHY_CTRL_RXMSK_MASK | + ZLL_PHY_CTRL_TXMSK_MASK | + ZLL_PHY_CTRL_SEQMSK_MASK | + ZLL_PHY_CTRL_RXACKRQD_MASK | + ZLL_PHY_CTRL_XCVSEQ(XCVSEQ_IDLE); + + /* Mask all unused timer interrupts and clear all interrupt flags */ + ZLL->IRQSTS = + ZLL_IRQSTS_TMR1MSK_MASK | + ZLL_IRQSTS_TMR4MSK_MASK | + ZLL_IRQSTS_TMR1IRQ_MASK | + ZLL_IRQSTS_TMR2IRQ_MASK | + ZLL_IRQSTS_TMR3IRQ_MASK | + ZLL_IRQSTS_TMR4IRQ_MASK | + ZLL_IRQSTS_WAKE_IRQ_MASK | + ZLL_IRQSTS_PLL_UNLOCK_IRQ_MASK | + ZLL_IRQSTS_FILTERFAIL_IRQ_MASK | + ZLL_IRQSTS_RXWTRMRKIRQ_MASK | + ZLL_IRQSTS_CCAIRQ_MASK | + ZLL_IRQSTS_RXIRQ_MASK | + ZLL_IRQSTS_TXIRQ_MASK | + ZLL_IRQSTS_SEQIRQ_MASK; + + /* Clear source address cache */ + ZLL->SAM_TABLE |= ZLL_SAM_TABLE_INVALIDATE_ALL_MASK; + + /* Accept FrameVersion 0 and 1, data, command, and beacon frames */ + ZLL->RX_FRAME_FILTER = ZLL_RX_FRAME_FILTER_FRM_VER_FILTER(3) | + ZLL_RX_FRAME_FILTER_BEACON_FT_MASK | + ZLL_RX_FRAME_FILTER_CMD_FT_MASK | + ZLL_RX_FRAME_FILTER_DATA_FT_MASK; + + /* Set prescaler to obtain 1 symbol (16us) timebase */ + kw41zrf_timer_init(dev, KW41ZRF_TIMEBASE_62500HZ); + + /* Set CCA threshold to KW41ZRF_DEFAULT_CCA_THRESHOLD dBm */ + /* The hardware default for this register is +75 dBm (0x4b), which is nonsense */ + ZLL->CCA_LQI_CTRL = (ZLL->CCA_LQI_CTRL & ~ZLL_CCA_LQI_CTRL_CCA1_THRESH_MASK) | + ZLL_CCA_LQI_CTRL_CCA1_THRESH(KW41ZRF_DEFAULT_CCA_THRESHOLD); + + /* Set default LQI compensation */ + /* Hardware reset default is 102 */ + ZLL->CCA_LQI_CTRL = (ZLL->CCA_LQI_CTRL & ~ZLL_CCA_LQI_CTRL_LQI_OFFSET_COMP_MASK) | + ZLL_CCA_LQI_CTRL_LQI_OFFSET_COMP(KW41ZRF_DEFAULT_LQI_COMPENSATION); + + /* set defaults */ + ZLL->SEQ_CTRL_STS = ZLL_SEQ_CTRL_STS_EVENT_TMR_DO_NOT_LATCH_MASK; + + return 0; +} + +int kw41zrf_reset(kw41zrf_t *dev) +{ + kw41zrf_mask_irqs(); + + /* Sometimes (maybe 1 in 30 reboots) there is a failure in the vendor + * routines in kw41zrf_rx_bba_dcoc_dac_trim_DCest() that can be worked + * around by retrying. Clearly this is not ideal. + */ + for (int retries = 0; ; retries++) { + int res = kw41zrf_reset_hardware(dev); + if (!res) { + if (retries) { + LOG_WARNING("kw41zrf_reset_hardware() needed %i retries\n", + retries); + } + break; + } + if (retries == 9) { + LOG_ERROR("kw41zrf_reset_hardware() returned %i\n", res); + kw41zrf_unmask_irqs(); + return res; + } + } + + /* Compute warmup times (scaled to 16us) */ + dev->rx_warmup_time = + (XCVR_TSM->END_OF_SEQ & XCVR_TSM_END_OF_SEQ_END_OF_RX_WU_MASK) >> + XCVR_TSM_END_OF_SEQ_END_OF_RX_WU_SHIFT; + dev->tx_warmup_time = + (XCVR_TSM->END_OF_SEQ & XCVR_TSM_END_OF_SEQ_END_OF_TX_WU_MASK) >> + XCVR_TSM_END_OF_SEQ_END_OF_TX_WU_SHIFT; + + /* divide by 16 and round up */ + dev->rx_warmup_time = (dev->rx_warmup_time + 15) / 16; + dev->tx_warmup_time = (dev->tx_warmup_time + 15) / 16; + + /* Reset software link layer driver state */ + netdev_ieee802154_reset(&dev->netdev); + + dev->tx_power = KW41ZRF_DEFAULT_TX_POWER; + dev->idle_seq = XCVSEQ_RECEIVE; + kw41zrf_set_power_mode(dev, KW41ZRF_POWER_IDLE); + kw41zrf_abort_sequence(dev); + kw41zrf_set_tx_power(dev, dev->tx_power); + + kw41zrf_set_channel(dev, KW41ZRF_DEFAULT_CHANNEL); + + kw41zrf_set_address(dev); + + kw41zrf_set_cca_mode(dev, 1); + + kw41zrf_set_rx_watermark(dev, 1); + + kw41zrf_set_option(dev, KW41ZRF_OPT_AUTOACK, 1); + kw41zrf_set_option(dev, KW41ZRF_OPT_CSMA, 1); + + static const netopt_enable_t enable = NETOPT_ENABLE; + netdev_ieee802154_set(&dev->netdev, NETOPT_ACK_REQ, + &enable, sizeof(enable)); + + kw41zrf_abort_sequence(dev); + kw41zrf_set_sequence(dev, dev->idle_seq); + kw41zrf_unmask_irqs(); + + DEBUG("[kw41zrf] reset radio and set to channel %d.\n", + KW41ZRF_DEFAULT_CHANNEL); + return 0; +} diff --git a/drivers/kw41zrf/kw41zrf_getset.c b/drivers/kw41zrf/kw41zrf_getset.c new file mode 100644 index 000000000000..d0b2539f421b --- /dev/null +++ b/drivers/kw41zrf/kw41zrf_getset.c @@ -0,0 +1,304 @@ +/* + * Copyright (C) 2017 SKF AB + * + * This file is subject to the terms and conditions of the GNU Lesser General + * Public License v2.1. See the file LICENSE in the top level directory for more + * details. + */ + +/** + * @ingroup drivers_kw41zrf + * @{ + * @file + * @brief get/set functionality of kw41zrf driver + * + * @author Joakim Nohlgård + * @} + */ + +#include +#include +#include "log.h" +#include "cpu.h" +#include "byteorder.h" +#include "kw41zrf.h" +#include "kw41zrf_intern.h" +#include "kw41zrf_getset.h" +#include "vendor/MKW41Z4.h" + +#define ENABLE_DEBUG (0) +#include "debug.h" + +#define KW41ZRF_NUM_CHANNEL (KW41ZRF_MAX_CHANNEL - KW41ZRF_MIN_CHANNEL + 1) + +/* Lookup table for PA_PWR register */ +/* Source: KW41Z data sheet, 5.3 Transmit and PLL Feature Summary, + * Table 8. Transmit Output Power as a function of PA_POWER[5:0] */ +static const uint8_t tx_power_dbm_to_pa_pwr[29] = { + 4, 4, 4, 4, /* -19:-16 dBm */ + 6, 6, /* -15:-14 dBm */ + 8, 8, /* -13:-12 dBm */ + 10, 10, /* -11:-10 dBm */ + 12, /* -9 dBm */ + 14, /* -8 dBm */ + 16, /* -7 dBm */ + 18, /* -6 dBm */ + 20, /* -5 dBm */ + 22, /* -4 dBm */ + 26, /* -3 dBm */ + 28, /* -2 dBm */ + 34, /* -1 dBm */ + 38, /* 0 dBm */ + 42, /* 1 dBm */ + 48, /* 2 dBm */ + 56, /* 3 dBm */ + 62, /* 4 dBm */ +}; + +void kw41zrf_set_tx_power(kw41zrf_t *dev, int16_t txpower_dbm) +{ + if (txpower_dbm < KW41ZRF_OUTPUT_POWER_MIN) { + txpower_dbm = KW41ZRF_OUTPUT_POWER_MIN; + } + else if (txpower_dbm > KW41ZRF_OUTPUT_POWER_MAX) { + txpower_dbm = KW41ZRF_OUTPUT_POWER_MAX; + } + + ZLL->PA_PWR = tx_power_dbm_to_pa_pwr[txpower_dbm - KW41ZRF_OUTPUT_POWER_MIN]; + + DEBUG("[kw41zrf] set txpower to: %d\n", txpower_dbm); + dev->tx_power = txpower_dbm; +} + +int16_t kw41zrf_get_txpower(kw41zrf_t *dev) +{ + return dev->tx_power; +} + +uint8_t kw41zrf_get_channel(kw41zrf_t *dev) +{ + (void) dev; + return (ZLL->CHANNEL_NUM0 & ZLL_CHANNEL_NUM0_CHANNEL_NUM0_MASK) + >> ZLL_CHANNEL_NUM0_CHANNEL_NUM0_SHIFT; +} + +uint16_t kw41zrf_get_pan(kw41zrf_t *dev) +{ + (void) dev; + return (ZLL->MACSHORTADDRS0 & ZLL_MACSHORTADDRS0_MACPANID0_MASK) + >> ZLL_MACSHORTADDRS0_MACPANID0_SHIFT; +} + +int kw41zrf_set_channel(kw41zrf_t *dev, uint8_t channel) +{ + (void) dev; + if (channel < KW41ZRF_MIN_CHANNEL || channel > KW41ZRF_MAX_CHANNEL) { + LOG_ERROR("[kw41zrf] Invalid channel %u\n", channel); + return -EINVAL; + } + + ZLL->CHANNEL_NUM0 = channel; + + DEBUG("[kw41zrf] set channel to %u\n", channel); + return 0; +} + +void kw41zrf_set_pan(kw41zrf_t *dev, uint16_t pan) +{ + (void) dev; + ZLL->MACSHORTADDRS0 = (ZLL->MACSHORTADDRS0 + & ~ZLL_MACSHORTADDRS0_MACPANID0_MASK) | + ZLL_MACSHORTADDRS0_MACPANID0(pan); + + DEBUG("[kw41zrf] set pan to: 0x%x\n", pan); +} + +void kw41zrf_set_addr_short(kw41zrf_t *dev, const network_uint16_t *addr) +{ + (void) dev; + ZLL->MACSHORTADDRS0 = (ZLL->MACSHORTADDRS0 + & ~ZLL_MACSHORTADDRS0_MACSHORTADDRS0_MASK) | + ZLL_MACSHORTADDRS0_MACSHORTADDRS0(addr->u16); +} + +void kw41zrf_set_addr_long(kw41zrf_t *dev, const eui64_t *addr) +{ + (void) dev; + ZLL->MACLONGADDRS0_LSB = addr->uint32[0].u32; + ZLL->MACLONGADDRS0_MSB = addr->uint32[1].u32; +} + +void kw41zrf_get_addr_short(kw41zrf_t *dev, network_uint16_t *addr) +{ + (void) dev; + addr->u16 = (ZLL->MACSHORTADDRS0 & ZLL_MACSHORTADDRS0_MACSHORTADDRS0_MASK) >> + ZLL_MACSHORTADDRS0_MACSHORTADDRS0_SHIFT; +} + +void kw41zrf_get_addr_long(kw41zrf_t *dev, eui64_t *addr) +{ + (void) dev; + addr->uint32[0] = (network_uint32_t)ZLL->MACLONGADDRS0_LSB; + addr->uint32[1] = (network_uint32_t)ZLL->MACLONGADDRS0_MSB; +} + +int8_t kw41zrf_get_cca_threshold(kw41zrf_t *dev) +{ + (void) dev; + uint8_t tmp = (ZLL->CCA_LQI_CTRL & ZLL_CCA_LQI_CTRL_CCA1_THRESH_MASK) >> + ZLL_CCA_LQI_CTRL_CCA1_THRESH_SHIFT; + return (int8_t)tmp; +} + +void kw41zrf_set_cca_threshold(kw41zrf_t *dev, int8_t value) +{ + (void) dev; + ZLL->CCA_LQI_CTRL = (ZLL->CCA_LQI_CTRL & ~ZLL_CCA_LQI_CTRL_CCA1_THRESH_MASK) | + ZLL_CCA_LQI_CTRL_CCA1_THRESH(value); +} + +void kw41zrf_set_cca_mode(kw41zrf_t *dev, uint8_t mode) +{ + (void) dev; + ZLL->PHY_CTRL = (ZLL->PHY_CTRL & ~ZLL_PHY_CTRL_CCATYPE_MASK) | + ZLL_PHY_CTRL_CCATYPE(mode); +} + +uint8_t kw41zrf_get_cca_mode(kw41zrf_t *dev) +{ + (void) dev; + return (ZLL->PHY_CTRL & ZLL_PHY_CTRL_CCATYPE_MASK) >> ZLL_PHY_CTRL_CCATYPE_SHIFT; +} + +int8_t kw41zrf_get_ed_level(kw41zrf_t *dev) +{ + (void) dev; + return (ZLL->LQI_AND_RSSI & ZLL_LQI_AND_RSSI_CCA1_ED_FNL_MASK) + >> ZLL_LQI_AND_RSSI_CCA1_ED_FNL_SHIFT; +} + +void kw41zrf_set_option(kw41zrf_t *dev, uint8_t option, uint8_t state) +{ + DEBUG("[kw41zrf] set option 0x%04x to %x\n", option, state); + + if (kw41zrf_is_dsm()) { + /* Transceiver is sleeping */ + switch (option) { + /* Modifying these options require that the transceiver is not in + * deep sleep mode */ + case KW41ZRF_OPT_CSMA: + case KW41ZRF_OPT_PROMISCUOUS: + case KW41ZRF_OPT_AUTOACK: + case KW41ZRF_OPT_ACK_PENDING: + case KW41ZRF_OPT_TELL_RX_START: + LOG_ERROR("[kw41zrf] Attempt to modify option %04x while radio is sleeping\n", + (unsigned) option); + assert(0); + return; + + default: + break; + } + } + + /* set option field */ + if (state) { + dev->flags |= option; + + /* trigger option specific actions */ + switch (option) { + case KW41ZRF_OPT_CSMA: + DEBUG("[kw41zrf] enable: CSMA\n"); + bit_set32(&ZLL->PHY_CTRL, ZLL_PHY_CTRL_CCABFRTX_SHIFT); + break; + + case KW41ZRF_OPT_PROMISCUOUS: + DEBUG("[kw41zrf] enable: PROMISCUOUS\n"); + /* enable promiscuous mode */ + bit_set32(&ZLL->PHY_CTRL, ZLL_PHY_CTRL_PROMISCUOUS_SHIFT); + /* auto ACK is always disabled in promiscuous mode by the hardware */ + break; + + case KW41ZRF_OPT_AUTOACK: + DEBUG("[kw41zrf] enable: AUTOACK\n"); + bit_set32(&ZLL->PHY_CTRL, ZLL_PHY_CTRL_AUTOACK_SHIFT); + break; + + case KW41ZRF_OPT_ACK_PENDING: + DEBUG("[kw41zrf] enable: PENDING_BIT\n"); + bit_set32(&ZLL->SAM_TABLE, ZLL_SAM_TABLE_ACK_FRM_PND_SHIFT); + break; + + case KW41ZRF_OPT_TELL_RX_START: + DEBUG("[kw41zrf] enable: TELL_RX_START\n"); + bit_clear32(&ZLL->PHY_CTRL, ZLL_PHY_CTRL_RX_WMRK_MSK_SHIFT); + break; + + case KW41ZRF_OPT_TELL_RX_END: + DEBUG("[kw41zrf] enable: TELL_RX_END\n"); + break; + + case KW41ZRF_OPT_TELL_TX_END: + DEBUG("[kw41zrf] enable: TELL_TX_END\n"); + break; + + case KW41ZRF_OPT_TELL_TX_START: + DEBUG("[kw41zrf] enable: TELL_TX_START (ignored)\n"); + + default: + /* do nothing */ + break; + } + } + else { + dev->flags &= ~(option); + /* trigger option specific actions */ + switch (option) { + case KW41ZRF_OPT_CSMA: + DEBUG("[kw41zrf] disable: CSMA\n"); + bit_clear32(&ZLL->PHY_CTRL, ZLL_PHY_CTRL_CCABFRTX_SHIFT); + break; + + case KW41ZRF_OPT_PROMISCUOUS: + DEBUG("[kw41zrf] disable: PROMISCUOUS\n"); + /* disable promiscuous mode */ + bit_clear32(&ZLL->PHY_CTRL, ZLL_PHY_CTRL_PROMISCUOUS_SHIFT); + break; + + case KW41ZRF_OPT_AUTOACK: + DEBUG("[kw41zrf] disable: AUTOACK\n"); + bit_clear32(&ZLL->PHY_CTRL, ZLL_PHY_CTRL_AUTOACK_SHIFT); + break; + + case KW41ZRF_OPT_ACK_PENDING: + DEBUG("[kw41zrf] disable: PENDING_BIT\n"); + bit_clear32(&ZLL->SAM_TABLE, ZLL_SAM_TABLE_ACK_FRM_PND_SHIFT); + break; + + case KW41ZRF_OPT_TELL_RX_START: + DEBUG("[kw41zrf] disable: TELL_RX_START\n"); + bit_set32(&ZLL->PHY_CTRL, ZLL_PHY_CTRL_RX_WMRK_MSK_SHIFT); + break; + + case KW41ZRF_OPT_TELL_RX_END: + DEBUG("[kw41zrf] disable: TELL_RX_END\n"); + break; + + case KW41ZRF_OPT_TELL_TX_END: + DEBUG("[kw41zrf] disable: TELL_TX_END\n"); + break; + + case KW41ZRF_OPT_TELL_TX_START: + DEBUG("[kw41zrf] disable: TELL_TX_START (ignored)\n"); + default: + /* do nothing */ + break; + } + } +} + +void kw41zrf_set_rx_watermark(kw41zrf_t *dev, uint8_t value) +{ + (void) dev; + ZLL->RX_WTR_MARK = ZLL_RX_WTR_MARK_RX_WTR_MARK(value); +} diff --git a/drivers/kw41zrf/kw41zrf_intern.c b/drivers/kw41zrf/kw41zrf_intern.c new file mode 100644 index 000000000000..7ccbacb0ebd8 --- /dev/null +++ b/drivers/kw41zrf/kw41zrf_intern.c @@ -0,0 +1,235 @@ +/* + * Copyright (C) 2017 SKF AB + * + * This file is subject to the terms and conditions of the GNU Lesser General + * Public License v2.1. See the file LICENSE in the top level directory for more + * details. + */ + +/** + * @ingroup drivers_kw41zrf + * @{ + * @file + * @brief Internal function of kw41zrf driver + * + * @author Joakim Nohlgård + * @} + */ + +#include "log.h" +#include "irq.h" +#include "panic.h" +#include "kw41zrf.h" +#include "kw41zrf_getset.h" +#include "kw41zrf_intern.h" + +#define ENABLE_DEBUG (0) +#include "debug.h" + +/** + * @brief Delay before entering deep sleep mode, in DSM_TIMER ticks (32.768 kHz) + * + * @attention must be >= 4 according to SoC ref. manual + */ +#define KW41ZRF_DSM_ENTER_DELAY 5 + +/** + * @brief Delay before leaving deep sleep mode, in DSM_TIMER ticks (32.768 kHz) + * + * @attention must be >= 4 according to SoC ref. manual + */ +#define KW41ZRF_DSM_EXIT_DELAY 5 + +struct { + void (*cb)(void *arg); /**< Callback function called from radio ISR */ + void *arg; /**< Argument to callback */ +} isr_config; + +void kw41zrf_set_irq_callback(void (*cb)(void *arg), void *arg) +{ + unsigned int mask = irq_disable(); + isr_config.cb = cb; + isr_config.arg = arg; + irq_restore(mask); +} + +void kw41zrf_set_power_mode(kw41zrf_t *dev, kw41zrf_powermode_t pm) +{ + DEBUG("[kw41zrf] set power mode to %u\n", pm); + unsigned state = irq_disable(); + switch (pm) { + case KW41ZRF_POWER_IDLE: + { + /* Disable some CPU power management if we need to be active, otherwise the + * radio will be stuck in state retention mode. */ + if (!dev->pm_blocked) { + PM_BLOCK(KW41ZRF_PM_BLOCKER); + dev->pm_blocked = 1; + } + /* Restore saved RF oscillator settings, enable oscillator in RUN mode + * to allow register access */ + /* This is also where the oscillator is enabled during kw41zrf_init: + * kw41zrf_init -> kw41zrf_reset_phy -> kw41zrf_set_power_mode + * => Do not return before this line during init */ + RSIM->CONTROL |= RSIM_CONTROL_RF_OSC_EN(1); + /* Assume DSM timer has been running since we entered sleep mode */ + /* In case it was not already running, however, we still set the + * enable flag here. */ + /* RSIM_DSM_CONTROL_ZIG_SYSCLK_REQUEST_EN lets the link layer + * request the RF oscillator to remain on during STOP and VLPS, to + * allow stopping the CPU core without affecting TX or RX operations */ + RSIM->DSM_CONTROL = (RSIM_DSM_CONTROL_DSM_TIMER_EN_MASK | + RSIM_DSM_CONTROL_ZIG_SYSCLK_REQUEST_EN_MASK); + /* Wait for oscillator ready signal before attempting to recover from DSM */ + while ((RSIM->CONTROL & RSIM_CONTROL_RF_OSC_READY_MASK) == 0) {} + KW41ZRF_LED_NDSM_ON; + /* If we are already awake we can just return now. */ + if (!(kw41zrf_is_dsm())) { + /* Already awake */ + break; + } + /* The wake target must be at least (4 + RSIM_DSM_OSC_OFFSET) ticks + * into the future, to let the oscillator stabilize before switching + * on the clocks */ + RSIM->ZIG_WAKE = KW41ZRF_DSM_EXIT_DELAY + RSIM->DSM_TIMER + RSIM->DSM_OSC_OFFSET; + /* Wait to come out of DSM */ + while (kw41zrf_is_dsm()) {} + + /* Convert DSM ticks (32.768 kHz) to event timer ticks (1 MHz) */ + uint64_t tmp = (uint64_t)(RSIM->ZIG_WAKE - RSIM->ZIG_SLEEP) * 15625ul; + uint32_t usec = (tmp >> 9); /* equivalent to (usec / 512) */ + /* Add the offset */ + ZLL->EVENT_TMR = ZLL_EVENT_TMR_EVENT_TMR_ADD_MASK | + ZLL_EVENT_TMR_EVENT_TMR(usec); + + /* Clear IRQ flags */ + uint32_t irqsts = ZLL->IRQSTS; + DEBUG("[kw41zrf] wake IRQSTS=%" PRIx32 "\n", irqsts); + ZLL->IRQSTS = irqsts; + + /* Disable DSM timer triggered sleep */ + ZLL->DSM_CTRL = 0; + + break; + } + case KW41ZRF_POWER_DSM: + { + if (kw41zrf_is_dsm()) { + /* Already asleep */ + break; + } + if (dev->pm_blocked) { + PM_UNBLOCK(KW41ZRF_PM_BLOCKER); + dev->pm_blocked = 0; + } + /* Race condition: if sleep is re-triggered after wake before the + * DSM_ZIG_FINISHED flag has been switched off, then the RSIM + * becomes stuck and never enters DSM. + * The time from ZIG_WAKE until DSM_ZIG_FINISHED is turned off seem + * to be constant at 2 DSM ticks */ + while (RSIM->DSM_CONTROL & RSIM_DSM_CONTROL_DSM_ZIG_FINISHED_MASK) {} + /* Clear IRQ flags */ + uint32_t irqsts = RSIM->DSM_CONTROL; + RSIM->DSM_CONTROL = irqsts; + irqsts = ZLL->IRQSTS; + DEBUG("[kw41zrf] sleep IRQSTS=%" PRIx32 "\n", irqsts); + ZLL->IRQSTS = irqsts; + NVIC_ClearPendingIRQ(Radio_1_IRQn); + + /* Enable timer triggered sleep */ + ZLL->DSM_CTRL = ZLL_DSM_CTRL_ZIGBEE_SLEEP_EN_MASK; + /* The device will automatically wake up 8.5 minutes from now if not + * awoken sooner by software */ + /* TODO handle automatic wake in the ISR if it becomes an issue */ + RSIM->ZIG_WAKE = RSIM->DSM_TIMER - KW41ZRF_DSM_EXIT_DELAY - RSIM->DSM_OSC_OFFSET; + /* Set sleep start time */ + /* The target time must be at least 4 DSM_TIMER ticks into the future */ + RSIM->ZIG_SLEEP = RSIM->DSM_TIMER + KW41ZRF_DSM_ENTER_DELAY; + /* Start the 32.768 kHz DSM timer in case it was not already running */ + /* If ZIG_SYSCLK_REQUEST_EN is not set then the hardware will not + * enter DSM and we get stuck in the while() below */ + RSIM->DSM_CONTROL = (RSIM_DSM_CONTROL_DSM_TIMER_EN_MASK | + RSIM_DSM_CONTROL_ZIG_SYSCLK_REQUEST_EN_MASK); + while (!(kw41zrf_is_dsm())) {} + KW41ZRF_LED_NDSM_OFF; + /* Restore saved RF_OSC_EN bits (from kw41zrf_init) + * This will disable the RF oscillator unless the system was + * configured to use the RF oscillator before kw41zrf_init() was + * called, for example when using the RF oscillator for the CPU core + * clock. */ + RSIM->CONTROL = (RSIM->CONTROL & ~RSIM_CONTROL_RF_OSC_EN_MASK) | + dev->rf_osc_en_idle; + /* Let the DSM timer run until we exit deep sleep mode */ + break; + } + default: + LOG_ERROR("[kw41zrf] Unknown power mode %u\n", pm); + break; + } + irq_restore(state); +} + +void kw41zrf_set_sequence(kw41zrf_t *dev, uint32_t seq) +{ + (void) dev; + DEBUG("[kw41zrf] set sequence to %x\n", (unsigned)seq); + assert(!kw41zrf_is_dsm()); + unsigned back_to_sleep = 0; + if (seq == XCVSEQ_DSM_IDLE) { + back_to_sleep = 1; + seq = XCVSEQ_IDLE; + } + else if ((seq == XCVSEQ_RECEIVE) && dev->recv_blocked) { + /* Wait in standby until recv has been called to avoid corrupting the RX + * buffer before the frame has been received by the higher layers */ + seq = XCVSEQ_IDLE; + } + uint32_t seq_old = ZLL->PHY_CTRL & ZLL_PHY_CTRL_XCVSEQ_MASK; + if (seq_old != XCVSEQ_IDLE && seq_old != XCVSEQ_RECEIVE) { + LOG_ERROR("[kw41zrf] seq not idle: 0x%" PRIu32 "\n", seq_old); + assert(0); + } + + kw41zrf_abort_sequence(dev); + + ZLL->PHY_CTRL = (ZLL->PHY_CTRL & ~(ZLL_PHY_CTRL_XCVSEQ_MASK | ZLL_PHY_CTRL_SEQMSK_MASK)) | seq; + while (((ZLL->SEQ_CTRL_STS & ZLL_SEQ_CTRL_STS_XCVSEQ_ACTUAL_MASK) >> + ZLL_SEQ_CTRL_STS_XCVSEQ_ACTUAL_SHIFT) != (ZLL_PHY_CTRL_XCVSEQ_MASK & seq)) {} + if (back_to_sleep) { + kw41zrf_set_power_mode(dev, KW41ZRF_POWER_DSM); + } +} + +int kw41zrf_can_switch_to_idle(kw41zrf_t *dev) +{ + (void) dev; + if (!kw41zrf_is_dsm()) { + uint8_t seq = (ZLL->PHY_CTRL & ZLL_PHY_CTRL_XCVSEQ_MASK) >> ZLL_PHY_CTRL_XCVSEQ_SHIFT; + + DEBUG("[kw41zrf] XCVSEQ=0x%x, SEQ_STATE=0x%" PRIx32 ", SEQ_CTRL_STS=0x%" PRIx32 "\n", seq, + ZLL->SEQ_STATE, ZLL->SEQ_CTRL_STS); + + switch (seq) + { + case XCVSEQ_TRANSMIT: + case XCVSEQ_TX_RX: + case XCVSEQ_CCA: + /* We should wait until TX or CCA has finished before moving to + * another mode */ + return 0; + default: + break; + } + } + + return 1; +} + +void isr_radio_1(void) +{ + DEBUG("[kw41zrf] INT1\n"); + if (isr_config.cb != NULL) { + isr_config.cb(isr_config.arg); + } + cortexm_isr_end(); +} diff --git a/drivers/kw41zrf/kw41zrf_netdev.c b/drivers/kw41zrf/kw41zrf_netdev.c new file mode 100644 index 000000000000..a5e15cb87820 --- /dev/null +++ b/drivers/kw41zrf/kw41zrf_netdev.c @@ -0,0 +1,1258 @@ +/* + * Copyright (C) 2017 SKF AB + * + * This file is subject to the terms and conditions of the GNU Lesser + * General Public License v2.1. See the file LICENSE in the top level + * directory for more details. + */ + +/** + * @ingroup drivers_kw41zrf + * @{ + * + * @file + * @brief Netdev interface for kw41zrf drivers + * + * @author Joakim Nohlgård + */ + +#include +#include +#include +#include +#include + +#include "log.h" +#include "random.h" +#include "thread_flags.h" +#include "net/eui64.h" +#include "net/ieee802154.h" +#include "net/netdev.h" +#include "net/netdev/ieee802154.h" + +#include "kw41zrf.h" +#include "kw41zrf_netdev.h" +#include "kw41zrf_intern.h" +#include "kw41zrf_getset.h" +#include "vendor/MKW41Z4.h" + +#ifdef MODULE_OD +#include "od.h" +#endif + +#define ENABLE_DEBUG (0) +#include "debug.h" + +#define _MAX_MHR_OVERHEAD (25) + +/* Timing units are in radio timer ticks (16 usec per tick) */ +#define KW41ZRF_CCA_TIME 8 +#define KW41ZRF_SHR_PHY_TIME 12 +#define KW41ZRF_PER_BYTE_TIME 2 +#define KW41ZRF_ACK_WAIT_TIME 54 +#define KW41ZRF_CSMA_UNIT_TIME 20 + +static void kw41zrf_netdev_isr(netdev_t *netdev); + +/* True while we've already queued a callback and don't need to queue another */ +static atomic_bool irq_is_queued = false; + +/* True while blocking the netdev thread waiting for some operation to finish */ +static bool blocking_for_irq = false; + +/* Set this to a flag bit that is not used by the MAC implementation */ +#define KW41ZRF_THREAD_FLAG_ISR (1u << 8) + +static void kw41zrf_irq_handler(void *arg) +{ + netdev_t *netdev = arg; + kw41zrf_t *dev = (kw41zrf_t *)netdev; + + KW41ZRF_LED_IRQ_ON; + kw41zrf_mask_irqs(); + + /* Signal to the thread that an IRQ has arrived, if it is waiting */ + thread_flags_set(dev->thread, KW41ZRF_THREAD_FLAG_ISR); + + /* Avoid filling the message queue with redundant ISR events */ + if (!irq_is_queued) { + irq_is_queued = true; + if (netdev->event_callback) { + netdev->event_callback(netdev, NETDEV_EVENT_ISR); + } + } +} + +static int kw41zrf_netdev_init(netdev_t *netdev) +{ + kw41zrf_t *dev = (kw41zrf_t *)netdev; + dev->thread = (thread_t *)thread_get(thread_getpid()); + + /* initialize hardware */ + if (kw41zrf_init(dev, kw41zrf_irq_handler)) { + LOG_ERROR("[kw41zrf] unable to initialize device\n"); + return -1; + } + + return 0; +} + +/** + * @brief Generate a random number for using as a CSMA delay value + */ +static inline uint32_t kw41zrf_csma_random_delay(kw41zrf_t *dev) +{ + /* Use topmost csma_be bits of the random number */ + uint32_t rnd = random_uint32() >> (32 - dev->csma_be); + return (rnd * KW41ZRF_CSMA_UNIT_TIME); +} + +static inline size_t kw41zrf_tx_load(const void *buf, size_t len, size_t offset) +{ + /* Array bounds are checked in the kw41zrf_netdev_send loop. */ + /* offset + 1 is used because buf[0] contains the frame length byte. */ + /* Don't use memcpy to work around a presumed compiler bug in + * arm-none-eabi-gcc 7.3.1 2018-q2-6 */ + for (unsigned i = 0; i < len; i++) { + ((uint8_t *)ZLL->PKT_BUFFER_TX)[i + offset + 1] = ((uint8_t *)buf)[i]; + } + + return offset + len; +} + +/** + * @brief set up TMR2 to trigger the TX sequence from the ISR + */ +static void kw41zrf_tx_exec(kw41zrf_t *dev) +{ + kw41zrf_abort_sequence(dev); + + if (dev->flags & KW41ZRF_OPT_CSMA) { + /* Use CSMA/CA random delay in the interval [0, 2**dev->csma_be) */ + dev->backoff_delay = kw41zrf_csma_random_delay(dev); + } + + /* less than 2 is sometimes in the past */ + if (dev->backoff_delay < 2) { + dev->backoff_delay = 2; + } + + /* Avoid risk of setting a timer in the past */ + int irq = irq_disable(); + kw41zrf_timer_set(dev, &ZLL->T2CMP, ~0ul); + + /* enable TMR2 */ + bit_clear32(&ZLL->PHY_CTRL, ZLL_PHY_CTRL_TMRTRIGEN_SHIFT); + bit_set32(&ZLL->PHY_CTRL, ZLL_PHY_CTRL_TMR2CMP_EN_SHIFT); + + /* Set real trigger time */ + kw41zrf_timer_set(dev, &ZLL->T2CMP, dev->backoff_delay); + irq_restore(irq); +} + +/** + * @brief Block the current thread until the radio is idle + * + * Any ongoing TX or CCA sequence will have finished when this function returns. + * + * @param[in] dev kw41zrf device descriptor + */ +static void kw41zrf_wait_idle(kw41zrf_t *dev) +{ + /* things that trigger this must run only from the netdev thread */ + assert(thread_getpid() == dev->thread->pid); + + /* make sure any ongoing T or TR sequence is finished */ + if (kw41zrf_can_switch_to_idle(dev) && dev->backoff_delay == 0) { + return; + } + + DEBUG("[kw41zrf] waiting for idle\n"); + /* we exit this wait using an IRQ so we can't do it from IRQ */ + assert(!irq_is_in()); + /* in case we're servicing an IRQ currently, IRQs will be masked */ + kw41zrf_unmask_irqs(); + + assert(!blocking_for_irq); + blocking_for_irq = true; + + PM_BLOCK(KW41ZRF_PM_BLOCKER); + while (1) { + /* TX or CCA or CSMA backoff in progress */ + /* Block until we get an IRQ */ + thread_flags_wait_any(KW41ZRF_THREAD_FLAG_ISR); + /* Handle the IRQ */ + kw41zrf_netdev_isr((netdev_t *)dev); + /* kw41zrf_netdev_isr() will switch the transceiver back to idle + * after handling the sequence complete IRQ */ + if (kw41zrf_can_switch_to_idle(dev) && dev->backoff_delay == 0) { + break; + } + } + + DEBUG("[kw41zrf] waited ISR\n"); + PM_UNBLOCK(KW41ZRF_PM_BLOCKER); + blocking_for_irq = false; +} + +int kw41zrf_cca(kw41zrf_t *dev) +{ + kw41zrf_wait_idle(dev); + if (kw41zrf_is_dsm()) { + /* bring the device out of DSM */ + kw41zrf_set_power_mode(dev, KW41ZRF_POWER_IDLE); + } + kw41zrf_abort_sequence(dev); + kw41zrf_unmask_irqs(); + KW41ZRF_LED_RX_ON; + kw41zrf_set_sequence(dev, XCVSEQ_CCA); + /* Wait for the CCA to finish, it will take exactly RX warmup time + 128 µs */ + kw41zrf_wait_idle(dev); + KW41ZRF_LED_RX_OFF; + DEBUG("[kw41zrf] CCA: %u RSSI: %d\n", (unsigned)dev->cca_result, + kw41zrf_get_ed_level(dev)); + return dev->cca_result; +} + +static int kw41zrf_netdev_send(netdev_t *netdev, const iolist_t *iolist) +{ + kw41zrf_t *dev = (kw41zrf_t *)netdev; + size_t len = 0; + + kw41zrf_wait_idle(dev); + + if (kw41zrf_is_dsm()) { + /* bring the device out of DSM */ + kw41zrf_set_power_mode(dev, KW41ZRF_POWER_IDLE); + } + + /* load packet data into buffer */ + for (const iolist_t *iol = iolist; iol; iol = iol->iol_next) { + /* current packet data + FCS too long */ + if ((len + iol->iol_len) > (KW41ZRF_MAX_PKT_LENGTH - IEEE802154_FCS_LEN)) { + LOG_ERROR("[kw41zrf] packet too large (%u byte) to fit\n", + (unsigned)len + IEEE802154_FCS_LEN); + return -EOVERFLOW; + } + len = kw41zrf_tx_load(iol->iol_base, iol->iol_len, len); + } + + DEBUG("[kw41zrf] TX %u bytes\n", len); + + /* + * First octet in the TX buffer contains the frame length. + * Nbytes = FRAME_LEN - 2 -> FRAME_LEN = Nbytes + 2 + * MKW41Z ref. man. 44.6.2.6.3.1.3 Sequence T (Transmit), p. 2147 + */ + *((volatile uint8_t *)&ZLL->PKT_BUFFER_TX[0]) = len + IEEE802154_FCS_LEN; +#if defined(MODULE_OD) && ENABLE_DEBUG + DEBUG("[kw41zrf] send:\n"); + od_hex_dump((const uint8_t *)ZLL->PKT_BUFFER_TX, len, OD_WIDTH_DEFAULT); +#endif + + /* send data out directly if pre-loading is disabled */ + if (!(dev->flags & KW41ZRF_OPT_PRELOADING)) { + dev->csma_be = dev->csma_min_be; + dev->csma_num_backoffs = 0; + dev->num_retrans = 0; + kw41zrf_tx_exec(dev); + } + + return (int)len; +} + +static inline void kw41zrf_unblock_rx(kw41zrf_t *dev) +{ + dev->recv_blocked = 0; + if (kw41zrf_can_switch_to_idle(dev)) { + kw41zrf_abort_sequence(dev); + kw41zrf_set_sequence(dev, dev->idle_seq); + } + KW41ZRF_LED_RX_OFF; +} + +static int kw41zrf_netdev_recv(netdev_t *netdev, void *buf, size_t len, void *info) +{ + kw41zrf_t *dev = (kw41zrf_t *)netdev; + if (kw41zrf_is_dsm()) { + /* bring the device out of DSM, sleep will be restored before returning */ + kw41zrf_set_power_mode(dev, KW41ZRF_POWER_IDLE); + } + /* get size of the received packet */ + uint8_t pkt_len = (ZLL->IRQSTS & ZLL_IRQSTS_RX_FRAME_LENGTH_MASK) >> ZLL_IRQSTS_RX_FRAME_LENGTH_SHIFT; + if (pkt_len < IEEE802154_FCS_LEN) { + kw41zrf_unblock_rx(dev); + return -EAGAIN; + } + /* skip FCS */ + pkt_len -= IEEE802154_FCS_LEN; + DEBUG("[kw41zrf] RX %u bytes\n", pkt_len); + + /* just return length when buf == NULL */ + if (buf == NULL) { + if (len > 0) { + /* discard what we have stored in the buffer, unblock RX */ + kw41zrf_unblock_rx(dev); + } + /* No set_sequence(idle_seq) here, keep transceiver turned on if the + * buffer was not discarded, we expect the higher layer to call again + * shortly with a proper buffer */ + return pkt_len; + } + +#if defined(MODULE_OD) && ENABLE_DEBUG + DEBUG("[kw41zrf] recv:\n"); + od_hex_dump((const uint8_t *)ZLL->PKT_BUFFER_RX, pkt_len, OD_WIDTH_DEFAULT); +#endif + + if (pkt_len > len) { + /* not enough space in buf */ + /* discard what we have stored in the buffer, unblock RX */ + kw41zrf_unblock_rx(dev); + return -ENOBUFS; + } + + /* Read packet buffer. */ + /* Don't use memcpy to work around a presumed compiler bug in + * arm-none-eabi-gcc 7.3.1 2018-q2-6 */ + for (int i = 0; i < pkt_len; i++) { + ((uint8_t *)buf)[i] = ((uint8_t *)ZLL->PKT_BUFFER_RX)[i]; + } + + if (info != NULL) { + netdev_ieee802154_rx_info_t *radio_info = info; + radio_info->lqi = (ZLL->LQI_AND_RSSI & ZLL_LQI_AND_RSSI_LQI_VALUE_MASK) + >> ZLL_LQI_AND_RSSI_LQI_VALUE_SHIFT; + radio_info->rssi = (int8_t)((ZLL->LQI_AND_RSSI & ZLL_LQI_AND_RSSI_RSSI_MASK) + >> ZLL_LQI_AND_RSSI_RSSI_SHIFT); + } + + /* Go back to RX mode */ + kw41zrf_unblock_rx(dev); + + return pkt_len; +} + +static int kw41zrf_netdev_set_state(kw41zrf_t *dev, netopt_state_t state) +{ + kw41zrf_wait_idle(dev); + + switch (state) { + case NETOPT_STATE_OFF: + /* There is no deeper 'off' mode than deep sleep mode */ + /* fall through */ + case NETOPT_STATE_SLEEP: + if (kw41zrf_is_dsm()) { + /* Transceiver is already in deep sleep mode */ + break; + } + kw41zrf_abort_sequence(dev); + kw41zrf_set_power_mode(dev, KW41ZRF_POWER_DSM); + dev->idle_seq = XCVSEQ_DSM_IDLE; + break; + case NETOPT_STATE_STANDBY: + kw41zrf_set_power_mode(dev, KW41ZRF_POWER_IDLE); + kw41zrf_abort_sequence(dev); + dev->idle_seq = XCVSEQ_IDLE; + kw41zrf_set_sequence(dev, dev->idle_seq); + break; + case NETOPT_STATE_IDLE: + kw41zrf_set_power_mode(dev, KW41ZRF_POWER_IDLE); + kw41zrf_abort_sequence(dev); + dev->idle_seq = XCVSEQ_RECEIVE; + kw41zrf_set_sequence(dev, dev->idle_seq); + break; + case NETOPT_STATE_TX: + if (dev->flags & KW41ZRF_OPT_PRELOADING) { + kw41zrf_wait_idle(dev); + if (kw41zrf_is_dsm()) { + /* bring the device out of DSM */ + kw41zrf_set_power_mode(dev, KW41ZRF_POWER_IDLE); + } + dev->csma_be = dev->csma_min_be; + dev->csma_num_backoffs = 0; + dev->num_retrans = 0; + kw41zrf_tx_exec(dev); + } + break; + case NETOPT_STATE_RESET: + kw41zrf_reset(dev); + break; + default: + return -ENOTSUP; + } + return sizeof(netopt_state_t); +} + +static netopt_state_t kw41zrf_netdev_get_state(kw41zrf_t *dev) +{ + (void) dev; + /* ZLL register access require that the transceiver is powered on and not in + * deep sleep mode */ + if (kw41zrf_is_dsm()) { + /* Transceiver is in deep sleep mode */ + return NETOPT_STATE_SLEEP; + } + uint32_t seq = (ZLL->PHY_CTRL & ZLL_PHY_CTRL_XCVSEQ_MASK) >> ZLL_PHY_CTRL_XCVSEQ_SHIFT; + + switch (seq) { + case XCVSEQ_TRANSMIT: + case XCVSEQ_TX_RX: + return NETOPT_STATE_TX; + + case XCVSEQ_CCA: + case XCVSEQ_CONTINUOUS_CCA: + return NETOPT_STATE_RX; + + case XCVSEQ_RECEIVE: + { + uint32_t seq_state = ZLL->SEQ_STATE; + if (seq_state & ZLL_SEQ_STATE_SFD_DET_MASK) { + /* SFD detection has been triggered */ + if (seq_state & ZLL_SEQ_STATE_RX_BYTE_COUNT_MASK) { + /* packet reception is in progress */ + return NETOPT_STATE_RX; + } + } + /* NETOPT_STATE_IDLE means on, and listening for incoming packets */ + return NETOPT_STATE_IDLE; + } + + case XCVSEQ_IDLE: + /* SEQ_IDLE in kw41z means on, but not listening for incoming traffic */ + return NETOPT_STATE_STANDBY; + + default: + /* Unknown state */ + LOG_ERROR("[kw41z] in unknown sequence: 0x%02" PRIx32 "\n", seq); + return NETOPT_STATE_OFF; + } +} + +int kw41zrf_netdev_get(netdev_t *netdev, netopt_t opt, void *value, size_t len) +{ + kw41zrf_t *dev = (kw41zrf_t *)netdev; + + if (dev == NULL) { + return -ENODEV; + } + + /* These settings do not require the transceiver to be powered on */ + switch (opt) { + case NETOPT_STATE: + assert(len >= sizeof(netopt_state_t)); + *((netopt_state_t *)value) = kw41zrf_netdev_get_state(dev); + return sizeof(netopt_state_t); + + case NETOPT_PRELOADING: + assert(len >= sizeof(netopt_enable_t)); + *((netopt_enable_t *)value) = + !!(dev->flags & KW41ZRF_OPT_PRELOADING); + return sizeof(netopt_enable_t); + + case NETOPT_PROMISCUOUSMODE: + assert(len >= sizeof(netopt_enable_t)); + *((netopt_enable_t *)value) = + !!(dev->flags & KW41ZRF_OPT_PROMISCUOUS); + return sizeof(netopt_enable_t); + + case NETOPT_RX_START_IRQ: + assert(len >= sizeof(netopt_enable_t)); + *((netopt_enable_t *)value) = + !!(dev->flags & KW41ZRF_OPT_TELL_RX_START); + return sizeof(netopt_enable_t); + + case NETOPT_RX_END_IRQ: + assert(len >= sizeof(netopt_enable_t)); + *((netopt_enable_t *)value) = + !!(dev->flags & KW41ZRF_OPT_TELL_RX_END); + return sizeof(netopt_enable_t); + + case NETOPT_TX_START_IRQ: + assert(len >= sizeof(netopt_enable_t)); + *((netopt_enable_t *)value) = + !!(dev->flags & KW41ZRF_OPT_TELL_TX_START); + return sizeof(netopt_enable_t); + + case NETOPT_TX_END_IRQ: + assert(len >= sizeof(netopt_enable_t)); + *((netopt_enable_t *)value) = + !!(dev->flags & KW41ZRF_OPT_TELL_TX_END); + return sizeof(netopt_enable_t); + + case NETOPT_CSMA: + assert(len >= sizeof(netopt_enable_t)); + *((netopt_enable_t *)value) = + !!(dev->flags & KW41ZRF_OPT_CSMA); + return sizeof(netopt_enable_t); + + case NETOPT_CSMA_RETRIES: + assert(len >= sizeof(uint8_t)); + *((uint8_t *)value) = dev->csma_max_backoffs; + return sizeof(uint8_t); + + case NETOPT_CSMA_MAXBE: + assert(len >= sizeof(uint8_t)); + *((uint8_t *)value) = dev->csma_max_be; + return sizeof(uint8_t); + + case NETOPT_CSMA_MINBE: + assert(len >= sizeof(uint8_t)); + *((uint8_t *)value) = dev->csma_min_be; + return sizeof(uint8_t); + + case NETOPT_RETRANS: + assert(len >= sizeof(uint8_t)); + *((uint8_t *)value) = dev->max_retrans; + return sizeof(uint8_t); + + case NETOPT_TX_RETRIES_NEEDED: + assert(len >= sizeof(uint8_t)); + *((uint8_t *)value) = dev->num_retrans; + return sizeof(uint8_t); + + case NETOPT_CHANNEL_PAGE: + assert(len >= sizeof(uint16_t)); + *((uint16_t *)value) = 0; + return sizeof(uint16_t); + + default: + break; + } + + /* The below settings require the transceiver to be powered on */ + unsigned put_to_sleep_when_done = 0; + if (kw41zrf_is_dsm()) { + /* Transceiver is in deep sleep mode */ + switch (opt) { + case NETOPT_CHANNEL: + case NETOPT_NID: + case NETOPT_ADDRESS: + case NETOPT_ADDRESS_LONG: + case NETOPT_TX_POWER: + case NETOPT_IS_CHANNEL_CLR: + case NETOPT_CCA_THRESHOLD: + case NETOPT_CCA_MODE: + case NETOPT_LAST_ED_LEVEL: + kw41zrf_set_power_mode(dev, KW41ZRF_POWER_IDLE); +#ifdef MODULE_NETOPT + DEBUG("[kw41zrf] Wake to get opt %s\n", netopt2str(opt)); +#else + DEBUG("[kw41zrf] Wake to get opt %d\n", (int)opt); +#endif + put_to_sleep_when_done = 1; + break; + + default: + break; + } + } + else { + /* Wait for oscillator ready signal if the CPU is coming out of low + * power mode */ + while ((RSIM->CONTROL & RSIM_CONTROL_RF_OSC_READY_MASK) == 0) {} + } + + int res = -ENOTSUP; + + switch (opt) { + case NETOPT_CHANNEL: + assert(len >= sizeof(uint16_t)); + *((uint16_t *)value) = (uint16_t)kw41zrf_get_channel(dev); + res = sizeof(uint16_t); + break; + + case NETOPT_NID: + assert(len >= sizeof(uint16_t)); + *((uint16_t *)value) = kw41zrf_get_pan(dev); + res = sizeof(uint16_t); + break; + + case NETOPT_ADDRESS: + assert(len >= sizeof(network_uint16_t)); + kw41zrf_get_addr_short(dev, value); + res = sizeof(network_uint16_t); + break; + + case NETOPT_ADDRESS_LONG: + assert(len >= sizeof(eui64_t)); + kw41zrf_get_addr_long(dev, value); + *(uint64_t*)value = byteorder_swapll(*(uint64_t*)value); + res = sizeof(eui64_t); + break; + + case NETOPT_TX_POWER: + assert(len >= sizeof(int16_t)); + *((int16_t *)value) = kw41zrf_get_txpower(dev); + res = sizeof(int16_t); + break; + + case NETOPT_IS_CHANNEL_CLR: + assert(len >= sizeof(netopt_enable_t)); + *((netopt_enable_t *)value) = !(kw41zrf_cca(dev)); + res = sizeof(netopt_enable_t); + break; + + case NETOPT_CCA_THRESHOLD: + assert(len >= sizeof(int8_t)); + *((int8_t *)value) = kw41zrf_get_cca_threshold(dev); + res = sizeof(int8_t); + break; + + case NETOPT_CCA_MODE: + assert(len >= sizeof(uint8_t)); + uint8_t mode = kw41zrf_get_cca_mode(dev); + switch (mode) { + case NETDEV_IEEE802154_CCA_MODE_1: + case NETDEV_IEEE802154_CCA_MODE_2: + case NETDEV_IEEE802154_CCA_MODE_3: + *((uint8_t *)value) = mode; + res = sizeof(uint8_t); + break; + default: + res = -EINVAL; + break; + } + break; + + case NETOPT_LAST_ED_LEVEL: + assert(len >= sizeof(int8_t)); + *((int8_t *)value) = kw41zrf_get_ed_level(dev); + res = sizeof(int8_t); + break; + + default: + break; + } + + if (put_to_sleep_when_done) { + DEBUG("[kw41zrf] Go back to sleep\n"); + kw41zrf_set_power_mode(dev, KW41ZRF_POWER_DSM); + } + + if (res == -ENOTSUP) { + res = netdev_ieee802154_get((netdev_ieee802154_t *)netdev, opt, value, len); + } + return res; +} + +static int kw41zrf_netdev_set(netdev_t *netdev, netopt_t opt, const void *value, size_t len) +{ + kw41zrf_t *dev = (kw41zrf_t *)netdev; + int res = -ENOTSUP; + + if (dev == NULL) { + return -ENODEV; + } + + kw41zrf_wait_idle(dev); + + /* These settings do not require the transceiver to be awake */ + switch (opt) { + case NETOPT_STATE: + assert(len <= sizeof(const netopt_state_t)); + res = kw41zrf_netdev_set_state(dev, *((const netopt_state_t *)value)); + break; + + case NETOPT_PRELOADING: + assert(len <= sizeof(const netopt_enable_t)); + kw41zrf_set_option(dev, KW41ZRF_OPT_PRELOADING, + *((const netopt_enable_t *)value)); + res = sizeof(const netopt_enable_t); + break; + + case NETOPT_RX_END_IRQ: + assert(len <= sizeof(const netopt_enable_t)); + kw41zrf_set_option(dev, KW41ZRF_OPT_TELL_RX_END, + *((const netopt_enable_t *)value)); + res = sizeof(const netopt_enable_t); + break; + + case NETOPT_TX_START_IRQ: + assert(len <= sizeof(const netopt_enable_t)); + kw41zrf_set_option(dev, KW41ZRF_OPT_TELL_TX_START, + *((const netopt_enable_t *)value)); + res = sizeof(const netopt_enable_t); + break; + + case NETOPT_TX_END_IRQ: + assert(len <= sizeof(const netopt_enable_t)); + kw41zrf_set_option(dev, KW41ZRF_OPT_TELL_TX_END, + *((const netopt_enable_t *)value)); + res = sizeof(const netopt_enable_t); + break; + + case NETOPT_CSMA_RETRIES: + assert(len <= sizeof(uint8_t)); + dev->csma_max_backoffs = *((const uint8_t*)value); + res = sizeof(uint8_t); + break; + + case NETOPT_CSMA_MAXBE: + assert(len <= sizeof(uint8_t)); + dev->csma_max_be = *((const uint8_t*)value); + res = sizeof(uint8_t); + break; + + case NETOPT_CSMA_MINBE: + assert(len <= sizeof(uint8_t)); + dev->csma_min_be = *((const uint8_t*)value); + res = sizeof(uint8_t); + break; + + case NETOPT_RETRANS: + assert(len <= sizeof(uint8_t)); + dev->max_retrans = *((const uint8_t *)value); + res = sizeof(uint8_t); + break; + + default: + break; + } + + unsigned put_to_sleep_when_done = 0; + + if (kw41zrf_is_dsm()) { + /* Transceiver is in deep sleep mode, check if setting the option + * requires the radio powered on */ + switch (opt) { + case NETOPT_AUTOACK: + case NETOPT_PROMISCUOUSMODE: + case NETOPT_RX_START_IRQ: + case NETOPT_CSMA: + case NETOPT_ADDRESS: + case NETOPT_ADDRESS_LONG: + case NETOPT_NID: + case NETOPT_CHANNEL: + case NETOPT_TX_POWER: + case NETOPT_CCA_THRESHOLD: + case NETOPT_CCA_MODE: + kw41zrf_set_power_mode(dev, KW41ZRF_POWER_IDLE); +#ifdef MODULE_NETOPT + DEBUG("[kw41zrf] Wake to set opt %s\n", netopt2str(opt)); +#else + DEBUG("[kw41zrf] Wake to set opt %d\n", (int)opt); +#endif + put_to_sleep_when_done = 1; + break; + + default: + break; + } + } + + switch (opt) { + case NETOPT_AUTOACK: + /* Set up HW generated automatic ACK after Receive */ + assert(len <= sizeof(const netopt_enable_t)); + kw41zrf_set_option(dev, KW41ZRF_OPT_AUTOACK, + *((const netopt_enable_t *)value)); + res = sizeof(const netopt_enable_t); + break; + + case NETOPT_ACK_PENDING: + /* Enable pending bit on automatic hardware ACKs */ + assert(len <= sizeof(const netopt_enable_t)); + kw41zrf_set_option(dev, KW41ZRF_OPT_ACK_PENDING, + *((const netopt_enable_t *)value)); + res = sizeof(const netopt_enable_t); + break; + + case NETOPT_PROMISCUOUSMODE: + assert(len <= sizeof(const netopt_enable_t)); + kw41zrf_set_option(dev, KW41ZRF_OPT_PROMISCUOUS, + *((const netopt_enable_t *)value)); + res = sizeof(const netopt_enable_t); + break; + + case NETOPT_RX_START_IRQ: + assert(len <= sizeof(const netopt_enable_t)); + kw41zrf_set_option(dev, KW41ZRF_OPT_TELL_RX_START, + *((const netopt_enable_t *)value)); + res = sizeof(const netopt_enable_t); + break; + + case NETOPT_CSMA: + assert(len <= sizeof(const netopt_enable_t)); + kw41zrf_set_option(dev, KW41ZRF_OPT_CSMA, + ((const netopt_enable_t *)value)[0]); + res = sizeof(const netopt_enable_t); + break; + + case NETOPT_ADDRESS: + assert(len <= sizeof(const network_uint16_t)); + kw41zrf_set_addr_short(dev, value); + res = sizeof(const network_uint16_t); + break; + + case NETOPT_ADDRESS_LONG: { + eui64_t addr; + assert(len <= sizeof(const eui64_t)); + addr.uint64.u64 = byteorder_swapll(*(uint64_t*)value); + kw41zrf_set_addr_long(dev, &addr); + res = sizeof(const eui64_t); + break; + } + + case NETOPT_NID: + assert(len <= sizeof(const uint16_t)); + kw41zrf_set_pan(dev, *((const uint16_t *)value)); + /* TODO uncomment this when the upper layer code is refactored to + * not need netdev_t::pan */ + //~ res = sizeof(const uint16_t); + break; + + case NETOPT_CHANNEL: + assert(len <= sizeof(const uint16_t)); + if (kw41zrf_set_channel(dev, *((const uint16_t *)value))) { + res = -EINVAL; + break; + } + res = sizeof(const uint16_t); + kw41zrf_abort_sequence(dev); + kw41zrf_set_sequence(dev, dev->idle_seq); + break; + + case NETOPT_TX_POWER: + assert(len <= sizeof(const int16_t)); + kw41zrf_set_tx_power(dev, *(const int16_t *)value); + res = sizeof(const int16_t); + break; + + case NETOPT_CCA_THRESHOLD: + assert(len <= sizeof(const uint8_t)); + kw41zrf_set_cca_threshold(dev, *((const uint8_t*)value)); + res = sizeof(const uint8_t); + break; + + case NETOPT_CCA_MODE: + assert(len <= sizeof(const uint8_t)); + uint8_t mode = *((const uint8_t*)value); + switch (mode) { + case NETDEV_IEEE802154_CCA_MODE_1: + case NETDEV_IEEE802154_CCA_MODE_2: + case NETDEV_IEEE802154_CCA_MODE_3: + kw41zrf_set_cca_mode(dev, mode); + res = sizeof(const uint8_t); + break; + case NETDEV_IEEE802154_CCA_MODE_4: + case NETDEV_IEEE802154_CCA_MODE_5: + case NETDEV_IEEE802154_CCA_MODE_6: + default: + res = -EINVAL; + break; + } + break; + + default: + break; + } + + if (put_to_sleep_when_done) { + DEBUG("[kw41zrf] Go back to sleep\n"); + kw41zrf_set_power_mode(dev, KW41ZRF_POWER_DSM); + } + + if (res == -ENOTSUP) { + res = netdev_ieee802154_set((netdev_ieee802154_t *)netdev, opt, value, len); + } + + return res; +} + +/* Common CCA check handler code for sequences Transmit and Transmit/Receive */ +static uint32_t _isr_event_seq_t_ccairq(kw41zrf_t *dev, uint32_t irqsts) +{ + uint32_t handled_irqs = 0; + if (irqsts & ZLL_IRQSTS_TMR2IRQ_MASK) { + assert(!(irqsts & ZLL_IRQSTS_CCAIRQ_MASK)); + handled_irqs |= ZLL_IRQSTS_TMR2IRQ_MASK; + + dev->tx_timeout = 0; + /* Check FCF field in the TX buffer to see if the ACK_REQ flag was set in + * the packet that is queued for transmission */ + uint16_t len_fcf = ZLL->PKT_BUFFER_TX[0]; + uint8_t fcf = (len_fcf >> 8) & 0xff; + if ((fcf & IEEE802154_FCF_ACK_REQ) && + (dev->netdev.flags & NETDEV_IEEE802154_ACK_REQ)) { + uint8_t payload_len = len_fcf & 0xff; + dev->tx_timeout = dev->backoff_delay + dev->tx_warmup_time + + KW41ZRF_SHR_PHY_TIME + payload_len * KW41ZRF_PER_BYTE_TIME + + KW41ZRF_ACK_WAIT_TIME + 2; + } + + KW41ZRF_LED_TX_ON; + if (dev->tx_timeout) { + kw41zrf_set_sequence(dev, XCVSEQ_TX_RX | ZLL_PHY_CTRL_TC3TMOUT_MASK); + } + else { + kw41zrf_set_sequence(dev, XCVSEQ_TRANSMIT); + } + + if (dev->tx_timeout > 0) { + /* Set real timeout for RX ACK */ + kw41zrf_timer_set(dev, &ZLL->T3CMP, dev->tx_timeout); + } + + dev->backoff_delay = 0; + /* disable TMR2 match */ + bit_clear32(&ZLL->PHY_CTRL, ZLL_PHY_CTRL_TMR2CMP_EN_SHIFT); + } + if (irqsts & ZLL_IRQSTS_CCAIRQ_MASK) { + /* CCA before TX has completed */ + handled_irqs |= ZLL_IRQSTS_CCAIRQ_MASK; + if (irqsts & ZLL_IRQSTS_CCA_MASK) { + /* Channel was determined busy */ + if (irqsts & ZLL_IRQSTS_SEQIRQ_MASK) { + handled_irqs |= ZLL_IRQSTS_SEQIRQ_MASK; + kw41zrf_abort_sequence(dev); + KW41ZRF_LED_TX_OFF; + } + + LOG_DEBUG("[kw41zrf] CCA ch busy (RSSI: %d retry: %u)\n", + (int8_t)((ZLL->LQI_AND_RSSI & ZLL_LQI_AND_RSSI_CCA1_ED_FNL_MASK) >> + ZLL_LQI_AND_RSSI_CCA1_ED_FNL_SHIFT), + dev->csma_num_backoffs + ); + + if (dev->csma_num_backoffs < dev->csma_max_backoffs) { + /* Perform CSMA/CA backoff algorithm */ + ++dev->csma_num_backoffs; + if (dev->csma_be < dev->csma_max_be) { + /* Increase delay exponent */ + ++dev->csma_be; + } + /* Resubmit the frame for transmission */ + kw41zrf_tx_exec(dev); + return handled_irqs; + } + + /* If we get here we've used up the csma retries and we're done */ + kw41zrf_abort_sequence(dev); + kw41zrf_set_sequence(dev, dev->idle_seq); + + if (dev->flags & KW41ZRF_OPT_TELL_TX_END) { + dev->netdev.netdev.event_callback(&dev->netdev.netdev, NETDEV_EVENT_TX_MEDIUM_BUSY); + LOG_INFO("[kw41zrf] dropping frame after %u backoffs\n", + dev->csma_num_backoffs); + } + + } + else { + /* Channel is idle */ + DEBUG("[kw41zrf] CCA ch idle (RSSI: %d retries: %u)\n", + (int8_t)((ZLL->LQI_AND_RSSI & ZLL_LQI_AND_RSSI_CCA1_ED_FNL_MASK) >> + ZLL_LQI_AND_RSSI_CCA1_ED_FNL_SHIFT), + dev->csma_num_backoffs + ); + if (dev->flags & KW41ZRF_OPT_TELL_TX_START) { + /* TX will start automatically after CCA check succeeded */ + dev->netdev.netdev.event_callback(&dev->netdev.netdev, NETDEV_EVENT_TX_STARTED); + } + } + } + return handled_irqs; +} + +/* Handler for Receive sequence */ +static uint32_t _isr_event_seq_r(kw41zrf_t *dev, uint32_t irqsts) +{ + uint32_t handled_irqs = 0; + + if (irqsts & ZLL_IRQSTS_RXWTRMRKIRQ_MASK) { + DEBUG("[kw41zrf] RXWTRMRKIRQ (R)\n"); + handled_irqs |= ZLL_IRQSTS_RXWTRMRKIRQ_MASK; + if (dev->flags & KW41ZRF_OPT_TELL_RX_START) { + dev->netdev.netdev.event_callback(&dev->netdev.netdev, NETDEV_EVENT_RX_STARTED); + } + } + + if (irqsts & ZLL_IRQSTS_FILTERFAIL_IRQ_MASK) { + KW41ZRF_LED_RX_OFF; + DEBUG("[kw41zrf] FILTERFAILIRQ: %04"PRIx32"\n", ZLL->FILTERFAIL_CODE); + handled_irqs |= ZLL_IRQSTS_FILTERFAIL_IRQ_MASK; + } + + if (irqsts & ZLL_IRQSTS_RXIRQ_MASK) { + KW41ZRF_LED_RX_ON; + DEBUG("[kw41zrf] finished RX\n"); + handled_irqs |= ZLL_IRQSTS_RXIRQ_MASK; + DEBUG("[kw41zrf] RX len: %3u\n", + (unsigned int)((ZLL->IRQSTS & ZLL_IRQSTS_RX_FRAME_LENGTH_MASK) >> + ZLL_IRQSTS_RX_FRAME_LENGTH_SHIFT)); + if (ZLL->PHY_CTRL & ZLL_PHY_CTRL_AUTOACK_MASK) { + KW41ZRF_LED_TX_ON; + DEBUG("[kw41zrf] perform TXACK\n"); + } + } + + if (irqsts & ZLL_IRQSTS_TXIRQ_MASK) { + KW41ZRF_LED_TX_OFF; + DEBUG("[kw41zrf] finished TXACK\n"); + handled_irqs |= ZLL_IRQSTS_TXIRQ_MASK; + } + + if (irqsts & ZLL_IRQSTS_SEQIRQ_MASK) { + uint32_t seq_ctrl_sts = ZLL->SEQ_CTRL_STS; + kw41zrf_abort_sequence(dev); + + DEBUG("[kw41zrf] SEQIRQ (R)\n"); + handled_irqs |= ZLL_IRQSTS_SEQIRQ_MASK; + KW41ZRF_LED_TX_OFF; + if ((irqsts & ZLL_IRQSTS_CRCVALID_MASK) == 0) { + DEBUG("[kw41zrf] CRC failure (R)\n"); + } + else if (seq_ctrl_sts & ZLL_SEQ_CTRL_STS_TC3_ABORTED_MASK) { + DEBUG("[kw41zrf] RX timeout (R)\n"); + } + else if (seq_ctrl_sts & ZLL_SEQ_CTRL_STS_PLL_ABORTED_MASK) { + LOG_ERROR("[kw41zrf] PLL unlock (R)\n"); + } + else if (seq_ctrl_sts & ZLL_SEQ_CTRL_STS_SW_ABORTED_MASK) { + LOG_ERROR("[kw41zrf] SW abort (R)\n"); + } + else { + /* No error reported */ + DEBUG("[kw41zrf] success (R)\n"); + /* Block XCVSEQ_RECEIVE until netdev->recv has been called */ + dev->recv_blocked = 1; + kw41zrf_set_sequence(dev, dev->idle_seq); + if (dev->flags & KW41ZRF_OPT_TELL_RX_END) { + dev->netdev.netdev.event_callback(&dev->netdev.netdev, NETDEV_EVENT_RX_COMPLETE); + } + return handled_irqs; + } + + kw41zrf_set_sequence(dev, dev->idle_seq); + } + + return handled_irqs; +} + +/* Handler for Transmit sequence */ +static uint32_t _isr_event_seq_t(kw41zrf_t *dev, uint32_t irqsts) +{ + uint32_t handled_irqs = 0; + if (irqsts & ZLL_IRQSTS_TXIRQ_MASK) { + DEBUG("[kw41zrf] finished TX (T)\n"); + handled_irqs |= ZLL_IRQSTS_TXIRQ_MASK; + } + if (irqsts & ZLL_IRQSTS_SEQIRQ_MASK) { + /* Finished T sequence */ + kw41zrf_abort_sequence(dev); + /* Go back to being idle */ + kw41zrf_set_sequence(dev, dev->idle_seq); + + DEBUG("[kw41zrf] SEQIRQ (T)\n"); + handled_irqs |= ZLL_IRQSTS_SEQIRQ_MASK; + if (dev->flags & KW41ZRF_OPT_TELL_TX_END) { + dev->netdev.netdev.event_callback(&dev->netdev.netdev, NETDEV_EVENT_TX_COMPLETE); + } + KW41ZRF_LED_TX_OFF; + } + + return handled_irqs; +} + +/* Handler for standalone CCA */ +static uint32_t _isr_event_seq_cca(kw41zrf_t *dev, uint32_t irqsts) +{ + uint32_t handled_irqs = 0; + + if (irqsts & ZLL_IRQSTS_SEQIRQ_MASK) { + /* Finished CCA sequence */ + kw41zrf_abort_sequence(dev); + kw41zrf_set_sequence(dev, dev->idle_seq); + + DEBUG("[kw41zrf] SEQIRQ (C)\n"); + handled_irqs |= ZLL_IRQSTS_SEQIRQ_MASK; + if (irqsts & ZLL_IRQSTS_CCA_MASK) { + DEBUG("[kw41zrf] CCA ch busy\n"); + dev->cca_result = 1; + } + else { + DEBUG("[kw41zrf] CCA ch idle\n"); + dev->cca_result = 0; + } + } + + return handled_irqs; +} + +/* Handler for Transmit/Receive sequence */ +static uint32_t _isr_event_seq_tr(kw41zrf_t *dev, uint32_t irqsts) +{ + uint32_t handled_irqs = 0; + if (irqsts & ZLL_IRQSTS_TXIRQ_MASK) { + KW41ZRF_LED_RX_ON; + DEBUG("[kw41zrf] finished TX (TR)\n"); + handled_irqs |= ZLL_IRQSTS_TXIRQ_MASK; + DEBUG("[kw41zrf] wait for RX ACK\n"); + } + + if (irqsts & ZLL_IRQSTS_RXIRQ_MASK) { + KW41ZRF_LED_RX_OFF; + DEBUG("[kw41zrf] got RX ACK\n"); + handled_irqs |= ZLL_IRQSTS_RXIRQ_MASK; + } + + if (irqsts & ZLL_IRQSTS_FILTERFAIL_IRQ_MASK) { + DEBUG("[kw41zrf] FILTERFAILIRQ (TR): %04"PRIx32"\n", ZLL->FILTERFAIL_CODE); + handled_irqs |= ZLL_IRQSTS_FILTERFAIL_IRQ_MASK; + } + + if (irqsts & ZLL_IRQSTS_SEQIRQ_MASK) { + uint32_t seq_ctrl_sts = ZLL->SEQ_CTRL_STS; + kw41zrf_abort_sequence(dev); + DEBUG("[kw41zrf] SEQIRQ (TR)\n"); + + handled_irqs |= ZLL_IRQSTS_SEQIRQ_MASK; + KW41ZRF_LED_TX_OFF; + KW41ZRF_LED_RX_OFF; + if (seq_ctrl_sts & ZLL_SEQ_CTRL_STS_TC3_ABORTED_MASK) { + if (dev->num_retrans < dev->max_retrans) { + /* Perform frame retransmission */ + ++dev->num_retrans; + DEBUG("[kw41zrf] TX retry %u\n", (unsigned)dev->num_retrans); + /* Reset CSMA counters for backoff handling */ + dev->csma_be = dev->csma_min_be; + dev->csma_num_backoffs = 0; + /* Resubmit the frame for transmission */ + kw41zrf_tx_exec(dev); + return handled_irqs; + } + } + + assert(!kw41zrf_is_dsm()); + kw41zrf_set_sequence(dev, dev->idle_seq); + + if (dev->flags & KW41ZRF_OPT_TELL_TX_END) { + if (seq_ctrl_sts & ZLL_SEQ_CTRL_STS_TC3_ABORTED_MASK) { + LOG_DEBUG("[kw41zrf] RXACK timeout (TR)\n"); + dev->netdev.netdev.event_callback(&dev->netdev.netdev, NETDEV_EVENT_TX_NOACK); + } + else if (seq_ctrl_sts & ZLL_SEQ_CTRL_STS_PLL_ABORTED_MASK) { + LOG_ERROR("[kw41zrf] PLL unlock (TR)\n"); + /* TODO: there is no other error event for TX failures */ + dev->netdev.netdev.event_callback(&dev->netdev.netdev, NETDEV_EVENT_TX_MEDIUM_BUSY); + /* if this does happen in development, it's worth checking why */ + assert(0); + } + else if (seq_ctrl_sts & ZLL_SEQ_CTRL_STS_SW_ABORTED_MASK) { + DEBUG("[kw41zrf] SW abort (TR)\n"); + /* TODO: there is no other error event for TX failures */ + dev->netdev.netdev.event_callback(&dev->netdev.netdev, NETDEV_EVENT_TX_MEDIUM_BUSY); + } + else { + /* No error reported */ + DEBUG("[kw41zrf] TX success (TR)\n"); + dev->netdev.netdev.event_callback(&dev->netdev.netdev, NETDEV_EVENT_TX_COMPLETE); + } + } + } + + return handled_irqs; +} + +/* Handler for Continuous CCA */ +static uint32_t _isr_event_seq_ccca(kw41zrf_t *dev, uint32_t irqsts) +{ + uint32_t handled_irqs = 0; + if (irqsts & ZLL_IRQSTS_SEQIRQ_MASK) { + DEBUG("[kw41zrf] SEQIRQ (CCCA)\n"); + handled_irqs |= ZLL_IRQSTS_SEQIRQ_MASK; + if (irqsts & ZLL_IRQSTS_CCA_MASK) { + DEBUG("[kw41zrf] CCCA ch busy\n"); + } + else { + DEBUG("[kw41zrf] CCCA ch idle\n"); + } + kw41zrf_abort_sequence(dev); + kw41zrf_set_sequence(dev, dev->idle_seq); + } + + return handled_irqs; +} + +static void kw41zrf_netdev_isr(netdev_t *netdev) +{ + kw41zrf_t *dev = (kw41zrf_t *)netdev; + + irq_is_queued = false; + thread_flags_clear(KW41ZRF_THREAD_FLAG_ISR); + + /* ZLL register access requires that the transceiver is not in deep sleep mode */ + if (kw41zrf_is_dsm()) { + /* Transceiver is sleeping, the IRQ must have occurred before entering + * sleep, discard the call */ + DEBUG("kw41zrf: unexpected IRQ while sleeping\n"); + kw41zrf_unmask_irqs(); + return; + } + + uint32_t irqsts = ZLL->IRQSTS; + /* Clear all IRQ flags now */ + ZLL->IRQSTS = irqsts; + + uint32_t handled_irqs = 0; + DEBUG("[kw41zrf] CTRL %08" PRIx32 ", IRQSTS %08" PRIx32 ", FILTERFAIL %08" PRIx32 "\n", + ZLL->PHY_CTRL, irqsts, ZLL->FILTERFAIL_CODE); + + uint8_t seq = (ZLL->PHY_CTRL & ZLL_PHY_CTRL_XCVSEQ_MASK) + >> ZLL_PHY_CTRL_XCVSEQ_SHIFT; + + switch (seq) { + case XCVSEQ_RECEIVE: + handled_irqs |= _isr_event_seq_r(dev, irqsts); + break; + + case XCVSEQ_IDLE: /* XCVSEQ is idle during csma backoff */ + case XCVSEQ_TRANSMIT: + /* First check CCA flags */ + handled_irqs |= _isr_event_seq_t_ccairq(dev, irqsts); + /* Then TX flags */ + handled_irqs |= _isr_event_seq_t(dev, irqsts & ~handled_irqs); + break; + + case XCVSEQ_CCA: + handled_irqs |= _isr_event_seq_cca(dev, irqsts); + break; + + case XCVSEQ_TX_RX: + /* First check CCA flags */ + handled_irqs |= _isr_event_seq_t_ccairq(dev, irqsts); + /* Then TX/RX flags */ + handled_irqs |= _isr_event_seq_tr(dev, irqsts & ~handled_irqs); + break; + + case XCVSEQ_CONTINUOUS_CCA: + handled_irqs |= _isr_event_seq_ccca(dev, irqsts); + break; + + default: + assert(0); + break; + } + + irqsts &= ~handled_irqs; + + /* doesn't need handling; just prevent outputting an error below */ + irqsts &= ~ZLL_IRQSTS_RXWTRMRKIRQ_MASK; + + if (irqsts & 0x000f017ful) { + LOG_ERROR("[kw41zrf] Unhandled IRQs: 0x%08lx\n", (irqsts & 0x000f017ful)); + } + + kw41zrf_unmask_irqs(); +} + +const netdev_driver_t kw41zrf_driver = { + .init = kw41zrf_netdev_init, + .send = kw41zrf_netdev_send, + .recv = kw41zrf_netdev_recv, + .get = kw41zrf_netdev_get, + .set = kw41zrf_netdev_set, + .isr = kw41zrf_netdev_isr, +}; + +/** @} */ diff --git a/drivers/kw41zrf/kw41zrf_xcvr.c b/drivers/kw41zrf/kw41zrf_xcvr.c new file mode 100644 index 000000000000..89b08fdccdcc --- /dev/null +++ b/drivers/kw41zrf/kw41zrf_xcvr.c @@ -0,0 +1,913 @@ +/* + * The Clear BSD License + * Copyright 2016-2017 NXP + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted (subject to the limitations in the + * disclaimer below) provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * * Neither the name of the copyright holder nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * NO EXPRESS OR IMPLIED LICENSES TO ANY PARTY'S PATENT RIGHTS ARE + * GRANTED BY THIS LICENSE. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT + * HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN + * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/** + * @ingroup drivers_kw41zrf + * @{ + * @file + * @brief NXP KW41Z XCVR module initialization and calibration of kw41zrf driver + * + * @author Joakim Nohlgård + * @} + */ +#include +#include +#include + +#include "log.h" +#include "bit.h" +#include "kw41zrf.h" +#include "vendor/XCVR/MKW41Z4/fsl_xcvr.h" +#include "vendor/XCVR/MKW41Z4/ifr_radio.h" + +#define ENABLE_DEBUG (0) +#include "debug.h" + +/* The implementations for these functions are taken from the vendor-provided + * XCVR driver from mcuxpresso.nxp.com (KSDK 2.2.0, framework_5.3.5) + * The code has been refactored to eliminate a lot of preprocessor + * conditionals. */ + +#define TsettleCal 10 +#define DCOC_DAC_BBF_STEP (16) +#define RX_DC_EST_SAMPLES (64) +#define RX_DC_EST_TOTAL_SAMPLES (2 * (RX_DC_EST_SAMPLES)) + +/* Macros used by the calibration routine */ +#define SAME_SIGN(a, b) (((a) ^ (b)) >= 0) +#define ABS(x) ((x) > 0 ? (x) : -(x)) + +/* dumb spin delay used in the calibration functions */ +static void kw41zrf_xcvr_spin(uint32_t time) +{ + time *= 32; /* Time delay is roughly in uSec. */ + while (time > 0) + { + __asm__ volatile ("" ::: "memory"); + --time; + } +} + +/* Collect RX DC estimation samples */ +static void rx_dc_est_samples(int32_t *i_sum, int32_t *q_sum, unsigned nsamples) +{ + /* Wait for TSM to reach the end of warmup (unless you want to capture some samples during DCOC cal phase). */ + uint32_t end_of_rx_wu = XCVR_CTRL_XCVR_STATUS_TSM_COUNT( + (XCVR_TSM->END_OF_SEQ & XCVR_TSM_END_OF_SEQ_END_OF_RX_WU_MASK) >> + XCVR_TSM_END_OF_SEQ_END_OF_RX_WU_SHIFT); + while ((XCVR_MISC->XCVR_STATUS & XCVR_CTRL_XCVR_STATUS_TSM_COUNT_MASK) != end_of_rx_wu) {}; + + int32_t sum_i = 0; + int32_t sum_q = 0; + /* Read DCOC DC EST register. */ + for (unsigned k = 0; k < nsamples; k++) + { + uint32_t dc_temp = XCVR_RX_DIG->DCOC_DC_EST; + int16_t dc_meas_i = (dc_temp & XCVR_RX_DIG_DCOC_DC_EST_DC_EST_I_MASK) >> XCVR_RX_DIG_DCOC_DC_EST_DC_EST_I_SHIFT; + dc_meas_i = (int16_t)(dc_meas_i << 4) / 16; /* Sign extend from 12 to 16 bits. */ + sum_i += dc_meas_i; + + int16_t dc_meas_q = (dc_temp & XCVR_RX_DIG_DCOC_DC_EST_DC_EST_Q_MASK) >> XCVR_RX_DIG_DCOC_DC_EST_DC_EST_Q_SHIFT; + dc_meas_q = (int16_t)(dc_meas_q << 4) / 16; /* Sign extend from 12 to 16 bits. */ + sum_q += dc_meas_q; + } + + *i_sum = sum_i; + *q_sum = sum_q; +} + +/* Unsigned integer division, rounded to nearest integer */ +static inline uint32_t calc_div_rounded(uint32_t num, uint32_t den) +{ + return (num + (den / 2)) / den; +} + +int kw41zrf_rx_bba_dcoc_dac_trim_DCest(void) +{ + /* Estimate the actual gain by measuring three points and approximating a line */ + int status = 0; + + /* Save register */ + uint32_t dcoc_ctrl_0_stack = XCVR_RX_DIG->DCOC_CTRL_0; /* Save state of DCOC_CTRL_0 for later restore */ + uint32_t dcoc_ctrl_1_stack = XCVR_RX_DIG->DCOC_CTRL_1; /* Save state of DCOC_CTRL_1 for later restore */ + uint32_t rx_dig_ctrl_stack = XCVR_RX_DIG->RX_DIG_CTRL; /* Save state of RX_DIG_CTRL for later restore */ + uint32_t agc_ctrl_1_stack = XCVR_RX_DIG->AGC_CTRL_1; /* Save state of RX_DIG_CTRL for later restore */ + uint32_t dcoc_cal_gain_state = XCVR_RX_DIG->DCOC_CAL_GAIN; /* Save state of DCOC_CAL_GAIN for later restore */ + + /* Register config */ + /* Ensure AGC, DCOC and RX_DIG_CTRL is in correct mode */ + XCVR_RX_DIG->RX_DIG_CTRL = XCVR_RX_DIG->RX_DIG_CTRL & + ~(XCVR_RX_DIG_RX_DIG_CTRL_RX_AGC_EN_MASK | /* Turn OFF AGC */ + XCVR_RX_DIG_RX_DIG_CTRL_RX_DCOC_CAL_EN_MASK | /* Disable for SW control of DCOC */ + XCVR_RX_DIG_RX_DIG_CTRL_RX_DC_RESID_EN_MASK); /* Disable for SW control of DCOC */ + + XCVR_RX_DIG->AGC_CTRL_1 = XCVR_RX_DIG_AGC_CTRL_1_USER_LNA_GAIN_EN(1) | /* Enable LNA Manual Gain */ + XCVR_RX_DIG_AGC_CTRL_1_USER_BBA_GAIN_EN(1) | /* Enable BBA Manual Gain */ + XCVR_RX_DIG_AGC_CTRL_1_LNA_USER_GAIN(0x0) | /* Set LNA Manual Gain */ + XCVR_RX_DIG_AGC_CTRL_1_BBA_USER_GAIN(0x0); /* Set BBA Manual Gain */ + + /* DCOC_CTRL_0 @ 4005_C02C -- Define default DCOC DAC settings in manual mode */ + XCVR_RX_DIG->DCOC_CTRL_0 = XCVR_RX_DIG->DCOC_CTRL_0 | + XCVR_RX_DIG_DCOC_CTRL_0_DCOC_MAN(1) | /* Enable Manual DCOC */ + XCVR_RX_DIG_DCOC_CTRL_0_DCOC_CORRECT_SRC(1) | /* Ensure DCOC Tracking is enabled */ + XCVR_RX_DIG_DCOC_CTRL_0_DCOC_TRK_EST_OVR(1) | /* Enable DC Estimator */ + XCVR_RX_DIG_DCOC_CTRL_0_DCOC_CORRECT_EN(1); /* Ensure DC correction is enabled */ + + /* Use reset defaults */ + uint8_t bbf_dacinit_i = 0x20; + uint8_t bbf_dacinit_q = 0x20; + uint8_t tza_dacinit_i = 0x80; + uint8_t tza_dacinit_q = 0x80; + + /* Set default DCOC DAC INIT Value */ + XCVR_RX_DIG->DCOC_DAC_INIT = + XCVR_RX_DIG_DCOC_DAC_INIT_BBA_DCOC_INIT_I(bbf_dacinit_i) | + XCVR_RX_DIG_DCOC_DAC_INIT_BBA_DCOC_INIT_Q(bbf_dacinit_q) | + XCVR_RX_DIG_DCOC_DAC_INIT_TZA_DCOC_INIT_I(tza_dacinit_i) | + XCVR_RX_DIG_DCOC_DAC_INIT_TZA_DCOC_INIT_Q(tza_dacinit_q); + /* Store DCOC_DAC_INIT value */ + uint32_t dcoc_init_reg_value_dcgain = XCVR_RX_DIG->DCOC_DAC_INIT; + + kw41zrf_xcvr_spin(TsettleCal * 2); + + uint32_t meas_sum = 0; + /* SWEEP I/Q CHANNEL */ + /* BBF NEG STEP */ + XCVR_RX_DIG->DCOC_DAC_INIT = XCVR_RX_DIG_DCOC_DAC_INIT_BBA_DCOC_INIT_I(bbf_dacinit_i - DCOC_DAC_BBF_STEP) | + XCVR_RX_DIG_DCOC_DAC_INIT_BBA_DCOC_INIT_Q(bbf_dacinit_q - DCOC_DAC_BBF_STEP) | + XCVR_RX_DIG_DCOC_DAC_INIT_TZA_DCOC_INIT_I(tza_dacinit_i) | + XCVR_RX_DIG_DCOC_DAC_INIT_TZA_DCOC_INIT_Q(tza_dacinit_q); + kw41zrf_xcvr_spin(TsettleCal * 4); + + int32_t dc_meas_im = 0; + int32_t dc_meas_qm = 0; + rx_dc_est_samples(&dc_meas_im, &dc_meas_qm, RX_DC_EST_SAMPLES); + + /* BBF POS STEP */ + XCVR_RX_DIG->DCOC_DAC_INIT = XCVR_RX_DIG_DCOC_DAC_INIT_BBA_DCOC_INIT_I(bbf_dacinit_i + DCOC_DAC_BBF_STEP) | + XCVR_RX_DIG_DCOC_DAC_INIT_BBA_DCOC_INIT_Q(bbf_dacinit_q + DCOC_DAC_BBF_STEP) | + XCVR_RX_DIG_DCOC_DAC_INIT_TZA_DCOC_INIT_I(tza_dacinit_i) | + XCVR_RX_DIG_DCOC_DAC_INIT_TZA_DCOC_INIT_Q(tza_dacinit_q); + kw41zrf_xcvr_spin(TsettleCal * 4); + int32_t dc_meas_ip = 0; + int32_t dc_meas_qp = 0; + rx_dc_est_samples(&dc_meas_ip, &dc_meas_qp, RX_DC_EST_SAMPLES); + DEBUG("dc_meas_i- = %" PRId32 "\n", dc_meas_im); + DEBUG("dc_meas_q- = %" PRId32 "\n", dc_meas_qm); + DEBUG("dc_meas_i+ = %" PRId32 "\n", dc_meas_ip); + DEBUG("dc_meas_q+ = %" PRId32 "\n", dc_meas_qp); + meas_sum += dc_meas_ip - dc_meas_im; + DEBUG("meas_sum = %" PRIu32 "\n", meas_sum); + meas_sum += dc_meas_qp - dc_meas_qm; + DEBUG("meas_sum = %" PRIu32 "\n", meas_sum); + meas_sum /= 2 * DCOC_DAC_BBF_STEP; + DEBUG("meas_sum = %" PRIu32 "\n", meas_sum); + + XCVR_RX_DIG->DCOC_DAC_INIT = dcoc_init_reg_value_dcgain; /* Return DAC setting to initial */ + + /* Compute the average sampled gain for the measured steps */ + + /* Calculate BBF DCOC STEPS, RECIPROCALS */ + /* meas_sum here is the average gain multiplied by (4 * RX_DC_EST_SAMPLES) */ + /* Compute the gain average as a Q6.3 number */ + /* rounded result, Q6.3 number */ + uint16_t bbf_dcoc_gain_measured = calc_div_rounded(meas_sum, (RX_DC_EST_TOTAL_SAMPLES / (1u << 3))); + + DEBUG("temp_step = %f\n", (float)meas_sum / RX_DC_EST_TOTAL_SAMPLES); + DEBUG("bbf_dcoc_gain_measured = %u\n", (unsigned)bbf_dcoc_gain_measured); + + /* Check the measured value for validity. Should be in the range: + * 250 < bbf_dcoc_gain_measured < 305, according to NXP wireless framework v5.4.3 (MCUXpresso KW36 SDK) + */ + if ((250 < bbf_dcoc_gain_measured) & (bbf_dcoc_gain_measured < 305)) + { + /* Compute reciprocal, as Q15 number, but only the 13 lowest bits are programmable */ + /* rounded result, ((2**15) / slope) */ + uint32_t bbf_dcoc_gain_measured_rcp = calc_div_rounded((1u << 15) * RX_DC_EST_TOTAL_SAMPLES, meas_sum); + DEBUG("bbf_dcoc_gain_measured_rcp = %"PRIu32"\n", bbf_dcoc_gain_measured_rcp); + + uint32_t bbf_dcoc_gain_default = + (xcvr_common_config.dcoc_bba_step_init & + XCVR_RX_DIG_DCOC_BBA_STEP_BBA_DCOC_STEP_MASK) >> + XCVR_RX_DIG_DCOC_BBA_STEP_BBA_DCOC_STEP_SHIFT; + /* Rescale all default TZA DCOC gains according to the measured BBF gain, + * using (bbf_dcoc_gain_measured / bbf_dcoc_gain_default) as the implicit + * scale factor, but rewrite it to use + * (meas_sum / (bbf_dcoc_gain_default * RX_DC_EST_TOTAL_SAMPLES / (1u << 3)))) + * for better numeric precision */ + /* rounded result, Q9.3 number */ + bbf_dcoc_gain_default *= (RX_DC_EST_TOTAL_SAMPLES / (1u << 3)); + DEBUG("base gain = %u\n", (unsigned)bbf_dcoc_gain_default); + /* Make the trims active */ + XCVR_RX_DIG->DCOC_BBA_STEP = + XCVR_RX_DIG_DCOC_BBA_STEP_BBA_DCOC_STEP(bbf_dcoc_gain_measured) | + XCVR_RX_DIG_DCOC_BBA_STEP_BBA_DCOC_STEP_RECIP(bbf_dcoc_gain_measured_rcp); + const uint32_t *dcoc_tza_step_config_ptr = &xcvr_common_config.dcoc_tza_step_00_init; + /* All tza_step_* configuration registers use sequential memory addresses */ + volatile uint32_t *xcvr_rx_dig_dcoc_tza_step_ptr = &XCVR_RX_DIG->DCOC_TZA_STEP_0; + for (unsigned k = 0; k <= 10; ++k) + { + /* Calculate TZA DCOC STEPSIZE & its RECIPROCAL */ + uint16_t tza_gain_default = + (dcoc_tza_step_config_ptr[k] & + XCVR_RX_DIG_DCOC_TZA_STEP_0_DCOC_TZA_STEP_GAIN_0_MASK) >> + XCVR_RX_DIG_DCOC_TZA_STEP_0_DCOC_TZA_STEP_GAIN_0_SHIFT; + /* Using meas_sum for higher precision */ + DEBUG("tza_gain_default[%u] = %u\n", k, (unsigned)tza_gain_default); + uint32_t dcoc_step = calc_div_rounded(tza_gain_default * meas_sum, bbf_dcoc_gain_default); + uint32_t dcoc_step_rcp = calc_div_rounded((0x8000ul << 3) * bbf_dcoc_gain_default, tza_gain_default * meas_sum); + DEBUG("tza_dcoc_step[%u].dcoc_step = %u\n", k, (unsigned)dcoc_step); + DEBUG("tza_dcoc_step[%u].dcoc_step_rcp = %u\n", k, (unsigned)dcoc_step_rcp); + xcvr_rx_dig_dcoc_tza_step_ptr[k] = + XCVR_RX_DIG_DCOC_TZA_STEP_0_DCOC_TZA_STEP_GAIN_0(dcoc_step) | + XCVR_RX_DIG_DCOC_TZA_STEP_0_DCOC_TZA_STEP_RCP_0(dcoc_step_rcp) ; + } + } + else + { + LOG_ERROR("!!! XCVR trim failed: bbf_dcoc_step = %u!\n", (unsigned)bbf_dcoc_gain_measured); + status = -EAGAIN; /* Failure */ + } + + /* Restore Registers */ + XCVR_RX_DIG->DCOC_CTRL_0 = dcoc_ctrl_0_stack; /* Restore DCOC_CTRL_0 state to prior settings */ + XCVR_RX_DIG->DCOC_CTRL_1 = dcoc_ctrl_1_stack; /* Restore DCOC_CTRL_1 state to prior settings */ + XCVR_RX_DIG->RX_DIG_CTRL = rx_dig_ctrl_stack; /* Restore RX_DIG_CTRL state to prior settings */ + XCVR_RX_DIG->DCOC_CAL_GAIN = dcoc_cal_gain_state; /* Restore DCOC_CAL_GAIN state to prior setting */ + XCVR_RX_DIG->AGC_CTRL_1 = agc_ctrl_1_stack; /* Save state of RX_DIG_CTRL for later restore */ + + return status; +} + +static void kw41zrf_dcoc_dac_init_cal(void) +{ + uint8_t p_tza_dac_i = 0, p_tza_dac_q = 0; + uint8_t p_bba_dac_i = 0, p_bba_dac_q = 0; + uint8_t i = 0; + uint8_t bba_gain = 11; + uint8_t TZA_I_OK = 0, TZA_Q_OK = 0, BBA_I_OK = 0, BBA_Q_OK = 0; + + uint32_t temp; + + /* Save registers */ + uint32_t dcoc_ctrl_0_stack = XCVR_RX_DIG->DCOC_CTRL_0; /* Save state of DCOC_CTRL_0 for later restore */ + uint32_t dcoc_ctrl_1_stack = XCVR_RX_DIG->DCOC_CTRL_1; /* Save state of DCOC_CTRL_1 for later restore */ + uint32_t rx_dig_ctrl_stack = XCVR_RX_DIG->RX_DIG_CTRL; /* Save state of RX_DIG_CTRL for later restore */ + uint32_t agc_ctrl_1_stack = XCVR_RX_DIG->AGC_CTRL_1; /* Save state of RX_DIG_CTRL for later restore */ + uint32_t dcoc_cal_gain_state = XCVR_RX_DIG->DCOC_CAL_GAIN; /* Save state of DCOC_CAL_GAIN for later restore */ + + /* Register config */ + /* Ensure AGC, DCOC and RX_DIG_CTRL is in correct mode */ + temp = XCVR_RX_DIG->RX_DIG_CTRL; + temp &= ~XCVR_RX_DIG_RX_DIG_CTRL_RX_AGC_EN_MASK; /* Turn OFF AGC */ + temp &= ~XCVR_RX_DIG_RX_DIG_CTRL_RX_DCOC_CAL_EN_MASK; /* Disable for SW control of DCOC */ + temp &= ~XCVR_RX_DIG_RX_DIG_CTRL_RX_DC_RESID_EN_MASK; /* Disable for SW control of DCOC */ + XCVR_RX_DIG->RX_DIG_CTRL = temp; + + XCVR_RX_DIG->AGC_CTRL_1 = XCVR_RX_DIG_AGC_CTRL_1_USER_LNA_GAIN_EN(1) | /* Enable LNA Manual Gain */ + XCVR_RX_DIG_AGC_CTRL_1_USER_BBA_GAIN_EN(1) | /* Enable BBA Manual Gain */ + XCVR_RX_DIG_AGC_CTRL_1_LNA_USER_GAIN(0x0) | /* Set LNA Manual Gain */ + XCVR_RX_DIG_AGC_CTRL_1_BBA_USER_GAIN(0x0); /* Set BBA Manual Gain */ + + /* DCOC_CTRL_0 @ 4005_C02C -- Define default DCOC DAC settings in manual mode */ + temp = XCVR_RX_DIG->DCOC_CTRL_0; + temp |= XCVR_RX_DIG_DCOC_CTRL_0_DCOC_MAN(1); /* Enable Manual DCOC */ + temp |= XCVR_RX_DIG_DCOC_CTRL_0_DCOC_CORRECT_SRC(1); /* Ensure DCOC Tracking is enabled */ + temp |= XCVR_RX_DIG_DCOC_CTRL_0_DCOC_TRK_EST_OVR(1); /* Enable DC Estimator */ + temp |= XCVR_RX_DIG_DCOC_CTRL_0_DCOC_CORRECT_EN(1); /* Ensure DC correction is enabled */ + XCVR_RX_DIG->DCOC_CTRL_0 = temp; + + kw41zrf_xcvr_spin(TsettleCal); + + /* Set default DCOC DAC INIT Value */ + /* LNA and BBA DAC Sweep */ + uint8_t curr_bba_dac_i = 0x20; + uint8_t curr_bba_dac_q = 0x20; + uint8_t curr_tza_dac_i = 0x80; + uint8_t curr_tza_dac_q = 0x80; + + /* Perform a first DC measurement to ensure that measurement is not clipping */ + XCVR_RX_DIG->DCOC_DAC_INIT = XCVR_RX_DIG_DCOC_DAC_INIT_BBA_DCOC_INIT_I(curr_bba_dac_i) | + XCVR_RX_DIG_DCOC_DAC_INIT_BBA_DCOC_INIT_Q(curr_bba_dac_q) | + XCVR_RX_DIG_DCOC_DAC_INIT_TZA_DCOC_INIT_I(curr_tza_dac_i) | + XCVR_RX_DIG_DCOC_DAC_INIT_TZA_DCOC_INIT_Q(curr_tza_dac_q); + + int32_t dc_meas_i = 2000, dc_meas_i_p = 2000; + int32_t dc_meas_q = 2000, dc_meas_q_p = 2000; + do { + bba_gain--; + /* Set DAC user gain */ + XCVR_RX_DIG->AGC_CTRL_1 = XCVR_RX_DIG_AGC_CTRL_1_USER_LNA_GAIN_EN(1) | + XCVR_RX_DIG_AGC_CTRL_1_LNA_USER_GAIN(0) | /* 2 */ + XCVR_RX_DIG_AGC_CTRL_1_USER_BBA_GAIN_EN(1) | + XCVR_RX_DIG_AGC_CTRL_1_BBA_USER_GAIN(bba_gain) ; /* 10 */ + kw41zrf_xcvr_spin(TsettleCal * 2); + rx_dc_est_samples(&dc_meas_i, &dc_meas_q, RX_DC_EST_SAMPLES); + DEBUG("rx i=%d q=%d\n", (int)dc_meas_i, (int)dc_meas_q); + dc_meas_i /= RX_DC_EST_SAMPLES; + dc_meas_q /= RX_DC_EST_SAMPLES; + DEBUG("rx i=%d q=%d\n", (int)dc_meas_i, (int)dc_meas_q); + DEBUG("[kw41zrf] bba_gain=%u, meas I=%" PRId32 ", Q=%" PRId32 "\n", (unsigned)bba_gain, dc_meas_i, dc_meas_q); + } while ((ABS(dc_meas_i) > 1900) || (ABS(dc_meas_q) > 1900)); + + for (i = 0; i < 0x0F; i++) + { + DEBUG("rx i=%d q=%d\n", (int)dc_meas_i, (int)dc_meas_q); + /* I channel : */ + if (!TZA_I_OK) { + if ((i > 0) && (!SAME_SIGN(dc_meas_i, dc_meas_i_p))) { + if (ABS(dc_meas_i) > ABS(dc_meas_i_p)) { + curr_tza_dac_i = p_tza_dac_i; + } + + TZA_I_OK = 1; + } + else { + p_tza_dac_i = curr_tza_dac_i; + + if (dc_meas_i > 0) { + curr_tza_dac_i--; + } + else { + curr_tza_dac_i++; + } + } + } + else if (!BBA_I_OK) { + /* Sweep BBA I */ + if ((curr_bba_dac_i != 0x20) && (!SAME_SIGN(dc_meas_i, dc_meas_i_p))) { + if (ABS(dc_meas_i) > ABS(dc_meas_i_p)) { + curr_bba_dac_i = p_bba_dac_i; + } + + BBA_I_OK = 1; + } + else { + p_bba_dac_i = curr_bba_dac_i; + if (dc_meas_i > 0) { + curr_bba_dac_i--; + } + else { + curr_bba_dac_i++; + } + } + } + + /* Q channel : */ + if (!TZA_Q_OK) { + if ((i > 0) && (!SAME_SIGN(dc_meas_q, dc_meas_q_p))) { + if (ABS(dc_meas_q) > ABS(dc_meas_q_p)) { + curr_tza_dac_q = p_tza_dac_q; + } + TZA_Q_OK = 1; + } + else { + p_tza_dac_q = curr_tza_dac_q; + if (dc_meas_q > 0) { + curr_tza_dac_q--; + } + else { + curr_tza_dac_q++; + } + } + } + else if (!BBA_Q_OK) { + /* Sweep BBA Q */ + if ((curr_bba_dac_q != 0x20) && (!SAME_SIGN(dc_meas_q, dc_meas_q_p))) { + if (ABS(dc_meas_q) > ABS(dc_meas_q_p)) { + curr_bba_dac_q = p_bba_dac_q; + } + BBA_Q_OK = 1; + } + else { + p_bba_dac_q = curr_bba_dac_q; + if (dc_meas_q > 0) { + curr_bba_dac_q--; + } + else { + curr_bba_dac_q++; + } + } + } + + /* DC OK break : */ + if (TZA_I_OK && TZA_Q_OK && BBA_I_OK && BBA_Q_OK) { + break; + } + + dc_meas_i_p = dc_meas_i; /* Store as previous value */ + dc_meas_q_p = dc_meas_q; /* Store as previous value */ + DEBUG("curr_bba_dac i=%d q=%d\n", (int)curr_bba_dac_i, (int)curr_bba_dac_q); + DEBUG("curr_tza_dac i=%d q=%d\n", (int)curr_tza_dac_i, (int)curr_tza_dac_q); + XCVR_RX_DIG->DCOC_DAC_INIT = XCVR_RX_DIG_DCOC_DAC_INIT_BBA_DCOC_INIT_I(curr_bba_dac_i) | + XCVR_RX_DIG_DCOC_DAC_INIT_BBA_DCOC_INIT_Q(curr_bba_dac_q) | + XCVR_RX_DIG_DCOC_DAC_INIT_TZA_DCOC_INIT_I(curr_tza_dac_i) | + XCVR_RX_DIG_DCOC_DAC_INIT_TZA_DCOC_INIT_Q(curr_tza_dac_q); + kw41zrf_xcvr_spin(TsettleCal * 2); + rx_dc_est_samples(&dc_meas_i, &dc_meas_q, RX_DC_EST_SAMPLES); + dc_meas_i /= RX_DC_EST_SAMPLES; + dc_meas_q /= RX_DC_EST_SAMPLES; + } + + /* Apply optimized DCOC DAC INIT : */ + XCVR_RX_DIG->DCOC_DAC_INIT = XCVR_RX_DIG_DCOC_DAC_INIT_BBA_DCOC_INIT_I(curr_bba_dac_i) | + XCVR_RX_DIG_DCOC_DAC_INIT_BBA_DCOC_INIT_Q(curr_bba_dac_q) | + XCVR_RX_DIG_DCOC_DAC_INIT_TZA_DCOC_INIT_I(curr_tza_dac_i) | + XCVR_RX_DIG_DCOC_DAC_INIT_TZA_DCOC_INIT_Q(curr_tza_dac_q); + + /* Restore register */ + XCVR_RX_DIG->DCOC_CTRL_0 = dcoc_ctrl_0_stack; /* Restore DCOC_CTRL_0 state to prior settings */ + XCVR_RX_DIG->DCOC_CTRL_1 = dcoc_ctrl_1_stack; /* Restore DCOC_CTRL_1 state to prior settings */ + XCVR_RX_DIG->RX_DIG_CTRL = rx_dig_ctrl_stack; /* Restore RX_DIG_CTRL state to prior settings */ + XCVR_RX_DIG->DCOC_CAL_GAIN = dcoc_cal_gain_state; /* Restore DCOC_CAL_GAIN state to prior setting */ + XCVR_RX_DIG->AGC_CTRL_1 = agc_ctrl_1_stack; /* Save state of RX_DIG_CTRL for later restore */ +} + +static int kw41zrf_xcvr_configure(kw41zrf_t *dev, + const xcvr_common_config_t *com_config, + const xcvr_mode_config_t *mode_config, + const xcvr_mode_datarate_config_t *mode_datarate_config, + const xcvr_datarate_config_t *datarate_config) +{ + (void)dev; + int config_status = 0; + uint32_t temp; + + /* Turn on the module clocks before doing anything */ + SIM->SCGC5 |= mode_config->scgc5_clock_ena_bits; + + /* XCVR_ANA configs */ + + /* Configure PLL Loop Filter */ + XCVR_ANA->SY_CTRL_1 &= ~com_config->ana_sy_ctrl1.mask; + XCVR_ANA->SY_CTRL_1 |= com_config->ana_sy_ctrl1.init; + + /* Configure VCO KVM */ + XCVR_ANA->SY_CTRL_2 &= ~mode_datarate_config->ana_sy_ctrl2.mask; + XCVR_ANA->SY_CTRL_2 |= mode_datarate_config->ana_sy_ctrl2.init; + + /* Configure analog filter bandwidth */ + XCVR_ANA->RX_BBA &= ~mode_datarate_config->ana_rx_bba.mask; + XCVR_ANA->RX_BBA |= mode_datarate_config->ana_rx_bba.init; + XCVR_ANA->RX_TZA &= ~mode_datarate_config->ana_rx_tza.mask; + XCVR_ANA->RX_TZA |= mode_datarate_config->ana_rx_tza.init; + + temp = XCVR_ANA->TX_DAC_PA; + temp &= ~XCVR_ANALOG_TX_DAC_PA_TX_PA_BUMP_VBIAS_MASK; + temp |= XCVR_ANALOG_TX_DAC_PA_TX_PA_BUMP_VBIAS(4); + XCVR_ANA->TX_DAC_PA = temp; + + temp = XCVR_ANA->BB_LDO_2; + temp &= ~XCVR_ANALOG_BB_LDO_2_BB_LDO_VCOLO_TRIM_MASK; + temp |= XCVR_ANALOG_BB_LDO_2_BB_LDO_VCOLO_TRIM(0); + XCVR_ANA->BB_LDO_2 = temp; + + temp = XCVR_ANA->RX_LNA; + temp &= ~XCVR_ANALOG_RX_LNA_RX_LNA_BUMP_MASK; + temp |= XCVR_ANALOG_RX_LNA_RX_LNA_BUMP(1); + XCVR_ANA->RX_LNA = temp; + + temp = XCVR_ANA->BB_LDO_1; + temp &= ~XCVR_ANALOG_BB_LDO_1_BB_LDO_FDBK_TRIM_MASK; + temp |= XCVR_ANALOG_BB_LDO_1_BB_LDO_FDBK_TRIM(1); + XCVR_ANA->BB_LDO_1 = temp; + + /* XCVR_MISC configs */ + temp = XCVR_MISC->XCVR_CTRL; + temp &= ~(mode_config->xcvr_ctrl.mask | XCVR_CTRL_XCVR_CTRL_REF_CLK_FREQ_MASK); + temp |= mode_config->xcvr_ctrl.init; + if (CLOCK_RADIOXTAL == 26000000ul) { + temp |= XCVR_CTRL_XCVR_CTRL_REF_CLK_FREQ(1); + } + + XCVR_MISC->XCVR_CTRL = temp; + + /* XCVR_PHY configs */ + XCVR_PHY->PHY_PRE_REF0 = mode_config->phy_pre_ref0_init; + XCVR_PHY->PRE_REF1 = mode_config->phy_pre_ref1_init; + XCVR_PHY->PRE_REF2 = mode_config->phy_pre_ref2_init; + XCVR_PHY->CFG1 = mode_config->phy_cfg1_init; + XCVR_PHY->CFG2 = mode_datarate_config->phy_cfg2_init; + XCVR_PHY->EL_CFG = mode_config->phy_el_cfg_init | datarate_config->phy_el_cfg_init; /* EL_WIN_SIZE and EL_INTERVAL are datarate dependent, */ + + /* XCVR_PLL_DIG configs */ + XCVR_PLL_DIG->HPM_BUMP = com_config->pll_hpm_bump; + XCVR_PLL_DIG->MOD_CTRL = com_config->pll_mod_ctrl; + XCVR_PLL_DIG->CHAN_MAP = com_config->pll_chan_map; + XCVR_PLL_DIG->LOCK_DETECT = com_config->pll_lock_detect; + XCVR_PLL_DIG->HPM_CTRL = com_config->pll_hpm_ctrl; + XCVR_PLL_DIG->HPMCAL_CTRL = com_config->pll_hpmcal_ctrl; + XCVR_PLL_DIG->HPM_SDM_RES = com_config->pll_hpm_sdm_res; + XCVR_PLL_DIG->LPM_CTRL = com_config->pll_lpm_ctrl; + XCVR_PLL_DIG->LPM_SDM_CTRL1 = com_config->pll_lpm_sdm_ctrl1; + XCVR_PLL_DIG->DELAY_MATCH = com_config->pll_delay_match; + XCVR_PLL_DIG->CTUNE_CTRL = com_config->pll_ctune_ctrl; + + /* XCVR_RX_DIG configs */ + + /* Configure RF Aux PLL for proper operation based on external clock frequency */ + temp = XCVR_ANA->RX_AUXPLL; + temp &= ~XCVR_ANALOG_RX_AUXPLL_VCO_DAC_REF_ADJUST_MASK; + if (CLOCK_RADIOXTAL == 26000000ul) { + temp |= XCVR_ANALOG_RX_AUXPLL_VCO_DAC_REF_ADJUST(4); + } + else { + temp |= XCVR_ANALOG_RX_AUXPLL_VCO_DAC_REF_ADJUST(7); + } + XCVR_ANA->RX_AUXPLL = temp; + + /* Configure RX_DIG_CTRL */ + if (CLOCK_RADIOXTAL == 26000000ul) { + temp = com_config->rx_dig_ctrl_init | /* Common portion of RX_DIG_CTRL init */ + mode_config->rx_dig_ctrl_init_26mhz | /* Mode specific portion of RX_DIG_CTRL init */ + datarate_config->rx_dig_ctrl_init_26mhz | /* Datarate specific portion of RX_DIG_CTRL init */ + XCVR_RX_DIG_RX_DIG_CTRL_RX_SRC_EN_MASK; /* Always enable the sample rate converter for 26MHz */ + } + else { + temp = com_config->rx_dig_ctrl_init | /* Common portion of RX_DIG_CTRL init */ + mode_config->rx_dig_ctrl_init_32mhz | /* Mode specific portion of RX_DIG_CTRL init */ + datarate_config->rx_dig_ctrl_init_32mhz | /* Datarate specific portion of RX_DIG_CTRL init */ + 0; /* Always disable the sample rate converter for 32MHz */ + } + + temp |= com_config->rx_dig_ctrl_init; /* Common portion of RX_DIG_CTRL init */ + XCVR_RX_DIG->RX_DIG_CTRL = temp; + + /* DCOC_CAL_IIR */ + if (CLOCK_RADIOXTAL == 26000000ul) { + XCVR_RX_DIG->DCOC_CAL_IIR = datarate_config->dcoc_cal_iir_init_26mhz; + } + else { + XCVR_RX_DIG->DCOC_CAL_IIR = datarate_config->dcoc_cal_iir_init_32mhz; + } + + /* DC_RESID_CTRL */ + if (CLOCK_RADIOXTAL == 26000000ul) { + XCVR_RX_DIG->DC_RESID_CTRL = com_config->dc_resid_ctrl_init | datarate_config->dc_resid_ctrl_26mhz; + } + else { + XCVR_RX_DIG->DC_RESID_CTRL = com_config->dc_resid_ctrl_init | datarate_config->dc_resid_ctrl_32mhz; + } + + /* DCOC_CTRL_0 & _1 */ + if (CLOCK_RADIOXTAL == 26000000ul) { + XCVR_RX_DIG->DCOC_CTRL_0 = com_config->dcoc_ctrl_0_init_26mhz | datarate_config->dcoc_ctrl_0_init_26mhz; /* Combine common and datarate specific settings */ + XCVR_RX_DIG->DCOC_CTRL_1 = com_config->dcoc_ctrl_1_init | datarate_config->dcoc_ctrl_1_init_26mhz; /* Combine common and datarate specific settings */ + + /* customize DCOC_CTRL_0 settings for Gen2 GFSK BT=0.5, h=0.32 */ + if ((mode_config->radio_mode == ANT_MODE) || (mode_config->radio_mode == GFSK_BT_0p5_h_0p32)) + { + if (datarate_config->data_rate == DR_1MBPS) /* only apply fix to 1Mbps data rates */ + { + /* apply the changes to the DCOC_CTRL_0 register XCVR_RX_DIG_DCOC_CTRL_0_DCOC_CORR_DLY & XCVR_RX_DIG_DCOC_CTRL_0_DCOC_CORR_HOLD_TIME */ + temp = XCVR_RX_DIG->DCOC_CTRL_0; + temp &= ~XCVR_RX_DIG_DCOC_CTRL_0_DCOC_CORR_DLY_MASK | XCVR_RX_DIG_DCOC_CTRL_0_DCOC_CORR_HOLD_TIME_MASK; + temp |= XCVR_RX_DIG_DCOC_CTRL_0_DCOC_CORR_DLY(0x10) | XCVR_RX_DIG_DCOC_CTRL_0_DCOC_CORR_HOLD_TIME(0x0C); + XCVR_RX_DIG->DCOC_CTRL_0 = temp; + } + } + } + else { + XCVR_RX_DIG->DCOC_CTRL_0 = com_config->dcoc_ctrl_0_init_32mhz | datarate_config->dcoc_ctrl_0_init_32mhz; /* Combine common and datarate specific settings */ + XCVR_RX_DIG->DCOC_CTRL_1 = com_config->dcoc_ctrl_1_init | datarate_config->dcoc_ctrl_1_init_32mhz; /* Combine common and datarate specific settings */ + } + + /* DCOC_CAL_GAIN */ + XCVR_RX_DIG->DCOC_CAL_GAIN = com_config->dcoc_cal_gain_init; + + /* DCOC_CAL_RCP */ + XCVR_RX_DIG->DCOC_CAL_RCP = com_config->dcoc_cal_rcp_init; + XCVR_RX_DIG->LNA_GAIN_VAL_3_0 = com_config->lna_gain_val_3_0; + XCVR_RX_DIG->LNA_GAIN_VAL_7_4 = com_config->lna_gain_val_7_4; + XCVR_RX_DIG->LNA_GAIN_VAL_8 = com_config->lna_gain_val_8; + XCVR_RX_DIG->BBA_RES_TUNE_VAL_7_0 = com_config->bba_res_tune_val_7_0; + XCVR_RX_DIG->BBA_RES_TUNE_VAL_10_8 = com_config->bba_res_tune_val_10_8; + + /* LNA_GAIN_LIN_VAL */ + XCVR_RX_DIG->LNA_GAIN_LIN_VAL_2_0 = com_config->lna_gain_lin_val_2_0_init; + XCVR_RX_DIG->LNA_GAIN_LIN_VAL_5_3 = com_config->lna_gain_lin_val_5_3_init; + XCVR_RX_DIG->LNA_GAIN_LIN_VAL_8_6 = com_config->lna_gain_lin_val_8_6_init; + XCVR_RX_DIG->LNA_GAIN_LIN_VAL_9 = com_config->lna_gain_lin_val_9_init; + + /* BBA_RES_TUNE_LIN_VAL */ + XCVR_RX_DIG->BBA_RES_TUNE_LIN_VAL_3_0 = com_config->bba_res_tune_lin_val_3_0_init; + XCVR_RX_DIG->BBA_RES_TUNE_LIN_VAL_7_4 = com_config->bba_res_tune_lin_val_7_4_init; + XCVR_RX_DIG->BBA_RES_TUNE_LIN_VAL_10_8 = com_config->bba_res_tune_lin_val_10_8_init; + + /* BBA_STEP */ + XCVR_RX_DIG->DCOC_BBA_STEP = com_config->dcoc_bba_step_init; + + /* DCOC_TZA_STEP */ + XCVR_RX_DIG->DCOC_TZA_STEP_0 = com_config->dcoc_tza_step_00_init; + XCVR_RX_DIG->DCOC_TZA_STEP_1 = com_config->dcoc_tza_step_01_init; + XCVR_RX_DIG->DCOC_TZA_STEP_2 = com_config->dcoc_tza_step_02_init; + XCVR_RX_DIG->DCOC_TZA_STEP_3 = com_config->dcoc_tza_step_03_init; + XCVR_RX_DIG->DCOC_TZA_STEP_4 = com_config->dcoc_tza_step_04_init; + XCVR_RX_DIG->DCOC_TZA_STEP_5 = com_config->dcoc_tza_step_05_init; + XCVR_RX_DIG->DCOC_TZA_STEP_6 = com_config->dcoc_tza_step_06_init; + XCVR_RX_DIG->DCOC_TZA_STEP_7 = com_config->dcoc_tza_step_07_init; + XCVR_RX_DIG->DCOC_TZA_STEP_8 = com_config->dcoc_tza_step_08_init; + XCVR_RX_DIG->DCOC_TZA_STEP_9 = com_config->dcoc_tza_step_09_init; + XCVR_RX_DIG->DCOC_TZA_STEP_10 = com_config->dcoc_tza_step_10_init; + + /* AGC_CTRL_0 .. _3 */ + XCVR_RX_DIG->AGC_CTRL_0 = com_config->agc_ctrl_0_init | mode_config->agc_ctrl_0_init; + + if (CLOCK_RADIOXTAL == 26000000ul) { + XCVR_RX_DIG->AGC_CTRL_1 = com_config->agc_ctrl_1_init_26mhz | datarate_config->agc_ctrl_1_init_26mhz; /* Combine common and datarate specific settings */ + XCVR_RX_DIG->AGC_CTRL_2 = mode_datarate_config->agc_ctrl_2_init_26mhz; + } + else { + XCVR_RX_DIG->AGC_CTRL_1 = com_config->agc_ctrl_1_init_32mhz | datarate_config->agc_ctrl_1_init_32mhz; /* Combine common and datarate specific settings */ + XCVR_RX_DIG->AGC_CTRL_2 = mode_datarate_config->agc_ctrl_2_init_32mhz; + } + + XCVR_RX_DIG->AGC_CTRL_3 = com_config->agc_ctrl_3_init; + + /* AGC_GAIN_TBL_** */ + XCVR_RX_DIG->AGC_GAIN_TBL_03_00 = com_config->agc_gain_tbl_03_00_init; + XCVR_RX_DIG->AGC_GAIN_TBL_07_04 = com_config->agc_gain_tbl_07_04_init; + XCVR_RX_DIG->AGC_GAIN_TBL_11_08 = com_config->agc_gain_tbl_11_08_init; + XCVR_RX_DIG->AGC_GAIN_TBL_15_12 = com_config->agc_gain_tbl_15_12_init; + XCVR_RX_DIG->AGC_GAIN_TBL_19_16 = com_config->agc_gain_tbl_19_16_init; + XCVR_RX_DIG->AGC_GAIN_TBL_23_20 = com_config->agc_gain_tbl_23_20_init; + XCVR_RX_DIG->AGC_GAIN_TBL_26_24 = com_config->agc_gain_tbl_26_24_init; + + /* RSSI_CTRL_0 */ + XCVR_RX_DIG->RSSI_CTRL_0 = com_config->rssi_ctrl_0_init; + + /* CCA_ED_LQI_0 and _1 */ + XCVR_RX_DIG->CCA_ED_LQI_CTRL_0 = com_config->cca_ed_lqi_ctrl_0_init; + XCVR_RX_DIG->CCA_ED_LQI_CTRL_1 = com_config->cca_ed_lqi_ctrl_1_init; + + /* Channel filter coefficients */ + if (CLOCK_RADIOXTAL == 26000000ul) { + XCVR_RX_DIG->RX_CHF_COEF_0 = mode_datarate_config->rx_chf_coeffs_26mhz.rx_chf_coef_0; + XCVR_RX_DIG->RX_CHF_COEF_1 = mode_datarate_config->rx_chf_coeffs_26mhz.rx_chf_coef_1; + XCVR_RX_DIG->RX_CHF_COEF_2 = mode_datarate_config->rx_chf_coeffs_26mhz.rx_chf_coef_2; + XCVR_RX_DIG->RX_CHF_COEF_3 = mode_datarate_config->rx_chf_coeffs_26mhz.rx_chf_coef_3; + XCVR_RX_DIG->RX_CHF_COEF_4 = mode_datarate_config->rx_chf_coeffs_26mhz.rx_chf_coef_4; + XCVR_RX_DIG->RX_CHF_COEF_5 = mode_datarate_config->rx_chf_coeffs_26mhz.rx_chf_coef_5; + XCVR_RX_DIG->RX_CHF_COEF_6 = mode_datarate_config->rx_chf_coeffs_26mhz.rx_chf_coef_6; + XCVR_RX_DIG->RX_CHF_COEF_7 = mode_datarate_config->rx_chf_coeffs_26mhz.rx_chf_coef_7; + XCVR_RX_DIG->RX_CHF_COEF_8 = mode_datarate_config->rx_chf_coeffs_26mhz.rx_chf_coef_8; + XCVR_RX_DIG->RX_CHF_COEF_9 = mode_datarate_config->rx_chf_coeffs_26mhz.rx_chf_coef_9; + XCVR_RX_DIG->RX_CHF_COEF_10 = mode_datarate_config->rx_chf_coeffs_26mhz.rx_chf_coef_10; + XCVR_RX_DIG->RX_CHF_COEF_11 = mode_datarate_config->rx_chf_coeffs_26mhz.rx_chf_coef_11; + } + else { + XCVR_RX_DIG->RX_CHF_COEF_0 = mode_datarate_config->rx_chf_coeffs_32mhz.rx_chf_coef_0; + XCVR_RX_DIG->RX_CHF_COEF_1 = mode_datarate_config->rx_chf_coeffs_32mhz.rx_chf_coef_1; + XCVR_RX_DIG->RX_CHF_COEF_2 = mode_datarate_config->rx_chf_coeffs_32mhz.rx_chf_coef_2; + XCVR_RX_DIG->RX_CHF_COEF_3 = mode_datarate_config->rx_chf_coeffs_32mhz.rx_chf_coef_3; + XCVR_RX_DIG->RX_CHF_COEF_4 = mode_datarate_config->rx_chf_coeffs_32mhz.rx_chf_coef_4; + XCVR_RX_DIG->RX_CHF_COEF_5 = mode_datarate_config->rx_chf_coeffs_32mhz.rx_chf_coef_5; + XCVR_RX_DIG->RX_CHF_COEF_6 = mode_datarate_config->rx_chf_coeffs_32mhz.rx_chf_coef_6; + XCVR_RX_DIG->RX_CHF_COEF_7 = mode_datarate_config->rx_chf_coeffs_32mhz.rx_chf_coef_7; + XCVR_RX_DIG->RX_CHF_COEF_8 = mode_datarate_config->rx_chf_coeffs_32mhz.rx_chf_coef_8; + XCVR_RX_DIG->RX_CHF_COEF_9 = mode_datarate_config->rx_chf_coeffs_32mhz.rx_chf_coef_9; + XCVR_RX_DIG->RX_CHF_COEF_10 = mode_datarate_config->rx_chf_coeffs_32mhz.rx_chf_coef_10; + XCVR_RX_DIG->RX_CHF_COEF_11 = mode_datarate_config->rx_chf_coeffs_32mhz.rx_chf_coef_11; + } + + XCVR_RX_DIG->RX_RCCAL_CTRL0 = mode_datarate_config->rx_rccal_ctrl_0; + XCVR_RX_DIG->RX_RCCAL_CTRL1 = mode_datarate_config->rx_rccal_ctrl_1; + + /* XCVR_TSM configs */ + XCVR_TSM->CTRL = com_config->tsm_ctrl; + + if ((mode_config->radio_mode != ZIGBEE_MODE) && (mode_config->radio_mode != BLE_MODE)) + { + XCVR_TSM->CTRL &= ~XCVR_TSM_CTRL_DATA_PADDING_EN_MASK; + } + + XCVR_MISC->LPPS_CTRL = com_config->lpps_ctrl_init; /* Register is in XCVR_MISC but grouped with TSM for initialization */ + + XCVR_TSM->OVRD2 = com_config->tsm_ovrd2_init; + /* TSM registers and timings - dependent upon clock frequency */ + if (CLOCK_RADIOXTAL == 26000000ul) { + XCVR_TSM->END_OF_SEQ = com_config->end_of_seq_init_26mhz; + XCVR_TSM->FAST_CTRL2 = com_config->tsm_fast_ctrl2_init_26mhz; + XCVR_TSM->RECYCLE_COUNT = com_config->recycle_count_init_26mhz; + XCVR_TSM->TIMING14 = com_config->tsm_timing_14_init_26mhz; + XCVR_TSM->TIMING16 = com_config->tsm_timing_16_init_26mhz; + XCVR_TSM->TIMING25 = com_config->tsm_timing_25_init_26mhz; + XCVR_TSM->TIMING27 = com_config->tsm_timing_27_init_26mhz; + XCVR_TSM->TIMING28 = com_config->tsm_timing_28_init_26mhz; + XCVR_TSM->TIMING29 = com_config->tsm_timing_29_init_26mhz; + XCVR_TSM->TIMING30 = com_config->tsm_timing_30_init_26mhz; + XCVR_TSM->TIMING31 = com_config->tsm_timing_31_init_26mhz; + XCVR_TSM->TIMING32 = com_config->tsm_timing_32_init_26mhz; + XCVR_TSM->TIMING33 = com_config->tsm_timing_33_init_26mhz; + XCVR_TSM->TIMING36 = com_config->tsm_timing_36_init_26mhz; + XCVR_TSM->TIMING37 = com_config->tsm_timing_37_init_26mhz; + XCVR_TSM->TIMING39 = com_config->tsm_timing_39_init_26mhz; + XCVR_TSM->TIMING40 = com_config->tsm_timing_40_init_26mhz; + XCVR_TSM->TIMING41 = com_config->tsm_timing_41_init_26mhz; + XCVR_TSM->TIMING52 = com_config->tsm_timing_52_init_26mhz; + XCVR_TSM->TIMING54 = com_config->tsm_timing_54_init_26mhz; + XCVR_TSM->TIMING55 = com_config->tsm_timing_55_init_26mhz; + XCVR_TSM->TIMING56 = com_config->tsm_timing_56_init_26mhz; + } + else { + XCVR_TSM->END_OF_SEQ = com_config->end_of_seq_init_32mhz; + XCVR_TSM->FAST_CTRL2 = com_config->tsm_fast_ctrl2_init_32mhz; + XCVR_TSM->RECYCLE_COUNT = com_config->recycle_count_init_32mhz; + XCVR_TSM->TIMING14 = com_config->tsm_timing_14_init_32mhz; + XCVR_TSM->TIMING16 = com_config->tsm_timing_16_init_32mhz; + XCVR_TSM->TIMING25 = com_config->tsm_timing_25_init_32mhz; + XCVR_TSM->TIMING27 = com_config->tsm_timing_27_init_32mhz; + XCVR_TSM->TIMING28 = com_config->tsm_timing_28_init_32mhz; + XCVR_TSM->TIMING29 = com_config->tsm_timing_29_init_32mhz; + XCVR_TSM->TIMING30 = com_config->tsm_timing_30_init_32mhz; + XCVR_TSM->TIMING31 = com_config->tsm_timing_31_init_32mhz; + XCVR_TSM->TIMING32 = com_config->tsm_timing_32_init_32mhz; + XCVR_TSM->TIMING33 = com_config->tsm_timing_33_init_32mhz; + XCVR_TSM->TIMING36 = com_config->tsm_timing_36_init_32mhz; + XCVR_TSM->TIMING37 = com_config->tsm_timing_37_init_32mhz; + XCVR_TSM->TIMING39 = com_config->tsm_timing_39_init_32mhz; + XCVR_TSM->TIMING40 = com_config->tsm_timing_40_init_32mhz; + XCVR_TSM->TIMING41 = com_config->tsm_timing_41_init_32mhz; + XCVR_TSM->TIMING52 = com_config->tsm_timing_52_init_32mhz; + XCVR_TSM->TIMING54 = com_config->tsm_timing_54_init_32mhz; + XCVR_TSM->TIMING55 = com_config->tsm_timing_55_init_32mhz; + XCVR_TSM->TIMING56 = com_config->tsm_timing_56_init_32mhz; + } + + /* TSM timings independent of clock frequency */ + XCVR_TSM->TIMING00 = com_config->tsm_timing_00_init; + XCVR_TSM->TIMING01 = com_config->tsm_timing_01_init; + XCVR_TSM->TIMING02 = com_config->tsm_timing_02_init; + XCVR_TSM->TIMING03 = com_config->tsm_timing_03_init; + XCVR_TSM->TIMING04 = com_config->tsm_timing_04_init; + XCVR_TSM->TIMING05 = com_config->tsm_timing_05_init; + XCVR_TSM->TIMING06 = com_config->tsm_timing_06_init; + XCVR_TSM->TIMING07 = com_config->tsm_timing_07_init; + XCVR_TSM->TIMING08 = com_config->tsm_timing_08_init; + XCVR_TSM->TIMING09 = com_config->tsm_timing_09_init; + XCVR_TSM->TIMING10 = com_config->tsm_timing_10_init; + XCVR_TSM->TIMING11 = com_config->tsm_timing_11_init; + XCVR_TSM->TIMING12 = com_config->tsm_timing_12_init; + XCVR_TSM->TIMING13 = com_config->tsm_timing_13_init; + XCVR_TSM->TIMING15 = com_config->tsm_timing_15_init; + XCVR_TSM->TIMING17 = com_config->tsm_timing_17_init; + XCVR_TSM->TIMING18 = com_config->tsm_timing_18_init; + XCVR_TSM->TIMING19 = com_config->tsm_timing_19_init; + XCVR_TSM->TIMING20 = com_config->tsm_timing_20_init; + XCVR_TSM->TIMING21 = com_config->tsm_timing_21_init; + XCVR_TSM->TIMING22 = com_config->tsm_timing_22_init; + XCVR_TSM->TIMING23 = com_config->tsm_timing_23_init; + XCVR_TSM->TIMING24 = com_config->tsm_timing_24_init; + XCVR_TSM->TIMING26 = com_config->tsm_timing_26_init; + XCVR_TSM->TIMING34 = com_config->tsm_timing_34_init; + XCVR_TSM->TIMING35 = com_config->tsm_timing_35_init; + XCVR_TSM->TIMING38 = com_config->tsm_timing_38_init; + XCVR_TSM->TIMING51 = com_config->tsm_timing_51_init; + XCVR_TSM->TIMING53 = com_config->tsm_timing_53_init; + XCVR_TSM->TIMING57 = com_config->tsm_timing_57_init; + XCVR_TSM->TIMING58 = com_config->tsm_timing_58_init; + + if (CLOCK_RADIOXTAL == 26000000ul) { + XCVR_TSM->END_OF_SEQ = XCVR_TSM_END_OF_SEQ_END_OF_TX_WU(END_OF_TX_WU) | + XCVR_TSM_END_OF_SEQ_END_OF_TX_WD(END_OF_TX_WD) | + XCVR_TSM_END_OF_SEQ_END_OF_RX_WU(END_OF_RX_WU_26MHZ) | + XCVR_TSM_END_OF_SEQ_END_OF_RX_WD(END_OF_RX_WD_26MHZ); + } + else { + XCVR_TSM->END_OF_SEQ = XCVR_TSM_END_OF_SEQ_END_OF_TX_WU(END_OF_TX_WU) | + XCVR_TSM_END_OF_SEQ_END_OF_TX_WD(END_OF_TX_WD) | + XCVR_TSM_END_OF_SEQ_END_OF_RX_WU(END_OF_RX_WU) | + XCVR_TSM_END_OF_SEQ_END_OF_RX_WD(END_OF_RX_WD); + } + + XCVR_TSM->PA_RAMP_TBL0 = com_config->pa_ramp_tbl_0_init; + XCVR_TSM->PA_RAMP_TBL1 = com_config->pa_ramp_tbl_1_init; + + if ((mode_datarate_config->radio_mode == MSK) && ((mode_datarate_config->data_rate == DR_500KBPS) || (mode_datarate_config->data_rate == DR_250KBPS))) { + /* Apply a specific value of TX_DIG_EN which assumes no DATA PADDING */ + XCVR_TSM->TIMING35 = com_config->tsm_timing_35_init | B0(TX_DIG_EN_ASSERT_MSK500); /* LSbyte is mode specific */ + } + else { + XCVR_TSM->TIMING35 = com_config->tsm_timing_35_init | mode_config->tsm_timing_35_init; /* LSbyte is mode specific, other bytes are common */ + } + + /* XCVR_TX_DIG configs */ + if (CLOCK_RADIOXTAL == 26000000ul) { + XCVR_TX_DIG->FSK_SCALE = mode_datarate_config->tx_fsk_scale_26mhz; /* Applies only to 802.15.4 & MSK but won't harm other protocols */ + XCVR_TX_DIG->GFSK_COEFF1 = mode_config->tx_gfsk_coeff1_26mhz; + XCVR_TX_DIG->GFSK_COEFF2 = mode_config->tx_gfsk_coeff2_26mhz; + } + else { + XCVR_TX_DIG->FSK_SCALE = mode_datarate_config->tx_fsk_scale_32mhz; /* Applies only to 802.15.4 & MSK but won't harm other protocols */ + XCVR_TX_DIG->GFSK_COEFF1 = mode_config->tx_gfsk_coeff1_32mhz; + XCVR_TX_DIG->GFSK_COEFF2 = mode_config->tx_gfsk_coeff2_32mhz; + } + + XCVR_TX_DIG->CTRL = com_config->tx_ctrl; + XCVR_TX_DIG->DATA_PADDING = com_config->tx_data_padding; + XCVR_TX_DIG->DFT_PATTERN = com_config->tx_dft_pattern; + + XCVR_TX_DIG->RF_DFT_BIST_1 = com_config->rf_dft_bist_1; + XCVR_TX_DIG->RF_DFT_BIST_2 = com_config->rf_dft_bist_2; + + XCVR_TX_DIG->GFSK_CTRL = mode_config->tx_gfsk_ctrl; + + /* Force receiver warmup */ + bit_set32(&XCVR_TSM->CTRL, XCVR_TSM_CTRL_FORCE_RX_EN_SHIFT); + /* Wait for TSM to reach the end of warmup (unless you want to capture some samples during DCOC cal phase) */ + uint32_t end_of_rx_wu = XCVR_CTRL_XCVR_STATUS_TSM_COUNT( + (XCVR_TSM->END_OF_SEQ & XCVR_TSM_END_OF_SEQ_END_OF_RX_WU_MASK) >> + XCVR_TSM_END_OF_SEQ_END_OF_RX_WU_SHIFT); + while ((XCVR_MISC->XCVR_STATUS & XCVR_CTRL_XCVR_STATUS_TSM_COUNT_MASK) != end_of_rx_wu) {}; + + int res = kw41zrf_rx_bba_dcoc_dac_trim_DCest(); + if (res < 0) { + config_status = res; + } + //~ DCOC_DAC_INIT_Cal(0); + kw41zrf_dcoc_dac_init_cal(); + /* Force receiver warmdown */ + bit_clear32(&XCVR_TSM->CTRL, XCVR_TSM_CTRL_FORCE_RX_EN_SHIFT); + + return config_status; +} + +int kw41zrf_xcvr_init(kw41zrf_t *dev) +{ + (void) dev; + uint8_t radio_id = ((RSIM->MISC & RSIM_MISC_RADIO_VERSION_MASK) >> RSIM_MISC_RADIO_VERSION_SHIFT); + switch (radio_id) { + case 0x3: /* KW41/31/21 v1 */ + case 0xb: /* KW41/31/21 v1.1 */ + break; + default: + return -ENODEV; + } + + RSIM->RF_OSC_CTRL = (RSIM->RF_OSC_CTRL & + ~(RSIM_RF_OSC_CTRL_RADIO_EXT_OSC_OVRD_MASK)) | /* Set EXT_OSC_OVRD value to zero */ + RSIM_RF_OSC_CTRL_RADIO_EXT_OSC_OVRD_EN_MASK; /* Enable over-ride with zero value */ + bit_set32(&SIM->SCGC5, SIM_SCGC5_PHYDIG_SHIFT); /* Enable PHY clock gate */ + + /* Load IFR trim values */ + IFR_SW_TRIM_TBL_ENTRY_T sw_trim_tbl[] = + { + {TRIM_STATUS, 0, 0}, /*< Fetch the trim status word if available.*/ + {TRIM_VERSION, 0, 0} /*< Fetch the trim version number if available.*/ + }; + handle_ifr(&sw_trim_tbl[0], ARRAY_SIZE(sw_trim_tbl)); + DEBUG("[kw41zrf] sw_trim_tbl:\n"); + + for (unsigned k = 0; k < ARRAY_SIZE(sw_trim_tbl); ++k) { + DEBUG("[kw41zrf] [%u] id=0x%04x ", k, (unsigned)sw_trim_tbl[k].trim_id); + if (sw_trim_tbl[k].trim_id == TRIM_STATUS) { + DEBUG("(TRIM_STATUS) "); + } + else if (sw_trim_tbl[k].trim_id == TRIM_VERSION) { + DEBUG("(TRIM_VERSION) "); + } + DEBUG("value=%" PRIu32 ", valid=%u\n", sw_trim_tbl[k].trim_value, + (unsigned)sw_trim_tbl[k].valid); + } + + /* We only use 802.15.4 mode in this driver */ + xcvrStatus_t status = kw41zrf_xcvr_configure(dev, &xcvr_common_config, + &zgbe_mode_config, &xcvr_ZIGBEE_500kbps_config, &xcvr_802_15_4_500kbps_config); + + if (status != gXcvrSuccess_c) { + return -EIO; + } + return 0; +} diff --git a/drivers/kw41zrf/vendor/OSAbstraction/Interface/fsl_os_abstraction.h b/drivers/kw41zrf/vendor/OSAbstraction/Interface/fsl_os_abstraction.h new file mode 100644 index 000000000000..defb6fa76276 --- /dev/null +++ b/drivers/kw41zrf/vendor/OSAbstraction/Interface/fsl_os_abstraction.h @@ -0,0 +1,616 @@ +/*! +* The Clear BSD License +* Copyright (c) 2015, Freescale Semiconductor, Inc. +* Copyright 2016-2017 NXP +* All rights reserved. +* +* \file +* +* Redistribution and use in source and binary forms, with or without +* modification, are permitted (subject to the limitations in the +* disclaimer below) provided that the following conditions are met: +* +* * Redistributions of source code must retain the above copyright +* notice, this list of conditions and the following disclaimer. +* +* * Redistributions in binary form must reproduce the above copyright +* notice, this list of conditions and the following disclaimer in the +* documentation and/or other materials provided with the distribution. +* +* * Neither the name of the copyright holder nor the names of its +* contributors may be used to endorse or promote products derived from +* this software without specific prior written permission. +* +* NO EXPRESS OR IMPLIED LICENSES TO ANY PARTY'S PATENT RIGHTS ARE +* GRANTED BY THIS LICENSE. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT +* HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED +* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE +* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR +* BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE +* OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN +* IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + + +#ifndef _FSL_OS_ABSTRACTION_H_ +#define _FSL_OS_ABSTRACTION_H_ + +#include +#include +#include +#include "fsl_os_abstraction_config.h" + +#ifdef __cplusplus +extern "C" +{ +#endif + + +/*! ********************************************************************************* +************************************************************************************* +* Public type definitions +************************************************************************************* +********************************************************************************** */ +/*! @brief Type for the Task Priority*/ + typedef uint16_t osaTaskPriority_t; +/*! @brief Type for the timer definition*/ + typedef enum { + osaTimer_Once = 0, /*!< one-shot timer*/ + osaTimer_Periodic = 1 /*!< repeating timer*/ + } osaTimer_t; + /*! @brief Type for a task handler, returned by the OSA_TaskCreate function. */ + typedef void* osaTaskId_t; +/*! @brief Type for the parameter to be passed to the task at its creation */ + typedef void* osaTaskParam_t; + /*! @brief Type for task pointer. Task prototype declaration */ + typedef void (*osaTaskPtr_t) (osaTaskParam_t task_param); +/*! @brief Type for the semaphore handler, returned by the OSA_SemaphoreCreate function. */ + typedef void* osaSemaphoreId_t; +/*! @brief Type for the mutex handler, returned by the OSA_MutexCreate function. */ + typedef void* osaMutexId_t; +/*! @brief Type for the event handler, returned by the OSA_EventCreate function. */ + typedef void* osaEventId_t; +/*! @brief Type for an event flags group, bit 32 is reserved. */ + typedef uint32_t osaEventFlags_t; +/*! @brief Message definition. */ + typedef void* osaMsg_t; +/*! @brief Type for the message queue handler, returned by the OSA_MsgQCreate function. */ + typedef void* osaMsgQId_t; + /*! @brief Type for the Timer handler, returned by the OSA_TimerCreate function. */ + typedef void *osaTimerId_t; +/*! @brief Type for the Timer callback function pointer. */ + typedef void (*osaTimerFctPtr_t) (void const *argument); +/*! @brief Thread Definition structure contains startup information of a thread.*/ +typedef struct osaThreadDef_tag { + osaTaskPtr_t pthread; /*!< start address of thread function*/ + uint32_t tpriority; /*!< initial thread priority*/ + uint32_t instances; /*!< maximum number of instances of that thread function*/ + uint32_t stacksize; /*!< stack size requirements in bytes; 0 is default stack size*/ + uint32_t *tstack; + void *tlink; + uint8_t *tname; + bool useFloat; +} osaThreadDef_t; +/*! @brief Thread Link Definition structure .*/ +typedef struct osaThreadLink_tag{ + uint8_t link[12]; + osaTaskId_t osThreadId; + osaThreadDef_t *osThreadDefHandle; + uint32_t *osThreadStackHandle; +}osaThreadLink_t, *osaThreadLinkHandle_t; + +/*! @Timer Definition structure contains timer parameters.*/ +typedef struct osaTimerDef_tag { + osaTimerFctPtr_t pfCallback; /* < start address of a timer function */ + void *argument; +} osaTimerDef_t; +/*! @brief Defines the return status of OSA's functions */ +typedef enum osaStatus_tag +{ + osaStatus_Success = 0U, /*!< Success */ + osaStatus_Error = 1U, /*!< Failed */ + osaStatus_Timeout = 2U, /*!< Timeout occurs while waiting */ + osaStatus_Idle = 3U /*!< Used for bare metal only, the wait object is not ready + and timeout still not occur */ +}osaStatus_t; + + +/*! ********************************************************************************* +************************************************************************************* +* Public macros +************************************************************************************* +********************************************************************************** */ +#if defined (FSL_RTOS_MQX) + #define USE_RTOS 1 +#elif defined (FSL_RTOS_FREE_RTOS) + #define USE_RTOS 1 +#elif defined (FSL_RTOS_UCOSII) + #define USE_RTOS 1 +#elif defined (FSL_RTOS_UCOSIII) + #define USE_RTOS 1 +#else + #define USE_RTOS 0 +#endif + +#define OSA_PRIORITY_IDLE (6) +#define OSA_PRIORITY_LOW (5) +#define OSA_PRIORITY_BELOW_NORMAL (4) +#define OSA_PRIORITY_NORMAL (3) +#define OSA_PRIORITY_ABOVE_NORMAL (2) +#define OSA_PRIORITY_HIGH (1) +#define OSA_PRIORITY_REAL_TIME (0) +#define OSA_TASK_PRIORITY_MAX (0) +#define OSA_TASK_PRIORITY_MIN (15) +#define SIZE_IN_UINT32_UNITS(size) (((size) + sizeof(uint32_t) - 1) / sizeof(uint32_t)) + +/*! @brief Constant to pass as timeout value in order to wait indefinitely. */ +#define osaWaitForever_c ((uint32_t)(-1)) +#define osaEventFlagsAll_c ((osaEventFlags_t)(0x00FFFFFF)) +#define osThreadStackArray(name) osThread_##name##_stack +#define osThreadStackDef(name, stacksize, instances) \ + uint32_t osThreadStackArray(name)[SIZE_IN_UINT32_UNITS(stacksize)*(instances)]; + +/* ==== Thread Management ==== */ + +/* Create a Thread Definition with function, priority, and stack requirements. + * \param name name of the thread function. + * \param priority initial priority of the thread function. + * \param instances number of possible thread instances. + * \param stackSz stack size (in bytes) requirements for the thread function. + * \param useFloat + */ +#if defined(FSL_RTOS_MQX) +#define OSA_TASK_DEFINE(name, priority, instances, stackSz, useFloat) \ +osaThreadLink_t osThreadLink_##name[instances] = {0}; \ +osThreadStackDef(name, stackSz, instances) \ +osaThreadDef_t os_thread_def_##name = { (name), \ + (priority), \ + (instances), \ + (stackSz), \ + osThreadStackArray(name), \ + osThreadLink_##name, \ + (uint8_t*) #name,\ + (useFloat)} +#elif defined (FSL_RTOS_UCOSII) + #if gTaskMultipleInstancesManagement_c +#define OSA_TASK_DEFINE(name, priority, instances, stackSz, useFloat) \ +osaThreadLink_t osThreadLink_##name[instances] = {0}; \ +osThreadStackDef(name, stackSz, instances) \ +osaThreadDef_t os_thread_def_##name = { (name), \ + (priority), \ + (instances), \ + (stackSz), \ + osThreadStackArray(name), \ + osThreadLink_##name, \ + (uint8_t*) #name,\ + (useFloat)} +#else +#define OSA_TASK_DEFINE(name, priority, instances, stackSz, useFloat) \ +osThreadStackDef(name, stackSz, instances) \ +osaThreadDef_t os_thread_def_##name = { (name), \ + (priority), \ + (instances), \ + (stackSz), \ + osThreadStackArray(name), \ + NULL, \ + (uint8_t*) #name,\ + (useFloat)} +#endif +#else +#define OSA_TASK_DEFINE(name, priority, instances, stackSz, useFloat) \ +osaThreadDef_t os_thread_def_##name = { (name), \ + (priority), \ + (instances), \ + (stackSz), \ + NULL, \ + NULL, \ + (uint8_t*) #name,\ + (useFloat)} +#endif +/* Access a Thread defintion. + * \param name name of the thread definition object. + */ +#define OSA_TASK(name) \ +&os_thread_def_##name + +#define OSA_TASK_PROTO(name) \ +extern osaThreadDef_t os_thread_def_##name +/* ==== Timer Management ==== + * Define a Timer object. + * \param name name of the timer object. + * \param function name of the timer call back function. + */ + +#define OSA_TIMER_DEF(name, function) \ +osaTimerDef_t os_timer_def_##name = \ +{ (function), NULL } + +/* Access a Timer definition. + * \param name name of the timer object. + */ +#define OSA_TIMER(name) \ +&os_timer_def_##name + + +/***************************************************************************** +****************************************************************************** +* Public memory declarations +****************************************************************************** +*****************************************************************************/ +extern const uint8_t gUseRtos_c; + + +/*! ********************************************************************************* +************************************************************************************* +* Public functions +************************************************************************************* +********************************************************************************** */ +/*! + * @name Task management + * @{ + */ + +/*! + * @brief Creates a task. + * + * This function is used to create task based on the resources defined + * by the macro OSA_TASK_DEFINE. + * + * @param thread_def pointer to the osaThreadDef_t structure which defines the task. + * @param task_param Pointer to be passed to the task when it is created. + * + * @retval taskId The task is successfully created. + * @retval NULL The task can not be created.. + * + * Example: + @code + osaTaskId_t taskId; + OSA_TASK_DEFINE( Job1, OSA_PRIORITY_HIGH, 1, 800, 0);; + taskId = OSA__TaskCreate(OSA__TASK(Job1), (osaTaskParam_t)NULL); + @endcode + */ +osaTaskId_t OSA_TaskCreate(osaThreadDef_t *thread_def, osaTaskParam_t task_param); + +/*! + * @brief Gets the handler of active task. + * + * @return Handler to current active task. + */ +osaTaskId_t OSA_TaskGetId(void); + +/*! + * @brief Puts the active task to the end of scheduler's queue. + * + * When a task calls this function, it gives up the CPU and puts itself to the + * end of a task ready list. + * + * @retval osaStatus_Success The function is called successfully. + * @retval osaStatus_Error Error occurs with this function. + */ +osaStatus_t OSA_TaskYield(void); + +/*! + * @brief Gets the priority of a task. + * + * @param taskId The handler of the task whose priority is received. + * + * @return Task's priority. + */ +osaTaskPriority_t OSA_TaskGetPriority(osaTaskId_t taskId); + +/*! + * @brief Sets the priority of a task. + * + * @param taskId The handler of the task whose priority is set. + * @param taskPriority The priority to set. + * + * @retval osaStatus_Success Task's priority is set successfully. + * @retval osaStatus_Error Task's priority can not be set. + */ +osaStatus_t OSA_TaskSetPriority(osaTaskId_t taskId, osaTaskPriority_t taskPriority); +/*! + * @brief Destroys a previously created task. + * + * @param taskId The handler of the task to destroy. Returned by the OSA_TaskCreate function. + * + * @retval osaStatus_Success The task was successfully destroyed. + * @retval osaStatus_Error Task destruction failed or invalid parameter. + */ +osaStatus_t OSA_TaskDestroy(osaTaskId_t taskId); + +/*! + * @brief Creates a semaphore with a given value. + * + * This function creates a semaphore and sets the value to the parameter + * initValue. + * + * @param initValue Initial value the semaphore will be set to. + * + * @retval handler to the new semaphore if the semaphore is created successfully. + * @retval NULL if the semaphore can not be created. + * + * + */ +osaSemaphoreId_t OSA_SemaphoreCreate(uint32_t initValue); + +/*! + * @brief Destroys a previously created semaphore. + * + * @param semId Pointer to the semaphore to destroy. + * + * @retval osaStatus_Success The semaphore is successfully destroyed. + * @retval osaStatus_Error The semaphore can not be destroyed. + */ +osaStatus_t OSA_SemaphoreDestroy(osaSemaphoreId_t semId); + +/*! + * @brief Pending a semaphore with timeout. + * + * This function checks the semaphore's counting value. If it is positive, + * decreases it and returns osaStatus_Success. Otherwise, a timeout is used + * to wait. + * + * @param semId Pointer to the semaphore. + * @param millisec The maximum number of milliseconds to wait if semaphore is not + * positive. Pass osaWaitForever_c to wait indefinitely, pass 0 + * will return osaStatus_Timeout immediately. + * + * @retval osaStatus_Success The semaphore is received. + * @retval osaStatus_Timeout The semaphore is not received within the specified 'timeout'. + * @retval osaStatus_Error An incorrect parameter was passed. + */ +osaStatus_t OSA_SemaphoreWait(osaSemaphoreId_t semId, uint32_t millisec); + +/*! + * @brief Signals for someone waiting on the semaphore to wake up. + * + * Wakes up one task that is waiting on the semaphore. If no task is waiting, increases + * the semaphore's counting value. + * + * @param semId Pointer to the semaphore to signal. + * + * @retval osaStatus_Success The semaphore is successfully signaled. + * @retval osaStatus_Error The object can not be signaled or invalid parameter. + * + */ +osaStatus_t OSA_SemaphorePost(osaSemaphoreId_t semId); + +/*! + * @brief Create an unlocked mutex. + * + * This function creates a non-recursive mutex and sets it to unlocked status. + * + * @param none. + * + * @retval handler to the new mutex if the mutex is created successfully. + * @retval NULL if the mutex can not be created. + */ +osaMutexId_t OSA_MutexCreate(void); + +/*! + * @brief Waits for a mutex and locks it. + * + * This function checks the mutex's status. If it is unlocked, locks it and returns the + * osaStatus_Success. Otherwise, waits for a timeout in milliseconds to lock. + * + * @param mutexId Pointer to the Mutex. + * @param millisec The maximum number of milliseconds to wait for the mutex. + * If the mutex is locked, Pass the value osaWaitForever_c will + * wait indefinitely, pass 0 will return osaStatus_Timeout + * immediately. + * + * @retval osaStatus_Success The mutex is locked successfully. + * @retval osaStatus_Timeout Timeout occurred. + * @retval osaStatus_Error Incorrect parameter was passed. + * + * @note This is non-recursive mutex, a task can not try to lock the mutex it has locked. + */ +osaStatus_t OSA_MutexLock(osaMutexId_t mutexId, uint32_t millisec); + +/*! + * @brief Unlocks a previously locked mutex. + * + * @param mutexId Pointer to the Mutex. + * + * @retval osaStatus_Success The mutex is successfully unlocked. + * @retval osaStatus_Error The mutex can not be unlocked or invalid parameter. + */ +osaStatus_t OSA_MutexUnlock(osaMutexId_t mutexId); + +/*! + * @brief Destroys a previously created mutex. + * + * @param mutexId Pointer to the Mutex. + * + * @retval osaStatus_Success The mutex is successfully destroyed. + * @retval osaStatus_Error The mutex can not be destroyed. + * + */ +osaStatus_t OSA_MutexDestroy(osaMutexId_t mutexId); + +/*! + * @brief Initializes an event object with all flags cleared. + * + * This function creates an event object and set its clear mode. If autoClear + * is TRUE, when a task gets the event flags, these flags will be + * cleared automatically. Otherwise these flags must + * be cleared manually. + * + * @param autoClear TRUE The event is auto-clear. + * FALSE The event manual-clear + * @retval handler to the new event if the event is created successfully. + * @retval NULL if the event can not be created. + */ +osaEventId_t OSA_EventCreate(bool autoClear); + +/*! + * @brief Sets one or more event flags. + * + * Sets specified flags of an event object. + * + * @param eventId Pointer to the event. + * @param flagsToSet Flags to be set. + * + * @retval osaStatus_Success The flags were successfully set. + * @retval osaStatus_Error An incorrect parameter was passed. + */ +osaStatus_t OSA_EventSet(osaEventId_t eventId, osaEventFlags_t flagsToSet); + +/*! + * @brief Clears one or more flags. + * + * Clears specified flags of an event object. + * + * @param eventId Pointer to the event. + * @param flagsToClear Flags to be clear. + * + * @retval osaStatus_Success The flags were successfully cleared. + * @retval osaStatus_Error An incorrect parameter was passed. + */ +osaStatus_t OSA_EventClear(osaEventId_t eventId, osaEventFlags_t flagsToClear); + +/*! + * @brief Waits for specified event flags to be set. + * + * This function waits for a combination of flags to be set in an event object. + * Applications can wait for any/all bits to be set. Also this function could + * obtain the flags who wakeup the waiting task. + * + * @param eventId Pointer to the event. + * @param flagsToWait Flags that to wait. + * @param waitAll Wait all flags or any flag to be set. + * @param millisec The maximum number of milliseconds to wait for the event. + * If the wait condition is not met, pass osaWaitForever_c will + * wait indefinitely, pass 0 will return osaStatus_Timeout + * immediately. + * @param setFlags Flags that wakeup the waiting task are obtained by this parameter. + * + * @retval osaStatus_Success The wait condition met and function returns successfully. + * @retval osaStatus_Timeout Has not met wait condition within timeout. + * @retval osaStatus_Error An incorrect parameter was passed. + + * + * @note Please pay attention to the flags bit width, FreeRTOS uses the most + * significant 8 bis as control bits, so do not wait these bits while using + * FreeRTOS. + * + */ +osaStatus_t OSA_EventWait(osaEventId_t eventId, osaEventFlags_t flagsToWait, bool waitAll, uint32_t millisec, osaEventFlags_t *pSetFlags); + +/*! + * @brief Destroys a previously created event object. + * + * @param eventId Pointer to the event. + * + * @retval osaStatus_Success The event is successfully destroyed. + * @retval osaStatus_Error Event destruction failed. + */ +osaStatus_t OSA_EventDestroy(osaEventId_t eventId); + +/*! + * @brief Initializes a message queue. + * + * This function allocates memory for and initializes a message queue. Message queue elements are hardcoded as void*. + * + * @param msgNo :number of messages the message queue should accommodate. + * This parameter should not exceed osNumberOfMessages defined in OSAbstractionConfig.h. + * +* @return: Handler to access the queue for put and get operations. If message queue + * creation failed, return NULL. + */ +osaMsgQId_t OSA_MsgQCreate(uint32_t msgNo); + +/*! + * @brief Puts a message at the end of the queue. + * + * This function puts a message to the end of the message queue. If the queue + * is full, this function returns the osaStatus_Error; + * + * @param msgQId pointer to queue returned by the OSA_MsgQCreate function. + * @param pMessage Pointer to the message to be put into the queue. + * + * @retval osaStatus_Success Message successfully put into the queue. + * @retval osaStatus_Error The queue was full or an invalid parameter was passed. + */ +osaStatus_t OSA_MsgQPut(osaMsgQId_t msgQId, osaMsg_t pMessage); + +/*! + * @brief Reads and remove a message at the head of the queue. + * + * This function gets a message from the head of the message queue. If the + * queue is empty, timeout is used to wait. + * + * @param msgQId Queue handler returned by the OSA_MsgQCreate function. + * @param pMessage Pointer to a memory to save the message. + * @param millisec The number of milliseconds to wait for a message. If the + * queue is empty, pass osaWaitForever_c will wait indefinitely, + * pass 0 will return osaStatus_Timeout immediately. + * + * @retval osaStatus_Success Message successfully obtained from the queue. + * @retval osaStatus_Timeout The queue remains empty after timeout. + * @retval osaStatus_Error Invalid parameter. + */ +osaStatus_t OSA_MsgQGet(osaMsgQId_t msgQId, osaMsg_t pMessage, uint32_t millisec); + +/*! + * @brief Destroys a previously created queue. + * + * @param msgQId queue handler returned by the OSA_MsgQCreate function. + * + * @retval osaStatus_Success The queue was successfully destroyed. + * @retval osaStatus_Error Message queue destruction failed. +*/ +osaStatus_t OSA_MsgQDestroy(osaMsgQId_t msgQId); + +/*! + * @brief Enable all interrupts. +*/ +void OSA_InterruptEnable(void); + +/*! + * @brief Disable all interrupts. +*/ +void OSA_InterruptDisable(void); + +/*! + * @brief Enable all interrupts using PRIMASK. +*/ +void OSA_EnableIRQGlobal(void); + +/*! + * @brief Disable all interrupts using PRIMASK. +*/ +void OSA_DisableIRQGlobal(void); + +/*! + * @brief Delays execution for a number of milliseconds. + * + * @param millisec The time in milliseconds to wait. + */ +void OSA_TimeDelay(uint32_t millisec); + +/*! + * @brief This function gets current time in milliseconds. + * + * @retval current time in milliseconds + */ +uint32_t OSA_TimeGetMsec(void); + +/*! + * @brief Installs the interrupt handler. + * + * @param IRQNumber IRQ number of the interrupt. + * @param handler The interrupt handler to install. + */ +void OSA_InstallIntHandler(uint32_t IRQNumber, void (*handler)(void)); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/drivers/kw41zrf/vendor/OSAbstraction/Interface/fsl_os_abstraction_config.h b/drivers/kw41zrf/vendor/OSAbstraction/Interface/fsl_os_abstraction_config.h new file mode 100644 index 000000000000..41fed342127e --- /dev/null +++ b/drivers/kw41zrf/vendor/OSAbstraction/Interface/fsl_os_abstraction_config.h @@ -0,0 +1,78 @@ +/*! +* The Clear BSD License +* Copyright (c) 2015, Freescale Semiconductor, Inc. +* Copyright 2016-2017 NXP +* All rights reserved. +* +* \file +* +* Redistribution and use in source and binary forms, with or without +* modification, are permitted (subject to the limitations in the +* disclaimer below) provided that the following conditions are met: +* +* * Redistributions of source code must retain the above copyright +* notice, this list of conditions and the following disclaimer. +* +* * Redistributions in binary form must reproduce the above copyright +* notice, this list of conditions and the following disclaimer in the +* documentation and/or other materials provided with the distribution. +* +* * Neither the name of the copyright holder nor the names of its +* contributors may be used to endorse or promote products derived from +* this software without specific prior written permission. +* +* NO EXPRESS OR IMPLIED LICENSES TO ANY PARTY'S PATENT RIGHTS ARE +* GRANTED BY THIS LICENSE. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT +* HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED +* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE +* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR +* BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE +* OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN +* IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + + +#ifndef _FSL_OS_ABSTRACTION_CONFIG_H_ +#define _FSL_OS_ABSTRACTION_CONFIG_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +#ifndef osNumberOfSemaphores +#define osNumberOfSemaphores 5 +#endif +#ifndef osNumberOfMutexes +#define osNumberOfMutexes 5 +#endif +#ifndef osNumberOfMessageQs +#define osNumberOfMessageQs 0 +#endif +#ifndef osNumberOfMessages +#define osNumberOfMessages 10 +#endif +#ifndef osNumberOfEvents +#define osNumberOfEvents 5 +#endif + +#ifndef gMainThreadStackSize_c +#define gMainThreadStackSize_c 1024 +#endif +#ifndef gMainThreadPriority_c +#define gMainThreadPriority_c 7 +#endif + +#ifndef gTaskMultipleInstancesManagement_c +#define gTaskMultipleInstancesManagement_c 0 +#endif + +#ifdef __cplusplus +} +#endif + +#endif /* _FSL_OS_ABSTRACTION_CONFIG_H_ */ diff --git a/drivers/kw41zrf/vendor/XCVR/MKW41Z4/Makefile b/drivers/kw41zrf/vendor/XCVR/MKW41Z4/Makefile new file mode 100644 index 000000000000..3c647adb54dc --- /dev/null +++ b/drivers/kw41zrf/vendor/XCVR/MKW41Z4/Makefile @@ -0,0 +1,8 @@ +MODULE = mcux_xcvr_mkw41z + +# This vendor code expect all the vendor headers to also be in the include path +# These include paths are only added when building this particular directory and +# should not be available to the rest of the system. +INCLUDES += -I../../OSAbstraction/Interface + +include $(RIOTBASE)/Makefile.base diff --git a/drivers/kw41zrf/vendor/XCVR/MKW41Z4/fsl_os_abstraction_riot.c b/drivers/kw41zrf/vendor/XCVR/MKW41Z4/fsl_os_abstraction_riot.c new file mode 100644 index 000000000000..c0f86fc9b67d --- /dev/null +++ b/drivers/kw41zrf/vendor/XCVR/MKW41Z4/fsl_os_abstraction_riot.c @@ -0,0 +1,27 @@ +#include "irq.h" + +#include "fsl_os_abstraction.h" + +static unsigned int irq_mask; + +/*FUNCTION********************************************************************** + * + * Function Name : OSA_InterruptEnable + * Description : self explanatory. + * + *END**************************************************************************/ +void OSA_InterruptEnable(void) +{ + irq_restore(irq_mask); +} + +/*FUNCTION********************************************************************** + * + * Function Name : OSA_InterruptDisable + * Description : self explanatory. + * + *END**************************************************************************/ +void OSA_InterruptDisable(void) +{ + irq_mask = irq_disable(); +} diff --git a/drivers/kw41zrf/vendor/XCVR/MKW41Z4/fsl_xcvr.h b/drivers/kw41zrf/vendor/XCVR/MKW41Z4/fsl_xcvr.h new file mode 100644 index 000000000000..02e3c96b4fd0 --- /dev/null +++ b/drivers/kw41zrf/vendor/XCVR/MKW41Z4/fsl_xcvr.h @@ -0,0 +1,833 @@ +/* + * The Clear BSD License + * Copyright (c) 2015, Freescale Semiconductor, Inc. + * Copyright 2016-2017 NXP + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted (subject to the limitations in the + * disclaimer below) provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * * Neither the name of the copyright holder nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * NO EXPRESS OR IMPLIED LICENSES TO ANY PARTY'S PATENT RIGHTS ARE + * GRANTED BY THIS LICENSE. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT + * HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN + * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +#ifndef _FSL_XCVR_H_ +/* clang-format off */ +#define _FSL_XCVR_H_ +/* clang-format on */ + +#include "cpu.h" + +/*! + * @addtogroup xcvr + * @{ + */ + +/*! @file*/ + +/******************************************************************************* + * Definitions + ******************************************************************************/ +/* KW4xZ/KW3xZ/KW2xZ Radio type */ +#define RADIO_IS_GEN_2P0 (1) + +#if (CLOCK_RADIOXTAL == 26000000ul) +#define RF_OSC_26MHZ 1 +#endif + +#define TBD_ZERO (0) +#define FSL_XCVR_DRIVER_VERSION (MAKE_VERSION(0, 1, 0)) + +#define B0(x) (((uint32_t)(((uint32_t)(x)) << 0)) & 0xFFU) +#define B1(x) (((uint32_t)(((uint32_t)(x)) << 8)) & 0xFF00U) +#define B2(x) (((uint32_t)(((uint32_t)(x)) << 16)) & 0xFF0000U) +#define B3(x) (((uint32_t)(((uint32_t)(x)) << 24)) & 0xFF000000U) + +#define USE_DEFAULT_PRE_REF (0) +#define TRIM_BBA_DCOC_DAC_AT_INIT (1) +#define PRESLOW_ENA (1) + +/* GEN3 TSM defines */ +#if RADIO_IS_GEN_3P0 + +/* TSM timings initializations for Gen3 radio */ +/* NOTE: These timings are stored in 32MHz or 26MHz "baseline" settings, selected by conditional compile below */ +/* The init structures for 32Mhz and 26MHz are made identical to allow the same code in fsl_xcvr.c to apply the */ +/* settings for all radio generations. The Gen2 radio init value storage had a different structure so this preserves compatibility */ +#if RF_OSC_26MHZ == 1 +#define TSM_TIMING00init (0x6d006f00U) /* (bb_ldo_hf_en) */ +#define TSM_TIMING01init (0x6d006f00U) /* (bb_ldo_adcdac_en) */ +#define TSM_TIMING02init (0x6d00ffffU) /* (bb_ldo_bba_en) */ +#define TSM_TIMING03init (0x6d006f00U) /* (bb_ldo_pd_en) */ +#define TSM_TIMING04init (0x6d006f00U) /* (bb_ldo_fdbk_en) */ +#define TSM_TIMING05init (0x6d006f00U) /* (bb_ldo_vcolo_en) */ +#define TSM_TIMING06init (0x6d006f00U) /* (bb_ldo_vtref_en) */ +#define TSM_TIMING07init (0x05000500U) /* (bb_ldo_fdbk_bleed_en) */ +#define TSM_TIMING08init (0x03000300U) /* (bb_ldo_vcolo_bleed_en) */ +#define TSM_TIMING09init (0x03000300U) /* (bb_ldo_vcolo_fastcharge_en) */ +#define TSM_TIMING10init (0x6d036f03U) /* (bb_xtal_pll_ref_clk_en) */ +#define TSM_TIMING11init (0xffff6f03U) /* (bb_xtal_dac_ref_clk_en) */ +#define TSM_TIMING12init (0x6d03ffffU) /* (rxtx_auxpll_vco_ref_clk_en) */ +#define TSM_TIMING13init (0x18004c00U) /* (sy_vco_autotune_en) */ +#define TSM_TIMING14init (0x6d356863U) /* (sy_pd_cycle_slip_ld_ft_en) */ +#define TSM_TIMING15init (0x6d036f03U) /* (sy_vco_en) */ +#define TSM_TIMING16init (0x6d20ffffU) /* (sy_lo_rx_buf_en) */ +#define TSM_TIMING17init (0xffff6f58U) /* (sy_lo_tx_buf_en) */ +#define TSM_TIMING18init (0x6d056f05U) /* (sy_divn_en) */ +#define TSM_TIMING19init (0x18034c03U) /* (sy_pd_filter_charge_en) */ +#define TSM_TIMING20init (0x6d036f03U) /* (sy_pd_en) */ +#define TSM_TIMING21init (0x6d046f04U) /* (sy_lo_divn_en) */ +#define TSM_TIMING22init (0x6d04ffffU) /* (sy_lo_rx_en) */ +#define TSM_TIMING23init (0xffff6f04U) /* (sy_lo_tx_en) */ +#define TSM_TIMING24init (0x18004c00U) /* (sy_divn_cal_en) */ +#define TSM_TIMING25init (0x6d21ffffU) /* (rx_lna_mixer_en) */ +#define TSM_TIMING26init (0xffff6e58U) /* (tx_pa_en) */ +#define TSM_TIMING27init (0x6d24ffffU) /* (rx_adc_i_q_en) */ +#define TSM_TIMING28init (0x2524ffffU) /* (rx_adc_reset_en) */ +#define TSM_TIMING29init (0x6d22ffffU) /* (rx_bba_i_q_en) */ +#define TSM_TIMING30init (0x6d24ffffU) /* (rx_bba_pdet_en) */ +#define TSM_TIMING31init (0x6d23ffffU) /* (rx_bba_tza_dcoc_en) */ +#define TSM_TIMING32init (0x6d21ffffU) /* (rx_tza_i_q_en) */ +#define TSM_TIMING33init (0x6d24ffffU) /* (rx_tza_pdet_en) */ +#define TSM_TIMING34init (0x6d076f07U) /* (pll_dig_en) */ +#define TSM_TIMING35init (0xffff6f5fU) /* (tx_dig_en) */ +#define TSM_TIMING36init (0x6d6affffU) /* (rx_dig_en) */ +#define TSM_TIMING37init (0x6b6affffU) /* (rx_init) */ +#define TSM_TIMING38init (0x6d0e6f42U) /* (sigma_delta_en) */ +#define TSM_TIMING39init (0x6d6affffU) /* (rx_phy_en) */ +#define TSM_TIMING40init (0x6d2affffU) /* (dcoc_en) */ +#define TSM_TIMING41init (0x2b2affffU) /* (dcoc_init) */ +#define TSM_TIMING42init (0xffffffffU) /* (sar_adc_trig_en) */ +#define TSM_TIMING43init (0xffffffffU) /* (tsm_spare0_en) */ +#define TSM_TIMING44init (0xffffffffU) /* (tsm_spare1_en) */ +#define TSM_TIMING45init (0xffffffffU) /* (tsm_spare2_en) */ +#define TSM_TIMING46init (0xffffffffU) /* (tsm_spare3_en) */ +#define TSM_TIMING47init (0xffffffffU) /* (gpio0_trig_en) */ +#define TSM_TIMING48init (0xffffffffU) /* (gpio1_trig_en) */ +#define TSM_TIMING49init (0xffffffffU) /* (gpio2_trig_en) */ +#define TSM_TIMING50init (0xffffffffU) /* (gpio3_trig_en) */ +#define TSM_TIMING51init (0x6d03ffffU) /* (rxtx_auxpll_bias_en) */ +#define TSM_TIMING52init (0x1b06ffffU) /* (rxtx_auxpll_fcal_en) */ +#define TSM_TIMING53init (0x6d03ffffU) /* (rxtx_auxpll_lf_pd_en) */ +#define TSM_TIMING54init (0x1b03ffffU) /* (rxtx_auxpll_pd_lf_filter_charge_en) */ +#define TSM_TIMING55init (0x6d24ffffU) /* (rxtx_auxpll_adc_buf_en) */ +#define TSM_TIMING56init (0x6d24ffffU) /* (rxtx_auxpll_dig_buf_en) */ +#define TSM_TIMING57init (0x1a03ffffU) /* (rxtx_rccal_en) */ +#define TSM_TIMING58init (0xffff6f03U) /* (tx_hpm_dac_en) */ +#define END_OF_SEQinit (0x6d6c6f67U) /* */ +#define TX_RX_ON_DELinit (0x00008a86U) /* */ +#define TX_RX_SYNTH_init (0x00002318U) /* */ +#else +#define TSM_TIMING00init (0x69006f00U) /* (bb_ldo_hf_en) */ +#define TSM_TIMING01init (0x69006f00U) /* (bb_ldo_adcdac_en) */ +#define TSM_TIMING02init (0x6900ffffU) /* (bb_ldo_bba_en) */ +#define TSM_TIMING03init (0x69006f00U) /* (bb_ldo_pd_en) */ +#define TSM_TIMING04init (0x69006f00U) /* (bb_ldo_fdbk_en) */ +#define TSM_TIMING05init (0x69006f00U) /* (bb_ldo_vcolo_en) */ +#define TSM_TIMING06init (0x69006f00U) /* (bb_ldo_vtref_en) */ +#define TSM_TIMING07init (0x05000500U) /* (bb_ldo_fdbk_bleed_en) */ +#define TSM_TIMING08init (0x03000300U) /* (bb_ldo_vcolo_bleed_en) */ +#define TSM_TIMING09init (0x03000300U) /* (bb_ldo_vcolo_fastcharge_en) */ +#define TSM_TIMING10init (0x69036f03U) /* (bb_xtal_pll_ref_clk_en) */ +#define TSM_TIMING11init (0xffff6f03U) /* (bb_xtal_dac_ref_clk_en) */ +#define TSM_TIMING12init (0x6903ffffU) /* (rxtx_auxpll_vco_ref_clk_en) */ +#define TSM_TIMING13init (0x18004c00U) /* (sy_vco_autotune_en) */ +#define TSM_TIMING14init (0x69316863U) /* (sy_pd_cycle_slip_ld_ft_en) */ +#define TSM_TIMING15init (0x69036f03U) /* (sy_vco_en) */ +#define TSM_TIMING16init (0x691cffffU) /* (sy_lo_rx_buf_en) */ +#define TSM_TIMING17init (0xffff6f58U) /* (sy_lo_tx_buf_en) */ +#define TSM_TIMING18init (0x69056f05U) /* (sy_divn_en) */ +#define TSM_TIMING19init (0x18034c03U) /* (sy_pd_filter_charge_en) */ +#define TSM_TIMING20init (0x69036f03U) /* (sy_pd_en) */ +#define TSM_TIMING21init (0x69046f04U) /* (sy_lo_divn_en) */ +#define TSM_TIMING22init (0x6904ffffU) /* (sy_lo_rx_en) */ +#define TSM_TIMING23init (0xffff6f04U) /* (sy_lo_tx_en) */ +#define TSM_TIMING24init (0x18004c00U) /* (sy_divn_cal_en) */ +#define TSM_TIMING25init (0x691dffffU) /* (rx_lna_mixer_en) */ +#define TSM_TIMING26init (0xffff6e58U) /* (tx_pa_en) */ +#define TSM_TIMING27init (0x6920ffffU) /* (rx_adc_i_q_en) */ +#define TSM_TIMING28init (0x2120ffffU) /* (rx_adc_reset_en) */ +#define TSM_TIMING29init (0x691effffU) /* (rx_bba_i_q_en) */ +#define TSM_TIMING30init (0x6920ffffU) /* (rx_bba_pdet_en) */ +#define TSM_TIMING31init (0x691fffffU) /* (rx_bba_tza_dcoc_en) */ +#define TSM_TIMING32init (0x691dffffU) /* (rx_tza_i_q_en) */ +#define TSM_TIMING33init (0x6920ffffU) /* (rx_tza_pdet_en) */ +#define TSM_TIMING34init (0x69076f07U) /* (pll_dig_en) */ +#define TSM_TIMING35init (0xffff6f5fU) /* (tx_dig_en) */ +#define TSM_TIMING36init (0x6966ffffU) /* (rx_dig_en) */ +#define TSM_TIMING37init (0x6766ffffU) /* (rx_init) */ +#define TSM_TIMING38init (0x690e6f42U) /* (sigma_delta_en) */ +#define TSM_TIMING39init (0x6966ffffU) /* (rx_phy_en) */ +#define TSM_TIMING40init (0x6926ffffU) /* (dcoc_en) */ +#define TSM_TIMING41init (0x2726ffffU) /* (dcoc_init) */ +#define TSM_TIMING42init (0xffffffffU) /* (sar_adc_trig_en) */ +#define TSM_TIMING43init (0xffffffffU) /* (tsm_spare0_en) */ +#define TSM_TIMING44init (0xffffffffU) /* (tsm_spare1_en) */ +#define TSM_TIMING45init (0xffffffffU) /* (tsm_spare2_en) */ +#define TSM_TIMING46init (0xffffffffU) /* (tsm_spare3_en) */ +#define TSM_TIMING47init (0xffffffffU) /* (gpio0_trig_en) */ +#define TSM_TIMING48init (0xffffffffU) /* (gpio1_trig_en) */ +#define TSM_TIMING49init (0xffffffffU) /* (gpio2_trig_en) */ +#define TSM_TIMING50init (0xffffffffU) /* (gpio3_trig_en) */ +#define TSM_TIMING51init (0x6903ffffU) /* (rxtx_auxpll_bias_en) */ +#define TSM_TIMING52init (0x1706ffffU) /* (rxtx_auxpll_fcal_en) */ +#define TSM_TIMING53init (0x6903ffffU) /* (rxtx_auxpll_lf_pd_en) */ +#define TSM_TIMING54init (0x1703ffffU) /* (rxtx_auxpll_pd_lf_filter_charge_en) */ +#define TSM_TIMING55init (0x6920ffffU) /* (rxtx_auxpll_adc_buf_en) */ +#define TSM_TIMING56init (0x6920ffffU) /* (rxtx_auxpll_dig_buf_en) */ +#define TSM_TIMING57init (0x1a03ffffU) /* (rxtx_rccal_en) */ +#define TSM_TIMING58init (0xffff6f03U) /* (tx_hpm_dac_en) */ +#define END_OF_SEQinit (0x69686f67U) /* */ +#define TX_RX_ON_DELinit (0x00008a86U) /* */ +#define TX_RX_SYNTH_init (0x00002318U) /* */ +#endif /* RF_OSC_26MHZ == 1 */ + +#define AUX_PLL_DELAY (0) +/* TSM bitfield shift and value definitions */ +#define TX_DIG_EN_ASSERT (95) /* Assertion time for TX_DIG_EN, used in mode specific settings */ +#define ZGBE_TX_DIG_EN_ASSERT (TX_DIG_EN_ASSERT - 1) /* Zigbee TX_DIG_EN must assert 1 tick sooner, see adjustment below based on data padding */ +/* EDIT THIS LINE TO CONTROL PA_RAMP! */ +#define PA_RAMP_TIME (2) /* Only allowable values are [0, 1, 2, or 4] in Gen3 */ +#define PA_RAMP_SEL_0US (0) +#define PA_RAMP_SEL_1US (1) +#define PA_RAMP_SEL_2US (2) +#define PA_RAMP_SEL_4US (3) +#if !((PA_RAMP_TIME == 0) || (PA_RAMP_TIME == 1) || (PA_RAMP_TIME == 2) || (PA_RAMP_TIME == 4)) +#error "Invalid value for PA_RAMP_TIME macro" +#endif /* Error check of PA RAMP TIME */ + +#define ADD_FOR_26MHZ (4) +#define END_OF_TX_WU_NORAMP (103) /* NOTE: NORAMP and 2us ramp time behaviors are identical for TX WU and WD */ +#define END_OF_TX_WD_NORAMP (111) /* NOTE: NORAMP and 2us ramp time behaviors are identical for TX WU and WD */ +/* Redefine the values of END_OF_TX_WU and END_OF_TX_WD based on whether DATA PADDING is enabled and the selection of ramp time */ +/* These two constants are then used on both common configuration and mode specific configuration files to define the TSM timing values */ +#if ((PA_RAMP_TIME == 0) || (PA_RAMP_TIME == 1) || (PA_RAMP_TIME == 2)) + #define END_OF_TX_WU (END_OF_TX_WU_NORAMP) + #define END_OF_TX_WD (END_OF_TX_WD_NORAMP) + #if (PA_RAMP_TIME == 0) + #define PA_RAMP_SEL PA_RAMP_SEL_0US + #define DATA_PADDING_EN (0) + #else + #define DATA_PADDING_EN (1) + #if (PA_RAMP_TIME == 1) + #define PA_RAMP_SEL PA_RAMP_SEL_1US + #else + #define PA_RAMP_SEL PA_RAMP_SEL_2US + #endif /* (PA_RAMP_TIME == 1) */ + #endif /* (PA_RAMP_TIME == 0) */ +#else /* ((PA_RAMP_TIME == 0) || (PA_RAMP_TIME == 1) || (PA_RAMP_TIME == 2)) */ + #if (PA_RAMP_TIME == 4) + #define END_OF_TX_WU (END_OF_TX_WU_NORAMP + 2) + #define END_OF_TX_WD (END_OF_TX_WD_NORAMP + 4) + #define PA_RAMP_SEL PA_RAMP_SEL_4US + #define DATA_PADDING_EN (1) + #else /* (PA_RAMP_TIME == 4) */ + #error "Invalid value for PA_RAMP_TIME macro" + #endif /* (PA_RAMP_TIME == 4) */ +#endif/* (PA_RAMP_TIME == 4) */ + +#define END_OF_RX_WU (104 + AUX_PLL_DELAY) + +#if RF_OSC_26MHZ == 1 +#define END_OF_RX_WD (END_OF_RX_WU + 1 + ADD_FOR_26MHZ) /* Need to handle normal signals extending when 26MHZ warmdown is extended */ +#else +#define END_OF_RX_WD (END_OF_RX_WU + 1) +#endif /* RF_OSC_26MHZ == 1 */ + +#define END_OF_RX_WU_26MHZ (END_OF_RX_WU + ADD_FOR_26MHZ) +#define END_OF_RX_WD_26MHZ (END_OF_RX_WU + 1 + ADD_FOR_26MHZ) + +/* PA Bias Table - Gen3 version */ +#define PA_RAMP_0 0x1 +#define PA_RAMP_1 0x2 +#define PA_RAMP_2 0x4 +#define PA_RAMP_3 0x6 +#define PA_RAMP_4 0x8 +#define PA_RAMP_5 0xc +#define PA_RAMP_6 0x10 +#define PA_RAMP_7 0x14 +#define PA_RAMP_8 0x18 +#define PA_RAMP_9 0x1c +#define PA_RAMP_10 0x22 +#define PA_RAMP_11 0x28 +#define PA_RAMP_12 0x2c +#define PA_RAMP_13 0x30 +#define PA_RAMP_14 0x36 +#define PA_RAMP_15 0x3c + +#else /* Gen2 TSM definitions */ +/* GEN2 TSM defines */ +#define AUX_PLL_DELAY (0) +/* TSM bitfield shift and value definitions */ +#define TX_DIG_EN_ASSERT (95) /* Assertion time for TX_DIG_EN, used in mode specific settings */ +#define ZGBE_TX_DIG_EN_ASSERT (TX_DIG_EN_ASSERT - 1) /* Zigbee TX_DIG_EN must assert 1 tick sooner, see adjustment below based on data padding */ +/* EDIT THIS LINE TO CONTROL PA_RAMP! */ +#define PA_RAMP_TIME (2) /* Only allowable values are [0, 2, 4, or 8] for PA RAMP times in Gen2.0 */ +#define PA_RAMP_SEL_0US (0) +#define PA_RAMP_SEL_2US (1) +#define PA_RAMP_SEL_4US (2) +#define PA_RAMP_SEL_8US (3) + +#if !((PA_RAMP_TIME == 0) || (PA_RAMP_TIME == 2) || (PA_RAMP_TIME == 4) || (PA_RAMP_TIME == 8)) +#error "Invalid value for PA_RAMP_TIME macro" +#endif /* Error check of PA RAMP TIME */ +#define ADD_FOR_26MHZ (4) +#define END_OF_TX_WU_NORAMP (103) /* NOTE: NORAMP and 2us ramp time behaviors are identical for TX WU and WD */ +#define END_OF_TX_WD_NORAMP (111) /* NOTE: NORAMP and 2us ramp time behaviors are identical for TX WU and WD */ +/* Redefine the values of END_OF_TX_WU and END_OF_TX_WD based on whether DATA PADDING is enabled and the selection of ramp time */ +/* These two constants are then used on both common configuration and mode specific configuration files to define the TSM timing values */ +#if ((PA_RAMP_TIME == 0) || (PA_RAMP_TIME == 2)) + #define END_OF_TX_WU (END_OF_TX_WU_NORAMP) + #define END_OF_TX_WD (END_OF_TX_WD_NORAMP) + #define TX_SYNTH_DELAY_ADJ (0) + #define PD_CYCLE_SLIP_TX_HI_ADJ (0) + #define PD_CYCLE_SLIP_TX_LO_ADJ (1) + #define ZGBE_TX_DIG_EN_TX_HI_ADJ (-5) /* Only applies to Zigbee mode */ + #if (PA_RAMP_TIME == 0) + #define PA_RAMP_SEL PA_RAMP_SEL_0US + #define DATA_PADDING_EN (0) + #define TX_DIG_EN_TX_HI_ADJ (-2) + #else + #define DATA_PADDING_EN (1) + #define TX_DIG_EN_TX_HI_ADJ (0) + #define PA_RAMP_SEL PA_RAMP_SEL_2US + #endif /* (PA_RAMP_TIME == 0) */ +#else /* ((PA_RAMP_TIME == 0) || (PA_RAMP_TIME == 2)) */ + #if (PA_RAMP_TIME == 4) + #define END_OF_TX_WU (END_OF_TX_WU_NORAMP + 2) + #define END_OF_TX_WD (END_OF_TX_WD_NORAMP + 4) + #define TX_SYNTH_DELAY_ADJ (2) + #define PD_CYCLE_SLIP_TX_HI_ADJ (2) + #define PD_CYCLE_SLIP_TX_LO_ADJ (1) + #define TX_DIG_EN_TX_HI_ADJ (0) + #define ZGBE_TX_DIG_EN_TX_HI_ADJ (-3) /* Only applies to Zigbee mode */ + #define PA_RAMP_SEL PA_RAMP_SEL_4US + #define DATA_PADDING_EN (1) + #else /* (PA_RAMP_TIME==4) */ + #if ((PA_RAMP_TIME == 8) && (!RADIO_IS_GEN_3P0)) + #define END_OF_TX_WU (END_OF_TX_WU_NORAMP + 6) + #define END_OF_TX_WD (END_OF_TX_WD_NORAMP + 12) + #define TX_SYNTH_DELAY_ADJ (6) + #define PD_CYCLE_SLIP_TX_HI_ADJ (6) + #define PD_CYCLE_SLIP_TX_LO_ADJ (1) + #define TX_DIG_EN_TX_HI_ADJ (4) + #define ZGBE_TX_DIG_EN_TX_HI_ADJ (1) /* Only applies to Zigbee mode */ + #define PA_RAMP_SEL PA_RAMP_SEL_8US + #define DATA_PADDING_EN (1) + #else /* (PA_RAMP_TIME == 8) */ + #error "Invalid value for PA_RAMP_TIME macro" + #endif /* (PA_RAMP_TIME == 8) */ + #endif/* (PA_RAMP_TIME == 4) */ +#endif /* ((PA_RAMP_TIME == 0) || (PA_RAMP_TIME == 2)) */ + +#define TX_DIG_EN_ASSERT_MSK500 (END_OF_TX_WU - 3) + +#define END_OF_RX_WU (104 + AUX_PLL_DELAY) +#if RF_OSC_26MHZ == 1 +#define END_OF_RX_WD (END_OF_RX_WU + 1 + ADD_FOR_26MHZ) /* Need to handle normal signals extending when 26MHZ warmdown is extended */ +#else +#define END_OF_RX_WD (END_OF_RX_WU + 1) +#endif /* RF_OSC_26MHZ == 1 */ +#define END_OF_RX_WU_26MHZ (END_OF_RX_WU + ADD_FOR_26MHZ) +#define END_OF_RX_WD_26MHZ (END_OF_RX_WU + 1 + ADD_FOR_26MHZ) + +/* PA Bias Table */ +#define PA_RAMP_0 0x1 +#define PA_RAMP_1 0x2 +#define PA_RAMP_2 0x4 +#define PA_RAMP_3 0x8 +#define PA_RAMP_4 0xe +#define PA_RAMP_5 0x16 +#define PA_RAMP_6 0x22 +#define PA_RAMP_7 0x2e + +/* BLE LL timing definitions */ +#define TX_ON_DELAY (0x85) /* Adjusted TX_ON_DELAY to make turnaround time 150usec */ +#define RX_ON_DELAY (29 + END_OF_RX_WU+4) +#define RX_ON_DELAY_26MHZ (29 + END_OF_RX_WU_26MHZ+4) +#define TX_RX_ON_DELAY_VAL (TX_ON_DELAY << 8 | RX_ON_DELAY) +#define TX_RX_ON_DELAY_VAL_26MHZ (TX_ON_DELAY << 8 | RX_ON_DELAY_26MHZ) +#define TX_SYNTH_DELAY (TX_ON_DELAY - END_OF_TX_WU - TX_SYNTH_DELAY_ADJ) /* Adjustment to TX_SYNTH_DELAY due to DATA_PADDING */ +#define RX_SYNTH_DELAY (0x18) +#define TX_RX_SYNTH_DELAY_VAL (TX_SYNTH_DELAY << 8 | RX_SYNTH_DELAY) + +/* PHY reference waveform assembly */ +#define RW0PS(loc, val) (((val) & 0x1F) << ((loc) * 5)) /* Ref Word 0 - loc is the phase info symbol number, val is the value of the phase info */ +#define RW1PS(loc, val) (((val) & 0x1F) << (((loc) * 5) - 32)) /* Ref Word 1 - loc is the phase info symbol number, val is the value of the phase info */ +#define RW2PS(loc, val) (((val) & 0x1F) << (((loc) * 5) - 64)) /* Ref Word 2 - loc is the phase info symbol number, val is the value of the phase info */ +#endif /* RADIO_IS_GEN_3P0 */ + +/*! @brief Error codes for the XCVR driver. */ +typedef enum _xcvrStatus +{ + gXcvrSuccess_c = 0, + gXcvrInvalidParameters_c, + gXcvrUnsupportedOperation_c, + gXcvrTrimFailure_c +} xcvrStatus_t; + +/*! @brief Health status returned from PHY upon status check function return. */ +typedef enum _healthStatus +{ + NO_ERRORS = 0, + PLL_CTUNE_FAIL = 1, + PLL_CYCLE_SLIP_FAIL = 2, + PLL_FREQ_TARG_FAIL = 4, + PLL_TSM_ABORT_FAIL = 8, +} healthStatus_t; + +/*! @brief Health status returned from PHY upon status check function return. */ +typedef enum _ext_clock_config +{ + EXT_CLK_32_MHZ = 0, + EXT_CLK_26_MHZ = 1, +} ext_clock_config_t; + +/*! @brief Radio operating mode setting types. */ +typedef enum +{ + BLE_MODE = 0, + ZIGBEE_MODE = 1, + ANT_MODE = 2, + + /* BT=0.5, h=** */ + GFSK_BT_0p5_h_0p5 = 3, /* < BT=0.5, h=0.5 [BLE at 1MBPS data rate; CS4 at 250KBPS data rate] */ + GFSK_BT_0p5_h_0p32 = 4, /* < BT=0.5, h=0.32*/ + GFSK_BT_0p5_h_0p7 = 5, /* < BT=0.5, h=0.7 [ CS1 at 500KBPS data rate] */ + GFSK_BT_0p5_h_1p0 = 6, /* < BT=0.5, h=1.0 [ CS4 at 250KBPS data rate] */ + + /* BT=** h=0.5 */ + GFSK_BT_0p3_h_0p5 = 7, /* < BT=0.3, h=0.5 [ CS2 at 1MBPS data rate] */ + GFSK_BT_0p7_h_0p5 = 8, /* < BT=0.7, h=0.5 */ + + MSK = 9, + NUM_RADIO_MODES = 10, +} radio_mode_t; + +/*! @brief Link layer types. */ +typedef enum +{ + BLE_LL = 0, /* Must match bit assignment in RADIO1_IRQ_SEL */ + ZIGBEE_LL = 1, /* Must match bit assignment in RADIO1_IRQ_SEL */ + ANT_LL = 2, /* Must match bit assignment in RADIO1_IRQ_SEL */ + GENFSK_LL = 3, /* Must match bit assignment in RADIO1_IRQ_SEL */ + UNASSIGNED_LL = 4, /* Must match bit assignment in RADIO1_IRQ_SEL */ +} link_layer_t; + +/*! @brief Data rate selections. */ +typedef enum +{ + DR_1MBPS = 0, /* Must match bit assignment in BITRATE field */ + DR_500KBPS = 1, /* Must match bit assignment in BITRATE field */ + DR_250KBPS = 2, /* Must match bit assignment in BITRATE field */ +#if RADIO_IS_GEN_3P0 + DR_2MBPS = 3, /* Must match bit assignment in BITRATE field */ +#endif /* RADIO_IS_GEN_3P0 */ + DR_UNASSIGNED = 4, /* Must match bit assignment in BITRATE field */ +} data_rate_t; + +/*! + * @brief XCVR RX_DIG channel filter coefficient storage + * Storage of the coefficients varies from 6 bits to 10 bits so all use int16_t for storage. + */ +typedef struct +{ + uint16_t rx_chf_coef_0; /* < 6 bit two's complement stored in a uint16_t */ + uint16_t rx_chf_coef_1; /* < 6 bit two's complement stored in a uint16_t */ + uint16_t rx_chf_coef_2; /* < 7 bit two's complement stored in a uint16_t */ + uint16_t rx_chf_coef_3; /* < 7 bit two's complement stored in a uint16_t */ + uint16_t rx_chf_coef_4; /* < 7 bit two's complement stored in a uint16_t */ + uint16_t rx_chf_coef_5; /* < 7 bit two's complement stored in a uint16_t */ + uint16_t rx_chf_coef_6; /* < 8 bit two's complement stored in a uint16_t */ + uint16_t rx_chf_coef_7; /* < 8 bit two's complement stored in a uint16_t */ + uint16_t rx_chf_coef_8; /* < 9 bit two's complement stored in a uint16_t */ + uint16_t rx_chf_coef_9; /* < 9 bit two's complement stored in a uint16_t */ + uint16_t rx_chf_coef_10; /* < 10 bit two's complement stored in a uint16_t */ + uint16_t rx_chf_coef_11; /* < 10 bit two's complement stored in a uint16_t */ +} xcvr_rx_chf_coeffs_t; + +/*! + * @brief XCVR masked init type for 32 bit registers + * Initialization uses the mask to clear selected fields of the register and then OR's in the init value. All init values must be in their proper field position. + */ +typedef struct +{ + uint32_t mask; + uint32_t init; +} xcvr_masked_init_32_t; + +/*! + * @brief XCVR common configure structure + */ +typedef struct +{ + /* XCVR_ANA configs */ + xcvr_masked_init_32_t ana_sy_ctrl1; + + /* XCVR_PLL_DIG configs */ + uint32_t pll_hpm_bump; + uint32_t pll_mod_ctrl; + uint32_t pll_chan_map; + uint32_t pll_lock_detect; + uint32_t pll_hpm_ctrl; +#if !RADIO_IS_GEN_2P1 + uint32_t pll_hpmcal_ctrl; +#endif /* !RADIO_IS_GEN_2P1 */ + uint32_t pll_hpm_sdm_res; + uint32_t pll_lpm_ctrl; + uint32_t pll_lpm_sdm_ctrl1; + uint32_t pll_delay_match; + uint32_t pll_ctune_ctrl; + + /* XCVR_RX_DIG configs */ + uint32_t rx_dig_ctrl_init; /* NOTE: Common init, mode init, and datarate init will be OR'd together for RX_DIG_CTRL to form complete register initialization */ + uint32_t dcoc_ctrl_0_init_26mhz; /* NOTE: This will be OR'd with mode specific init for DCOC_CTRL_0 to form complete register initialization */ + uint32_t dcoc_ctrl_0_init_32mhz; /* NOTE: This will be OR'd with mode specific init for DCOC_CTRL_0 to form complete register initialization */ + uint32_t dcoc_ctrl_1_init; + uint32_t dcoc_cal_gain_init; + uint32_t dc_resid_ctrl_init; /* NOTE: This will be OR'd with datarate specific init for DCOC_RESID_CTRL to form complete register initialization */ + uint32_t dcoc_cal_rcp_init; + uint32_t lna_gain_val_3_0; + uint32_t lna_gain_val_7_4; + uint32_t lna_gain_val_8; + uint32_t bba_res_tune_val_7_0; + uint32_t bba_res_tune_val_10_8; + uint32_t lna_gain_lin_val_2_0_init; + uint32_t lna_gain_lin_val_5_3_init; + uint32_t lna_gain_lin_val_8_6_init; + uint32_t lna_gain_lin_val_9_init; + uint32_t bba_res_tune_lin_val_3_0_init; + uint32_t bba_res_tune_lin_val_7_4_init; + uint32_t bba_res_tune_lin_val_10_8_init; + uint32_t dcoc_bba_step_init; + uint32_t dcoc_tza_step_00_init; + uint32_t dcoc_tza_step_01_init; + uint32_t dcoc_tza_step_02_init; + uint32_t dcoc_tza_step_03_init; + uint32_t dcoc_tza_step_04_init; + uint32_t dcoc_tza_step_05_init; + uint32_t dcoc_tza_step_06_init; + uint32_t dcoc_tza_step_07_init; + uint32_t dcoc_tza_step_08_init; + uint32_t dcoc_tza_step_09_init; + uint32_t dcoc_tza_step_10_init; +#if (RADIO_IS_GEN_3P0 || RADIO_IS_GEN_2P1) + uint32_t dcoc_cal_fail_th_init; + uint32_t dcoc_cal_pass_th_init; +#endif /* (RADIO_IS_GEN_3P0 || RADIO_IS_GEN_2P1) */ + uint32_t agc_ctrl_0_init; /* NOTE: Common init and mode init will be OR'd together for AGC_CTRL_0 to form complete register initialization */ + uint32_t agc_ctrl_1_init_26mhz; /* NOTE: This will be OR'd with datarate specific init to form complete register initialization */ + uint32_t agc_ctrl_1_init_32mhz; /* NOTE: This will be OR'd with datarate specific init to form complete register initialization */ + uint32_t agc_ctrl_3_init; + /* Other agc config inits are in the modeXdatarate config table */ + uint32_t agc_gain_tbl_03_00_init; + uint32_t agc_gain_tbl_07_04_init; + uint32_t agc_gain_tbl_11_08_init; + uint32_t agc_gain_tbl_15_12_init; + uint32_t agc_gain_tbl_19_16_init; + uint32_t agc_gain_tbl_23_20_init; + uint32_t agc_gain_tbl_26_24_init; + uint32_t rssi_ctrl_0_init; +#if RADIO_IS_GEN_3P0 + uint32_t rssi_ctrl_1_init; +#endif /* RADIO_IS_GEN_3P0 */ + uint32_t cca_ed_lqi_ctrl_0_init; + uint32_t cca_ed_lqi_ctrl_1_init; + + /* XCVR_TSM configs */ + uint32_t tsm_ctrl; + uint32_t tsm_ovrd2_init; + uint32_t end_of_seq_init_26mhz; + uint32_t end_of_seq_init_32mhz; +#if !RADIO_IS_GEN_2P1 + uint32_t lpps_ctrl_init; +#endif /* !RADIO_IS_GEN_2P1 */ + uint32_t tsm_fast_ctrl2_init_26mhz; + uint32_t tsm_fast_ctrl2_init_32mhz; + uint32_t recycle_count_init_26mhz; + uint32_t recycle_count_init_32mhz; + uint32_t pa_ramp_tbl_0_init; + uint32_t pa_ramp_tbl_1_init; +#if RADIO_IS_GEN_3P0 + uint32_t pa_ramp_tbl_2_init; + uint32_t pa_ramp_tbl_3_init; +#endif /* RADIO_IS_GEN_3P0 */ + uint32_t tsm_timing_00_init; + uint32_t tsm_timing_01_init; + uint32_t tsm_timing_02_init; + uint32_t tsm_timing_03_init; + uint32_t tsm_timing_04_init; + uint32_t tsm_timing_05_init; + uint32_t tsm_timing_06_init; + uint32_t tsm_timing_07_init; + uint32_t tsm_timing_08_init; + uint32_t tsm_timing_09_init; + uint32_t tsm_timing_10_init; + uint32_t tsm_timing_11_init; + uint32_t tsm_timing_12_init; + uint32_t tsm_timing_13_init; + uint32_t tsm_timing_14_init_26mhz; /* tsm_timing_14 has mode specific LSbyte (both LS bytes) */ + uint32_t tsm_timing_14_init_32mhz; /* tsm_timing_14 has mode specific LSbyte (both LS bytes) */ + uint32_t tsm_timing_15_init; + uint32_t tsm_timing_16_init_26mhz; + uint32_t tsm_timing_16_init_32mhz; + uint32_t tsm_timing_17_init; + uint32_t tsm_timing_18_init; + uint32_t tsm_timing_19_init; + uint32_t tsm_timing_20_init; + uint32_t tsm_timing_21_init; + uint32_t tsm_timing_22_init; + uint32_t tsm_timing_23_init; + uint32_t tsm_timing_24_init; + uint32_t tsm_timing_25_init_26mhz; + uint32_t tsm_timing_25_init_32mhz; + uint32_t tsm_timing_26_init; + uint32_t tsm_timing_27_init_26mhz; + uint32_t tsm_timing_27_init_32mhz; + uint32_t tsm_timing_28_init_26mhz; + uint32_t tsm_timing_28_init_32mhz; + uint32_t tsm_timing_29_init_26mhz; + uint32_t tsm_timing_29_init_32mhz; + uint32_t tsm_timing_30_init_26mhz; + uint32_t tsm_timing_30_init_32mhz; + uint32_t tsm_timing_31_init_26mhz; + uint32_t tsm_timing_31_init_32mhz; + uint32_t tsm_timing_32_init_26mhz; + uint32_t tsm_timing_32_init_32mhz; + uint32_t tsm_timing_33_init_26mhz; + uint32_t tsm_timing_33_init_32mhz; + uint32_t tsm_timing_34_init; + uint32_t tsm_timing_35_init; /* tsm_timing_35 has a mode specific LSbyte*/ + uint32_t tsm_timing_36_init_26mhz; + uint32_t tsm_timing_36_init_32mhz; + uint32_t tsm_timing_37_init_26mhz; + uint32_t tsm_timing_37_init_32mhz; + uint32_t tsm_timing_38_init; + uint32_t tsm_timing_39_init_26mhz; + uint32_t tsm_timing_39_init_32mhz; + uint32_t tsm_timing_40_init_26mhz; + uint32_t tsm_timing_40_init_32mhz; + uint32_t tsm_timing_41_init_26mhz; + uint32_t tsm_timing_41_init_32mhz; + uint32_t tsm_timing_51_init; + uint32_t tsm_timing_52_init_26mhz; + uint32_t tsm_timing_52_init_32mhz; + uint32_t tsm_timing_53_init; + uint32_t tsm_timing_54_init_26mhz; + uint32_t tsm_timing_54_init_32mhz; + uint32_t tsm_timing_55_init_26mhz; + uint32_t tsm_timing_55_init_32mhz; + uint32_t tsm_timing_56_init_26mhz; + uint32_t tsm_timing_56_init_32mhz; + uint32_t tsm_timing_57_init; + uint32_t tsm_timing_58_init; + + /* XCVR_TX_DIG configs */ + uint32_t tx_ctrl; + uint32_t tx_data_padding; + uint32_t tx_dft_pattern; +#if !RADIO_IS_GEN_2P1 + uint32_t rf_dft_bist_1; + uint32_t rf_dft_bist_2; +#endif /* !RADIO_IS_GEN_2P1 */ +} xcvr_common_config_t; + +/*! @brief XCVR mode specific configure structure (varies by radio mode) */ +typedef struct +{ + radio_mode_t radio_mode; + uint32_t scgc5_clock_ena_bits; + + /* XCVR_MISC configs */ + xcvr_masked_init_32_t xcvr_ctrl; + + /* XCVR_PHY configs */ +#if RADIO_IS_GEN_3P0 + uint32_t phy_fsk_pd_cfg0; + uint32_t phy_fsk_pd_cfg1; + uint32_t phy_fsk_cfg; + uint32_t phy_fsk_misc; + uint32_t phy_fad_ctrl; +#else + uint32_t phy_pre_ref0_init; + uint32_t phy_pre_ref1_init; + uint32_t phy_pre_ref2_init; + uint32_t phy_cfg1_init; + uint32_t phy_el_cfg_init; /* EL_WIN_SIZE and EL_INTERVAL are in the data_rate specific configuration */ +#endif /* RADIO_IS_GEN_3P0 */ + + /* XCVR_RX_DIG configs */ + uint32_t rx_dig_ctrl_init_26mhz; /* NOTE: Common init, mode init, and datarate init will be OR'd together for RX_DIG_CTRL to form complete register initialization */ + uint32_t rx_dig_ctrl_init_32mhz; /* NOTE: Common init, mode init, and datarate init will be OR'd together for RX_DIG_CTRL to form complete register initialization */ + uint32_t agc_ctrl_0_init; /* NOTE: Common init and mode init will be OR'd together for AGC_CTRL_0 to form complete register initialization */ + + /* XCVR_TSM configs */ +#if (RADIO_IS_GEN_2P0 || RADIO_IS_GEN_2P1) + uint32_t tsm_timing_35_init; /* Only the LSbyte is mode specific */ +#endif /* (RADIO_IS_GEN_2P0 || RADIO_IS_GEN_2P1) */ + + /* XCVR_TX_DIG configs */ + uint32_t tx_gfsk_ctrl; + uint32_t tx_gfsk_coeff1_26mhz; + uint32_t tx_gfsk_coeff2_26mhz; + uint32_t tx_gfsk_coeff1_32mhz; + uint32_t tx_gfsk_coeff2_32mhz; +} xcvr_mode_config_t; + +/*! + * @brief XCVR modeXdatarate specific configure structure (varies by radio mode AND data rate) + * This structure is used to store all of the XCVR settings which are dependent upon both radio mode and data rate. It is used as an overlay + * on top of the xcvr_mode_config_t structure to supply definitions which are either not in that table or which must be overridden for data rate. + */ +typedef struct +{ + radio_mode_t radio_mode; + data_rate_t data_rate; + + /* XCVR_ANA configs */ + xcvr_masked_init_32_t ana_sy_ctrl2; + xcvr_masked_init_32_t ana_rx_bba; + xcvr_masked_init_32_t ana_rx_tza; + + /* XCVR_PHY configs */ +#if RADIO_IS_GEN_3P0 + uint32_t phy_fsk_misc_mode_datarate; +#else + uint32_t phy_cfg2_init; +#endif /* RADIO_IS_GEN_3P0 */ + + uint32_t agc_ctrl_2_init_26mhz; + uint32_t agc_ctrl_2_init_32mhz; + xcvr_rx_chf_coeffs_t rx_chf_coeffs_26mhz; /* 26MHz ext clk */ + xcvr_rx_chf_coeffs_t rx_chf_coeffs_32mhz; /* 32MHz ext clk */ + uint32_t rx_rccal_ctrl_0; + uint32_t rx_rccal_ctrl_1; + + /* XCVR_TX_DIG configs */ + uint32_t tx_fsk_scale_26mhz; /* Only used by MSK mode, but dependent on datarate */ + uint32_t tx_fsk_scale_32mhz; /* Only used by MSK mode, but dependent on datarate */ +} xcvr_mode_datarate_config_t; + +/*! + * @brief XCVR datarate specific configure structure (varies by data rate) + * This structure is used to store all of the XCVR settings which are dependent upon data rate. It is used as an overlay + * on top of the xcvr_mode_config_t structure to supply definitions which are either not in that table or which must be overridden for data rate. + */ +typedef struct +{ + data_rate_t data_rate; + + /* XCVR_PHY configs */ + uint32_t phy_el_cfg_init; /* Note: EL_ENABLE is set in xcvr_mode_config_t settings */ + + /* XCVR_RX_DIG configs */ + uint32_t rx_dig_ctrl_init_26mhz; /* NOTE: Common init, mode init, and datarate init will be OR'd together for RX_DIG_CTRL to form complete register initialization */ + uint32_t rx_dig_ctrl_init_32mhz; /* NOTE: Common init, mode init, and datarate init will be OR'd together for RX_DIG_CTRL to form complete register initialization */ + uint32_t agc_ctrl_1_init_26mhz; + uint32_t agc_ctrl_1_init_32mhz; + uint32_t dcoc_ctrl_0_init_26mhz; /* NOTE: This will be OR'd with common init for DCOC_CTRL_0 to form complete register initialization */ + uint32_t dcoc_ctrl_0_init_32mhz; /* NOTE: This will be OR'd with common init for DCOC_CTRL_0 to form complete register initialization */ + uint32_t dcoc_ctrl_1_init_26mhz; /* NOTE: This will be OR'd with common init for DCOC_CTRL_1 to form complete register initialization */ + uint32_t dcoc_ctrl_1_init_32mhz; /* NOTE: This will be OR'd with common init for DCOC_CTRL_1 to form complete register initialization */ + uint32_t dcoc_ctrl_2_init_26mhz; + uint32_t dcoc_ctrl_2_init_32mhz; + uint32_t dcoc_cal_iir_init_26mhz; + uint32_t dcoc_cal_iir_init_32mhz; + uint32_t dc_resid_ctrl_26mhz;/* NOTE: This will be OR'd with common init for DCOC_RESID_CTRL to form complete register initialization */ + uint32_t dc_resid_ctrl_32mhz;/* NOTE: This will be OR'd with common init for DCOC_RESID_CTRL to form complete register initialization */ +} xcvr_datarate_config_t; + +/*! + * @brief LPUART callback function type + * + * The panic callback function is defined by system if system need to be informed of XCVR fatal errors. + * refer to #XCVR_RegisterPanicCb + */ +typedef void (*panic_fptr)(uint32_t panic_id, uint32_t location, uint32_t extra1, uint32_t extra2); + +/* Make available const structures from config files */ +extern const xcvr_common_config_t xcvr_common_config; +extern const xcvr_mode_config_t zgbe_mode_config; +extern const xcvr_mode_config_t ble_mode_config; +extern const xcvr_mode_config_t ant_mode_config; +extern const xcvr_mode_config_t gfsk_bt_0p5_h_0p5_mode_config; +extern const xcvr_mode_config_t gfsk_bt_0p5_h_0p7_mode_config; +extern const xcvr_mode_config_t gfsk_bt_0p5_h_0p32_mode_config; +extern const xcvr_mode_config_t gfsk_bt_0p5_h_1p0_mode_config; +extern const xcvr_mode_config_t gfsk_bt_0p3_h_0p5_mode_config; +extern const xcvr_mode_config_t gfsk_bt_0p7_h_0p5_mode_config; +extern const xcvr_mode_config_t msk_mode_config; + +#if RADIO_IS_GEN_3P0 +extern const xcvr_datarate_config_t xcvr_2mbps_config; +#endif /* RADIO_IS_GEN_3P0 */ +extern const xcvr_datarate_config_t xcvr_1mbps_config; +extern const xcvr_datarate_config_t xcvr_500kbps_config; +extern const xcvr_datarate_config_t xcvr_250kbps_config; +extern const xcvr_datarate_config_t xcvr_802_15_4_500kbps_config; /* Custom datarate settings for 802.15.4 since it is 2MChips/sec */ + +#if RADIO_IS_GEN_3P0 +extern const xcvr_mode_datarate_config_t xcvr_GFSK_BT_0p5_h_0p5_2mbps_config; +extern const xcvr_mode_datarate_config_t xcvr_GFSK_BT_0p5_h_0p32_2mbps_config; +extern const xcvr_mode_datarate_config_t xcvr_GFSK_BT_0p3_h_0p5_2mbps_config; +extern const xcvr_mode_datarate_config_t xcvr_GFSK_BT_0p7_h_0p5_2mbps_config; +extern const xcvr_mode_datarate_config_t xcvr_MSK_2mbps_config; +#endif /* RADIO_IS_GEN_3P0 */ +extern const xcvr_mode_datarate_config_t xcvr_BLE_1mbps_config; +extern const xcvr_mode_datarate_config_t xcvr_ZIGBEE_500kbps_config; +extern const xcvr_mode_datarate_config_t xcvr_ANT_1mbps_config; +extern const xcvr_mode_datarate_config_t xcvr_GFSK_BT_0p5_h_0p5_1mbps_config; +extern const xcvr_mode_datarate_config_t xcvr_GFSK_BT_0p5_h_0p5_500kbps_config; +extern const xcvr_mode_datarate_config_t xcvr_GFSK_BT_0p5_h_0p5_250kbps_config; +extern const xcvr_mode_datarate_config_t xcvr_GFSK_BT_0p5_h_0p32_1mbps_config; +extern const xcvr_mode_datarate_config_t xcvr_GFSK_BT_0p5_h_0p32_500kbps_config; +extern const xcvr_mode_datarate_config_t xcvr_GFSK_BT_0p5_h_0p32_250kbps_config; +extern const xcvr_mode_datarate_config_t xcvr_GFSK_BT_0p5_h_0p7_1mbps_config; +extern const xcvr_mode_datarate_config_t xcvr_GFSK_BT_0p5_h_0p7_500kbps_config; +extern const xcvr_mode_datarate_config_t xcvr_GFSK_BT_0p5_h_0p7_250kbps_config; +extern const xcvr_mode_datarate_config_t xcvr_GFSK_BT_0p5_h_1p0_1mbps_config; +extern const xcvr_mode_datarate_config_t xcvr_GFSK_BT_0p5_h_1p0_500kbps_config; +extern const xcvr_mode_datarate_config_t xcvr_GFSK_BT_0p5_h_1p0_250kbps_config; +extern const xcvr_mode_datarate_config_t xcvr_GFSK_BT_0p3_h_0p5_1mbps_config; +extern const xcvr_mode_datarate_config_t xcvr_GFSK_BT_0p3_h_0p5_500kbps_config; +extern const xcvr_mode_datarate_config_t xcvr_GFSK_BT_0p3_h_0p5_250kbps_config; +extern const xcvr_mode_datarate_config_t xcvr_GFSK_BT_0p7_h_0p5_1mbps_config; +extern const xcvr_mode_datarate_config_t xcvr_GFSK_BT_0p7_h_0p5_500kbps_config; +extern const xcvr_mode_datarate_config_t xcvr_GFSK_BT_0p7_h_0p5_250kbps_config; +extern const xcvr_mode_datarate_config_t xcvr_MSK_1mbps_config; +extern const xcvr_mode_datarate_config_t xcvr_MSK_500kbps_config; +extern const xcvr_mode_datarate_config_t xcvr_MSK_250kbps_config; + +#ifdef __cplusplus +extern "C" { +#endif + +#ifdef __cplusplus +} +#endif + +/*! @}*/ + +#endif /* _FSL_XCVR_H_ */ diff --git a/drivers/kw41zrf/vendor/XCVR/MKW41Z4/fsl_xcvr_common_config.c b/drivers/kw41zrf/vendor/XCVR/MKW41Z4/fsl_xcvr_common_config.c new file mode 100644 index 000000000000..2220b86d8af5 --- /dev/null +++ b/drivers/kw41zrf/vendor/XCVR/MKW41Z4/fsl_xcvr_common_config.c @@ -0,0 +1,628 @@ +/* +* The Clear BSD License +* Copyright (c) 2015, Freescale Semiconductor, Inc. +* Copyright 2016-2017 NXP +* All rights reserved. +* +* Redistribution and use in source and binary forms, with or without +* modification, are permitted (subject to the limitations in the +* disclaimer below) provided that the following conditions are met: +* +* * Redistributions of source code must retain the above copyright +* notice, this list of conditions and the following disclaimer. +* +* * Redistributions in binary form must reproduce the above copyright +* notice, this list of conditions and the following disclaimer in the +* documentation and/or other materials provided with the distribution. +* +* * Neither the name of the copyright holder nor the names of its +* contributors may be used to endorse or promote products derived from +* this software without specific prior written permission. +* +* NO EXPRESS OR IMPLIED LICENSES TO ANY PARTY'S PATENT RIGHTS ARE +* GRANTED BY THIS LICENSE. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT +* HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED +* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE +* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR +* BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE +* OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN +* IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#include "fsl_xcvr.h" + +/******************************************************************************* + * Definitions + ******************************************************************************/ + +/******************************************************************************* + * Prototypes + ******************************************************************************/ + +/******************************************************************************* + * Variables + ******************************************************************************/ + +/******************************************************************************* + * Code + ******************************************************************************/ +const xcvr_common_config_t xcvr_common_config = +{ + /* XCVR_ANA configs */ + .ana_sy_ctrl1.mask = XCVR_ANALOG_SY_CTRL_1_SY_LPF_FILT_CTRL_MASK, + .ana_sy_ctrl1.init = XCVR_ANALOG_SY_CTRL_1_SY_LPF_FILT_CTRL(3), /* PLL Analog Loop Filter */ + +#define hpm_vcm_tx 0 +#define hpm_vcm_cal 1 +#define hpm_fdb_res_tx 0 +#define hpm_fdb_res_cal 1 +#define modulation_word_manual 0 +#define mod_disable 0 +#define hpm_mod_manual 0 +#define hpm_mod_disable 0 +#define hpm_sdm_out_manual 0 +#define hpm_sdm_out_disable 0 +#define channel_num 0 +#define boc 0 +#define bmr 1 +#define zoc 0 +#define ctune_ldf_lev 8 +#define ftf_rx_thrsh 33 +#define ftw_rx 0 +#define ftf_tx_thrsh 6 +#define ftw_tx 0 +#define freq_count_go 0 +#define freq_count_time 0 +#define hpm_sdm_in_manual 0 +#define hpm_sdm_out_invert 0 +#define hpm_sdm_in_disable 0 +#define hpm_lfsr_size 4 +#define hpm_dth_scl 0 +#define hpm_dth_en 1 +#define hpm_integer_scale 0 +#define hpm_integer_invert 0 +#define hpm_cal_invert 1 +#define hpm_mod_in_invert 1 +#define hpm_cal_not_bumped 0 +#define hpm_cal_count_scale 0 +#define hp_cal_disable 0 +#define hpm_cal_factor_manual 0 +#define hpm_cal_array_size 1 +#define hpm_cal_time 0 +#define hpm_sdm_denom 256 +#define hpm_count_adjust 0 +#define pll_ld_manual 0 +#define pll_ld_disable 0 +#define lpm_sdm_inv 0 +#define lpm_disable 0 +#define lpm_dth_scl 8 +#define lpm_d_ctrl 1 +#define lpm_d_ovrd 1 +#define lpm_scale 8 +#define lpm_sdm_use_neg 0 +#define hpm_array_bias 0 +#define lpm_intg 38 +#define sdm_map_disable 0 +#define lpm_sdm_delay 4 +#define hpm_sdm_delay 0 +#define hpm_integer_delay 0 +#define ctune_target_manual 0 +#define ctune_target_disable 0 +#define ctune_adjust 0 +#define ctune_manual 0 +#define ctune_disable 0 + +/*-------------------------------------------------------------------------------------------------*/ + + .pll_hpm_bump = XCVR_PLL_DIG_HPM_BUMP_HPM_FDB_RES_CAL(hpm_fdb_res_cal) | + XCVR_PLL_DIG_HPM_BUMP_HPM_FDB_RES_TX(hpm_fdb_res_tx) | + XCVR_PLL_DIG_HPM_BUMP_HPM_VCM_CAL(hpm_vcm_cal) | + XCVR_PLL_DIG_HPM_BUMP_HPM_VCM_TX(hpm_vcm_tx), + +/*-------------------------------------------------------------------------------------------------*/ + + .pll_mod_ctrl = XCVR_PLL_DIG_MOD_CTRL_HPM_MOD_DISABLE(hpm_mod_disable) | + XCVR_PLL_DIG_MOD_CTRL_HPM_MOD_MANUAL(hpm_mod_manual) | + XCVR_PLL_DIG_MOD_CTRL_HPM_SDM_OUT_DISABLE(hpm_sdm_out_disable) | + XCVR_PLL_DIG_MOD_CTRL_HPM_SDM_OUT_MANUAL(hpm_sdm_out_manual) | + XCVR_PLL_DIG_MOD_CTRL_MOD_DISABLE(mod_disable) | + XCVR_PLL_DIG_MOD_CTRL_MODULATION_WORD_MANUAL(modulation_word_manual), + +/*-------------------------------------------------------------------------------------------------*/ + + .pll_chan_map = XCVR_PLL_DIG_CHAN_MAP_BMR(bmr) | + XCVR_PLL_DIG_CHAN_MAP_BOC(boc) | + XCVR_PLL_DIG_CHAN_MAP_CHANNEL_NUM(channel_num) +#if !RADIO_IS_GEN_2P1 + | XCVR_PLL_DIG_CHAN_MAP_ZOC(zoc) +#endif /* !RADIO_IS_GEN_2P1 */ + , + +/*-------------------------------------------------------------------------------------------------*/ + + .pll_lock_detect = XCVR_PLL_DIG_LOCK_DETECT_CTUNE_LDF_LEV(ctune_ldf_lev) | + XCVR_PLL_DIG_LOCK_DETECT_FREQ_COUNT_GO(freq_count_go) | + XCVR_PLL_DIG_LOCK_DETECT_FREQ_COUNT_TIME(freq_count_time) | + XCVR_PLL_DIG_LOCK_DETECT_FTF_RX_THRSH(ftf_rx_thrsh) | + XCVR_PLL_DIG_LOCK_DETECT_FTF_TX_THRSH(ftf_tx_thrsh) | + XCVR_PLL_DIG_LOCK_DETECT_FTW_RX(ftw_rx) | + XCVR_PLL_DIG_LOCK_DETECT_FTW_TX(ftw_tx), + +/*-------------------------------------------------------------------------------------------------*/ + + .pll_hpm_ctrl = XCVR_PLL_DIG_HPM_CTRL_HPM_CAL_INVERT(hpm_cal_invert) | + XCVR_PLL_DIG_HPM_CTRL_HPM_DTH_EN(hpm_dth_en) | + XCVR_PLL_DIG_HPM_CTRL_HPM_DTH_SCL(hpm_dth_scl) | + XCVR_PLL_DIG_HPM_CTRL_HPM_INTEGER_INVERT(hpm_integer_invert) | + XCVR_PLL_DIG_HPM_CTRL_HPM_INTEGER_SCALE(hpm_integer_scale) | + XCVR_PLL_DIG_HPM_CTRL_HPM_LFSR_SIZE(hpm_lfsr_size) | + XCVR_PLL_DIG_HPM_CTRL_HPM_MOD_IN_INVERT(hpm_mod_in_invert) | + XCVR_PLL_DIG_HPM_CTRL_HPM_SDM_IN_DISABLE(hpm_sdm_in_disable) | + XCVR_PLL_DIG_HPM_CTRL_HPM_SDM_IN_MANUAL(hpm_sdm_in_manual) | + XCVR_PLL_DIG_HPM_CTRL_HPM_SDM_OUT_INVERT(hpm_sdm_out_invert), +/*-------------------------------------------------------------------------------------------------*/ +#if !RADIO_IS_GEN_2P1 + .pll_hpmcal_ctrl = XCVR_PLL_DIG_HPMCAL_CTRL_HP_CAL_DISABLE(hp_cal_disable) | + XCVR_PLL_DIG_HPMCAL_CTRL_HPM_CAL_ARRAY_SIZE(hpm_cal_array_size) | + XCVR_PLL_DIG_HPMCAL_CTRL_HPM_CAL_COUNT_SCALE(hpm_cal_count_scale) | + XCVR_PLL_DIG_HPMCAL_CTRL_HPM_CAL_FACTOR_MANUAL(hpm_cal_factor_manual) | + XCVR_PLL_DIG_HPMCAL_CTRL_HPM_CAL_NOT_BUMPED(hpm_cal_not_bumped) | + XCVR_PLL_DIG_HPMCAL_CTRL_HPM_CAL_TIME(hpm_cal_time), +#endif /* !RADIO_IS_GEN_2P1 */ +/*-------------------------------------------------------------------------------------------------*/ + .pll_hpm_sdm_res = XCVR_PLL_DIG_HPM_SDM_RES_HPM_COUNT_ADJUST(hpm_count_adjust) | + XCVR_PLL_DIG_HPM_SDM_RES_HPM_DENOM(hpm_sdm_denom), +/*-------------------------------------------------------------------------------------------------*/ + .pll_lpm_ctrl = XCVR_PLL_DIG_LPM_CTRL_LPM_D_CTRL(lpm_d_ctrl) | + XCVR_PLL_DIG_LPM_CTRL_LPM_D_OVRD(lpm_d_ovrd) | + XCVR_PLL_DIG_LPM_CTRL_LPM_DISABLE(lpm_disable) | + XCVR_PLL_DIG_LPM_CTRL_LPM_DTH_SCL(lpm_dth_scl) | + XCVR_PLL_DIG_LPM_CTRL_LPM_SCALE(lpm_scale) | + XCVR_PLL_DIG_LPM_CTRL_LPM_SDM_INV(lpm_sdm_inv) | + XCVR_PLL_DIG_LPM_CTRL_LPM_SDM_USE_NEG(lpm_sdm_use_neg) | + XCVR_PLL_DIG_LPM_CTRL_PLL_LD_DISABLE(pll_ld_disable) | + XCVR_PLL_DIG_LPM_CTRL_PLL_LD_MANUAL(pll_ld_manual), +/*-------------------------------------------------------------------------------------------------*/ + .pll_lpm_sdm_ctrl1 = XCVR_PLL_DIG_LPM_SDM_CTRL1_HPM_ARRAY_BIAS(hpm_array_bias) | + XCVR_PLL_DIG_LPM_SDM_CTRL1_LPM_INTG(lpm_intg) | + XCVR_PLL_DIG_LPM_SDM_CTRL1_SDM_MAP_DISABLE(sdm_map_disable), +/*-------------------------------------------------------------------------------------------------*/ + .pll_delay_match = XCVR_PLL_DIG_DELAY_MATCH_HPM_INTEGER_DELAY(hpm_integer_delay) | + XCVR_PLL_DIG_DELAY_MATCH_HPM_SDM_DELAY(hpm_sdm_delay) | + XCVR_PLL_DIG_DELAY_MATCH_LPM_SDM_DELAY(lpm_sdm_delay), +/*-------------------------------------------------------------------------------------------------*/ + .pll_ctune_ctrl = XCVR_PLL_DIG_CTUNE_CTRL_CTUNE_ADJUST(ctune_adjust) | + XCVR_PLL_DIG_CTUNE_CTRL_CTUNE_DISABLE(ctune_disable) | + XCVR_PLL_DIG_CTUNE_CTRL_CTUNE_MANUAL(ctune_manual) | + XCVR_PLL_DIG_CTUNE_CTRL_CTUNE_TARGET_DISABLE(ctune_target_disable) | + XCVR_PLL_DIG_CTUNE_CTRL_CTUNE_TARGET_MANUAL(ctune_target_manual), +/*-------------------------------------------------------------------------------------------------*/ + + /* XCVR_RX_DIG configs */ + /* NOTE: Clock specific settings are embedded in the mode dependent configs */ + .rx_dig_ctrl_init = XCVR_RX_DIG_RX_DIG_CTRL_RX_ADC_NEGEDGE(0) | + XCVR_RX_DIG_RX_DIG_CTRL_RX_CH_FILT_BYPASS(0) | +#if !RADIO_IS_GEN_2P1 + XCVR_RX_DIG_RX_DIG_CTRL_RX_ADC_RAW_EN(0) | +#endif /* !RADIO_IS_GEN_2P1 */ + XCVR_RX_DIG_RX_DIG_CTRL_RX_ADC_POL(0) | + XCVR_RX_DIG_RX_DIG_CTRL_RX_NORM_EN(1) | + XCVR_RX_DIG_RX_DIG_CTRL_RX_RSSI_EN(1) | + XCVR_RX_DIG_RX_DIG_CTRL_RX_AGC_EN(1) | + XCVR_RX_DIG_RX_DIG_CTRL_RX_DCOC_EN(1) | + XCVR_RX_DIG_RX_DIG_CTRL_RX_DCOC_CAL_EN(1) | + XCVR_RX_DIG_RX_DIG_CTRL_RX_IQ_SWAP(0) | + XCVR_RX_DIG_RX_DIG_CTRL_RX_DMA_DTEST_EN(0) | + XCVR_RX_DIG_RX_DIG_CTRL_RX_DEC_FILT_HZD_CORR_DIS(1), + + .agc_ctrl_0_init = XCVR_RX_DIG_AGC_CTRL_0_SLOW_AGC_EN(1) | + XCVR_RX_DIG_AGC_CTRL_0_SLOW_AGC_SRC(2) | + XCVR_RX_DIG_AGC_CTRL_0_AGC_FREEZE_EN(1) | + XCVR_RX_DIG_AGC_CTRL_0_AGC_FREEZE_PRE_OR_AA(0) | + XCVR_RX_DIG_AGC_CTRL_0_AGC_UP_EN(1) | + XCVR_RX_DIG_AGC_CTRL_0_AGC_UP_SRC(0) | + XCVR_RX_DIG_AGC_CTRL_0_AGC_DOWN_BBA_STEP_SZ(2) | + XCVR_RX_DIG_AGC_CTRL_0_AGC_DOWN_LNA_STEP_SZ(2) | + XCVR_RX_DIG_AGC_CTRL_0_AGC_UP_RSSI_THRESH(0xe7), + + .agc_ctrl_3_init = XCVR_RX_DIG_AGC_CTRL_3_AGC_UNFREEZE_TIME(21) | + XCVR_RX_DIG_AGC_CTRL_3_AGC_PDET_LO_DLY(2) | + XCVR_RX_DIG_AGC_CTRL_3_AGC_RSSI_DELT_H2S(20) | + XCVR_RX_DIG_AGC_CTRL_3_AGC_H2S_STEP_SZ(6) | + XCVR_RX_DIG_AGC_CTRL_3_AGC_UP_STEP_SZ(2), + + /* DCOC configs */ + .dcoc_ctrl_0_init_26mhz = XCVR_RX_DIG_DCOC_CTRL_0_DCOC_CAL_DURATION(16) | /* Only the duration changes between 26MHz and 32MHz ref osc settings */ +#if (RADIO_IS_GEN_2P1) + XCVR_RX_DIG_DCOC_CTRL_0_DCOC_CAL_CHECK_EN(0) | +#endif /* (RADIO_IS_GEN_2P1) */ + XCVR_RX_DIG_DCOC_CTRL_0_TRACK_FROM_ZERO(0) | + XCVR_RX_DIG_DCOC_CTRL_0_DCOC_CORRECT_EN(1) | + XCVR_RX_DIG_DCOC_CTRL_0_TZA_CORR_POL(0) | + XCVR_RX_DIG_DCOC_CTRL_0_BBA_CORR_POL(0) | + XCVR_RX_DIG_DCOC_CTRL_0_DCOC_CORRECT_SRC(1), + .dcoc_ctrl_0_init_32mhz = XCVR_RX_DIG_DCOC_CTRL_0_DCOC_CAL_DURATION(20) | /* Only the duration changes between 26MHz and 32MHz ref osc settings */ +#if (RADIO_IS_GEN_2P1) + XCVR_RX_DIG_DCOC_CTRL_0_DCOC_CAL_CHECK_EN(0) | +#endif /* (RADIO_IS_GEN_2P1) */ + XCVR_RX_DIG_DCOC_CTRL_0_TRACK_FROM_ZERO(0) | + XCVR_RX_DIG_DCOC_CTRL_0_DCOC_CORRECT_EN(1) | + XCVR_RX_DIG_DCOC_CTRL_0_TZA_CORR_POL(0) | + XCVR_RX_DIG_DCOC_CTRL_0_BBA_CORR_POL(0) | + XCVR_RX_DIG_DCOC_CTRL_0_DCOC_CORRECT_SRC(1), + + .dcoc_ctrl_1_init = XCVR_RX_DIG_DCOC_CTRL_1_DCOC_TRK_MIN_AGC_IDX(26), + + .dc_resid_ctrl_init = XCVR_RX_DIG_DC_RESID_CTRL_DC_RESID_ITER_FREEZE(5) | + XCVR_RX_DIG_DC_RESID_CTRL_DC_RESID_ALPHA(1) | + XCVR_RX_DIG_DC_RESID_CTRL_DC_RESID_EXT_DC_EN(1) | + XCVR_RX_DIG_DC_RESID_CTRL_DC_RESID_MIN_AGC_IDX(26), + + .dcoc_cal_gain_init = XCVR_RX_DIG_DCOC_CAL_GAIN_DCOC_BBA_CAL_GAIN1(1) | + XCVR_RX_DIG_DCOC_CAL_GAIN_DCOC_LNA_CAL_GAIN1(1) | + XCVR_RX_DIG_DCOC_CAL_GAIN_DCOC_BBA_CAL_GAIN2(1) | + XCVR_RX_DIG_DCOC_CAL_GAIN_DCOC_LNA_CAL_GAIN2(2) | + XCVR_RX_DIG_DCOC_CAL_GAIN_DCOC_BBA_CAL_GAIN3(3) | + XCVR_RX_DIG_DCOC_CAL_GAIN_DCOC_LNA_CAL_GAIN3(1) , + + .dcoc_cal_rcp_init = XCVR_RX_DIG_DCOC_CAL_RCP_ALPHA_CALC_RECIP(1) | + XCVR_RX_DIG_DCOC_CAL_RCP_DCOC_TMP_CALC_RECIP(711), + + .lna_gain_val_3_0 = XCVR_RX_DIG_LNA_GAIN_VAL_3_0_LNA_GAIN_VAL_0(0x1DU) | + XCVR_RX_DIG_LNA_GAIN_VAL_3_0_LNA_GAIN_VAL_1(0x32U) | + XCVR_RX_DIG_LNA_GAIN_VAL_3_0_LNA_GAIN_VAL_2(0x09U) | + XCVR_RX_DIG_LNA_GAIN_VAL_3_0_LNA_GAIN_VAL_3(0x38U), + + .lna_gain_val_7_4 = XCVR_RX_DIG_LNA_GAIN_VAL_7_4_LNA_GAIN_VAL_4(0x4FU) | + XCVR_RX_DIG_LNA_GAIN_VAL_7_4_LNA_GAIN_VAL_5(0x5BU) | + XCVR_RX_DIG_LNA_GAIN_VAL_7_4_LNA_GAIN_VAL_6(0x72U) | + XCVR_RX_DIG_LNA_GAIN_VAL_7_4_LNA_GAIN_VAL_7(0x8AU), + .lna_gain_val_8 = XCVR_RX_DIG_LNA_GAIN_VAL_8_LNA_GAIN_VAL_8(0xA0U) | + XCVR_RX_DIG_LNA_GAIN_VAL_8_LNA_GAIN_VAL_9(0xB6U), + + .bba_res_tune_val_7_0 = XCVR_RX_DIG_BBA_RES_TUNE_VAL_7_0_BBA_RES_TUNE_VAL_0(0x0) | + XCVR_RX_DIG_BBA_RES_TUNE_VAL_7_0_BBA_RES_TUNE_VAL_1(0x0) | + XCVR_RX_DIG_BBA_RES_TUNE_VAL_7_0_BBA_RES_TUNE_VAL_2(0x0) | + XCVR_RX_DIG_BBA_RES_TUNE_VAL_7_0_BBA_RES_TUNE_VAL_3(0x0) | + XCVR_RX_DIG_BBA_RES_TUNE_VAL_7_0_BBA_RES_TUNE_VAL_4(0x0) | + XCVR_RX_DIG_BBA_RES_TUNE_VAL_7_0_BBA_RES_TUNE_VAL_5(0x0) | + XCVR_RX_DIG_BBA_RES_TUNE_VAL_7_0_BBA_RES_TUNE_VAL_6(0x0) | + XCVR_RX_DIG_BBA_RES_TUNE_VAL_7_0_BBA_RES_TUNE_VAL_7(0xF), + .bba_res_tune_val_10_8 = XCVR_RX_DIG_BBA_RES_TUNE_VAL_10_8_BBA_RES_TUNE_VAL_8(0x0) | + XCVR_RX_DIG_BBA_RES_TUNE_VAL_10_8_BBA_RES_TUNE_VAL_9(0x1) | + XCVR_RX_DIG_BBA_RES_TUNE_VAL_10_8_BBA_RES_TUNE_VAL_10(0x2), + + .lna_gain_lin_val_2_0_init = XCVR_RX_DIG_LNA_GAIN_LIN_VAL_2_0_LNA_GAIN_LIN_VAL_0(0) | + XCVR_RX_DIG_LNA_GAIN_LIN_VAL_2_0_LNA_GAIN_LIN_VAL_1(0) | + XCVR_RX_DIG_LNA_GAIN_LIN_VAL_2_0_LNA_GAIN_LIN_VAL_2(1), + + .lna_gain_lin_val_5_3_init = XCVR_RX_DIG_LNA_GAIN_LIN_VAL_5_3_LNA_GAIN_LIN_VAL_3(3) | + XCVR_RX_DIG_LNA_GAIN_LIN_VAL_5_3_LNA_GAIN_LIN_VAL_4(5) | + XCVR_RX_DIG_LNA_GAIN_LIN_VAL_5_3_LNA_GAIN_LIN_VAL_5(7), + + .lna_gain_lin_val_8_6_init = XCVR_RX_DIG_LNA_GAIN_LIN_VAL_8_6_LNA_GAIN_LIN_VAL_6(14) | + XCVR_RX_DIG_LNA_GAIN_LIN_VAL_8_6_LNA_GAIN_LIN_VAL_7(27) | + XCVR_RX_DIG_LNA_GAIN_LIN_VAL_8_6_LNA_GAIN_LIN_VAL_8(50), + + .lna_gain_lin_val_9_init = XCVR_RX_DIG_LNA_GAIN_LIN_VAL_9_LNA_GAIN_LIN_VAL_9(91), + + .bba_res_tune_lin_val_3_0_init = XCVR_RX_DIG_BBA_RES_TUNE_LIN_VAL_3_0_BBA_RES_TUNE_LIN_VAL_0(8) | + XCVR_RX_DIG_BBA_RES_TUNE_LIN_VAL_3_0_BBA_RES_TUNE_LIN_VAL_1(11) | + XCVR_RX_DIG_BBA_RES_TUNE_LIN_VAL_3_0_BBA_RES_TUNE_LIN_VAL_2(16) | + XCVR_RX_DIG_BBA_RES_TUNE_LIN_VAL_3_0_BBA_RES_TUNE_LIN_VAL_3(22), + + .bba_res_tune_lin_val_7_4_init = XCVR_RX_DIG_BBA_RES_TUNE_LIN_VAL_7_4_BBA_RES_TUNE_LIN_VAL_4(31) | + XCVR_RX_DIG_BBA_RES_TUNE_LIN_VAL_7_4_BBA_RES_TUNE_LIN_VAL_5(44) | + XCVR_RX_DIG_BBA_RES_TUNE_LIN_VAL_7_4_BBA_RES_TUNE_LIN_VAL_6(62) | + XCVR_RX_DIG_BBA_RES_TUNE_LIN_VAL_7_4_BBA_RES_TUNE_LIN_VAL_7(42), /* Has 2 fractional bits unlike other BBA_RES_TUNE_LIN_VALs */ + + .bba_res_tune_lin_val_10_8_init = XCVR_RX_DIG_BBA_RES_TUNE_LIN_VAL_10_8_BBA_RES_TUNE_LIN_VAL_8(128) | + XCVR_RX_DIG_BBA_RES_TUNE_LIN_VAL_10_8_BBA_RES_TUNE_LIN_VAL_9(188) | + XCVR_RX_DIG_BBA_RES_TUNE_LIN_VAL_10_8_BBA_RES_TUNE_LIN_VAL_10(288), + + .dcoc_bba_step_init = XCVR_RX_DIG_DCOC_BBA_STEP_BBA_DCOC_STEP_RECIP(939) | + XCVR_RX_DIG_DCOC_BBA_STEP_BBA_DCOC_STEP(279), + + .dcoc_tza_step_00_init = XCVR_RX_DIG_DCOC_TZA_STEP_0_DCOC_TZA_STEP_GAIN_0(77) | + XCVR_RX_DIG_DCOC_TZA_STEP_0_DCOC_TZA_STEP_RCP_0(3404), + .dcoc_tza_step_01_init = XCVR_RX_DIG_DCOC_TZA_STEP_1_DCOC_TZA_STEP_GAIN_1(108) | + XCVR_RX_DIG_DCOC_TZA_STEP_1_DCOC_TZA_STEP_RCP_1(2439), + .dcoc_tza_step_02_init = XCVR_RX_DIG_DCOC_TZA_STEP_2_DCOC_TZA_STEP_GAIN_2(155) | + XCVR_RX_DIG_DCOC_TZA_STEP_2_DCOC_TZA_STEP_RCP_2(1691), + .dcoc_tza_step_03_init = XCVR_RX_DIG_DCOC_TZA_STEP_3_DCOC_TZA_STEP_GAIN_3(220) | + XCVR_RX_DIG_DCOC_TZA_STEP_3_DCOC_TZA_STEP_RCP_3(1192), + .dcoc_tza_step_04_init = XCVR_RX_DIG_DCOC_TZA_STEP_4_DCOC_TZA_STEP_GAIN_4(314) | + XCVR_RX_DIG_DCOC_TZA_STEP_4_DCOC_TZA_STEP_RCP_4(835), + .dcoc_tza_step_05_init = XCVR_RX_DIG_DCOC_TZA_STEP_5_DCOC_TZA_STEP_GAIN_5(436) | + XCVR_RX_DIG_DCOC_TZA_STEP_5_DCOC_TZA_STEP_RCP_5(601), + .dcoc_tza_step_06_init = XCVR_RX_DIG_DCOC_TZA_STEP_6_DCOC_TZA_STEP_GAIN_6(614) | + XCVR_RX_DIG_DCOC_TZA_STEP_6_DCOC_TZA_STEP_RCP_6(427), + .dcoc_tza_step_07_init = XCVR_RX_DIG_DCOC_TZA_STEP_7_DCOC_TZA_STEP_GAIN_7(845) | + XCVR_RX_DIG_DCOC_TZA_STEP_7_DCOC_TZA_STEP_RCP_7(310), + .dcoc_tza_step_08_init = XCVR_RX_DIG_DCOC_TZA_STEP_8_DCOC_TZA_STEP_GAIN_8(1256) | + XCVR_RX_DIG_DCOC_TZA_STEP_8_DCOC_TZA_STEP_RCP_8(209), + .dcoc_tza_step_09_init = XCVR_RX_DIG_DCOC_TZA_STEP_9_DCOC_TZA_STEP_GAIN_9(1805) | + XCVR_RX_DIG_DCOC_TZA_STEP_9_DCOC_TZA_STEP_RCP_9(145), + .dcoc_tza_step_10_init = XCVR_RX_DIG_DCOC_TZA_STEP_10_DCOC_TZA_STEP_GAIN_10(2653) | + XCVR_RX_DIG_DCOC_TZA_STEP_10_DCOC_TZA_STEP_RCP_10(99), +#if (RADIO_IS_GEN_2P1) + .dcoc_cal_fail_th_init = XCVR_RX_DIG_DCOC_CAL_FAIL_TH_DCOC_CAL_BETA_F_TH(20) | + XCVR_RX_DIG_DCOC_CAL_FAIL_TH_DCOC_CAL_ALPHA_F_TH(10), + .dcoc_cal_pass_th_init = XCVR_RX_DIG_DCOC_CAL_PASS_TH_DCOC_CAL_BETA_P_TH(16) | + XCVR_RX_DIG_DCOC_CAL_PASS_TH_DCOC_CAL_ALPHA_P_TH(2), +#endif /* (RADIO_IS_GEN_2P1) */ + /* AGC Configs */ + .agc_gain_tbl_03_00_init = XCVR_RX_DIG_AGC_GAIN_TBL_03_00_LNA_GAIN_00(0) | + XCVR_RX_DIG_AGC_GAIN_TBL_03_00_BBA_GAIN_00(0) | + XCVR_RX_DIG_AGC_GAIN_TBL_03_00_LNA_GAIN_01(1) | + XCVR_RX_DIG_AGC_GAIN_TBL_03_00_BBA_GAIN_01(1) | + XCVR_RX_DIG_AGC_GAIN_TBL_03_00_LNA_GAIN_02(2) | + XCVR_RX_DIG_AGC_GAIN_TBL_03_00_BBA_GAIN_02(1) | + XCVR_RX_DIG_AGC_GAIN_TBL_03_00_LNA_GAIN_03(2) | + XCVR_RX_DIG_AGC_GAIN_TBL_03_00_BBA_GAIN_03(2), + + .agc_gain_tbl_07_04_init = XCVR_RX_DIG_AGC_GAIN_TBL_07_04_LNA_GAIN_04(2) | + XCVR_RX_DIG_AGC_GAIN_TBL_07_04_BBA_GAIN_04(3) | + XCVR_RX_DIG_AGC_GAIN_TBL_07_04_LNA_GAIN_05(3) | + XCVR_RX_DIG_AGC_GAIN_TBL_07_04_BBA_GAIN_05(0) | + XCVR_RX_DIG_AGC_GAIN_TBL_07_04_LNA_GAIN_06(3) | + XCVR_RX_DIG_AGC_GAIN_TBL_07_04_BBA_GAIN_06(1) | + XCVR_RX_DIG_AGC_GAIN_TBL_07_04_LNA_GAIN_07(3) | + XCVR_RX_DIG_AGC_GAIN_TBL_07_04_BBA_GAIN_07(2), + + .agc_gain_tbl_11_08_init = XCVR_RX_DIG_AGC_GAIN_TBL_11_08_LNA_GAIN_08(3) | + XCVR_RX_DIG_AGC_GAIN_TBL_11_08_BBA_GAIN_08(3) | + XCVR_RX_DIG_AGC_GAIN_TBL_11_08_LNA_GAIN_09(4) | + XCVR_RX_DIG_AGC_GAIN_TBL_11_08_BBA_GAIN_09(2) | + XCVR_RX_DIG_AGC_GAIN_TBL_11_08_LNA_GAIN_10(4) | + XCVR_RX_DIG_AGC_GAIN_TBL_11_08_BBA_GAIN_10(3) | + XCVR_RX_DIG_AGC_GAIN_TBL_11_08_LNA_GAIN_11(4) | + XCVR_RX_DIG_AGC_GAIN_TBL_11_08_BBA_GAIN_11(4), + + .agc_gain_tbl_15_12_init = XCVR_RX_DIG_AGC_GAIN_TBL_15_12_LNA_GAIN_12(5) | + XCVR_RX_DIG_AGC_GAIN_TBL_15_12_BBA_GAIN_12(4) | + XCVR_RX_DIG_AGC_GAIN_TBL_15_12_LNA_GAIN_13(5) | + XCVR_RX_DIG_AGC_GAIN_TBL_15_12_BBA_GAIN_13(5) | + XCVR_RX_DIG_AGC_GAIN_TBL_15_12_LNA_GAIN_14(6) | + XCVR_RX_DIG_AGC_GAIN_TBL_15_12_BBA_GAIN_14(4) | + XCVR_RX_DIG_AGC_GAIN_TBL_15_12_LNA_GAIN_15(6) | + XCVR_RX_DIG_AGC_GAIN_TBL_15_12_BBA_GAIN_15(5), + + .agc_gain_tbl_19_16_init = XCVR_RX_DIG_AGC_GAIN_TBL_19_16_LNA_GAIN_16(6) | + XCVR_RX_DIG_AGC_GAIN_TBL_19_16_BBA_GAIN_16(6) | + XCVR_RX_DIG_AGC_GAIN_TBL_19_16_LNA_GAIN_17(6) | + XCVR_RX_DIG_AGC_GAIN_TBL_19_16_BBA_GAIN_17(7) | + XCVR_RX_DIG_AGC_GAIN_TBL_19_16_LNA_GAIN_18(7) | + XCVR_RX_DIG_AGC_GAIN_TBL_19_16_BBA_GAIN_18(6) | + XCVR_RX_DIG_AGC_GAIN_TBL_19_16_LNA_GAIN_19(7) | + XCVR_RX_DIG_AGC_GAIN_TBL_19_16_BBA_GAIN_19(7), + + .agc_gain_tbl_23_20_init = XCVR_RX_DIG_AGC_GAIN_TBL_23_20_LNA_GAIN_20(8) | + XCVR_RX_DIG_AGC_GAIN_TBL_23_20_BBA_GAIN_20(6) | + XCVR_RX_DIG_AGC_GAIN_TBL_23_20_LNA_GAIN_21(8) | + XCVR_RX_DIG_AGC_GAIN_TBL_23_20_BBA_GAIN_21(7) | + XCVR_RX_DIG_AGC_GAIN_TBL_23_20_LNA_GAIN_22(9) | + XCVR_RX_DIG_AGC_GAIN_TBL_23_20_BBA_GAIN_22(6) | + XCVR_RX_DIG_AGC_GAIN_TBL_23_20_LNA_GAIN_23(9) | + XCVR_RX_DIG_AGC_GAIN_TBL_23_20_BBA_GAIN_23(7), + + .agc_gain_tbl_26_24_init = XCVR_RX_DIG_AGC_GAIN_TBL_26_24_LNA_GAIN_24(9) | + XCVR_RX_DIG_AGC_GAIN_TBL_26_24_BBA_GAIN_24(8) | + XCVR_RX_DIG_AGC_GAIN_TBL_26_24_LNA_GAIN_25(9) | + XCVR_RX_DIG_AGC_GAIN_TBL_26_24_BBA_GAIN_25(9) | + XCVR_RX_DIG_AGC_GAIN_TBL_26_24_LNA_GAIN_26(9) | + XCVR_RX_DIG_AGC_GAIN_TBL_26_24_BBA_GAIN_26(10), + + .rssi_ctrl_0_init = XCVR_RX_DIG_RSSI_CTRL_0_RSSI_USE_VALS(1) | + XCVR_RX_DIG_RSSI_CTRL_0_RSSI_HOLD_SRC(0) | + XCVR_RX_DIG_RSSI_CTRL_0_RSSI_HOLD_EN(1) | + XCVR_RX_DIG_RSSI_CTRL_0_RSSI_IIR_CW_WEIGHT(0) | +#if !RADIO_IS_GEN_2P1 + XCVR_RX_DIG_RSSI_CTRL_0_RSSI_N_WINDOW_AVG(1) | +#else + XCVR_RX_DIG_RSSI_CTRL_0_RSSI_N_WINDOW_NB(1) | +#endif /* !RADIO_IS_GEN_2P1 */ + XCVR_RX_DIG_RSSI_CTRL_0_RSSI_HOLD_DELAY(4) | + XCVR_RX_DIG_RSSI_CTRL_0_RSSI_IIR_WEIGHT(3) | + XCVR_RX_DIG_RSSI_CTRL_0_RSSI_VLD_SETTLE(3) | + XCVR_RX_DIG_RSSI_CTRL_0_RSSI_ADJ(0xE8) , + + .cca_ed_lqi_ctrl_0_init = XCVR_RX_DIG_CCA_ED_LQI_CTRL_0_LQI_CORR_THRESH(0) | + XCVR_RX_DIG_CCA_ED_LQI_CTRL_0_CORR_CNTR_THRESH(0) | + XCVR_RX_DIG_CCA_ED_LQI_CTRL_0_LQI_CNTR(0x1A) | + XCVR_RX_DIG_CCA_ED_LQI_CTRL_0_SNR_ADJ(0), + + .cca_ed_lqi_ctrl_1_init = XCVR_RX_DIG_CCA_ED_LQI_CTRL_1_RSSI_NOISE_AVG_DELAY(0) | + XCVR_RX_DIG_CCA_ED_LQI_CTRL_1_RSSI_NOISE_AVG_FACTOR(0) | + XCVR_RX_DIG_CCA_ED_LQI_CTRL_1_LQI_RSSI_WEIGHT(0x4) | + XCVR_RX_DIG_CCA_ED_LQI_CTRL_1_LQI_RSSI_SENS(0x7) | + XCVR_RX_DIG_CCA_ED_LQI_CTRL_1_SNR_LQI_DIS(0) | +#if !RADIO_IS_GEN_2P1 + XCVR_RX_DIG_CCA_ED_LQI_CTRL_1_SEL_SNR_MODE(0) | +#endif /* !RADIO_IS_GEN_2P1 */ + XCVR_RX_DIG_CCA_ED_LQI_CTRL_1_MEAS_TRANS_TO_IDLE(0) | + XCVR_RX_DIG_CCA_ED_LQI_CTRL_1_CCA1_ED_EN_DIS(0) | + XCVR_RX_DIG_CCA_ED_LQI_CTRL_1_MAN_MEAS_COMPLETE(0) | + XCVR_RX_DIG_CCA_ED_LQI_CTRL_1_MAN_AA_MATCH(0) | + XCVR_RX_DIG_CCA_ED_LQI_CTRL_1_SNR_LQI_WEIGHT(0x5) | + XCVR_RX_DIG_CCA_ED_LQI_CTRL_1_LQI_BIAS(0x2), + + /* XCVR_TSM configs */ + .tsm_ctrl = XCVR_TSM_CTRL_PA_RAMP_SEL(PA_RAMP_SEL) | + XCVR_TSM_CTRL_DATA_PADDING_EN(DATA_PADDING_EN) | + XCVR_TSM_CTRL_TSM_IRQ0_EN(0) | + XCVR_TSM_CTRL_TSM_IRQ1_EN(0) | + XCVR_TSM_CTRL_RAMP_DN_DELAY(0x4) | + XCVR_TSM_CTRL_TX_ABORT_DIS(0) | + XCVR_TSM_CTRL_RX_ABORT_DIS(0) | + XCVR_TSM_CTRL_ABORT_ON_CTUNE(0) | + XCVR_TSM_CTRL_ABORT_ON_CYCLE_SLIP(0) | + XCVR_TSM_CTRL_ABORT_ON_FREQ_TARG(0) | + XCVR_TSM_CTRL_BKPT(0xFF) , + + .tsm_ovrd2_init = XCVR_TSM_OVRD2_FREQ_TARG_LD_EN_OVRD(0) | XCVR_TSM_OVRD2_FREQ_TARG_LD_EN_OVRD_EN_MASK, + .end_of_seq_init_26mhz = B3(END_OF_RX_WD_26MHZ) | B2(END_OF_RX_WU_26MHZ) | B1(END_OF_TX_WD) | B0(END_OF_TX_WU), + .end_of_seq_init_32mhz = B3(END_OF_RX_WD) | B2(END_OF_RX_WU) | B1(END_OF_TX_WD) | B0(END_OF_TX_WU), + +#if !RADIO_IS_GEN_2P1 + .lpps_ctrl_init = B3(102) | B2(40) | B1(0) | B0(0), +#endif /* !RADIO_IS_GEN_2P1 */ + + .tsm_fast_ctrl2_init_26mhz = B3(102 + ADD_FOR_26MHZ) | B2(40 + ADD_FOR_26MHZ) | B1(66) | B0(8), + .tsm_fast_ctrl2_init_32mhz = B3(102) | B2(40) | B1(66) | B0(8), + + .pa_ramp_tbl_0_init = XCVR_TSM_PA_RAMP_TBL0_PA_RAMP0(PA_RAMP_0) | XCVR_TSM_PA_RAMP_TBL0_PA_RAMP1(PA_RAMP_1) | + XCVR_TSM_PA_RAMP_TBL0_PA_RAMP2(PA_RAMP_2) | XCVR_TSM_PA_RAMP_TBL0_PA_RAMP3(PA_RAMP_3), + .pa_ramp_tbl_1_init = XCVR_TSM_PA_RAMP_TBL1_PA_RAMP4(PA_RAMP_4) | XCVR_TSM_PA_RAMP_TBL1_PA_RAMP5(PA_RAMP_5) | + XCVR_TSM_PA_RAMP_TBL1_PA_RAMP6(PA_RAMP_6) | XCVR_TSM_PA_RAMP_TBL1_PA_RAMP7(PA_RAMP_7), + + .recycle_count_init_26mhz = B3(0) | B2(0x1C + ADD_FOR_26MHZ) | B1(0x06) | B0(0x66 + ADD_FOR_26MHZ), + .recycle_count_init_32mhz = B3(0) | B2(0x1C) | B1(0x06) | B0(0x66), + + .tsm_timing_00_init = B3(END_OF_RX_WD) | B2(0x00) | B1(END_OF_TX_WD) | B0(0x00), /* bb_ldo_hf_en */ + .tsm_timing_01_init = B3(END_OF_RX_WD) | B2(0x00) | B1(END_OF_TX_WD) | B0(0x00), /* bb_ldo_adcdac_en */ + .tsm_timing_02_init = B3(END_OF_RX_WD) | B2(0x00) | B1(0xFF) | B0(0xFF), /* bb_ldo_bba_en */ + .tsm_timing_03_init = B3(END_OF_RX_WD) | B2(0x00) | B1(END_OF_TX_WD) | B0(0x00), /* bb_ldo_pd_en */ + .tsm_timing_04_init = B3(END_OF_RX_WD) | B2(0x00) | B1(END_OF_TX_WD) | B0(0x00), /* bb_ldo_fdbk_en */ + .tsm_timing_05_init = B3(END_OF_RX_WD) | B2(0x00) | B1(END_OF_TX_WD) | B0(0x00), /* bb_ldo_vcolo_en */ + .tsm_timing_06_init = B3(END_OF_RX_WD) | B2(0x00) | B1(END_OF_TX_WD) | B0(0x00), /* bb_ldo_vtref_en */ + .tsm_timing_07_init = B3(0x05) | B2(0x00) | B1(0x05) | B0(0x00), /* bb_ldo_fdbk_bleed_en */ + .tsm_timing_08_init = B3(0x03) | B2(0x00) | B1(0x03) | B0(0x00), /* bb_ldo_vcolo_bleed_en */ + .tsm_timing_09_init = B3(0x03) | B2(0x00) | B1(0x03) | B0(0x00), /* bb_ldo_vcolo_fastcharge_en */ + + .tsm_timing_10_init = B3(END_OF_RX_WD) | B2(0x03 + AUX_PLL_DELAY) | B1(END_OF_TX_WD) | B0(0x03), /* bb_xtal_pll_ref_clk_en */ + .tsm_timing_11_init = B3(0xFF) | B2(0xFF) | B1(END_OF_TX_WD) | B0(0x03), /* bb_xtal_dac_ref_clk_en */ + .tsm_timing_12_init = B3(END_OF_RX_WD) | B2(0x03 + AUX_PLL_DELAY) | B1(0xFF) | B0(0xFF), /* rxtx_auxpll_vco_ref_clk_en */ + .tsm_timing_13_init = B3(0x18) | B2(0x00) | B1(0x4C) | B0(0x00), /* sy_vco_autotune_en */ + .tsm_timing_14_init_26mhz = B3(END_OF_RX_WD_26MHZ) | B2(0x31+ADD_FOR_26MHZ) | B1(END_OF_TX_WU + PD_CYCLE_SLIP_TX_LO_ADJ) | B0(0x63 + PD_CYCLE_SLIP_TX_HI_ADJ), /* sy_pd_cycle_slip_ld_ft_en */ + .tsm_timing_14_init_32mhz = B3(END_OF_RX_WD) | B2(0x31 + AUX_PLL_DELAY) | B1(END_OF_TX_WU + PD_CYCLE_SLIP_TX_LO_ADJ) | B0(0x63 + PD_CYCLE_SLIP_TX_HI_ADJ), + .tsm_timing_15_init = B3(END_OF_RX_WD) | B2(0x03 + AUX_PLL_DELAY) | B1(END_OF_TX_WD) | B0(0x03), /* sy_vco_en */ + .tsm_timing_16_init_26mhz = B3(END_OF_RX_WD_26MHZ) | B2(0x1C + ADD_FOR_26MHZ) | B1(0xFF) | B0(0xFF), /* sy_lo_rx_buf_en */ + .tsm_timing_16_init_32mhz = B3(END_OF_RX_WD) | B2(0x1C + AUX_PLL_DELAY) | B1(0xFF) | B0(0xFF), + .tsm_timing_17_init = B3(0xFF) | B2(0xFF) | B1(END_OF_TX_WD) | B0(0x55), /* sy_lo_tx_buf_en */ + .tsm_timing_18_init = B3(END_OF_RX_WD) | B2(0x05 + AUX_PLL_DELAY) | B1(END_OF_TX_WD) | B0(0x05), /* sy_divn_en */ + .tsm_timing_19_init = B3(0x18+AUX_PLL_DELAY) | B2(0x03 + AUX_PLL_DELAY) | B1(0x4C) | B0(0x03), /* sy_pd_filter_charge_en */ + + .tsm_timing_20_init = B3(END_OF_RX_WD) | B2(0x03 + AUX_PLL_DELAY) | B1(END_OF_TX_WD) | B0(0x03), /* sy_pd_en */ + .tsm_timing_21_init = B3(END_OF_RX_WD) | B2(0x04 + AUX_PLL_DELAY) | B1(END_OF_TX_WD) | B0(0x04), /* sy_lo_divn_en */ + .tsm_timing_22_init = B3(END_OF_RX_WD) | B2(0x04 + AUX_PLL_DELAY) | B1(0xFF) | B0(0xFF), /* sy_lo_rx_en */ + .tsm_timing_23_init = B3(0xFF) | B2(0xFF) | B1(END_OF_TX_WD) | B0(0x04), /*sy_lo_tx_en */ + .tsm_timing_24_init = B3(0x18) | B2(0x00) | B1(0x4C) | B0(0x00), /* sy_divn_cal_en */ + .tsm_timing_25_init_26mhz = B3(END_OF_RX_WD_26MHZ) | B2(0x1D + ADD_FOR_26MHZ + AUX_PLL_DELAY) | B1(0xFF) | B0(0xFF), /* rx_lna_mixer_en */ + .tsm_timing_25_init_32mhz = B3(END_OF_RX_WD) | B2(0x1D + AUX_PLL_DELAY) | B1(0xFF) | B0(0xFF), + .tsm_timing_26_init = B3(0xFF) | B2(0xFF) | B1(END_OF_TX_WD) | B0(0x58), /* tx_pa_en */ + .tsm_timing_27_init_26mhz = B3(END_OF_RX_WD_26MHZ) | B2(0x20 + ADD_FOR_26MHZ + AUX_PLL_DELAY) | B1(0xFF) | B0(0xFF), /* rx_adc_i_q_en */ + .tsm_timing_27_init_32mhz = B3(END_OF_RX_WD) | B2(0x20 + AUX_PLL_DELAY) | B1(0xFF) | B0(0xFF), + .tsm_timing_28_init_26mhz = B3(0x21 + ADD_FOR_26MHZ) | B2(0x20 + ADD_FOR_26MHZ + AUX_PLL_DELAY) | B1(0xFF) | B0(0xFF), /* rx_adc_reset_en */ + .tsm_timing_28_init_32mhz = B3(0x21 + AUX_PLL_DELAY) | B2(0x20 + AUX_PLL_DELAY) | B1(0xFF) | B0(0xFF), + .tsm_timing_29_init_26mhz = B3(END_OF_RX_WD_26MHZ) | B2(0x1E + ADD_FOR_26MHZ + AUX_PLL_DELAY) | B1(0xFF) | B0(0xFF), /* rx_bba_i_q_en */ + .tsm_timing_29_init_32mhz = B3(END_OF_RX_WD) | B2(0x1E + AUX_PLL_DELAY) | B1(0xFF) | B0(0xFF), + + .tsm_timing_30_init_26mhz = B3(END_OF_RX_WD_26MHZ) | B2(0x20 + ADD_FOR_26MHZ + AUX_PLL_DELAY) | B1(0xFF) | B0(0xFF), /* rx_bba_pdet_en */ + .tsm_timing_30_init_32mhz = B3(END_OF_RX_WD) | B2(0x20 + AUX_PLL_DELAY) | B1(0xFF) | B0(0xFF), + .tsm_timing_31_init_26mhz = B3(END_OF_RX_WD_26MHZ) | B2(0x1F + ADD_FOR_26MHZ + AUX_PLL_DELAY) | B1(0xFF) | B0(0xFF), /* rx_bba_tza_dcoc_en */ + .tsm_timing_31_init_32mhz = B3(END_OF_RX_WD) | B2(0x1F + AUX_PLL_DELAY) | B1(0xFF) | B0(0xFF), + .tsm_timing_32_init_26mhz = B3(END_OF_RX_WD_26MHZ) | B2(0x1D + ADD_FOR_26MHZ + AUX_PLL_DELAY) | B1(0xFF) | B0(0xFF), /* rx_tza_i_q_en */ + .tsm_timing_32_init_32mhz = B3(END_OF_RX_WD) | B2(0x1D + AUX_PLL_DELAY) | B1(0xFF) | B0(0xFF), + .tsm_timing_33_init_26mhz = B3(END_OF_RX_WD_26MHZ) | B2(0x20 + ADD_FOR_26MHZ + AUX_PLL_DELAY) | B1(0xFF) | B0(0xFF), /* rx_tza_pdet_en */ + .tsm_timing_33_init_32mhz = B3(END_OF_RX_WD) | B2(0x20 + AUX_PLL_DELAY) | B1(0xFF) | B0(0xFF), + .tsm_timing_34_init = B3(END_OF_RX_WD) | B2(0x07 + AUX_PLL_DELAY) | B1(END_OF_TX_WD) | B0(0x07), /* pll_dig_en */ + .tsm_timing_35_init = B3(0xFF) | B2(0xFF) | B1(END_OF_TX_WD), /* tx_dig_en - Byte 0 comes from mode specific settings */ + .tsm_timing_36_init_26mhz = B3(END_OF_RX_WD) | B2(0x66 + ADD_FOR_26MHZ + AUX_PLL_DELAY) | B1(0xFF) | B0(0xFF), /* rx_dig_en */ + .tsm_timing_36_init_32mhz = B3(END_OF_RX_WD) | B2(0x66 + AUX_PLL_DELAY) | B1(0xFF) | B0(0xFF), + .tsm_timing_37_init_26mhz = B3(0x67 + ADD_FOR_26MHZ + AUX_PLL_DELAY) | B2(0x66 + ADD_FOR_26MHZ + AUX_PLL_DELAY) | B1(0xFF) | B0(0xFF), /* rx_init */ + .tsm_timing_37_init_32mhz = B3(0x67 + AUX_PLL_DELAY) | B2(0x66 + AUX_PLL_DELAY) | B1(0xFF) | B0(0xFF), + .tsm_timing_38_init = B3(END_OF_RX_WD) | B2(0x0E + AUX_PLL_DELAY) | B1(END_OF_TX_WD) | B0(0x42), /* sigma_delta_en */ + .tsm_timing_39_init_26mhz = B3(END_OF_RX_WD_26MHZ) | B2(0x66 + ADD_FOR_26MHZ + AUX_PLL_DELAY) | B1(0xFF) | B0(0xFF), /* rx_phy_en */ + .tsm_timing_39_init_32mhz = B3(END_OF_RX_WD) | B2(0x66 + AUX_PLL_DELAY) | B1(0xFF) | B0(0xFF), + + .tsm_timing_40_init_26mhz = B3(END_OF_RX_WD_26MHZ) | B2(0x26 + ADD_FOR_26MHZ + AUX_PLL_DELAY) | B1(0xFF) | B0(0xFF), /* dcoc_en */ + .tsm_timing_40_init_32mhz = B3(END_OF_RX_WD) | B2(0x26 + AUX_PLL_DELAY) | B1(0xFF) | B0(0xFF), + .tsm_timing_41_init_26mhz = B3(0x27 + ADD_FOR_26MHZ + AUX_PLL_DELAY) | B2(0x26 + ADD_FOR_26MHZ + AUX_PLL_DELAY) | B1(0xFF) | B0(0xFF), /* dcoc_init */ + .tsm_timing_41_init_32mhz = B3(0x27 + AUX_PLL_DELAY) | B2(0x26 + AUX_PLL_DELAY) | B1(0xFF) | B0(0xFF), + .tsm_timing_51_init = B3(END_OF_RX_WD) | B2(0x03 + AUX_PLL_DELAY) | B1(0xFF) | B0(0xFF), /* rxtx_auxpll_bias_en */ + .tsm_timing_52_init_26mhz = B3(0x17 + ADD_FOR_26MHZ + AUX_PLL_DELAY) | B2(0x06 + ADD_FOR_26MHZ + AUX_PLL_DELAY) | B1(0xFF) | B0(0xFF), /* rxtx_auxpll_fcal_en */ + .tsm_timing_52_init_32mhz = B3(0x17 + AUX_PLL_DELAY) | B2(0x06 + AUX_PLL_DELAY) | B1(0xFF) | B0(0xFF), + .tsm_timing_53_init = B3(END_OF_RX_WD) | B2(0x03 + AUX_PLL_DELAY) | B1(0xFF) | B0(0xFF), /* rxtx_auxpll_lf_pd_en */ + .tsm_timing_54_init_26mhz = B3(0x17 + ADD_FOR_26MHZ + AUX_PLL_DELAY) | B2(0x03 + ADD_FOR_26MHZ + AUX_PLL_DELAY) | B1(0xFF) | B0(0xFF), /* rxtx_auxpll_pd_lf_filter_charge_en */ + .tsm_timing_54_init_32mhz = B3(0x17 + AUX_PLL_DELAY) | B2(0x03 + AUX_PLL_DELAY) | B1(0xFF) | B0(0xFF), + .tsm_timing_55_init_26mhz = B3(END_OF_RX_WD_26MHZ) | B2(0x20 + ADD_FOR_26MHZ + AUX_PLL_DELAY) | B1(0xFF) | B0(0xFF), /* rxtx_auxpll_adc_buf_en */ + .tsm_timing_55_init_32mhz = B3(END_OF_RX_WD) | B2(0x20 + AUX_PLL_DELAY) | B1(0xFF) | B0(0xFF), + .tsm_timing_56_init_26mhz = B3(END_OF_RX_WD_26MHZ) | B2(0x20 + ADD_FOR_26MHZ + AUX_PLL_DELAY) | B1(0xFF) | B0(0xFF), /* rxtx_auxpll_dig_buf_en */ + .tsm_timing_56_init_32mhz = B3(END_OF_RX_WD) | B2(0x20 + AUX_PLL_DELAY) | B1(0xFF) | B0(0xFF), + .tsm_timing_57_init = B3(0x1A + AUX_PLL_DELAY) | B2(0x03 + AUX_PLL_DELAY) | B1(0xFF) | B0(0xFF), /*rxtx_rccal_en */ + .tsm_timing_58_init = B3(0xFF) | B2(0xFF) | B1(END_OF_TX_WD) | B0(0x03), /* tx_hpm_dac_en */ + +/* XCVR_TX_DIG configs */ +#define radio_dft_mode 0 +#define lfsr_length 4 +#define lfsr_en 0 +#define dft_clk_sel 4 +#define tx_dft_en 0 +#define soc_test_sel 0 +#define tx_capture_pol 0 +#define freq_word_adj 0 +#define lrm 0 +#define data_padding_pat_1 0x55 +#define data_padding_pat_0 0xAA +#define gfsk_multiply_table_manual 0 +#define gfsk_mi 1 +#define gfsk_mld 0 +#define gfsk_fld 0 +#define gfsk_mod_index_scaling 0 +#define tx_image_filter_ovrd_en 0 +#define tx_image_filter_0_ovrd 0 +#define tx_image_filter_1_ovrd 0 +#define tx_image_filter_2_ovrd 0 +#define gfsk_filter_coeff_manual2 0xC0630401 +#define gfsk_filter_coeff_manual1 0xBB29960D +#define fsk_modulation_scale_0 0x1800 +#define fsk_modulation_scale_1 0x0800 +#define dft_mod_patternval 0 +#define ctune_bist_go 0 +#define ctune_bist_thrshld 0 +#define pa_am_mod_freq 0 +#define pa_am_mod_entries 0 +#define pa_am_mod_en 0 +#define syn_bist_go 0 +#define syn_bist_all_channels 0 +#define freq_count_threshold 0 +#define hpm_inl_bist_go 0 +#define hpm_dnl_bist_go 0 +#define dft_max_ram_size 0 + + .tx_ctrl = XCVR_TX_DIG_CTRL_RADIO_DFT_MODE(radio_dft_mode) | + XCVR_TX_DIG_CTRL_LFSR_LENGTH(lfsr_length) | + XCVR_TX_DIG_CTRL_LFSR_EN(lfsr_en) | + XCVR_TX_DIG_CTRL_DFT_CLK_SEL(dft_clk_sel) | + XCVR_TX_DIG_CTRL_TX_DFT_EN(tx_dft_en) | + XCVR_TX_DIG_CTRL_SOC_TEST_SEL(soc_test_sel) | + XCVR_TX_DIG_CTRL_TX_CAPTURE_POL(tx_capture_pol) | + XCVR_TX_DIG_CTRL_FREQ_WORD_ADJ(freq_word_adj), +/*-------------------------------------------------------------------------------------------------*/ + .tx_data_padding = XCVR_TX_DIG_DATA_PADDING_LRM(lrm) | + XCVR_TX_DIG_DATA_PADDING_DATA_PADDING_PAT_1(data_padding_pat_1) | + XCVR_TX_DIG_DATA_PADDING_DATA_PADDING_PAT_0(data_padding_pat_0), +/*-------------------------------------------------------------------------------------------------*/ + .tx_dft_pattern = XCVR_TX_DIG_DFT_PATTERN_DFT_MOD_PATTERN(dft_mod_patternval), +#if !RADIO_IS_GEN_2P1 +/*-------------------------------------------------------------------------------------------------*/ + .rf_dft_bist_1 = XCVR_TX_DIG_RF_DFT_BIST_1_CTUNE_BIST_GO(ctune_bist_go) | + XCVR_TX_DIG_RF_DFT_BIST_1_CTUNE_BIST_THRSHLD(ctune_bist_thrshld) | + XCVR_TX_DIG_RF_DFT_BIST_1_PA_AM_MOD_FREQ(pa_am_mod_freq) | + XCVR_TX_DIG_RF_DFT_BIST_1_PA_AM_MOD_ENTRIES(pa_am_mod_entries) | + XCVR_TX_DIG_RF_DFT_BIST_1_PA_AM_MOD_EN(pa_am_mod_en), +/*-------------------------------------------------------------------------------------------------*/ + .rf_dft_bist_2 = XCVR_TX_DIG_RF_DFT_BIST_2_SYN_BIST_GO(syn_bist_go) | + XCVR_TX_DIG_RF_DFT_BIST_2_SYN_BIST_ALL_CHANNELS(syn_bist_all_channels) | + XCVR_TX_DIG_RF_DFT_BIST_2_FREQ_COUNT_THRESHOLD(freq_count_threshold) | + XCVR_TX_DIG_RF_DFT_BIST_2_HPM_INL_BIST_GO(hpm_inl_bist_go) | + XCVR_TX_DIG_RF_DFT_BIST_2_HPM_DNL_BIST_GO(hpm_dnl_bist_go) | + XCVR_TX_DIG_RF_DFT_BIST_2_DFT_MAX_RAM_SIZE(dft_max_ram_size), +#endif /* !RADIO_IS_GEN_2P1 */ +}; diff --git a/drivers/kw41zrf/vendor/XCVR/MKW41Z4/fsl_xcvr_mode_datarate_config.c b/drivers/kw41zrf/vendor/XCVR/MKW41Z4/fsl_xcvr_mode_datarate_config.c new file mode 100644 index 000000000000..7347abad4c6e --- /dev/null +++ b/drivers/kw41zrf/vendor/XCVR/MKW41Z4/fsl_xcvr_mode_datarate_config.c @@ -0,0 +1,217 @@ +/* +* The Clear BSD License +* Copyright 2016-2017 NXP +* All rights reserved. +* +* Redistribution and use in source and binary forms, with or without +* modification, are permitted (subject to the limitations in the +* disclaimer below) provided that the following conditions are met: +* +* * Redistributions of source code must retain the above copyright +* notice, this list of conditions and the following disclaimer. +* +* * Redistributions in binary form must reproduce the above copyright +* notice, this list of conditions and the following disclaimer in the +* documentation and/or other materials provided with the distribution. +* +* * Neither the name of the copyright holder nor the names of its +* contributors may be used to endorse or promote products derived from +* this software without specific prior written permission. +* +* NO EXPRESS OR IMPLIED LICENSES TO ANY PARTY'S PATENT RIGHTS ARE +* GRANTED BY THIS LICENSE. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT +* HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED +* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE +* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR +* BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE +* OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN +* IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#include "fsl_xcvr.h" + +/******************************************************************************* + * Definitions + ******************************************************************************/ + +/******************************************************************************* + * Prototypes + ******************************************************************************/ + +/******************************************************************************* + * Variables + ******************************************************************************/ + +/******************************************************************************* + * Code + ******************************************************************************/ +/* ========================= DATA RATE ONLY settings ===============*/ +/*! + * @brief XCVR 1Mbps DATA RATE specific configure structure + */ +const xcvr_datarate_config_t xcvr_1mbps_config = +{ + .data_rate = DR_1MBPS, + .phy_el_cfg_init = XCVR_PHY_EL_CFG_EL_WIN_SIZE(0xF) | +#if !RADIO_IS_GEN_2P1 + XCVR_PHY_EL_CFG_EL_ZB_WIN_SIZE(0) | +#endif /* !RADIO_IS_GEN_2P1 */ + XCVR_PHY_EL_CFG_EL_INTERVAL(0x20) , + .rx_dig_ctrl_init_26mhz = XCVR_RX_DIG_RX_DIG_CTRL_RX_DEC_FILT_OSR(0) | + XCVR_RX_DIG_RX_DIG_CTRL_RX_DEC_FILT_GAIN(16), + .rx_dig_ctrl_init_32mhz = XCVR_RX_DIG_RX_DIG_CTRL_RX_DEC_FILT_OSR(1) | + XCVR_RX_DIG_RX_DIG_CTRL_RX_DEC_FILT_GAIN(16), + + .agc_ctrl_1_init_26mhz = XCVR_RX_DIG_AGC_CTRL_1_LNA_GAIN_SETTLE_TIME(10) | + XCVR_RX_DIG_AGC_CTRL_1_PRESLOW_EN(PRESLOW_ENA), + .agc_ctrl_1_init_32mhz = XCVR_RX_DIG_AGC_CTRL_1_LNA_GAIN_SETTLE_TIME(12) | + XCVR_RX_DIG_AGC_CTRL_1_PRESLOW_EN(PRESLOW_ENA), + + .dcoc_ctrl_0_init_26mhz = XCVR_RX_DIG_DCOC_CTRL_0_DCOC_CORR_DLY(10) | + XCVR_RX_DIG_DCOC_CTRL_0_DCOC_CORR_HOLD_TIME(3), + .dcoc_ctrl_0_init_32mhz = XCVR_RX_DIG_DCOC_CTRL_0_DCOC_CORR_DLY(12) | + XCVR_RX_DIG_DCOC_CTRL_0_DCOC_CORR_HOLD_TIME(4), + + .dcoc_ctrl_1_init_26mhz = XCVR_RX_DIG_DCOC_CTRL_1_DCOC_SIGN_SCALE_IDX(1) | + XCVR_RX_DIG_DCOC_CTRL_1_DCOC_ALPHAC_SCALE_IDX(3) | + XCVR_RX_DIG_DCOC_CTRL_1_DCOC_ALPHA_RADIUS_IDX(3) | + XCVR_RX_DIG_DCOC_CTRL_1_DCOC_TRK_EST_GS_CNT(0) | + XCVR_RX_DIG_DCOC_CTRL_1_DCOC_SIGN_SCALE_GS_IDX(1) | + XCVR_RX_DIG_DCOC_CTRL_1_DCOC_ALPHAC_SCALE_GS_IDX(3) | + XCVR_RX_DIG_DCOC_CTRL_1_DCOC_ALPHA_RADIUS_GS_IDX(3), + + .dcoc_ctrl_1_init_32mhz = XCVR_RX_DIG_DCOC_CTRL_1_DCOC_SIGN_SCALE_IDX(1) | + XCVR_RX_DIG_DCOC_CTRL_1_DCOC_ALPHAC_SCALE_IDX(3) | + XCVR_RX_DIG_DCOC_CTRL_1_DCOC_ALPHA_RADIUS_IDX(3) | + XCVR_RX_DIG_DCOC_CTRL_1_DCOC_TRK_EST_GS_CNT(0) | + XCVR_RX_DIG_DCOC_CTRL_1_DCOC_SIGN_SCALE_GS_IDX(1) | + XCVR_RX_DIG_DCOC_CTRL_1_DCOC_ALPHAC_SCALE_GS_IDX(3) | + XCVR_RX_DIG_DCOC_CTRL_1_DCOC_ALPHA_RADIUS_GS_IDX(3), + + .dcoc_cal_iir_init_26mhz = XCVR_RX_DIG_DCOC_CAL_IIR_DCOC_CAL_IIR3A_IDX(2) | + XCVR_RX_DIG_DCOC_CAL_IIR_DCOC_CAL_IIR2A_IDX(2) | + XCVR_RX_DIG_DCOC_CAL_IIR_DCOC_CAL_IIR1A_IDX(2), + .dcoc_cal_iir_init_32mhz = XCVR_RX_DIG_DCOC_CAL_IIR_DCOC_CAL_IIR3A_IDX(2) | + XCVR_RX_DIG_DCOC_CAL_IIR_DCOC_CAL_IIR2A_IDX(3) | + XCVR_RX_DIG_DCOC_CAL_IIR_DCOC_CAL_IIR1A_IDX(2), + + .dc_resid_ctrl_26mhz = XCVR_RX_DIG_DC_RESID_CTRL_DC_RESID_NWIN(48) | + XCVR_RX_DIG_DC_RESID_CTRL_DC_RESID_DLY(5), + .dc_resid_ctrl_32mhz = XCVR_RX_DIG_DC_RESID_CTRL_DC_RESID_NWIN(48) | + XCVR_RX_DIG_DC_RESID_CTRL_DC_RESID_DLY(6), +}; + +/*! + * @brief XCVR 500K bps DATA RATE specific configure structure + */ +const xcvr_datarate_config_t xcvr_500kbps_config = +{ + .data_rate = DR_500KBPS, + .phy_el_cfg_init = XCVR_PHY_EL_CFG_EL_WIN_SIZE(0x8) | +#if !RADIO_IS_GEN_2P1 + XCVR_PHY_EL_CFG_EL_ZB_WIN_SIZE(0) | +#endif /* !RADIO_IS_GEN_2P1 */ + XCVR_PHY_EL_CFG_EL_INTERVAL(0x10), + .rx_dig_ctrl_init_26mhz = XCVR_RX_DIG_RX_DIG_CTRL_RX_DEC_FILT_OSR(1) | + XCVR_RX_DIG_RX_DIG_CTRL_RX_DEC_FILT_GAIN(16), + .rx_dig_ctrl_init_32mhz = XCVR_RX_DIG_RX_DIG_CTRL_RX_DEC_FILT_OSR(2) | + XCVR_RX_DIG_RX_DIG_CTRL_RX_DEC_FILT_GAIN(16), + + .agc_ctrl_1_init_26mhz = XCVR_RX_DIG_AGC_CTRL_1_LNA_GAIN_SETTLE_TIME(15) | + XCVR_RX_DIG_AGC_CTRL_1_PRESLOW_EN(PRESLOW_ENA), + .agc_ctrl_1_init_32mhz = XCVR_RX_DIG_AGC_CTRL_1_LNA_GAIN_SETTLE_TIME(18) | + XCVR_RX_DIG_AGC_CTRL_1_PRESLOW_EN(PRESLOW_ENA), + + .dcoc_ctrl_0_init_26mhz = XCVR_RX_DIG_DCOC_CTRL_0_DCOC_CORR_DLY(13) | + XCVR_RX_DIG_DCOC_CTRL_0_DCOC_CORR_HOLD_TIME(29), + .dcoc_ctrl_0_init_32mhz = XCVR_RX_DIG_DCOC_CTRL_0_DCOC_CORR_DLY(16) | + XCVR_RX_DIG_DCOC_CTRL_0_DCOC_CORR_HOLD_TIME(36), + + .dcoc_ctrl_1_init_26mhz = XCVR_RX_DIG_DCOC_CTRL_1_DCOC_SIGN_SCALE_IDX(1) | + XCVR_RX_DIG_DCOC_CTRL_1_DCOC_ALPHAC_SCALE_IDX(3) | + XCVR_RX_DIG_DCOC_CTRL_1_DCOC_ALPHA_RADIUS_IDX(2) | + XCVR_RX_DIG_DCOC_CTRL_1_DCOC_TRK_EST_GS_CNT(0) | + XCVR_RX_DIG_DCOC_CTRL_1_DCOC_SIGN_SCALE_GS_IDX(1) | + XCVR_RX_DIG_DCOC_CTRL_1_DCOC_ALPHAC_SCALE_GS_IDX(3) | + XCVR_RX_DIG_DCOC_CTRL_1_DCOC_ALPHA_RADIUS_GS_IDX(2), + + .dcoc_ctrl_1_init_32mhz = XCVR_RX_DIG_DCOC_CTRL_1_DCOC_SIGN_SCALE_IDX(1) | + XCVR_RX_DIG_DCOC_CTRL_1_DCOC_ALPHAC_SCALE_IDX(3) | + XCVR_RX_DIG_DCOC_CTRL_1_DCOC_ALPHA_RADIUS_IDX(2) | + XCVR_RX_DIG_DCOC_CTRL_1_DCOC_TRK_EST_GS_CNT(0) | + XCVR_RX_DIG_DCOC_CTRL_1_DCOC_SIGN_SCALE_GS_IDX(1) | + XCVR_RX_DIG_DCOC_CTRL_1_DCOC_ALPHAC_SCALE_GS_IDX(3) | + XCVR_RX_DIG_DCOC_CTRL_1_DCOC_ALPHA_RADIUS_GS_IDX(2), + + .dcoc_cal_iir_init_26mhz = XCVR_RX_DIG_DCOC_CAL_IIR_DCOC_CAL_IIR3A_IDX(2) | + XCVR_RX_DIG_DCOC_CAL_IIR_DCOC_CAL_IIR2A_IDX(2) | + XCVR_RX_DIG_DCOC_CAL_IIR_DCOC_CAL_IIR1A_IDX(2), + .dcoc_cal_iir_init_32mhz = XCVR_RX_DIG_DCOC_CAL_IIR_DCOC_CAL_IIR3A_IDX(2) | + XCVR_RX_DIG_DCOC_CAL_IIR_DCOC_CAL_IIR2A_IDX(2) | + XCVR_RX_DIG_DCOC_CAL_IIR_DCOC_CAL_IIR1A_IDX(2), + + .dc_resid_ctrl_26mhz = XCVR_RX_DIG_DC_RESID_CTRL_DC_RESID_NWIN(32) | + XCVR_RX_DIG_DC_RESID_CTRL_DC_RESID_DLY(4), + .dc_resid_ctrl_32mhz = XCVR_RX_DIG_DC_RESID_CTRL_DC_RESID_NWIN(32) | + XCVR_RX_DIG_DC_RESID_CTRL_DC_RESID_DLY(4), +}; + +/*! + * @brief XCVR 250K bps DATA RATE specific configure structure + */ +const xcvr_datarate_config_t xcvr_250kbps_config = +{ + .data_rate = DR_250KBPS, + .phy_el_cfg_init = XCVR_PHY_EL_CFG_EL_WIN_SIZE(0x4) | +#if !RADIO_IS_GEN_2P1 + XCVR_PHY_EL_CFG_EL_ZB_WIN_SIZE(0) | +#endif /* !RADIO_IS_GEN_2P1 */ + XCVR_PHY_EL_CFG_EL_INTERVAL(0x8) , + .rx_dig_ctrl_init_26mhz = XCVR_RX_DIG_RX_DIG_CTRL_RX_DEC_FILT_OSR(2) | + XCVR_RX_DIG_RX_DIG_CTRL_RX_DEC_FILT_GAIN(16), + .rx_dig_ctrl_init_32mhz = XCVR_RX_DIG_RX_DIG_CTRL_RX_DEC_FILT_OSR(4) | + XCVR_RX_DIG_RX_DIG_CTRL_RX_DEC_FILT_GAIN(16), + + .agc_ctrl_1_init_26mhz = XCVR_RX_DIG_AGC_CTRL_1_LNA_GAIN_SETTLE_TIME(18) | + XCVR_RX_DIG_AGC_CTRL_1_PRESLOW_EN(PRESLOW_ENA), + .agc_ctrl_1_init_32mhz = XCVR_RX_DIG_AGC_CTRL_1_LNA_GAIN_SETTLE_TIME(22) | + XCVR_RX_DIG_AGC_CTRL_1_PRESLOW_EN(PRESLOW_ENA), + + .dcoc_ctrl_0_init_26mhz = XCVR_RX_DIG_DCOC_CTRL_0_DCOC_CORR_DLY(16) | + XCVR_RX_DIG_DCOC_CTRL_0_DCOC_CORR_HOLD_TIME(34), + .dcoc_ctrl_0_init_32mhz = XCVR_RX_DIG_DCOC_CTRL_0_DCOC_CORR_DLY(20) | + XCVR_RX_DIG_DCOC_CTRL_0_DCOC_CORR_HOLD_TIME(42), + + .dcoc_ctrl_1_init_26mhz = XCVR_RX_DIG_DCOC_CTRL_1_DCOC_SIGN_SCALE_IDX(1) | + XCVR_RX_DIG_DCOC_CTRL_1_DCOC_ALPHAC_SCALE_IDX(3) | + XCVR_RX_DIG_DCOC_CTRL_1_DCOC_ALPHA_RADIUS_IDX(1) | + XCVR_RX_DIG_DCOC_CTRL_1_DCOC_TRK_EST_GS_CNT(0) | + XCVR_RX_DIG_DCOC_CTRL_1_DCOC_SIGN_SCALE_GS_IDX(1) | + XCVR_RX_DIG_DCOC_CTRL_1_DCOC_ALPHAC_SCALE_GS_IDX(3) | + XCVR_RX_DIG_DCOC_CTRL_1_DCOC_ALPHA_RADIUS_GS_IDX(2), + + .dcoc_ctrl_1_init_32mhz = XCVR_RX_DIG_DCOC_CTRL_1_DCOC_SIGN_SCALE_IDX(1) | + XCVR_RX_DIG_DCOC_CTRL_1_DCOC_ALPHAC_SCALE_IDX(3) | + XCVR_RX_DIG_DCOC_CTRL_1_DCOC_ALPHA_RADIUS_IDX(1) | + XCVR_RX_DIG_DCOC_CTRL_1_DCOC_TRK_EST_GS_CNT(0) | + XCVR_RX_DIG_DCOC_CTRL_1_DCOC_SIGN_SCALE_GS_IDX(1) | + XCVR_RX_DIG_DCOC_CTRL_1_DCOC_ALPHAC_SCALE_GS_IDX(3) | + XCVR_RX_DIG_DCOC_CTRL_1_DCOC_ALPHA_RADIUS_GS_IDX(2), + + .dcoc_cal_iir_init_26mhz = XCVR_RX_DIG_DCOC_CAL_IIR_DCOC_CAL_IIR3A_IDX(0) | + XCVR_RX_DIG_DCOC_CAL_IIR_DCOC_CAL_IIR2A_IDX(1) | + XCVR_RX_DIG_DCOC_CAL_IIR_DCOC_CAL_IIR1A_IDX(1), + .dcoc_cal_iir_init_32mhz = XCVR_RX_DIG_DCOC_CAL_IIR_DCOC_CAL_IIR3A_IDX(0) | + XCVR_RX_DIG_DCOC_CAL_IIR_DCOC_CAL_IIR2A_IDX(1) | + XCVR_RX_DIG_DCOC_CAL_IIR_DCOC_CAL_IIR1A_IDX(1), + + .dc_resid_ctrl_26mhz = XCVR_RX_DIG_DC_RESID_CTRL_DC_RESID_NWIN(16) | + XCVR_RX_DIG_DC_RESID_CTRL_DC_RESID_DLY(4), + .dc_resid_ctrl_32mhz = XCVR_RX_DIG_DC_RESID_CTRL_DC_RESID_NWIN(16) | + XCVR_RX_DIG_DC_RESID_CTRL_DC_RESID_DLY(4), +}; diff --git a/drivers/kw41zrf/vendor/XCVR/MKW41Z4/fsl_xcvr_zgbe_config.c b/drivers/kw41zrf/vendor/XCVR/MKW41Z4/fsl_xcvr_zgbe_config.c new file mode 100644 index 000000000000..41890c45561d --- /dev/null +++ b/drivers/kw41zrf/vendor/XCVR/MKW41Z4/fsl_xcvr_zgbe_config.c @@ -0,0 +1,249 @@ +/* +* The Clear BSD License +* Copyright (c) 2015, Freescale Semiconductor, Inc. +* Copyright 2016-2017 NXP +* All rights reserved. +* +* Redistribution and use in source and binary forms, with or without +* modification, are permitted (subject to the limitations in the +* disclaimer below) provided that the following conditions are met: +* +* * Redistributions of source code must retain the above copyright +* notice, this list of conditions and the following disclaimer. +* +* * Redistributions in binary form must reproduce the above copyright +* notice, this list of conditions and the following disclaimer in the +* documentation and/or other materials provided with the distribution. +* +* * Neither the name of the copyright holder nor the names of its +* contributors may be used to endorse or promote products derived from +* this software without specific prior written permission. +* +* NO EXPRESS OR IMPLIED LICENSES TO ANY PARTY'S PATENT RIGHTS ARE +* GRANTED BY THIS LICENSE. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT +* HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED +* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE +* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR +* BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE +* OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN +* IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#include "fsl_xcvr.h" + +/******************************************************************************* + * Definitions + ******************************************************************************/ + +/******************************************************************************* + * Prototypes + ******************************************************************************/ + +/******************************************************************************* + * Variables + ******************************************************************************/ + +/******************************************************************************* + * Code + ******************************************************************************/ +const xcvr_mode_config_t zgbe_mode_config = +{ + .radio_mode = ZIGBEE_MODE, + .scgc5_clock_ena_bits = SIM_SCGC5_PHYDIG_MASK | SIM_SCGC5_ZigBee_MASK, + + /* XCVR_MISC configs */ + .xcvr_ctrl.mask = XCVR_CTRL_XCVR_CTRL_PROTOCOL_MASK | + XCVR_CTRL_XCVR_CTRL_TGT_PWR_SRC_MASK | + XCVR_CTRL_XCVR_CTRL_DEMOD_SEL_MASK, + .xcvr_ctrl.init = XCVR_CTRL_XCVR_CTRL_PROTOCOL(4) | + XCVR_CTRL_XCVR_CTRL_TGT_PWR_SRC(7) | + XCVR_CTRL_XCVR_CTRL_DEMOD_SEL(2), + + /* XCVR_PHY configs */ + .phy_pre_ref0_init = 0x0, /* Not used in Zigbee */ + .phy_pre_ref1_init = 0x0, /* Not used in Zigbee */ + .phy_pre_ref2_init = 0x0, /* Not used in Zigbee */ + + .phy_cfg1_init = XCVR_PHY_CFG1_AA_PLAYBACK(0) | + XCVR_PHY_CFG1_AA_OUTPUT_SEL(1) | + XCVR_PHY_CFG1_FSK_BIT_INVERT(0) | + XCVR_PHY_CFG1_BSM_EN_BLE(0) | + XCVR_PHY_CFG1_DEMOD_CLK_MODE(0) | + XCVR_PHY_CFG1_CTS_THRESH(0xC0) | + XCVR_PHY_CFG1_FSK_FTS_TIMEOUT(2), + + .phy_el_cfg_init = XCVR_PHY_EL_CFG_EL_ENABLE(1) +#if !RADIO_IS_GEN_2P1 + | XCVR_PHY_EL_CFG_EL_ZB_ENABLE(0) +#endif /* !RADIO_IS_GEN_2P1 */ + , + + /* XCVR_PLL_DIG configs */ + + /* XCVR_RX_DIG configs */ + .rx_dig_ctrl_init_26mhz = XCVR_RX_DIG_RX_DIG_CTRL_RX_FSK_ZB_SEL(1) | /* Depends on protocol */ + XCVR_RX_DIG_RX_DIG_CTRL_RX_DC_RESID_EN(1) | /* Depends on protocol */ + XCVR_RX_DIG_RX_DIG_CTRL_RX_SRC_RATE(0), + + .rx_dig_ctrl_init_32mhz = XCVR_RX_DIG_RX_DIG_CTRL_RX_FSK_ZB_SEL(1) | /* Depends on protocol */ + XCVR_RX_DIG_RX_DIG_CTRL_RX_DC_RESID_EN(1), /* Depends on protocol */ + + .agc_ctrl_0_init = XCVR_RX_DIG_AGC_CTRL_0_AGC_DOWN_RSSI_THRESH(0xFF), + /* XCVR_TSM configs */ +#if (DATA_PADDING_EN) + .tsm_timing_35_init = B0(TX_DIG_EN_ASSERT+ZGBE_TX_DIG_EN_TX_HI_ADJ), /* DATA_PADDING adjustments are specified relative to the non-Zigbee base timing */ +#else + .tsm_timing_35_init = B0(ZGBE_TX_DIG_EN_ASSERT), +#endif /* (DATA_PADDING_EN) */ + + /* XCVR_TX_DIG configs */ + .tx_gfsk_ctrl = XCVR_TX_DIG_GFSK_CTRL_GFSK_MULTIPLY_TABLE_MANUAL(0x4000) | + XCVR_TX_DIG_GFSK_CTRL_GFSK_MI(1) | + XCVR_TX_DIG_GFSK_CTRL_GFSK_MLD(0) | + XCVR_TX_DIG_GFSK_CTRL_GFSK_FLD(0) | + XCVR_TX_DIG_GFSK_CTRL_GFSK_MOD_INDEX_SCALING(0) | + XCVR_TX_DIG_GFSK_CTRL_TX_IMAGE_FILTER_OVRD_EN(0) | + XCVR_TX_DIG_GFSK_CTRL_TX_IMAGE_FILTER_0_OVRD(0) | + XCVR_TX_DIG_GFSK_CTRL_TX_IMAGE_FILTER_1_OVRD(0) | + XCVR_TX_DIG_GFSK_CTRL_TX_IMAGE_FILTER_2_OVRD(0) , + .tx_gfsk_coeff1_26mhz = 0, + .tx_gfsk_coeff2_26mhz = 0, + .tx_gfsk_coeff1_32mhz = 0, + .tx_gfsk_coeff2_32mhz = 0, +}; + +const xcvr_mode_datarate_config_t xcvr_ZIGBEE_500kbps_config = +{ + .radio_mode = ZIGBEE_MODE, + .data_rate = DR_500KBPS, + + .ana_sy_ctrl2.mask = XCVR_ANALOG_SY_CTRL_2_SY_VCO_KVM_MASK, + .ana_sy_ctrl2.init = XCVR_ANALOG_SY_CTRL_2_SY_VCO_KVM(1), /* VCO KVM */ + + .ana_rx_bba.mask = XCVR_ANALOG_RX_BBA_RX_BBA_BW_SEL_MASK | XCVR_ANALOG_RX_BBA_RX_BBA2_BW_SEL_MASK, + .ana_rx_bba.init = XCVR_ANALOG_RX_BBA_RX_BBA_BW_SEL(1) | XCVR_ANALOG_RX_BBA_RX_BBA2_BW_SEL(1), /* BBA_BW_SEL and BBA2_BW_SEL */ + .ana_rx_tza.mask = XCVR_ANALOG_RX_TZA_RX_TZA_BW_SEL_MASK, + .ana_rx_tza.init = XCVR_ANALOG_RX_TZA_RX_TZA_BW_SEL(1), /*TZA_BW_SEL */ + + .phy_cfg2_init = XCVR_PHY_CFG2_PHY_FIFO_PRECHG(8) | + XCVR_PHY_CFG2_X2_DEMOD_GAIN(0xA) , + + /* AGC configs */ + .agc_ctrl_2_init_26mhz = XCVR_RX_DIG_AGC_CTRL_2_BBA_GAIN_SETTLE_TIME(8) | + XCVR_RX_DIG_AGC_CTRL_2_BBA_PDET_SEL_LO(5) | + XCVR_RX_DIG_AGC_CTRL_2_BBA_PDET_SEL_HI(6) | + XCVR_RX_DIG_AGC_CTRL_2_TZA_PDET_SEL_LO(3) | + XCVR_RX_DIG_AGC_CTRL_2_TZA_PDET_SEL_HI(5) | + XCVR_RX_DIG_AGC_CTRL_2_AGC_FAST_EXPIRE(5), + .agc_ctrl_2_init_32mhz = XCVR_RX_DIG_AGC_CTRL_2_BBA_GAIN_SETTLE_TIME(10) | + XCVR_RX_DIG_AGC_CTRL_2_BBA_PDET_SEL_LO(5) | + XCVR_RX_DIG_AGC_CTRL_2_BBA_PDET_SEL_HI(6) | + XCVR_RX_DIG_AGC_CTRL_2_TZA_PDET_SEL_LO(3) | + XCVR_RX_DIG_AGC_CTRL_2_TZA_PDET_SEL_HI(5) | + XCVR_RX_DIG_AGC_CTRL_2_AGC_FAST_EXPIRE(5), + + /* All constant values are represented as 16 bits, register writes will remove unused bits */ + .rx_chf_coeffs_26mhz.rx_chf_coef_0 = 0xFFFF, + .rx_chf_coeffs_26mhz.rx_chf_coef_1 = 0xFFFF, + .rx_chf_coeffs_26mhz.rx_chf_coef_2 = 0x0002, + .rx_chf_coeffs_26mhz.rx_chf_coef_3 = 0x0008, + .rx_chf_coeffs_26mhz.rx_chf_coef_4 = 0x000A, + .rx_chf_coeffs_26mhz.rx_chf_coef_5 = 0x0000, + .rx_chf_coeffs_26mhz.rx_chf_coef_6 = 0xFFE8, + .rx_chf_coeffs_26mhz.rx_chf_coef_7 = 0xFFD7, + .rx_chf_coeffs_26mhz.rx_chf_coef_8 = 0xFFE6, + .rx_chf_coeffs_26mhz.rx_chf_coef_9 = 0x0022, + .rx_chf_coeffs_26mhz.rx_chf_coef_10 = 0x0075, + .rx_chf_coeffs_26mhz.rx_chf_coef_11 = 0x00B2, + + /* IEEE 802.15.4 32MHz Channel Filter -- 1.55/1.25/5/0.97/B5 */ + .rx_chf_coeffs_32mhz.rx_chf_coef_0 = 0xFFFF, + .rx_chf_coeffs_32mhz.rx_chf_coef_1 = 0xFFFF, + .rx_chf_coeffs_32mhz.rx_chf_coef_2 = 0x0005, + .rx_chf_coeffs_32mhz.rx_chf_coef_3 = 0x0004, + .rx_chf_coeffs_32mhz.rx_chf_coef_4 = 0xFFF2, + .rx_chf_coeffs_32mhz.rx_chf_coef_5 = 0xFFF2, + .rx_chf_coeffs_32mhz.rx_chf_coef_6 = 0x001D, + .rx_chf_coeffs_32mhz.rx_chf_coef_7 = 0x0025, + .rx_chf_coeffs_32mhz.rx_chf_coef_8 = 0xFFCE, + .rx_chf_coeffs_32mhz.rx_chf_coef_9 = 0xFFA1, + .rx_chf_coeffs_32mhz.rx_chf_coef_10 = 0x0040, + .rx_chf_coeffs_32mhz.rx_chf_coef_11 = 0x0124, + + .rx_rccal_ctrl_0 = XCVR_RX_DIG_RX_RCCAL_CTRL0_BBA_RCCAL_OFFSET(0) | + XCVR_RX_DIG_RX_RCCAL_CTRL0_BBA_RCCAL_MANUAL(0) | + XCVR_RX_DIG_RX_RCCAL_CTRL0_BBA_RCCAL_DIS(0) | + XCVR_RX_DIG_RX_RCCAL_CTRL0_RCCAL_SMP_DLY(0) | + XCVR_RX_DIG_RX_RCCAL_CTRL0_RCCAL_COMP_INV(0) | + XCVR_RX_DIG_RX_RCCAL_CTRL0_TZA_RCCAL_OFFSET(0) | + XCVR_RX_DIG_RX_RCCAL_CTRL0_TZA_RCCAL_MANUAL(0) | + XCVR_RX_DIG_RX_RCCAL_CTRL0_TZA_RCCAL_DIS(0) , + .rx_rccal_ctrl_1 = XCVR_RX_DIG_RX_RCCAL_CTRL1_ADC_RCCAL_OFFSET(0) | + XCVR_RX_DIG_RX_RCCAL_CTRL1_ADC_RCCAL_MANUAL(0) | + XCVR_RX_DIG_RX_RCCAL_CTRL1_ADC_RCCAL_DIS(0) | + XCVR_RX_DIG_RX_RCCAL_CTRL1_BBA2_RCCAL_OFFSET(0) | + XCVR_RX_DIG_RX_RCCAL_CTRL1_BBA2_RCCAL_MANUAL(0) | + XCVR_RX_DIG_RX_RCCAL_CTRL1_BBA2_RCCAL_DIS(0) , + + .tx_fsk_scale_26mhz = XCVR_TX_DIG_FSK_SCALE_FSK_MODULATION_SCALE_0(0x1627) | XCVR_TX_DIG_FSK_SCALE_FSK_MODULATION_SCALE_1(0x09d9), + .tx_fsk_scale_32mhz = XCVR_TX_DIG_FSK_SCALE_FSK_MODULATION_SCALE_0(0x1800) | XCVR_TX_DIG_FSK_SCALE_FSK_MODULATION_SCALE_1(0x0800), +}; + +/* CUSTOM datarate dependent config structure for ONLY 802.15.4 */ +/*! + * @brief XCVR 500K bps DATA RATE specific configure structure + */ +const xcvr_datarate_config_t xcvr_802_15_4_500kbps_config = +{ + .data_rate = DR_500KBPS, + .phy_el_cfg_init = XCVR_PHY_EL_CFG_EL_ZB_WIN_SIZE(0) | + XCVR_PHY_EL_CFG_EL_WIN_SIZE(0x8) | + XCVR_PHY_EL_CFG_EL_INTERVAL(0x10) , + .rx_dig_ctrl_init_26mhz = XCVR_RX_DIG_RX_DIG_CTRL_RX_DEC_FILT_OSR(1) | + XCVR_RX_DIG_RX_DIG_CTRL_RX_DEC_FILT_GAIN(16), + .rx_dig_ctrl_init_32mhz = XCVR_RX_DIG_RX_DIG_CTRL_RX_DEC_FILT_OSR(2) | + XCVR_RX_DIG_RX_DIG_CTRL_RX_DEC_FILT_GAIN(16), + + .agc_ctrl_1_init_26mhz = XCVR_RX_DIG_AGC_CTRL_1_LNA_GAIN_SETTLE_TIME(13) | + XCVR_RX_DIG_AGC_CTRL_1_PRESLOW_EN(PRESLOW_ENA), + .agc_ctrl_1_init_32mhz = XCVR_RX_DIG_AGC_CTRL_1_LNA_GAIN_SETTLE_TIME(10) | + XCVR_RX_DIG_AGC_CTRL_1_PRESLOW_EN(PRESLOW_ENA), + + .dcoc_ctrl_0_init_26mhz = XCVR_RX_DIG_DCOC_CTRL_0_DCOC_CORR_DLY(13) | + XCVR_RX_DIG_DCOC_CTRL_0_DCOC_CORR_HOLD_TIME(29), + .dcoc_ctrl_0_init_32mhz = XCVR_RX_DIG_DCOC_CTRL_0_DCOC_CORR_DLY(21) | + XCVR_RX_DIG_DCOC_CTRL_0_DCOC_CORR_HOLD_TIME(47), + + .dcoc_ctrl_1_init_26mhz = XCVR_RX_DIG_DCOC_CTRL_1_DCOC_SIGN_SCALE_IDX(1) | + XCVR_RX_DIG_DCOC_CTRL_1_DCOC_ALPHAC_SCALE_IDX(3) | + XCVR_RX_DIG_DCOC_CTRL_1_DCOC_ALPHA_RADIUS_IDX(2) | + XCVR_RX_DIG_DCOC_CTRL_1_DCOC_TRK_EST_GS_CNT(0) | + XCVR_RX_DIG_DCOC_CTRL_1_DCOC_SIGN_SCALE_GS_IDX(1) | + XCVR_RX_DIG_DCOC_CTRL_1_DCOC_ALPHAC_SCALE_GS_IDX(3) | + XCVR_RX_DIG_DCOC_CTRL_1_DCOC_ALPHA_RADIUS_GS_IDX(2), + + .dcoc_ctrl_1_init_32mhz = XCVR_RX_DIG_DCOC_CTRL_1_DCOC_SIGN_SCALE_IDX(1) | + XCVR_RX_DIG_DCOC_CTRL_1_DCOC_ALPHAC_SCALE_IDX(3) | + XCVR_RX_DIG_DCOC_CTRL_1_DCOC_ALPHA_RADIUS_IDX(2) | + XCVR_RX_DIG_DCOC_CTRL_1_DCOC_TRK_EST_GS_CNT(0) | + XCVR_RX_DIG_DCOC_CTRL_1_DCOC_SIGN_SCALE_GS_IDX(1) | + XCVR_RX_DIG_DCOC_CTRL_1_DCOC_ALPHAC_SCALE_GS_IDX(3) | + XCVR_RX_DIG_DCOC_CTRL_1_DCOC_ALPHA_RADIUS_GS_IDX(2), + + .dcoc_cal_iir_init_26mhz = XCVR_RX_DIG_DCOC_CAL_IIR_DCOC_CAL_IIR3A_IDX(2) | + XCVR_RX_DIG_DCOC_CAL_IIR_DCOC_CAL_IIR2A_IDX(2) | + XCVR_RX_DIG_DCOC_CAL_IIR_DCOC_CAL_IIR1A_IDX(2), + .dcoc_cal_iir_init_32mhz = XCVR_RX_DIG_DCOC_CAL_IIR_DCOC_CAL_IIR3A_IDX(1) | + XCVR_RX_DIG_DCOC_CAL_IIR_DCOC_CAL_IIR2A_IDX(2) | + XCVR_RX_DIG_DCOC_CAL_IIR_DCOC_CAL_IIR1A_IDX(1), + + .dc_resid_ctrl_26mhz = XCVR_RX_DIG_DC_RESID_CTRL_DC_RESID_NWIN(26) | + XCVR_RX_DIG_DC_RESID_CTRL_DC_RESID_DLY(4), + .dc_resid_ctrl_32mhz = XCVR_RX_DIG_DC_RESID_CTRL_DC_RESID_NWIN(40) | + XCVR_RX_DIG_DC_RESID_CTRL_DC_RESID_DLY(0), +}; diff --git a/drivers/kw41zrf/vendor/XCVR/MKW41Z4/ifr_radio.c b/drivers/kw41zrf/vendor/XCVR/MKW41Z4/ifr_radio.c new file mode 100644 index 000000000000..92a8786c94e6 --- /dev/null +++ b/drivers/kw41zrf/vendor/XCVR/MKW41Z4/ifr_radio.c @@ -0,0 +1,535 @@ +/* +* The Clear BSD License +* Copyright (c) 2015, Freescale Semiconductor, Inc. +* Copyright 2016-2017 NXP +* All rights reserved. +* +* Redistribution and use in source and binary forms, with or without +* modification, are permitted (subject to the limitations in the +* disclaimer below) provided that the following conditions are met: +* +* * Redistributions of source code must retain the above copyright +* notice, this list of conditions and the following disclaimer. +* +* * Redistributions in binary form must reproduce the above copyright +* notice, this list of conditions and the following disclaimer in the +* documentation and/or other materials provided with the distribution. +* +* * Neither the name of the copyright holder nor the names of its +* contributors may be used to endorse or promote products derived from +* this software without specific prior written permission. +* +* NO EXPRESS OR IMPLIED LICENSES TO ANY PARTY'S PATENT RIGHTS ARE +* GRANTED BY THIS LICENSE. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT +* HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED +* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE +* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR +* BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE +* OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN +* IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#include "cpu.h" +#include "fsl_xcvr.h" +#include "ifr_radio.h" +#include "fsl_os_abstraction.h" +/******************************************************************************* + * Definitions + ******************************************************************************/ +#define IFR_RAM (0) + +#if RADIO_IS_GEN_3P0 +#define RDINDX (0x41U) +#define K3_BASE_INDEX (0x11U) /* Based for read index */ +#else +#define RDRSRC (0x03U) +#define KW4x_512_BASE (0x20000U) +#define KW4x_256_BASE (0x10000U) +#endif /* RADIO_IS_GEN_3P0 */ + +#if RADIO_IS_GEN_2P1 +#define FTFA (FTFE) +#endif /* RADIO_IS_GEN_2P1 */ + +/******************************************************************************* + * Prototypes + ******************************************************************************/ +static uint32_t read_another_ifr_word(void); +static uint32_t read_first_ifr_word(uint32_t read_addr); + +#if RADIO_IS_GEN_3P0 +static uint64_t read_index_ifr(uint32_t read_addr); +#else +/*! ********************************************************************************* + * @brief Reads a location in block 1 IFR for use by the radio. + * + * This function handles reading IFR data from flash memory for trim loading. + * + * @param read_addr the address in the IFR to be read. + * + * @details This function wraps both the Gen2 read_resource command and the Gen2.1 and Gen3 read_index +***********************************************************************************/ +#if RADIO_IS_GEN_2P1 +static uint64_t read_resource_ifr(uint32_t read_addr); +#else +static uint32_t read_resource_ifr(uint32_t read_addr); +#endif /* RADIO_IS_GEN_2P1 */ +#endif /* RADIO_IS_GEN_3P0 */ + +static void store_sw_trim(IFR_SW_TRIM_TBL_ENTRY_T * sw_trim_tbl, uint16_t num_entries, uint32_t addr, uint32_t data); + +/******************************************************************************* + * Variables + ******************************************************************************/ +static uint32_t ifr_read_addr; + +#if RADIO_IS_GEN_3P0 +static uint64_t packed_data_long; /* Storage for 2 32 bit values to be read by read_index */ +static uint8_t num_words_avail; /* Number of 32 bit words available in packed_data_long storage */ +const uint32_t BLOCK_1_IFR[]= +{ + /* Revised fallback table which should work with untrimmed parts */ + 0xABCDFFFEU, /* Version #FFFE indicates default trim values */ + + /* Trim table is empty for Gen3 by default */ + + /* No TRIM_STATUS in SW fallback array. */ + 0xFEED0E0FU /* End of File */ +}; +#else +#if RADIO_IS_GEN_2P0 +const uint32_t BLOCK_1_IFR[]= +{ + /* Revised fallback table which should work with untrimmed parts */ + 0xABCDFFFEU, /* Version #FFFE indicates default trim values */ + + 0x4005912CU, /* RSIM_ANA_TRIM address */ + 0x784B0000U, /* RSIM_ANA_TRIM default value */ + + /* No TRIM_STATUS in SW fallback array. */ + 0xFEED0E0FU /* End of File */ +}; +#else +static uint64_t packed_data_long; /* Storage for 2 32 bit values to be read by read_index */ +static uint8_t num_words_avail; /* Number of 32 bit words available in packed_data_long storage */ +const uint32_t BLOCK_1_IFR[]= +{ + /* Revised fallback table which should work with untrimmed parts */ + 0xABCDFFFEU, /* Version #FFFE indicates default trim values */ + + 0x4005912CU, /* RSIM_ANA_TRIM address */ + 0x784B0000U, /* RSIM_ANA_TRIM default value */ + + /* No TRIM_STATUS in SW fallback array. */ + 0xFEED0E0FU /* End of File */ +}; +#endif /* RADIO_IS_GEN_2P0 */ +#endif /* RADIO_IS_GEN_3P0 */ + +/******************************************************************************* + * Code + ******************************************************************************/ + +/*! ********************************************************************************* + * \brief Read command for reading the first 32bit word from IFR, encapsulates different + * flash IFR read mechanisms for multiple generations of SOC + * + * \param read_addr flash address + * + * \return 8 bytes of packed data containing radio trims only + * +***********************************************************************************/ +static uint32_t read_first_ifr_word(uint32_t read_addr) +{ + ifr_read_addr = read_addr; + return read_another_ifr_word(); +} + +/*! ********************************************************************************* + * \brief Read command for reading additional 32bit words from IFR. Encapsulates multiple IFR read mechanisms. + * + * \param read_addr flash address + * + * \return 8 bytes of packed data containing radio trims only + * + * \remarks PRE-CONDITIONS: + * The function read_first_ifr_word() must have been called so that the ifr_read_addr variable is setup prior to use. + * +***********************************************************************************/ +static uint32_t read_another_ifr_word(void) +{ + uint32_t packed_data; + +#if (RADIO_IS_GEN_3P0 || RADIO_IS_GEN_2P1) + /* Using some static storage and alternating reads to read_index_ifr to replace read_resource_ifr */ + if (num_words_avail == 0) + { +#if RADIO_IS_GEN_3P0 + packed_data_long = read_index_ifr(ifr_read_addr); +#else /* Use 64 bit return version of read_resource */ + packed_data_long = read_resource_ifr(ifr_read_addr); +#endif /* RADIO_IS_GEN_3P0 */ + + num_words_avail = 2; + ifr_read_addr++; /* Read index addresses increment by 1 */ + } + + packed_data = (uint32_t)(packed_data_long & 0xFFFFFFFF); + packed_data_long = packed_data_long >> 32; + num_words_avail--; +#else + packed_data = read_resource_ifr(ifr_read_addr); + ifr_read_addr += 4; /* Read resource addresses increment by 4 */ +#endif /* (RADIO_IS_GEN_3P0 || RADIO_IS_GEN_2P1) */ + + return packed_data; +} + +#if RADIO_IS_GEN_3P0 +/*! ********************************************************************************* + * \brief Read command for reading from IFR using RDINDEX command + * + * \param read_addr flash address + * + * \return 8 bytes of packed data containing radio trims only + * +***********************************************************************************/ +static uint64_t read_index_ifr(uint32_t read_addr) +{ + uint8_t rdindex = read_addr; + uint64_t read_data; + uint8_t i; + + while ((FTFE_FSTAT_CCIF_MASK & FTFE->FSTAT) == 0); /* Wait till CCIF=1 to make sure not interrupting a prior operation */ + + if ((FTFE->FSTAT & FTFE_FSTAT_ACCERR_MASK) == FTFE_FSTAT_ACCERR_MASK ) + { + FTFE->FSTAT = (1 << FTFE_FSTAT_ACCERR_SHIFT); /* Write 1 to ACCEER to clear errors */ + } + + FTFE->FCCOB[0] = RDINDX; + FTFE->FCCOB[1] = rdindex; + + OSA_InterrupDisable(); + FTFE->FSTAT = FTFE_FSTAT_CCIF_MASK; + while((FTFE_FSTAT_CCIF_MASK & FTFE->FSTAT) == 0); /* Wait till CCIF=1 */ + OSA_InterruptEnable(); + + /* Pack read data back into 64 bit type */ + read_data = FTFE->FCCOB[11]; /* MSB goes in first, will be shifted left sequentially */ + for (i = 10; i > 3; i--) + { + read_data = read_data << 8; + read_data |= FTFE->FCCOB[i]; + } + + return read_data; +} +#else + +/*! ********************************************************************************* + * \brief Read command for reading from IFR + * + * \param read_addr flash address + * + * \return packed data containing radio trims only + * +***********************************************************************************/ +#if RADIO_IS_GEN_2P0 +static uint32_t read_resource_ifr(uint32_t read_addr) +{ + + uint32_t packed_data; + uint8_t flash_addr23_16, flash_addr15_8, flash_addr7_0; + uint32_t read_data31_24, read_data23_16, read_data15_8, read_data7_0; + + flash_addr23_16 = (uint8_t)((read_addr & 0xFF0000) >> 16); + flash_addr15_8 = (uint8_t)((read_addr & 0x00FF00) >> 8); + flash_addr7_0 = (uint8_t)(read_addr & 0xFF); + + while ((FTFA_FSTAT_CCIF_MASK & FTFA->FSTAT) == 0); /* Wait till CCIF=1 */ + + if ((FTFA->FSTAT & FTFA_FSTAT_ACCERR_MASK) == FTFA_FSTAT_ACCERR_MASK ) + { + FTFA->FSTAT = (1<FCCOB0 = RDRSRC; + FTFA->FCCOB1 = flash_addr23_16; + FTFA->FCCOB2 = flash_addr15_8; + FTFA->FCCOB3 = flash_addr7_0; + FTFA->FCCOB8 = 0x00; + + OSA_InterruptDisable(); + FTFA->FSTAT = FTFA_FSTAT_CCIF_MASK; + while ((FTFA_FSTAT_CCIF_MASK & FTFA->FSTAT) == 0); /* Wait till CCIF=1 */ + OSA_InterruptEnable(); + + /* Start reading */ + read_data31_24 = FTFA->FCCOB4; /* FTFA->FCCOB[4] */ + read_data23_16 = FTFA->FCCOB5; /* FTFA->FCCOB[5] */ + read_data15_8 = FTFA->FCCOB6; /* FTFA->FCCOB[6] */ + read_data7_0 = FTFA->FCCOB7; /* FTFA->FCCOB[7] */ + + packed_data = (read_data31_24 << 24) | (read_data23_16 << 16) | (read_data15_8 << 8) | (read_data7_0 << 0); + + return packed_data; +} +#else +static uint64_t read_resource_ifr(uint32_t read_addr) +{ + + uint64_t packed_data; + uint8_t flash_addr23_16, flash_addr15_8, flash_addr7_0; + uint8_t read_data[8]; + uint64_t temp_64; + uint8_t i; + + flash_addr23_16 = (uint8_t)((read_addr & 0xFF0000) >> 16); + flash_addr15_8 = (uint8_t)((read_addr & 0x00FF00) >> 8); + flash_addr7_0 = (uint8_t)(read_addr & 0xFF); + while((FTFE_FSTAT_CCIF_MASK & FTFE->FSTAT) == 0); /* Wait till CCIF=1 */ + + if ((FTFE->FSTAT & FTFE_FSTAT_ACCERR_MASK) == FTFE_FSTAT_ACCERR_MASK ) + { + FTFE->FSTAT = (1<FCCOB0 = RDRSRC; + FTFE->FCCOB1 = flash_addr23_16; + FTFE->FCCOB2 = flash_addr15_8; + FTFE->FCCOB3 = flash_addr7_0; + FTFE->FCCOB4 = 0x00; + + OSA_InterruptDisable(); + FTFE->FSTAT = FTFE_FSTAT_CCIF_MASK; + while ((FTFE_FSTAT_CCIF_MASK & FTFE->FSTAT) == 0); /* Wait till CCIF=1 */ + OSA_InterruptEnable(); + + /* Start reading */ + read_data[7] = FTFE->FCCOB4; + read_data[6] = FTFE->FCCOB5; + read_data[5] = FTFE->FCCOB6; + read_data[4] = FTFE->FCCOB7; + read_data[3] = FTFE->FCCOB8; + read_data[2] = FTFE->FCCOB9; + read_data[1] = FTFE->FCCOBA; + read_data[0] = FTFE->FCCOBB; + + packed_data = 0; + for (i = 0; i < 8; i++) + { + temp_64 = read_data[i]; + packed_data |= temp_64 << (i * 8); + } + + return packed_data; +} + +#endif /* RADIO_IS_GEN_2P0 */ +#endif /* RADIO_IS_GEN_3P0 */ + +/*! ********************************************************************************* + * \brief Store a SW trim value in the table passed in from calling function. + * + * \param sw_trim_tbl pointer to the software trim table to hold SW trim values + * \param num_entries the number of entries in the SW trim table + * \param addr the software trim ID + * \param data the value of the software trim + * +***********************************************************************************/ +static void store_sw_trim(IFR_SW_TRIM_TBL_ENTRY_T * sw_trim_tbl, uint16_t num_entries, uint32_t addr, uint32_t data) +{ + uint16_t i; + + if (sw_trim_tbl != NULL) + { + for (i = 0; i < num_entries; i++) + { + if (addr == sw_trim_tbl[i].trim_id) + { + sw_trim_tbl[i].trim_value = data; + sw_trim_tbl[i].valid = 1; + break; /* Don't need to scan the array any further... */ + } + } + } +} + +/*! ********************************************************************************* + * \brief Process block 1 IFR data. + * + * \param sw_trim_tbl pointer to the software trim table to hold SW trim values + * \param num_entries the number of entries in the SW trim table + * + * \remarks + * Uses a IFR v2 formatted default array if the IFR is blank or corrupted. + * Stores SW trim values to an array passed into this function. + * +***********************************************************************************/ +void handle_ifr(IFR_SW_TRIM_TBL_ENTRY_T * sw_trim_tbl, uint16_t num_entries) +{ + uint32_t dest_addr; + uint32_t read_addr; + uint32_t dest_data; + uint32_t packed_data; + const uint32_t *ifr_ptr; + +#if RADIO_IS_GEN_3P0 + num_words_avail = 0; /* Prep for handling 64 bit words from flash */ +#endif /* RADIO_IS_GEN_3P0 */ + +#if RADIO_IS_GEN_3P0 + read_addr = K3_BASE_INDEX; +#else +#ifdef CPU_MODEL_MKW41Z256VHT4 + read_addr = KW4x_256_BASE; +#else + read_addr = KW4x_512_BASE; +#endif /* CPU_MODEL_MKW41Z256VHT4 */ +#endif /* RADIO_IS_GEN_3P0 */ + + /* Read first entry in IFR table */ + packed_data = read_first_ifr_word(read_addr); + if ((packed_data&~IFR_VERSION_MASK) == IFR_VERSION_HDR) + { + /* Valid header was found, process real IFR data */ + XCVR_MISC->OVERWRITE_VER = (packed_data & IFR_VERSION_MASK); + store_sw_trim(sw_trim_tbl, num_entries, 0xABCD, (packed_data & IFR_VERSION_MASK)); /* Place IFR version # in SW trim array*/ + packed_data = read_another_ifr_word(); + + while (packed_data !=IFR_EOF_SYMBOL) + { + if (IS_A_SW_ID(packed_data)) /* SW Trim case (non_reg writes) */ + { + dest_addr = packed_data; + packed_data = read_another_ifr_word(); + dest_data = packed_data; + /* Place SW trim in array for driver SW to use */ + store_sw_trim(sw_trim_tbl, num_entries, dest_addr, dest_data); + } + else + { + if (IS_VALID_REG_ADDR(packed_data)) /* Valid register write address */ + { + dest_addr = packed_data; + packed_data = read_another_ifr_word(); + dest_data = packed_data; + *(uint32_t *)(dest_addr) = dest_data; + } + else + { /* Invalid address case */ + + } + } + + packed_data=read_another_ifr_word(); + } + } + else + { + /* Valid header is not present, use blind IFR trim table */ + ifr_ptr = BLOCK_1_IFR; + packed_data = *ifr_ptr; + XCVR_MISC->OVERWRITE_VER = (packed_data & IFR_VERSION_MASK); + store_sw_trim(sw_trim_tbl, num_entries, 0xABCD, (packed_data & IFR_VERSION_MASK)); /* Place IFR version # in SW trim array */ + ifr_ptr++; + packed_data= *ifr_ptr; + + while (packed_data != IFR_EOF_SYMBOL) + { + if (IS_A_SW_ID(packed_data)) + { + /* SW Trim case (non_reg writes) */ + dest_addr = packed_data; + ifr_ptr++; + packed_data = *(ifr_ptr); + dest_data = packed_data; + /* Place SW trim in array for driver SW to use */ + store_sw_trim(sw_trim_tbl, num_entries, dest_addr, dest_data); + } + else + { + dest_addr = packed_data; + ifr_ptr++; + packed_data = *ifr_ptr; + dest_data = packed_data; + + /* Valid register write address */ + if (IS_VALID_REG_ADDR(dest_addr)) + { + *(uint32_t *)(dest_addr) = dest_data; + } + else + { + /* Invalid address case */ + } + } + + ifr_ptr++; + packed_data= *ifr_ptr; + } + } +} + +#if RADIO_IS_GEN_3P0 + +#else +uint32_t handle_ifr_die_id(void) +{ + uint32_t id_x, id_y; + uint32_t id; + + id = read_resource_ifr(0x90); + id_x = id & 0x00FF0000; + id_y = id & 0x000000FF; + + return (id_x | id_y); +} + +uint32_t handle_ifr_die_kw_type(void) +{ + uint32_t zb, ble; + + zb = read_resource_ifr(0x80) & 0x8000; + ble= read_resource_ifr(0x88) & 0x100000; + + return (zb | ble); +} + +#endif /* RADIO_IS_GEN_3P0 */ + +/*! ********************************************************************************* + * \brief Dumps block 1 IFR data to an array. + * + * \param dump_tbl pointer to the table to hold the dumped IFR values + * \param num_entries the number of entries to dump + * + * \remarks + * Starts at the first address in IFR and dumps sequential entries. + * +***********************************************************************************/ +void dump_ifr(uint32_t * dump_tbl, uint8_t num_entries) +{ +#if RADIO_IS_GEN_3P0 + uint32_t ifr_address = 0x20000; +#else + uint32_t ifr_address = 0x20000; +#endif /* RADIO_IS_GEN_3P0 */ + uint32_t * dump_ptr = dump_tbl; + uint8_t i; + + *dump_ptr = read_first_ifr_word(ifr_address); + dump_ptr++; + + for (i = 0; i < num_entries - 1; i++) + { + *dump_ptr = read_another_ifr_word(); + dump_ptr++; + } +} diff --git a/drivers/kw41zrf/vendor/XCVR/MKW41Z4/ifr_radio.h b/drivers/kw41zrf/vendor/XCVR/MKW41Z4/ifr_radio.h new file mode 100644 index 000000000000..e7fbb6039830 --- /dev/null +++ b/drivers/kw41zrf/vendor/XCVR/MKW41Z4/ifr_radio.h @@ -0,0 +1,193 @@ +/* +* The Clear BSD License +* Copyright (c) 2015, Freescale Semiconductor, Inc. +* Copyright 2016-2017 NXP +* All rights reserved. +* +* Redistribution and use in source and binary forms, with or without +* modification, are permitted (subject to the limitations in the +* disclaimer below) provided that the following conditions are met: +* +* * Redistributions of source code must retain the above copyright +* notice, this list of conditions and the following disclaimer. +* +* * Redistributions in binary form must reproduce the above copyright +* notice, this list of conditions and the following disclaimer in the +* documentation and/or other materials provided with the distribution. +* +* * Neither the name of the copyright holder nor the names of its +* contributors may be used to endorse or promote products derived from +* this software without specific prior written permission. +* +* NO EXPRESS OR IMPLIED LICENSES TO ANY PARTY'S PATENT RIGHTS ARE +* GRANTED BY THIS LICENSE. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT +* HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED +* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE +* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR +* BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE +* OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN +* IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +#ifndef __IFR_RADIO_H__ +/* clang-format off */ +#define __IFR_RADIO_H__ +/* clang-format on */ + +#include +#include "fsl_xcvr.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/*! + * @addtogroup xcvr + * @{ + */ + +/*! @file*/ + +/******************************************************************************* + * Definitions + ******************************************************************************/ +#define IFR_EOF_SYMBOL (0xFEED0E0FU) /* < Denotes the "End of File" for IFR data */ +#define IFR_VERSION_HDR (0xABCD0000U) /* < Constant value for upper 16 bits of IFR data header */ +#define IFR_VERSION_MASK (0x0000FFFFU) /* < Mask for version number (lower 16 bits) of IFR data header */ +#define IFR_SW_ID_MIN (0x00000000U) /* < Lower limit of SW trim IDs */ +#define IFR_SW_ID_MAX (0x0000FFFFU) /* < Lower limit of SW trim IDs */ + +#define IS_A_SW_ID(x) ((IFR_SW_ID_MIN < (x)) && (IFR_SW_ID_MAX >= (x))) + +/* K3 valid registers support */ +#if (defined(CPU_MODEL_K32W042S1M2CAx_M0P) || defined(CPU_MODEL_K32W042S1M2VPJ_M0P)) +#define IS_VALID_REG_ADDR(x) (((x) & 0xFFFF0000U) == 0x41000000U) /* Valid addresses are 0x410xxxxx */ +#endif /* (defined(CPU_MODEL_K32W042S1M2CAx_M0P) || defined(CPU_MODEL_K32W042S1M2VPJ_M0P)) */ +/* KW41 and KW35/36 valid registers support */ +#if (defined(CPU_MODEL_MKW41Z256VHT4) || defined(CPU_MODEL_MKW41Z512VHT4) || \ + defined(CPU_MODEL_MKW31Z256VHT4) || defined(CPU_MODEL_MKW31Z512VHT4) || \ + defined(CPU_MODEL_MKW21Z256VHT4) || defined(CPU_MODEL_MKW21Z512VHT4) || \ + defined(CPU_MODEL_MKW35A512VFP4) || defined(CPU_MODEL_MKW36A512VFP4) ) + +#define IS_VALID_REG_ADDR(x) (((x) & 0xFFFF0000U) == 0x40050000U) /* Valid addresses are 0x4005xxxx */ +#endif + +#define MAKE_MASK(size) ((1 << (size)) - 1) +#define MAKE_MASKSHFT(size, bitpos) (MAKE_MASK(size) << (bitpos)) + +#define IFR_TZA_CAP_TUNE_MASK (0x0000000FU) +#define IFR_TZA_CAP_TUNE_SHIFT (0) +#define IFR_BBF_CAP_TUNE_MASK (0x000F0000U) +#define IFR_BBF_CAP_TUNE_SHIFT (16) +#define IFR_RES_TUNE2_MASK (0x00F00000U) +#define IFR_RES_TUNE2_SHIFT (20) + +/* \var typedef uint8_t IFR_ERROR_T */ +/* \brief The IFR error reporting type. */ +/* See #IFR_ERROR_T_enum for the enumeration definitions. */ +typedef uint8_t IFR_ERROR_T; + +/* \brief The enumerations used to describe IFR errors. */ +enum IFR_ERROR_T_enum +{ + IFR_SUCCESS = 0, + INVALID_POINTER = 1, /* < NULL pointer error */ + INVALID_DEST_SIZE_SHIFT = 2, /* < the bits won't fit as specified in the destination */ +}; + +/* \var typedef uint16_t SW_TRIM_ID_T */ +/* \brief The SW trim ID type. */ +/* See #SW_TRIM_ID_T_enum for the enumeration definitions. */ +typedef uint16_t SW_TRIM_ID_T; + +/* \brief The enumerations used to define SW trim IDs. */ +enum SW_TRIM_ID_T_enum +{ + Q_RELATIVE_GAIN_BY_PART = 0, /* < Q vs I relative gain trim ID */ + ADC_GAIN = 1, /* < ADC gain trim ID */ + ZB_FILT_TRIM = 2, /* < Baseband Bandwidth filter trim ID for BLE */ + BLE_FILT_TRIM = 3, /* < Baseband Bandwidth filter trim ID for BLE */ + TRIM_STATUS = 4, /* < Status result of the trim process (error indications) */ + TRIM_VERSION = 0xABCD, /* < Version number of the IFR trim algorithm/format. */ +}; + +/* \var typedef uint32_t IFR_TRIM_STATUS_T */ +/* \brief The definition of failure bits stored in IFR trim status word. */ +/* See #IFR_TRIM_STATUS_T_enum for the enumeration definitions. */ +typedef uint32_t IFR_TRIM_STATUS_T; + +/* \brief The enumerations used to describe trim algorithm failures in the status entry in IFR. */ +/* This enum represents multiple values which can be OR'd together in a single status word. */ +enum IFR_TRIM_STATUS_T_enum +{ + TRIM_ALGORITHM_SUCCESS = 0, + BGAP_VOLTAGE_TRIM_FAILED = 1, /* < algorithm failure in BGAP voltagetrim */ + IQMC_GAIN_ADJ_FAILED = 2, /* < algorithm failure in IQMC gain trim */ + IQMC_PHASE_ADJ_FAILED = 4, /* < algorithm failure in IQMC phase trim */ + IQMC_DC_GAIN_ADJ_FAILED = 8, /* < IQMC DC gain trim failure */ + ADC_GAIN_TRIM_FAILED = 10, /* < Trim failure for ADC Gain Trim */ + ZB_FILT_TRIM_FAILED = 20, /* < Filter trim failure for 802.15.4 */ + BLE_FILT_TRIM_FAILED = 40, /* < Filter trim failure for BLE */ +}; + +/* \var typedef struct IFR_SW_TRIM_TBL_ENTRY_T */ +/* \brief Structure defining an entry in a table used to contain values to be passed back from IFR */ +/* handling routine to XCVR driver software. */ +typedef struct +{ + SW_TRIM_ID_T trim_id; /* < The assigned ID */ + uint32_t trim_value; /* < The value fetched from IFR.*/ + uint8_t valid; /* < validity of the trim_value field after IFR processing is complete (TRUE/FALSE).*/ +} IFR_SW_TRIM_TBL_ENTRY_T; + +/******************************************************************************* + * API + ******************************************************************************/ + +/*! + * @brief Main IFR handler function called by XCVR driver software to process trim table. + * + * This function handles reading data from IFR and either loading to registers or storing to a SW trim values table. + * + * @param sw_trim_tbl pointer to the table used to store software trim values. + * @param num_entries the number of entries that can be stored in the SW trim table. + */ +void handle_ifr(IFR_SW_TRIM_TBL_ENTRY_T * sw_trim_tbl, uint16_t num_entries); + +/*! + * @brief Handler function to read die_id from IFR locations.. + * + * This function handles reading die ID value for debug and testing usage. + * + * @return the value of the die ID field. + */ +uint32_t handle_ifr_die_id(void); + +/*! + * @brief Handler function to read KW chip version from IFR locations.. + * + * This function handles reading KW chip version for debug and testing usage. + * + * @return the value of the KW version field. + */ +uint32_t handle_ifr_die_kw_type(void); + +/*! + * @brief Debug function to dump the IFR contents to a RAM array. + * + * This function handles reading data from IFR and storing to a RAM array for debug. + * + * @param dump_tbl pointer to the table used to store IFR entry values. + * @param num_entries the number of entries that can be stored in the dump table. + */ +void dump_ifr(uint32_t * dump_tbl, uint8_t num_entries); + +#ifdef __cplusplus +} +#endif + +#endif /*__IFR_RADIO_H__ */ diff --git a/examples/default/Makefile b/examples/default/Makefile index 705ef541c895..5f74b7929046 100644 --- a/examples/default/Makefile +++ b/examples/default/Makefile @@ -38,7 +38,8 @@ BOARD_PROVIDES_NETIF := acd52832 airfy-beacon atmega256rfr2-xpro avr-rss2 b-l072 derfmega128 derfmega256 hamilton iotlab-m3 iotlab-a8-m3 lobaro-lorabox lsn50 mulle microbit msba2 \ microduino-corerf native nrf51dk nrf51dongle nrf52dk nrf52840dk nrf52840-mdk nrf6310 \ nucleo-f207zg nucleo-f767zi openmote-b openmote-cc2538 pba-d-01-kw2x remote-pa \ - remote-reva ruuvitag samr21-xpro samr30-xpro spark-core telosb thingy52 yunjia-nrf51822 z1 + remote-reva ruuvitag samr21-xpro samr30-xpro spark-core telosb thingy52 yunjia-nrf51822 z1 \ + frdm-kw41z phynode-kw41z usb-kw41z openlabs-kw41z-mini openlabs-kw41z-mini-256kib ifneq (,$(filter $(BOARD),$(BOARD_PROVIDES_NETIF))) # Use modules for networking diff --git a/sys/net/gnrc/netif/init_devs/auto_init_kw41zrf.c b/sys/net/gnrc/netif/init_devs/auto_init_kw41zrf.c new file mode 100644 index 000000000000..f17246b2bded --- /dev/null +++ b/sys/net/gnrc/netif/init_devs/auto_init_kw41zrf.c @@ -0,0 +1,81 @@ +/* + * Copyright (C) 2017 SKF AB + * + * This file is subject to the terms and conditions of the GNU Lesser + * General Public License v2.1. See the file LICENSE in the top level + * directory for more details. + * + */ + +/** + * @ingroup sys_auto_init_gnrc_netif + * @{ + * + * @file + * @brief Auto initialization for kw41zrf network interfaces + * + * @author Joakim Nohlgård + * @author Thomas Stilwell + */ + +#ifdef MODULE_KW41ZRF + +#include "log.h" +#include "board.h" +#include "net/gnrc.h" +#include "net/gnrc/netif/ieee802154.h" + +#ifdef MODULE_GNRC_LWMAC +#include "net/gnrc/lwmac/lwmac.h" +#endif +#ifdef MODULE_GNRC_GOMACH +#include "net/gnrc/gomach/gomach.h" +#endif + +#include "kw41zrf.h" + +/** + * @name Stack parameters for the MAC layer thread + * @{ + */ +#ifndef KW41ZRF_NETIF_STACKSIZE +#define KW41ZRF_NETIF_STACKSIZE (THREAD_STACKSIZE_DEFAULT) +#endif +#ifndef KW41ZRF_NETIF_PRIO +#define KW41ZRF_NETIF_PRIO (GNRC_NETIF_PRIO) +#endif +/** @} */ + +/* There is only one memory mapped transceiver in the supported SoCs, the driver + * does not try to take into account multiple instances of the hardware module */ +#define KW41ZRF_NUMOF 1 + +static kw41zrf_t kw41zrf_devs[KW41ZRF_NUMOF]; +static char _kw41zrf_stacks[KW41ZRF_NUMOF][KW41ZRF_NETIF_STACKSIZE]; + +void auto_init_kw41zrf(void) +{ + for (unsigned i = 0; i < KW41ZRF_NUMOF; i++) { + LOG_DEBUG("[auto_init_netif] initializing kw41zrf #%u\n", i); + kw41zrf_setup(&kw41zrf_devs[i]); + +#if defined(MODULE_GNRC_GOMACH) + gnrc_netif_gomach_create(_kw41zrf_stacks[i], KW41ZRF_NETIF_STACKSIZE, + KW41ZRF_NETIF_PRIO, "kw41zrf-gomach", + (netdev_t *)&kw41zrf_devs[i]); +#elif defined(MODULE_GNRC_LWMAC) + gnrc_netif_lwmac_create(_kw41zrf_stacks[i], KW41ZRF_NETIF_STACKSIZE, + KW41ZRF_NETIF_PRIO, "kw41zrf-lwmac", + (netdev_t *)&kw41zrf_devs[i]); +#else + gnrc_netif_ieee802154_create(_kw41zrf_stacks[i], KW41ZRF_NETIF_STACKSIZE, + KW41ZRF_NETIF_PRIO, "kw41zrf", + (netdev_t *)&kw41zrf_devs[i]); +#endif + } +} +#else +typedef int dont_be_pedantic; +#endif /* MODULE_KW41ZRF */ + +/** @} */ diff --git a/sys/net/gnrc/netif/init_devs/init.c b/sys/net/gnrc/netif/init_devs/init.c index 7ca10c56972d..7f8ea6f05862 100644 --- a/sys/net/gnrc/netif/init_devs/init.c +++ b/sys/net/gnrc/netif/init_devs/init.c @@ -109,6 +109,11 @@ void gnrc_netif_init_devs(void) auto_init_kw2xrf(); } + if (IS_USED(MODULE_KW41ZRF)) { + extern void auto_init_kw41zrf(void); + auto_init_kw41zrf(); + } + if (IS_USED(MODULE_USBUS_CDC_ECM)) { extern void auto_init_netdev_cdcecm(void); auto_init_netdev_cdcecm();