Newer
Older
mbed-os / connectivity / drivers / 802.15.4_RF / TARGET_Freescale / TARGET_KW41Z / NanostackRfPhyKw41z.cpp
/*
* Copyright (c) 2016-2018 ARM Limited. All rights reserved.
* SPDX-License-Identifier: Apache-2.0
* Licensed under the Apache License, Version 2.0 (the License); you may
* not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an AS IS BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "mbed_power_mgmt.h"
#include "common_functions.h"
#include "platform/arm_hal_interrupt.h"
#include "platform/arm_hal_phy.h"
#include "NanostackRfPhyKw41z.h"

#include "fsl_xcvr.h"

#define RF_THREAD_STACK_SIZE 1024

static void rf_thread_loop();
Thread rf_thread(osPriorityRealtime, RF_THREAD_STACK_SIZE);

#define PHY_MTU_SIZE     127
#define CRC_LENGTH 0
#define PHY_HEADER_LENGTH 0

#define BM_ZLL_IRQSTS_TMRxMSK (ZLL_IRQSTS_TMR1MSK_MASK | \
                               ZLL_IRQSTS_TMR2MSK_MASK | \
                               ZLL_IRQSTS_TMR3MSK_MASK | \
                               ZLL_IRQSTS_TMR4MSK_MASK)

#define RF_CCA_THRESHOLD 75 /* -75 dBm */

#define gPhyDefaultTxPowerLevel_d     (22)
#define gPhyMaxTxPowerLevel_d         (32)
#define gCcaED_c                      (0)
#define gCcaCCA_MODE1_c               (1)

#define gPhyTimeMask_c                (0x00FFFFFF)

#define KW41Z_SHR_PHY_TIME            12
#define KW41Z_PER_BYTE_TIME           2
#define KW41Z_ACK_WAIT_TIME           54

static int8_t rf_radio_driver_id = -1;
static uint8_t need_ack = 0;

/* PHY states */
typedef enum xcvrState_tag {
    gIdle_c,
    gRX_c,
    gTX_c,
    gCCA_c,
    gTR_c,
    gCCCA_c,
} xcvrState_t;

static int8_t rf_interface_state_control(phy_interface_state_e new_state, uint8_t rf_channel);
static int8_t rf_start_cca(uint8_t *data_ptr, uint16_t data_length, uint8_t tx_handle, data_protocol_e data_protocol);
static int8_t rf_address_write(phy_address_type_e address_type, uint8_t *address_ptr);
static int8_t rf_extension(phy_extension_type_e extension_type, uint8_t *data_ptr);
static void rf_mac_hw_init(void);
static void rf_mac_ed_state_enable(void);
static void rf_mac_set_pending(uint8_t status);
static void rf_mac_set_shortAddress(uint8_t *valueAddress);
static void rf_mac_set_panId(uint8_t *valueAddress);
static void rf_mac_set_mac64(const uint8_t *valueAddress);
static uint8_t rf_convert_energy_level(uint8_t energyLevel);
static void rf_abort(void);
static void rf_ack_wait_timer_start(uint16_t time);
static void rf_get_timestamp(uint32_t *pRetClk);
static uint32_t rf_get_timeout(void);
static void rf_set_timeout(uint32_t timeout);
static void rf_promiscuous(uint8_t state);
static void handle_IRQ_events(void);
static uint8_t PhyPlmeSetCurrentChannelRequest(uint8_t channel, uint8_t pan);
static void rf_receive(void);
static void rf_handle_rx_end(void);

static uint8_t MAC64_addr_default[8] = {1, 2, 3, 4, 5, 6, 7, 8};
static uint8_t MAC64_addr[8];

static xcvrState_t mPhySeqState;
static uint8_t rf_mac_handle;
static volatile uint8_t rf_ed_value = 0;
static volatile bool rf_ack_pending_state = false;
static volatile bool sleep_blocked = false;

static NanostackRfPhyKw41z *rf = NULL;

#define MAC_PACKET_SIZE 127 //MAX MAC payload is 127 bytes
static uint8_t PHYPAYLOAD[MAC_PACKET_SIZE];

const phy_rf_channel_configuration_s phy_2_4ghz = {2405000000U, 5000000U, 250000U, 16U, M_OQPSK};

const phy_device_channel_page_s phy_channel_pages[] = {
    {CHANNEL_PAGE_0, &phy_2_4ghz},
    {CHANNEL_PAGE_0, NULL}
};

static phy_device_driver_s device_driver = {
    PHY_LINK_15_4_2_4GHZ_TYPE,
    PHY_LAYER_PAYLOAD_DATA_FLOW,
    MAC64_addr,
    PHY_MTU_SIZE,
    (char *)"NXP kw41z",
    CRC_LENGTH,
    PHY_HEADER_LENGTH,
    &rf_interface_state_control,
    &rf_start_cca,
    &rf_address_write,
    &rf_extension,
    phy_channel_pages,
    NULL,
    NULL,
    NULL,
    NULL
};

static void rf_thread_loop()
{
    for (;;) {
        ThisThread::flags_wait_all(1);

        platform_enter_critical();

        handle_IRQ_events();

        platform_exit_critical();
        NVIC_ClearPendingIRQ(Radio_1_IRQn);
        NVIC_EnableIRQ(Radio_1_IRQn);
    }
}

static int8_t rf_device_register(void)
{
    if (rf_radio_driver_id < 0) {
        rf_mac_hw_init();
        /**
         * Read factory stored Mac address to RAM
         */
        common_write_32_bit(ZLL->MACLONGADDRS0_MSB, MAC64_addr);
        common_write_32_bit(ZLL->MACLONGADDRS0_LSB, MAC64_addr + 4);

        rf_radio_driver_id = arm_net_phy_register(&device_driver);
    }

    return rf_radio_driver_id;
}

static void rf_device_unregister(void)
{
    arm_net_phy_unregister(rf_radio_driver_id);

    if (sleep_blocked) {
        sleep_manager_unlock_deep_sleep();
        sleep_blocked = false;
    }
}

/*
 * \brief Function enables/disables Rx promiscuous mode.
 *
 * \param state of XCVR promiscuous mode
 *
 * \return none
 */
static void rf_promiscuous(uint8_t state)
{
    if (state) {
        ZLL->PHY_CTRL |= ZLL_PHY_CTRL_PROMISCUOUS_MASK;
        /* FRM_VER[11:8] = b1111. Any FrameVersion accepted */
        ZLL->RX_FRAME_FILTER |= (ZLL_RX_FRAME_FILTER_FRM_VER_FILTER_MASK |
                                 ZLL_RX_FRAME_FILTER_ACK_FT_MASK |
                                 ZLL_RX_FRAME_FILTER_NS_FT_MASK);
    } else {
        ZLL->PHY_CTRL &= ~ZLL_PHY_CTRL_PROMISCUOUS_MASK;
        /* FRM_VER[11:8] = b0011. Accept FrameVersion 0 and 1 packets, reject all others */
        /* Beacon, Data and MAC command frame types accepted */
        ZLL->RX_FRAME_FILTER &= ~(ZLL_RX_FRAME_FILTER_FRM_VER_FILTER_MASK |
                                  ZLL_RX_FRAME_FILTER_ACK_FT_MASK  |
                                  ZLL_RX_FRAME_FILTER_NS_FT_MASK   |
                                  ZLL_RX_FRAME_FILTER_ACTIVE_PROMISCUOUS_MASK);
        ZLL->RX_FRAME_FILTER |= ZLL_RX_FRAME_FILTER_FRM_VER_FILTER(3);
    }
}

static void rf_mac_set_pending(uint8_t status)
{
    uint32_t reg = ZLL->SAM_TABLE;

    /* Disable the Source Address Matching feature and set FP manually */
    reg |= ZLL_SAM_TABLE_ACK_FRM_PND_CTRL_MASK;
    if (status) {
        reg |= ZLL_SAM_TABLE_ACK_FRM_PND_MASK;
        rf_ack_pending_state = true;
    } else {
        reg &= ~ZLL_SAM_TABLE_ACK_FRM_PND_MASK;
        rf_ack_pending_state = false;
    }
    ZLL->SAM_TABLE = reg;
}

static int8_t rf_interface_state_control(phy_interface_state_e new_state, uint8_t rf_channel)
{
    platform_enter_critical();

    switch (new_state) {
        /*Reset PHY driver and set to idle*/
        case PHY_INTERFACE_RESET:
            rf_abort();
            if (sleep_blocked) {
                sleep_manager_unlock_deep_sleep();
                sleep_blocked = false;
            }
            break;
        /*Disable PHY Interface driver*/
        case PHY_INTERFACE_DOWN:
            rf_abort();
            if (sleep_blocked) {
                sleep_manager_unlock_deep_sleep();
                sleep_blocked = false;
            }
            break;
        /*Enable PHY Interface driver*/
        case PHY_INTERFACE_UP:
            if (PhyPlmeSetCurrentChannelRequest(rf_channel, 0)) {
                return 1;
            }
            if (!sleep_blocked) {
                /* Disable enter to deep sleep when transfer active */
                sleep_manager_lock_deep_sleep();
                sleep_blocked = true;
            }
            rf_receive();
            break;
        /*Enable wireless interface ED scan mode*/
        case PHY_INTERFACE_RX_ENERGY_STATE:
            if (PhyPlmeSetCurrentChannelRequest(rf_channel, 0)) {
                return 1;
            }
            if (!sleep_blocked) {
                /* Disable enter to deep sleep when transfer active */
                sleep_manager_lock_deep_sleep();
                sleep_blocked = true;
            }
            rf_abort();
            rf_mac_ed_state_enable();
            break;
        case PHY_INTERFACE_SNIFFER_STATE:             /**< Enable Sniffer state */
            rf_promiscuous(1);
            if (PhyPlmeSetCurrentChannelRequest(rf_channel, 0)) {
                return 1;
            }
            if (!sleep_blocked) {
                /* Disable enter to deep sleep when transfer active */
                sleep_manager_lock_deep_sleep();
                sleep_blocked = true;
            }
            rf_receive();
            break;
    }

    platform_exit_critical();

    return 0;
}

/*
 * \brief Function forces the XCVR to Idle state.
 *
 * \param none
 *
 * \return none
 */
static void rf_abort(void)
{
    /* Mask XCVR irq */
    NVIC_DisableIRQ(Radio_1_IRQn);

    mPhySeqState = gIdle_c;

    /* Mask SEQ interrupt */
    ZLL->PHY_CTRL |= ZLL_PHY_CTRL_SEQMSK_MASK;

    /* Disable timer trigger (for scheduled XCVSEQ) */
    if (ZLL->PHY_CTRL & ZLL_PHY_CTRL_TMRTRIGEN_MASK) {
        ZLL->PHY_CTRL &= ~ZLL_PHY_CTRL_TMRTRIGEN_MASK;
        /* give the FSM enough time to start if it was triggered */
        while ((XCVR_MISC->XCVR_CTRL & XCVR_CTRL_XCVR_STATUS_TSM_COUNT_MASK) == 0) {}
    }

    /* If XCVR is not idle, abort current SEQ */
    if (ZLL->PHY_CTRL & ZLL_PHY_CTRL_XCVSEQ_MASK) {
        /* Abort current SEQ */
        ZLL->PHY_CTRL &= ~ZLL_PHY_CTRL_XCVSEQ_MASK;

        /* Wait for Sequence Idle (if not already) */
        while (ZLL->SEQ_STATE & ZLL_SEQ_STATE_SEQ_STATE_MASK) {}
    }

    /* Stop timers */
    ZLL->PHY_CTRL &= ~(ZLL_PHY_CTRL_TMR1CMP_EN_MASK |
                       ZLL_PHY_CTRL_TMR2CMP_EN_MASK |
                       ZLL_PHY_CTRL_TMR3CMP_EN_MASK |
                       ZLL_PHY_CTRL_TC3TMOUT_MASK);

    /* clear all IRQ bits to avoid unexpected interrupts */
    ZLL->IRQSTS = ZLL->IRQSTS;

    /* Unmask XCVR irq */
    NVIC_EnableIRQ(Radio_1_IRQn);
}

static int8_t rf_start_cca(uint8_t *data_ptr, uint16_t data_length, uint8_t tx_handle, data_protocol_e data_protocol)
{
    uint32_t reg, tx_len;
    uint32_t irqSts;
    volatile uint8_t *pPB;
    uint8_t i;
    uint32_t tx_warmup_time;

    platform_enter_critical();

    if (mPhySeqState == gRX_c) {
        rf_abort();
    }

    /* Check if transmitter is busy*/
    if (mPhySeqState != gIdle_c) {
        platform_exit_critical();
        /*Return busy*/
        return -1;
    }

    /*Store TX handle*/
    rf_mac_handle = tx_handle;

    /* Check if transmitted data needs to be acked */
    need_ack = (*data_ptr & 0x20) == 0x20;

    /* Load data into Packet Buffer */
    pPB = (uint8_t *)ZLL->PKT_BUFFER_TX;

    tx_len = data_length + 2;
    *pPB++ = tx_len; /* including 2 bytes of FCS */

    for (i = 0; i < data_length; i++) {
        *pPB++ = *data_ptr++;
    }

    reg = ZLL->PHY_CTRL;

    /* Perform CCA before TX */
    reg |= ZLL_PHY_CTRL_CCABFRTX_MASK;

    /* Set CCA mode 1 */
    reg &= ~(ZLL_PHY_CTRL_CCATYPE_MASK);
    reg |= ZLL_PHY_CTRL_CCATYPE(gCcaCCA_MODE1_c);
    ZLL->PHY_CTRL = reg;

    /* Perform TxRxAck sequence if required by phyTxMode */
    if (need_ack) {
        ZLL->PHY_CTRL |= ZLL_PHY_CTRL_RXACKRQD_MASK;
        mPhySeqState = gTR_c;
    } else {
        ZLL->PHY_CTRL &= ~ZLL_PHY_CTRL_RXACKRQD_MASK;
        mPhySeqState = gTX_c;
    }

    /* Ensure that no spurious interrupts are raised */
    irqSts = ZLL->IRQSTS;
    irqSts &= ~(ZLL_IRQSTS_TMR1IRQ_MASK | ZLL_IRQSTS_TMR4IRQ_MASK);
    irqSts |= ZLL_IRQSTS_TMR3MSK_MASK;
    ZLL->IRQSTS = irqSts;

    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;

    /* Compute warmup times (scaled to 16us) */
    if (tx_warmup_time & 0x0F) {
        tx_warmup_time = 1 + (tx_warmup_time >> 4);
    } else {
        tx_warmup_time = tx_warmup_time >> 4;
    }

    if (need_ack) {
        rf_ack_wait_timer_start(tx_warmup_time + KW41Z_SHR_PHY_TIME +
                                tx_len * KW41Z_PER_BYTE_TIME  + 10 + KW41Z_ACK_WAIT_TIME);
    }

    /* Unmask SEQ interrupt */
    ZLL->PHY_CTRL &= ~(ZLL_PHY_CTRL_SEQMSK_MASK);

    /* Start the TX / TRX */
    reg = ZLL->PHY_CTRL;
    reg &= ~(ZLL_PHY_CTRL_XCVSEQ_MASK);
    reg |= mPhySeqState;
    ZLL->PHY_CTRL = reg;

    platform_exit_critical();

    /*Return success*/
    return 0;
}

static int8_t rf_address_write(phy_address_type_e address_type, uint8_t *address_ptr)
{
    int8_t ret_val = 0;

    platform_enter_critical();

    switch (address_type) {
        case PHY_MAC_64BIT:
            rf_mac_set_mac64(address_ptr);
            break;
        /*Set 16-bit address*/
        case PHY_MAC_16BIT:
            rf_mac_set_shortAddress(address_ptr);
            break;
        /*Set PAN Id*/
        case PHY_MAC_PANID:
            rf_mac_set_panId(address_ptr);
            break;
        default:
            ret_val = -1;
    }

    platform_exit_critical();

    return ret_val;
}

static int8_t rf_extension(phy_extension_type_e extension_type, uint8_t *data_ptr)
{
    int ret_value = 0;

    platform_enter_critical();

    switch (extension_type) {
        case PHY_EXTENSION_CTRL_PENDING_BIT: /**< Control MAC pending bit for indirect data. */
            rf_mac_set_pending(*data_ptr);
            break;
        /* Return frame Auto Ack frame pending status */
        case PHY_EXTENSION_READ_LAST_ACK_PENDING_STATUS: {
            *data_ptr = rf_ack_pending_state;
            break;
        }
        case PHY_EXTENSION_SET_CHANNEL:  /**< Net library channel set. */
            break;
        case PHY_EXTENSION_READ_CHANNEL_ENERGY: /**< RF interface ED scan energy read. */
            *data_ptr = rf_ed_value;
            break;
        case PHY_EXTENSION_READ_LINK_STATUS: /**< Net library could read link status. */
        case PHY_EXTENSION_CONVERT_SIGNAL_INFO: /**< Convert signal info. */
        default:
            ret_value = -1;
    }

    platform_exit_critical();

    return ret_value;
}

/*
 * \brief Function converts the energy level from dBm to a 0-255 value.
 *
 * \param energyLevel in dBm
 *
 * \return energy level (0-255)
 */
static uint8_t rf_convert_energy_level(uint8_t energyLevel)
{
    int32_t temp = (int8_t)energyLevel;

    if (temp <= -82) {
        temp = 0x00;
    } else if (temp >= -3) {
        temp = 0xFF;
    } else {
        /* Convert energy level from dbm into a 0x00-0xFF value */
        temp = (255 * temp + 20910) / 79;
    }

    return (uint8_t)temp;
}

/**
 *  SET MAC 16 address to Register
 */
static void rf_mac_set_shortAddress(uint8_t *valueAddress)
{
    ZLL->MACSHORTADDRS0 &= ~ZLL_MACSHORTADDRS0_MACSHORTADDRS0_MASK;
    ZLL->MACSHORTADDRS0 |= ZLL_MACSHORTADDRS0_MACSHORTADDRS0(common_read_16_bit(valueAddress));
}

/**
 *  SET PAN-ID to Register
 */
static void rf_mac_set_panId(uint8_t *valueAddress)
{
    ZLL->MACSHORTADDRS0 &= ~ZLL_MACSHORTADDRS0_MACPANID0_MASK;
    ZLL->MACSHORTADDRS0 |= ZLL_MACSHORTADDRS0_MACPANID0(common_read_16_bit(valueAddress));
}

/**
 *  SET MAC64 address to register
 */
static void rf_mac_set_mac64(const uint8_t *valueAddress)
{
    ZLL->MACLONGADDRS0_MSB = common_read_32_bit(valueAddress);
    valueAddress += 4;
    ZLL->MACLONGADDRS0_LSB = common_read_32_bit(valueAddress);
}

static void PhyPlmeSetPwrLevelRequest(uint8_t pwrStep)
{
    /* Do not exceed the Tx power limit for the current channel */
    if (pwrStep > gPhyMaxTxPowerLevel_d) {
        pwrStep = gPhyMaxTxPowerLevel_d;
    }

    if (pwrStep > 2) {
        pwrStep = (pwrStep << 1) - 2;
    }

    ZLL->PA_PWR = pwrStep;
}

static uint8_t PhyPlmeGetPwrLevelRequest(void)
{
    uint8_t pwrStep = (uint8_t)ZLL->PA_PWR;

    if (pwrStep > 2) {
        pwrStep = (pwrStep + 2) >> 1;
    }

    return pwrStep;
}

static uint8_t PhyPlmeSetCurrentChannelRequest(uint8_t channel, uint8_t pan)
{
    if ((channel < 11) || (channel > 26)) {
        return 1;
    }

    if (!pan) {
        ZLL->CHANNEL_NUM0 = channel;
    } else {
        ZLL->CHANNEL_NUM1 = channel;
    }

    /* Make sure the current Tx power doesn't exceed the Tx power limit for the new channel */
    if (PhyPlmeGetPwrLevelRequest() > gPhyMaxTxPowerLevel_d) {
        PhyPlmeSetPwrLevelRequest(gPhyMaxTxPowerLevel_d);
    }

    return 0;
}

/*
 * Function is a RF interrupt vector.
 */
static void PHY_InterruptHandler(void)
{
    /* Disable and clear transceiver(IRQ_B) interrupt */
    NVIC_DisableIRQ(Radio_1_IRQn);
    rf_thread.flags_set(1);
}

static void PhyIsrSeqCleanup(void)
{
    uint32_t irqStatus;

    /* Set the PHY sequencer back to IDLE */
    ZLL->PHY_CTRL &= ~ZLL_PHY_CTRL_XCVSEQ_MASK;
    /* Mask SEQ, RX, TX and CCA interrupts */
    ZLL->PHY_CTRL |= ZLL_PHY_CTRL_CCAMSK_MASK |
                     ZLL_PHY_CTRL_RXMSK_MASK  |
                     ZLL_PHY_CTRL_TXMSK_MASK  |
                     ZLL_PHY_CTRL_SEQMSK_MASK;

    while (ZLL->SEQ_STATE & ZLL_SEQ_STATE_SEQ_STATE_MASK) {}

    irqStatus = ZLL->IRQSTS;
    /* Mask TMR3 interrupt */
    irqStatus |= ZLL_IRQSTS_TMR3MSK_MASK;
    /* Clear transceiver interrupts except TMRxIRQ */
    irqStatus &= ~(ZLL_IRQSTS_TMR1IRQ_MASK |
                   ZLL_IRQSTS_TMR2IRQ_MASK |
                   ZLL_IRQSTS_TMR3IRQ_MASK |
                   ZLL_IRQSTS_TMR4IRQ_MASK);
    ZLL->IRQSTS = irqStatus;
}

static void PhyIsrTimeoutCleanup(void)
{
    uint32_t irqStatus;

    /* Set the PHY sequencer back to IDLE and disable TMR3 comparator and timeout */
    ZLL->PHY_CTRL &= ~(ZLL_PHY_CTRL_TMR3CMP_EN_MASK |
                       ZLL_PHY_CTRL_TC3TMOUT_MASK   |
                       ZLL_PHY_CTRL_XCVSEQ_MASK);
    /* Mask SEQ, RX, TX and CCA interrupts */
    ZLL->PHY_CTRL |= ZLL_PHY_CTRL_CCAMSK_MASK |
                     ZLL_PHY_CTRL_RXMSK_MASK  |
                     ZLL_PHY_CTRL_TXMSK_MASK  |
                     ZLL_PHY_CTRL_SEQMSK_MASK;

    while (ZLL->SEQ_STATE & ZLL_SEQ_STATE_SEQ_STATE_MASK) {}

    irqStatus = ZLL->IRQSTS;
    /* Mask TMR3 interrupt */
    irqStatus |= ZLL_IRQSTS_TMR3MSK_MASK;
    /* Clear transceiver interrupts except TMR1IRQ and TMR4IRQ. */
    irqStatus &= ~(ZLL_IRQSTS_TMR1IRQ_MASK |
                   ZLL_IRQSTS_TMR4IRQ_MASK);
    ZLL->IRQSTS = irqStatus;

    /* The packet was transmitted successfully, but no ACK was received */
    if (device_driver.phy_tx_done_cb) {
        device_driver.phy_tx_done_cb(rf_radio_driver_id, rf_mac_handle, PHY_LINK_TX_SUCCESS, 1, 1);
    }
}

/*
 * \brief Function get the time-out for the current sequence.
 *
 * \return sequence time-out value
 */
static uint32_t rf_get_timeout(void)
{
    return ZLL->T3CMP;
}

/*
 * \brief Function set a time-out to an XCVR sequence.
 *
 * \param pEndTime sequence time-out value [symbols]
 *
 * \return none
 */
static void rf_set_timeout(uint32_t pEndTime)
{
    uint32_t irqsts;

    platform_enter_critical();

    /* disable TMR3 compare */
    ZLL->PHY_CTRL &= ~ZLL_PHY_CTRL_TMR3CMP_EN_MASK;
    ZLL->T3CMP = pEndTime & ZLL_T3CMP_T3CMP_MASK;

    /* acknowledge TMR3 IRQ */
    irqsts  = ZLL->IRQSTS & BM_ZLL_IRQSTS_TMRxMSK;
    irqsts |= ZLL_IRQSTS_TMR3IRQ_MASK;
    ZLL->IRQSTS = irqsts;
    /* enable TMR3 compare */
    ZLL->PHY_CTRL |= ZLL_PHY_CTRL_TMR3CMP_EN_MASK;
    /* enable autosequence stop by TC3 match */
    ZLL->PHY_CTRL |= ZLL_PHY_CTRL_TC3TMOUT_MASK;

    platform_exit_critical();

}

/**
 * Call this only One time
 */
static void rf_mac_hw_init(void)
{
    xcvrStatus_t xcvrStatus;
    uint32_t phyReg;

    /* The data rate argument only matters when GFSK/MSK protocol is selected */
    xcvrStatus = XCVR_Init(ZIGBEE_MODE, DR_500KBPS);
    if (xcvrStatus != gXcvrSuccess_c) {
        return;
    }

    mPhySeqState = gIdle_c;

    /* Enable 16 bit mode for TC2 - TC2 prime EN, disable all timers,
       enable AUTOACK, mask all interrupts */
    ZLL->PHY_CTRL = (gCcaCCA_MODE1_c << ZLL_PHY_CTRL_CCATYPE_SHIFT) |
                    ZLL_PHY_CTRL_TC2PRIME_EN_MASK    |
                    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_AUTOACK_MASK        |
                    ZLL_PHY_CTRL_TRCV_MSK_MASK;

    /* Clear all PP IRQ bits to avoid unexpected interrupts immediately after init
       disable all timer interrupts */
    ZLL->IRQSTS = ZLL->IRQSTS;

    /* Clear HW indirect queue */
    ZLL->SAM_TABLE |= ZLL_SAM_TABLE_INVALIDATE_ALL_MASK;

    /*  Frame Filtering
        FRM_VER[7:6] = b11. Accept FrameVersion 0 and 1 packets, reject all others */
    ZLL->RX_FRAME_FILTER &= ~ZLL_RX_FRAME_FILTER_FRM_VER_FILTER_MASK;
    ZLL->RX_FRAME_FILTER = ZLL_RX_FRAME_FILTER_FRM_VER_FILTER(3) |
                           ZLL_RX_FRAME_FILTER_CMD_FT_MASK  |
                           ZLL_RX_FRAME_FILTER_DATA_FT_MASK |
                           ZLL_RX_FRAME_FILTER_BEACON_FT_MASK;

    /* Set prescaller to obtain 1 symbol (16us) timebase */
    ZLL->TMR_PRESCALE = 0x05;

    /* Set CCA threshold to -75 dBm */
    ZLL->CCA_LQI_CTRL &= ~ZLL_CCA_LQI_CTRL_CCA1_THRESH_MASK;
    ZLL->CCA_LQI_CTRL |= ZLL_CCA_LQI_CTRL_CCA1_THRESH(RF_CCA_THRESHOLD);

    /* Set the default power level */
    PhyPlmeSetPwrLevelRequest(gPhyDefaultTxPowerLevel_d);

    /* Adjust ACK delay to fulfill the 802.15.4 turnaround requirements */
    ZLL->ACKDELAY &= ~ZLL_ACKDELAY_ACKDELAY_MASK;
    ZLL->ACKDELAY |= ZLL_ACKDELAY_ACKDELAY(-8);

    /* Adjust LQI compensation */
    ZLL->CCA_LQI_CTRL &= ~ZLL_CCA_LQI_CTRL_LQI_OFFSET_COMP_MASK;
    ZLL->CCA_LQI_CTRL |= ZLL_CCA_LQI_CTRL_LQI_OFFSET_COMP(96);

    /* Enable the RxWatermark IRQ */
    ZLL->PHY_CTRL &= ~ZLL_PHY_CTRL_RX_WMRK_MSK_MASK;
    /* Set Rx watermark level */
    ZLL->RX_WTR_MARK = 0;

    /* Set default channels */
    PhyPlmeSetCurrentChannelRequest(0x0B, 0); /* 2405 MHz */
    PhyPlmeSetCurrentChannelRequest(0x0B, 1); /* 2405 MHz */

    /* DSM settings */
    phyReg = (RSIM->RF_OSC_CTRL & RSIM_RF_OSC_CTRL_BB_XTAL_READY_COUNT_SEL_MASK) >>
             RSIM_RF_OSC_CTRL_BB_XTAL_READY_COUNT_SEL_SHIFT;
    phyReg = (1024U << phyReg) / (SystemCoreClock / 32768) + 1;
    RSIM->DSM_OSC_OFFSET = phyReg;

    osStatus status = rf_thread.start(mbed::callback(rf_thread_loop));
    MBED_ASSERT(status == osOK);

    /** Clear and enable MAC IRQ at task level, when scheduler is on. */
    InstallIRQHandler((IRQn_Type)Radio_1_IRQn, (uint32_t)PHY_InterruptHandler);

    /* Unmask Transceiver Global Interrupts */
    ZLL->PHY_CTRL &= ~ZLL_PHY_CTRL_TRCV_MSK_MASK;

    NVIC_ClearPendingIRQ(Radio_1_IRQn);
    NVIC_EnableIRQ(Radio_1_IRQn);
}

/*
 * \brief Function reads a time-stamp value from XCVR [symbols]
 *
 * \param pEndTime pointer to location where time-stamp will be stored
 *
 * \return none
 */
static void rf_get_timestamp(uint32_t *pRetClk)
{
    if (NULL == pRetClk) {
        return;
    }

    platform_enter_critical();

    *pRetClk = 0;
    *pRetClk = ZLL->EVENT_TMR >> ZLL_EVENT_TMR_EVENT_TMR_SHIFT;

    platform_exit_critical();
}

/*
 * \brief Function starts the ACK wait time-out.
 *
 * \param slots The ACK wait time-out in [symbols]
 *
 * \return none
 */
static void rf_ack_wait_timer_start(uint16_t time)
{
    uint32_t timestamp, t;

    rf_get_timestamp(&timestamp);

    t = (rf_get_timeout() - timestamp) & gPhyTimeMask_c;

    if (t > 1) {
        timestamp += time;
        rf_set_timeout(timestamp);
    }
}

/*
 * \brief Function sets the RF in RX state.
 *
 * \param none
 *
 * \return none
 */
static void rf_receive(void)
{
    uint32_t irqSts;

    /* RX can start only from Idle state */
    if (mPhySeqState != gIdle_c) {
        rf_abort();
    }

    mPhySeqState = gRX_c;

    /* Ensure that no spurious interrupts are raised, but do not change TMR1 and TMR4 IRQ status */
    irqSts = ZLL->IRQSTS;
    irqSts &= ~(ZLL_IRQSTS_TMR1IRQ_MASK | ZLL_IRQSTS_TMR4IRQ_MASK);
    irqSts |= ZLL_IRQSTS_TMR3MSK_MASK;
    ZLL->IRQSTS = irqSts;

    /* unmask SEQ interrupt */
    ZLL->PHY_CTRL &= ~ZLL_PHY_CTRL_SEQMSK_MASK;

    /* Start the RX sequence */
    ZLL->PHY_CTRL |= gRX_c;
}

static void rf_mac_ed_state_enable(void)
{
    uint32_t ccaMode, irqSts;

    mPhySeqState = gCCA_c;

    /* Switch to ED mode */
    ccaMode = (ZLL->PHY_CTRL  & ZLL_PHY_CTRL_CCATYPE_MASK) >> ZLL_PHY_CTRL_CCATYPE_SHIFT;
    if (ccaMode != gCcaED_c) {
        ZLL->PHY_CTRL &= ~(ZLL_PHY_CTRL_CCATYPE_MASK);
    }

    /* Ensure that no spurious interrupts are raised(do not change TMR1 and TMR4 IRQ status) */
    irqSts = ZLL->IRQSTS;
    irqSts &= ~(ZLL_IRQSTS_TMR1IRQ_MASK | ZLL_IRQSTS_TMR4IRQ_MASK);
    irqSts |= ZLL_IRQSTS_TMR3MSK_MASK;
    ZLL->IRQSTS = irqSts;

    /* Unmask SEQ interrupt */
    ZLL->PHY_CTRL &= ~ZLL_PHY_CTRL_SEQMSK_MASK;

    /* start ED sequence */
    ZLL->PHY_CTRL |= gCCA_c;
}

/*
 * \brief Function is a call back for TX end interrupt.
 *
 * \param none
 *
 * \return none
 */
static void rf_handle_tx_end(bool framePending)
{
    /*Start receiver*/
    rf_receive();

    if (!device_driver.phy_tx_done_cb) {
        return;
    }

    /*Call PHY TX Done API*/
    if (need_ack) {
        if (framePending) {
            device_driver.phy_tx_done_cb(rf_radio_driver_id, rf_mac_handle, PHY_LINK_TX_DONE_PENDING, 1, 1);
        } else {
            device_driver.phy_tx_done_cb(rf_radio_driver_id, rf_mac_handle, PHY_LINK_TX_DONE, 1, 1);
        }
    } else {
        device_driver.phy_tx_done_cb(rf_radio_driver_id, rf_mac_handle, PHY_LINK_TX_SUCCESS, 1, 1);
    }
}

/*
 * \brief Function converts LQI into RSSI.
 *
 * \param LQI
 *
 * \return RSSI
 */
static int8_t rf_convert_LQI_to_RSSI(uint8_t lqi)
{
    int32_t rssi = (36 * lqi - 9836) / 109;

    return (int8_t)rssi;
}

/*
 * \brief Function scale the LQI value reported by RF into a 0-255 value.
 *
 * \param hwLqi - the LQI value reported by RF
 *
 * \return scaled LQI
 */
static uint8_t rf_convert_LQI(uint8_t hwLqi)
{
    if (hwLqi >= 220) {
        return 255;
    } else {
        return (51 * hwLqi) / 44;
    }
}

/*
 * \brief Function is a call back for RX end interrupt.
 *
 * \param none
 *
 * \return none
 */
static void rf_handle_rx_end(void)
{
    uint8_t rf_lqi = (ZLL->LQI_AND_RSSI & ZLL_LQI_AND_RSSI_LQI_VALUE_MASK) >>
                     ZLL_LQI_AND_RSSI_LQI_VALUE_SHIFT;
    int8_t rf_rssi = 0;
    uint8_t len;
    uint8_t i;
    volatile uint8_t *pPB;

    len = (ZLL->IRQSTS & ZLL_IRQSTS_RX_FRAME_LENGTH_MASK) >> ZLL_IRQSTS_RX_FRAME_LENGTH_SHIFT; /* Including FCS (2 bytes) */

    /* Excluding FCS (2 bytes) */
    len -= 2;

    /*Check the length is valid*/
    if (len > 1 && len < MAC_PACKET_SIZE) {
        rf_lqi  = rf_convert_LQI(rf_lqi);
        rf_rssi = rf_convert_LQI_to_RSSI(rf_lqi);

        /* Load data from Packet Buffer */
        pPB = (uint8_t *)ZLL->PKT_BUFFER_RX;

        for (i = 0; i < len; i++) {
            PHYPAYLOAD[i] = *pPB++;
        }

        /* Start receiver */
        rf_receive();

        if (device_driver.phy_rx_cb) {
            device_driver.phy_rx_cb(PHYPAYLOAD, len, rf_lqi, rf_rssi, rf_radio_driver_id);
        }
    } else {
        /* Start receiver */
        rf_receive();
    }
}

static void handle_IRQ_events(void)
{
    uint8_t xcvseqCopy;
    uint32_t irqStatus;

    /* Read current XCVRSEQ and interrupt status */
    xcvseqCopy = ZLL->PHY_CTRL & ZLL_PHY_CTRL_XCVSEQ_MASK;
    irqStatus     = ZLL->IRQSTS;
    /* Clear all xcvr interrupts */
    ZLL->IRQSTS = irqStatus;

    /* Filter Fail IRQ */
    if (irqStatus & ZLL_IRQSTS_FILTERFAIL_IRQ_MASK) {

    } else {
        /* Rx Watermark IRQ */
        if ((!(ZLL->PHY_CTRL & ZLL_PHY_CTRL_RX_WMRK_MSK_MASK)) &&
                (irqStatus & ZLL_IRQSTS_RXWTRMRKIRQ_MASK)) {
            uint32_t rx_len = (irqStatus & ZLL_IRQSTS_RX_FRAME_LENGTH_MASK) >> ZLL_IRQSTS_RX_FRAME_LENGTH_SHIFT;

            /* Convert to symbols and add IFS and ACK duration */
            rx_len = rx_len * 2 + 12 + 22 + 2;
            rf_ack_wait_timer_start(rx_len);
        }
    }

    /* Sequencer interrupt, the autosequence has completed */
    if (irqStatus & ZLL_IRQSTS_SEQIRQ_MASK) {

        /* XCVR will be set to Idle */
        mPhySeqState = gIdle_c;

        /* PLL unlock, the autosequence has been aborted due to PLL unlock */
        if (irqStatus & ZLL_IRQSTS_PLL_UNLOCK_IRQ_MASK) {
            PhyIsrSeqCleanup();
            /* Start receiver */
            rf_receive();
        }
        /* TMR3 timeout, the autosequence has been aborted due to TMR3 timeout */
        else if ((irqStatus & ZLL_IRQSTS_TMR3IRQ_MASK) &&
                 (!(irqStatus & ZLL_IRQSTS_RXIRQ_MASK)) &&
                 (xcvseqCopy != gTX_c)) {
            PhyIsrTimeoutCleanup();
            /* Start receiver */
            rf_receive();
        } else {
            PhyIsrSeqCleanup();
            switch (xcvseqCopy) {
                case gTX_c:
                    if ((ZLL->PHY_CTRL & ZLL_PHY_CTRL_CCABFRTX_MASK) &&
                            (irqStatus & ZLL_IRQSTS_CCA_MASK)) {
                        device_driver.phy_tx_done_cb(rf_radio_driver_id, rf_mac_handle,
                                                     PHY_LINK_CCA_FAIL, 1, 1);
                    } else {
                        rf_handle_tx_end(false);
                    }
                    break;

                case gTR_c:
                    if ((ZLL->PHY_CTRL & ZLL_PHY_CTRL_CCABFRTX_MASK) &&
                            (irqStatus & ZLL_IRQSTS_CCA_MASK)) {
                        device_driver.phy_tx_done_cb(rf_radio_driver_id, rf_mac_handle,
                                                     PHY_LINK_CCA_FAIL, 1, 1);
                    } else {
                        rf_handle_tx_end((irqStatus & ZLL_IRQSTS_RX_FRM_PEND_MASK) > 0);
                    }
                    break;

                case gRX_c:
                    rf_handle_rx_end();
                    break;

                case gCCA_c:
                    rf_ed_value = rf_convert_energy_level((ZLL->LQI_AND_RSSI &
                                                           ZLL_LQI_AND_RSSI_CCA1_ED_FNL_MASK) >>
                                                          ZLL_LQI_AND_RSSI_CCA1_ED_FNL_SHIFT);
                    break;

                default:
                    break;
            }
        }
    }
}

NanostackRfPhyKw41z::NanostackRfPhyKw41z()
{
    memcpy(MAC64_addr, MAC64_addr_default, sizeof(MAC64_addr));
}

NanostackRfPhyKw41z::~NanostackRfPhyKw41z()
{
    // Do nothing
}

int8_t NanostackRfPhyKw41z::rf_register()
{
    platform_enter_critical();

    if (rf != NULL) {
        platform_exit_critical();
        error("Multiple registrations of NanostackRfPhyKw41z not supported");
        return -1;
    }

    rf = this;
    int8_t radio_id = rf_device_register();
    if (radio_id < 0) {
        rf = NULL;
    }

    platform_exit_critical();
    return radio_id;
}

void NanostackRfPhyKw41z::rf_unregister()
{
    platform_enter_critical();

    if (rf != this) {
        platform_exit_critical();
        return;
    }

    rf_device_unregister();
    rf = NULL;

    platform_exit_critical();
}

void NanostackRfPhyKw41z::get_mac_address(uint8_t *mac)
{
    platform_enter_critical();

    memcpy((void *)mac, (void *)MAC64_addr, sizeof(MAC64_addr));

    platform_exit_critical();
}

void NanostackRfPhyKw41z::set_mac_address(uint8_t *mac)
{
    platform_enter_critical();

    if (NULL != rf) {
        error("NanostackRfPhyKw41z cannot change mac address when running");
        platform_exit_critical();
        return;
    }
    memcpy((void *)MAC64_addr, (void *)mac, sizeof(MAC64_addr));

    platform_exit_critical();
}

NanostackRfPhy &NanostackRfPhy::get_default_instance()
{
    static NanostackRfPhyKw41z rf_phy;
    return rf_phy;
}