<commit_before>/*
 * Copyright (c) 2013 Nordic Semiconductor ASA
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without modification,
 * are permitted provided that the following conditions are met:
 *
 *   1. Redistributions of source code must retain the above copyright notice, this list
 *      of conditions and the following disclaimer.
 *
 *   2. Redistributions in binary form, except as embedded into a Nordic Semiconductor ASA
 *      integrated circuit in a product or a software update for such product, 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.
 *
 *   3. Neither the name of Nordic Semiconductor ASA nor the names of its contributors may be
 *      used to endorse or promote products derived from this software without specific prior
 *      written permission.
 *
 *   4. This software, with or without modification, must only be used with a
 *      Nordic Semiconductor ASA integrated circuit.
 *
 *   5. Any software provided in binary or object form under this license must not be reverse
 *      engineered, decompiled, modified and/or disassembled.
 *
 * 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.
 *
 */

#if DEVICE_SERIAL

#include "hal/serial_api.h"

#include "nrf_uarte.h"
#include "nrfx_uarte.h"
#include "nrfx_uart.h"
#include "nrf_atfifo.h"
#include "app_util_platform.h"
#include "pinmap_ex.h"
#include "nrfx_gpiote.h"
#include "nrfx_ppi.h"
#include "PeripheralPins.h"

#include "platform/mbed_atomic.h"
#include "platform/mbed_critical.h"

#if UART0_ENABLED == 0
#error UART0 is disabled. DEVICE_SERIAL must also be disabled to continue.
#endif


/***
 *       _____             __ _                       _   _
 *      / ____|           / _(_)                     | | (_)
 *     | |     ___  _ __ | |_ _  __ _ _   _ _ __ __ _| |_ _  ___  _ __
 *     | |    / _ \| '_ \|  _| |/ _` | | | | '__/ _` | __| |/ _ \| '_ \
 *     | |___| (_) | | | | | | | (_| | |_| | | | (_| | |_| | (_) | | | |
 *      \_____\___/|_| |_|_| |_|\__, |\__,_|_|  \__,_|\__|_|\___/|_| |_|
 *                               __/ |
 *                              |___/
 */

/**
 * Default FIFO buffer size for UARTE0.
 */
#ifndef MBED_CONF_NORDIC_UART_0_FIFO_SIZE
#define MBED_CONF_NORDIC_UART_0_FIFO_SIZE 32
#endif

/**
 * Default FIFO buffer size for UARTE1.
 */
#ifndef MBED_CONF_NORDIC_UART_1_FIFO_SIZE
#define MBED_CONF_NORDIC_UART_1_FIFO_SIZE 32
#endif

/**
 * Internal short names.
 */
#define UART0_FIFO_BUFFER_SIZE  MBED_CONF_NORDIC_UART_0_FIFO_SIZE
#define UART1_FIFO_BUFFER_SIZE  MBED_CONF_NORDIC_UART_1_FIFO_SIZE
#define DMA_BUFFER_SIZE         1
#define NUMBER_OF_BANKS         2
#define FIFO_MIN                3

/***
 *      _______                   _       __
 *     |__   __|                 | |     / _|
 *        | |_   _ _ __   ___  __| | ___| |_ ___
 *        | | | | | '_ \ / _ \/ _` |/ _ \  _/ __|
 *        | | |_| | |_) |  __/ (_| |  __/ | \__ \
 *        |_|\__, | .__/ \___|\__,_|\___|_| |___/
 *            __/ | |
 *           |___/|_|
 */

/**
 * Missing event typedefs.
 */
typedef enum
{
    NRF_UARTE_EVENT_TXDRDY    = offsetof(NRF_UARTE_Type, EVENTS_TXDRDY),
} nrf_uarte_event_extra_t;


/**
 * Internal struct for storing each UARTE instance's state:
 *
 *  owner: pointer to serial object currently using instance.
 *  buffer: buffers assigned to EasyDMA.
 *  tx_data: 1 byte Tx buffer for blocking putc.
 *  tx_in_progress: mutex for atomic Tx.
 *  rx_in_progress: mutex for atomic Rx when using async API.
 *  tx_asynch: set synch or asynch mode for Tx.
 *  rx_asynch: set synch or asynch mode for Rx.
 *  callback_posted: flag for posting only one callback.
 *  active_bank: flag for buffer swapping.
 *  fifo: pointer to the FIFO buffer.
 */
typedef struct {
    struct serial_s *owner;
    uint8_t buffer[NUMBER_OF_BANKS][DMA_BUFFER_SIZE];
    uint32_t usage_counter;
    uint8_t tx_data;
    bool tx_in_progress;
    bool rx_in_progress;
    bool tx_asynch;
    bool rx_asynch;
    bool callback_posted;
    uint8_t active_bank;
    nrf_atfifo_t *fifo;
    uint32_t fifo_free_count;
    nrf_ppi_channel_t ppi_rts;
    nrfx_gpiote_pin_t rts;
    bool rx_suspended;
} nordic_uart_state_t;

/**
 * Turn Mbed HAL IRQ flags into maskable bit masks.
 */
typedef enum {
    NORDIC_TX_IRQ = (1 << 0),
    NORDIC_RX_IRQ = (1 << 1),
} nordic_irq_t;


/***
 *       _____ _       _           _  __      __        _       _     _
 *      / ____| |     | |         | | \ \    / /       (_)     | |   | |
 *     | |  __| | ___ | |__   __ _| |  \ \  / /_ _ _ __ _  __ _| |__ | | ___  ___
 *     | | |_ | |/ _ \| '_ \ / _` | |   \ \/ / _` | '__| |/ _` | '_ \| |/ _ \/ __|
 *     | |__| | | (_) | |_) | (_| | |    \  / (_| | |  | | (_| | |_) | |  __/\__ \
 *      \_____|_|\___/|_.__/ \__,_|_|     \/ \__,_|_|  |_|\__,_|_.__/|_|\___||___/
 *
 *
 */

/**
 * UARTE state. One for each instance.
 */
static nordic_uart_state_t nordic_nrf5_uart_state[NRFX_UARTE_ENABLED_COUNT] = { 0 };

/**
 * Array with UARTE register pointers for easy access.
 */
static NRF_UARTE_Type *nordic_nrf5_uart_register[NRFX_UARTE_ENABLED_COUNT] = {
    NRF_UARTE0,
#if UART1_ENABLED
    NRF_UARTE1,
#endif
};

/**
 * @brief      Create atomic fifo using macro. Macro defines static arrays
 *             for buffer and internal state.
 */
NRF_ATFIFO_DEF(nordic_nrf5_uart_fifo_0, uint8_t, UART0_FIFO_BUFFER_SIZE);

#if UART1_ENABLED
NRF_ATFIFO_DEF(nordic_nrf5_uart_fifo_1, uint8_t, UART1_FIFO_BUFFER_SIZE);
#endif

/**
 * SWI IRQ mask.
 */
static uint8_t nordic_nrf5_uart_swi_mask_tx_0 = 0;
static uint8_t nordic_nrf5_uart_swi_mask_rx_0 = 0;
#if UART1_ENABLED
static uint8_t nordic_nrf5_uart_swi_mask_tx_1 = 0;
static uint8_t nordic_nrf5_uart_swi_mask_rx_1 = 0;
#endif

/**
 * Global variables expected by mbed_retarget.cpp for STDOUT.
 */
int stdio_uart_inited = 0;
serial_t stdio_uart = { 0 };


/***
 *       _____        __ _                            _____       _                             _
 *      / ____|      / _| |                          |_   _|     | |                           | |
 *     | (___   ___ | |_| |___      ____ _ _ __ ___    | |  _ __ | |_ ___ _ __ _ __ _   _ _ __ | |_
 *      \___ \ / _ \|  _| __\ \ /\ / / _` | '__/ _ \   | | | '_ \| __/ _ \ '__| '__| | | | '_ \| __|
 *      ____) | (_) | | | |_ \ V  V / (_| | | |  __/  _| |_| | | | ||  __/ |  | |  | |_| | |_) | |_
 *     |_____/ \___/|_|  \__| \_/\_/ \__,_|_|  \___| |_____|_| |_|\__\___|_|  |_|   \__,_| .__/ \__|
 *                                                                                       | |
 *                                                                                       |_|
 */

/**
 * @brief      SWI interrupt handler for signaling RxIrq handler.
 *
 * @param[in]  instance  The instance
 */
static void nordic_nrf5_uart_callback_handler(uint32_t instance)
{
    /* Flag that no callback is posted. */
    nordic_nrf5_uart_state[instance].callback_posted = false;

    /* Check if callback handler is set and if event mask match. */
    uart_irq_handler callback = (uart_irq_handler) nordic_nrf5_uart_state[instance].owner->handler;
    uint32_t mask = nordic_nrf5_uart_state[instance].owner->mask;

    if (callback && (mask & NORDIC_RX_IRQ)) {

        /* Invoke callback function. */
        uint32_t context = nordic_nrf5_uart_state[instance].owner->context;
        callback(context, RxIrq);
    }
}

/**
 * @brief      SWI interrupt handler for when the Tx buffer has been transmitted.
 *
 * @param[in]  instance  The instance
 */
static void nordic_nrf5_uart_event_handler_endtx(int instance)
{
    /* Release mutex. As the owner this call is safe. */
    core_util_atomic_store_bool(&nordic_nrf5_uart_state[instance].tx_in_progress, false);

    /* Check if callback handler and Tx event mask is set. */
    uart_irq_handler callback = (uart_irq_handler) nordic_nrf5_uart_state[instance].owner->handler;
    uint32_t mask = nordic_nrf5_uart_state[instance].owner->mask;

    if (callback && (mask & NORDIC_TX_IRQ)) {

        /* Invoke callback function. */
        uint32_t context = nordic_nrf5_uart_state[instance].owner->context;
        callback(context, TxIrq);
    }
}

/**
 * @brief      Asynchronous event handler for when Tx DMA buffer has been sent.
 *
 * @param[in]  instance  The instance
 */
#if DEVICE_SERIAL_ASYNCH
static void nordic_nrf5_uart_event_handler_endtx_asynch(int instance)
{
    /* Set Tx done and reset Tx mode to be not asynchronous. */
    nordic_nrf5_uart_state[instance].tx_asynch = false;
    core_util_atomic_store_bool(&nordic_nrf5_uart_state[instance].tx_in_progress, false);

    /* Cast handler to callback function pointer. */
    void (*callback)(void) = (void (*)(void)) nordic_nrf5_uart_state[instance].owner->tx_handler;
    uint32_t mask = nordic_nrf5_uart_state[instance].owner->tx_mask;

    /* Signal error if event mask matches and event handler is set. */
    if (callback && (mask & SERIAL_EVENT_TX_COMPLETE)) {

        /* Store event value so it can be read back. */
        nordic_nrf5_uart_state[instance].owner->tx_event = SERIAL_EVENT_TX_COMPLETE;

        /* Signal callback handler. */
        callback();
    }
}
#endif

static void nordic_nrf5_uart_swi0(void)
{
    if (nordic_nrf5_uart_swi_mask_tx_0) {

        nordic_nrf5_uart_swi_mask_tx_0 = 0;

#if DEVICE_SERIAL_ASYNCH
        if (nordic_nrf5_uart_state[0].tx_asynch) {

            nordic_nrf5_uart_event_handler_endtx_asynch(0);
        } else
#endif
        {
            nordic_nrf5_uart_event_handler_endtx(0);
        }
    }

    if (nordic_nrf5_uart_swi_mask_rx_0) {

        nordic_nrf5_uart_swi_mask_rx_0 = 0;

        nordic_nrf5_uart_callback_handler(0);
    }


#if UART1_ENABLED
    if (nordic_nrf5_uart_swi_mask_tx_1) {

        nordic_nrf5_uart_swi_mask_tx_1 = 0;

#if DEVICE_SERIAL_ASYNCH
        if (nordic_nrf5_uart_state[1].tx_asynch) {

            nordic_nrf5_uart_event_handler_endtx_asynch(1);
        } else
#endif
        {
            nordic_nrf5_uart_event_handler_endtx(1);
        }
    }

    if (nordic_nrf5_uart_swi_mask_rx_1) {

        nordic_nrf5_uart_swi_mask_rx_1 = 0;

        nordic_nrf5_uart_callback_handler(1);
    }
#endif
}

/**
 * @brief      Trigger Tx SWI.
 *
 * @param[in]  instance  The instance.
 */
static void nordic_swi_tx_trigger(int instance)
{
    if (instance == 0) {

        nordic_nrf5_uart_swi_mask_tx_0 = 1;
        NVIC_SetPendingIRQ(SWI0_EGU0_IRQn);
    }
#if UART1_ENABLED
    else if (instance == 1) {

        nordic_nrf5_uart_swi_mask_tx_1 = 1;
        NVIC_SetPendingIRQ(SWI0_EGU0_IRQn);
    }
#endif
}

/**
 * @brief      Trigger Rx SWI.
 *
 * @param[in]  instance  The instance
 */
static void nordic_swi_rx_trigger(int instance)
{
    if (instance == 0) {

        nordic_nrf5_uart_swi_mask_rx_0 = 1;
        NVIC_SetPendingIRQ(SWI0_EGU0_IRQn);
    }
#if UART1_ENABLED
    else if (instance == 1) {

        nordic_nrf5_uart_swi_mask_rx_1 = 1;
        NVIC_SetPendingIRQ(SWI0_EGU0_IRQn);
    }
#endif
}

/***
 *      _    _         _____ _______   ______               _     _    _                 _ _
 *     | |  | |  /\   |  __ \__   __| |  ____|             | |   | |  | |               | | |
 *     | |  | | /  \  | |__) | | |    | |____   _____ _ __ | |_  | |__| | __ _ _ __   __| | | ___ _ __
 *     | |  | |/ /\ \ |  _  /  | |    |  __\ \ / / _ \ '_ \| __| |  __  |/ _` | '_ \ / _` | |/ _ \ '__|
 *     | |__| / ____ \| | \ \  | |    | |___\ V /  __/ | | | |_  | |  | | (_| | | | | (_| | |  __/ |
 *      \____/_/    \_\_|  \_\ |_|    |______\_/ \___|_| |_|\__| |_|  |_|\__,_|_| |_|\__,_|_|\___|_|
 *
 *
 */

/**
 * @brief      Event handler for when Rx buffer is full or buffer swap has been
 *             triggered by idle task.
 *
 *             Copy data from DMA buffer to FIFO buffer.
 *             Post callback if not already posted.
 *
 * @param[in]  instance  The instance
 */
static void nordic_nrf5_uart_event_handler_endrx(int instance)
{
    /* Read out active bank flag and swap DMA buffers. */
    uint8_t active_bank = nordic_nrf5_uart_state[instance].active_bank;
    nordic_nrf5_uart_state[instance].active_bank = active_bank ^ 0x01;

    /* Get number of bytes in DMA buffer. */
    uint32_t available = nrf_uarte_rx_amount_get(nordic_nrf5_uart_register[instance]);

    if (available > 0) {

        /* Copy data from DMA buffer to FIFO buffer. */
        for (size_t index = 0; index < available; index++) {

            /* Atomic FIFO can be used safely without disabling interrutps. */
            nrf_atfifo_item_put_t fifo_context;

            /* Get pointer to available space. */
            uint8_t *byte = (uint8_t *) nrf_atfifo_item_alloc(nordic_nrf5_uart_state[instance].fifo, &fifo_context);

            if (byte != NULL) {

                /* Copy 1 byte from DMA buffer and commit to FIFO buffer. */
                *byte = nordic_nrf5_uart_state[instance].buffer[active_bank][index];
                nrf_atfifo_item_put(nordic_nrf5_uart_state[instance].fifo, &fifo_context);
                core_util_atomic_decr_u32(&nordic_nrf5_uart_state[instance].fifo_free_count, 1);

            } else {

                /* Buffer overflow. */
                break;
            }
        }

        /* Signal callback through lower priority SWI if not already posted. */
        if (nordic_nrf5_uart_state[instance].callback_posted == false) {

            nordic_nrf5_uart_state[instance].callback_posted = true;
            nordic_swi_rx_trigger(instance);
        }
    }
}

/**
 * @brief      Event handler for when DMA has been armed with Rx buffer.
 *
 *             Arm Rx buffer with second buffer for optimal reception.
 *
 * @param[in]  instance  The instance
 */
static void nordic_nrf5_uart_event_handler_rxstarted(int instance)
{
    uint8_t next_bank = nordic_nrf5_uart_state[instance].active_bank ^ 0x01;

    nrf_uarte_rx_buffer_set(nordic_nrf5_uart_register[instance], nordic_nrf5_uart_state[instance].buffer[next_bank], DMA_BUFFER_SIZE);
    if (nordic_nrf5_uart_state[instance].rts != NRF_UART_PSEL_DISCONNECTED) {
        if (nordic_nrf5_uart_state[instance].fifo_free_count > FIFO_MIN) {
            /* Clear rts since we are ready to receive the next byte */
            nrfx_gpiote_clr_task_trigger(nordic_nrf5_uart_state[instance].rts);
        } else {
            /* Suspend reception since there isn't enough buffer space.
             * The function serial_getc will restart reception. */
            nordic_nrf5_uart_state[instance].rx_suspended = true;
        }
    }
}

#if DEVICE_SERIAL_ASYNCH
/**
 * @brief      Asynchronous event handler for when Rx DMA buffer is full.
 *
 * @param[in]  instance  The instance
 */
static void nordic_nrf5_uart_event_handler_endrx_asynch(int instance)
{
    /* Set Rx done and reset Rx mode to be not asynchronous. */
    nordic_nrf5_uart_state[instance].rx_asynch = false;
    core_util_atomic_store_bool(&nordic_nrf5_uart_state[instance].rx_in_progress, false);

    /* Cast handler to callback function pointer. */
    void (*callback)(void) = (void (*)(void)) nordic_nrf5_uart_state[instance].owner->rx_handler;
    uint32_t mask = nordic_nrf5_uart_state[instance].owner->rx_mask;

    /* Signal error if event mask matches and event handler is set. */
    if (callback && (mask & SERIAL_EVENT_RX_COMPLETE)) {

        /* Store event value so it can be read back. */
        nordic_nrf5_uart_state[instance].owner->rx_event = SERIAL_EVENT_RX_COMPLETE;

        /* Signal callback handler. */
        callback();
    }
}
#endif

/**
 * @brief      UARTE event handler.
 *
 *             Collect signals from UARTE0 and UARTE1 ISR and translate to instance.
 *
 * @param[in]  instance  The instance
 */
static void nordic_nrf5_uart_event_handler(int instance)
{
    /* DMA buffer is full or has been swapped out by idle timeout. */
    if (nrf_uarte_event_check(nordic_nrf5_uart_register[instance], NRF_UARTE_EVENT_ENDRX))
    {
        nrf_uarte_event_clear(nordic_nrf5_uart_register[instance], NRF_UARTE_EVENT_ENDRX);

#if DEVICE_SERIAL_ASYNCH
        /* Call appropriate event handler based on current mode. */
        if (nordic_nrf5_uart_state[instance].rx_asynch) {

            nordic_nrf5_uart_event_handler_endrx_asynch(instance);
        } else
#endif
        {
            nordic_nrf5_uart_event_handler_endrx(instance);
        }
    }

    /* Rx DMA buffer has been armed.
     *
     * Warning - Do not process NRF_UARTE_EVENT_RXSTARTED if NRF_UARTE_EVENT_ENDRX is pending.
     * NRF_UARTE_EVENT_RXSTARTED must be processed first or nordic_nrf5_uart_event_handler_rxstarted
     * will setup the wrong DMA buffer and cause data to be lost.
     */
    if (nrf_uarte_event_check(nordic_nrf5_uart_register[instance], NRF_UARTE_EVENT_RXSTARTED) &&
            !nrf_uarte_event_check(nordic_nrf5_uart_register[instance], NRF_UARTE_EVENT_ENDRX))
    {
        nrf_uarte_event_clear(nordic_nrf5_uart_register[instance], NRF_UARTE_EVENT_RXSTARTED);

        nordic_nrf5_uart_event_handler_rxstarted(instance);
    }

    /* Tx DMA buffer has been sent. */
    if (nrf_uarte_event_check(nordic_nrf5_uart_register[instance], NRF_UARTE_EVENT_ENDTX))
    {
        nrf_uarte_event_clear(nordic_nrf5_uart_register[instance], NRF_UARTE_EVENT_ENDTX);

        /* Use SWI to de-prioritize callback. */
        nordic_swi_tx_trigger(instance);
    }
}

/**
 * @brief      UARTE0 ISR.
 */
static void nordic_nrf5_uart0_handler(void)
{
    /* Call event handler with instance ID. */
    nordic_nrf5_uart_event_handler(0);
}

#if UART1_ENABLED
/**
 * @brief      UARTE1 ISR.
 */
static void nordic_nrf5_uart1_handler(void)
{
    /* Call event handler with instance ID. */
    nordic_nrf5_uart_event_handler(1);
}
#endif


/***
 *       _____             __ _                       _   _
 *      / ____|           / _(_)                     | | (_)
 *     | |     ___  _ __ | |_ _  __ _ _   _ _ __ __ _| |_ _  ___  _ __
 *     | |    / _ \| '_ \|  _| |/ _` | | | | '__/ _` | __| |/ _ \| '_ \
 *     | |___| (_) | | | | | | | (_| | |_| | | | (_| | |_| | (_) | | | |
 *      \_____\___/|_| |_|_| |_|\__, |\__,_|_|  \__,_|\__|_|\___/|_| |_|
 *                               __/ |
 *                              |___/
 */

/**
 * @brief      Configure UARTE based on serial object settings.
 *
 *             Common for both Rx and Tx.
 *
 * @param      obj   The object
 */
static void nordic_nrf5_uart_configure_object(serial_t *obj)
{
    MBED_ASSERT(obj);

#if DEVICE_SERIAL_ASYNCH
    struct serial_s *uart_object = &obj->serial;
#else
    struct serial_s *uart_object = obj;
#endif

    /* Configure Tx and Rx pins. */
    if (uart_object->tx != NRF_UART_PSEL_DISCONNECTED) {

        nrf_gpio_pin_set(uart_object->tx);
        nrf_gpio_cfg_output(uart_object->tx);
    }

    if (uart_object->rx != NRF_UART_PSEL_DISCONNECTED) {

        nrf_gpio_cfg_input(uart_object->rx, NRF_GPIO_PIN_NOPULL);
    }

    nrf_uarte_txrx_pins_set(nordic_nrf5_uart_register[uart_object->instance],
                            uart_object->tx,
                            uart_object->rx);

    /* Set hardware flow control pins. */
    if (uart_object->hwfc == NRF_UART_HWFC_ENABLED) {

        /* Check if pin is set before configuring it. */
        if (uart_object->cts != NRF_UART_PSEL_DISCONNECTED) {

            nrf_gpio_cfg_input(uart_object->cts, NRF_GPIO_PIN_NOPULL);
        }

        /* Only let UARTE module handle CTS, RTS is handled manually due to buggy UARTE logic. */
        nrf_uarte_hwfc_pins_set(nordic_nrf5_uart_register[uart_object->instance],
                                NRF_UART_PSEL_DISCONNECTED,
                                uart_object->cts);
    }

    /* Check if the rts pin changed */
    if (uart_object->rts != nordic_nrf5_uart_state[uart_object->instance].rts) {
        uint32_t ret;

        /* Disable the PPI interconnect */
        ret = nrfx_ppi_channel_disable(nordic_nrf5_uart_state[uart_object->instance].ppi_rts);
        MBED_ASSERT(ret == NRF_SUCCESS);

        /* Free flow control gpiote pin if it was previously set */
        if (nordic_nrf5_uart_state[uart_object->instance].rts != NRF_UART_PSEL_DISCONNECTED) {
            nrfx_gpiote_out_uninit((nrfx_gpiote_pin_t)uart_object->rts);
        }

        /* Allocate and enable flow control gpiote pin if it is being used */
        if (uart_object->rts != NRF_UART_PSEL_DISCONNECTED) {

            static const nrfx_gpiote_out_config_t config = {
                .init_state = NRF_GPIOTE_INITIAL_VALUE_HIGH,
                .task_pin   = true,
                .action     = NRF_GPIOTE_POLARITY_LOTOHI
            };

            /* Allocate gpiote channel */
            ret = nrfx_gpiote_out_init((nrfx_gpiote_pin_t)uart_object->rts, &config);
            if (ret == NRF_ERROR_INVALID_STATE) {
                /* Pin was previously set to GPIO so uninitialize it */
                nrfx_gpiote_out_uninit((nrfx_gpiote_pin_t)uart_object->rts);
                ret = nrfx_gpiote_out_init((nrfx_gpiote_pin_t)uart_object->rts, &config);
            }
            MBED_ASSERT(ret == NRF_SUCCESS);

            /* Set RTS high on the ENDRX event */
            ret = nrfx_ppi_channel_assign(nordic_nrf5_uart_state[uart_object->instance].ppi_rts,
                                          nrf_uarte_event_address_get(nordic_nrf5_uart_register[uart_object->instance], NRF_UARTE_EVENT_ENDRX),
                                          nrfx_gpiote_out_task_addr_get(uart_object->rts));
            MBED_ASSERT(ret == NRF_SUCCESS);

            ret = nrfx_ppi_channel_enable(nordic_nrf5_uart_state[uart_object->instance].ppi_rts);
            MBED_ASSERT(ret == NRF_SUCCESS);

            /* Enable gpiote task - rts pin can no longer be used as GPIO at this point */
            nrfx_gpiote_out_task_enable((nrfx_gpiote_pin_t)uart_object->rts);
        }

        nordic_nrf5_uart_state[uart_object->instance].rts = uart_object->rts;
    }

    /* Enable flow control and parity. */
    nrf_uarte_configure(nordic_nrf5_uart_register[uart_object->instance],
                        (nrf_uarte_parity_t) uart_object->parity,
                        (nrf_uarte_hwfc_t) uart_object->hwfc);

    /* Set baudrate. */
    nrf_uarte_baudrate_set(nordic_nrf5_uart_register[uart_object->instance],
                           (nrf_uarte_baudrate_t) uart_object->baudrate);
}

/**
 * @brief      Setup non-asynchronous reception.
 *
 * @param[in]  instance  The instance
 */
static void nordic_nrf5_uart_configure_rx(int instance)
{
    /* Disable interrupts during confiration. */
    nrf_uarte_int_disable(nordic_nrf5_uart_register[instance], NRF_UARTE_INT_RXSTARTED_MASK |
                                                               NRF_UARTE_INT_ENDRX_MASK);

    /* Clear FIFO buffer. */
    nrf_atfifo_clear(nordic_nrf5_uart_state[instance].fifo);
    nordic_nrf5_uart_state[instance].fifo_free_count = UART0_FIFO_BUFFER_SIZE;

    /* Clear Rx related events. */
    nrf_uarte_event_clear(nordic_nrf5_uart_register[instance], NRF_UARTE_EVENT_RXSTARTED);
    nrf_uarte_event_clear(nordic_nrf5_uart_register[instance], NRF_UARTE_EVENT_ENDRX);

    /* Enable shortcut between buffer full and begin reception on next buffer armed. */
    nrf_uarte_shorts_enable(nordic_nrf5_uart_register[instance], NRF_UARTE_SHORT_ENDRX_STARTRX);

    /* Reset bank flag. */
    nordic_nrf5_uart_state[instance].active_bank = 0;

    /* Arm first DMA buffer. */
    nrf_uarte_rx_buffer_set(nordic_nrf5_uart_register[instance],
                            nordic_nrf5_uart_state[instance].buffer[0],
                            DMA_BUFFER_SIZE);

    /* Set non-asynchronous mode. */
    nordic_nrf5_uart_state[instance].rx_asynch = false;

    /* Clear suspend condition */
    nordic_nrf5_uart_state[instance].rx_suspended = false;

    /* Enable interrupts again. */
    nrf_uarte_int_enable(nordic_nrf5_uart_register[instance], NRF_UARTE_INT_RXSTARTED_MASK |
                                                              NRF_UARTE_INT_ENDRX_MASK);
}

#if DEVICE_SERIAL_ASYNCH
/**
 * @brief      Setup asynchronous reception.
 *
 * @param[in]  instance  The instance
 */
static void nordic_nrf5_uart_configure_rx_asynch(int instance)
{
    /* Disable Rx related interrupts. */
    nrf_uarte_int_disable(nordic_nrf5_uart_register[instance], NRF_UARTE_INT_RXSTARTED_MASK |
                                                               NRF_UARTE_INT_ENDRX_MASK);

    /* Clear Rx related events. */
    nrf_uarte_event_clear(nordic_nrf5_uart_register[instance], NRF_UARTE_EVENT_RXSTARTED);
    nrf_uarte_event_clear(nordic_nrf5_uart_register[instance], NRF_UARTE_EVENT_ENDRX);

    /* Disable shortcut. Next Rx buffer must be manually started. */
    nrf_uarte_shorts_disable(nordic_nrf5_uart_register[instance], NRF_UARTE_SHORT_ENDRX_STARTRX);

    /* Set asynchronous mode. */
    nordic_nrf5_uart_state[instance].rx_asynch = true;

    /* Clear suspend condition */
    nordic_nrf5_uart_state[instance].rx_suspended = false;

    /* Enable Rx interrupt. */
    nrf_uarte_int_enable(nordic_nrf5_uart_register[instance], NRF_UARTE_INT_ENDRX_MASK);
}
#endif

/**
 * @brief      Main configuration function.
 *
 * @param      obj   The serial object
 */
static void nordic_nrf5_serial_configure(serial_t *obj)
{
    MBED_ASSERT(obj);

#if DEVICE_SERIAL_ASYNCH
    struct serial_s *uart_object = &obj->serial;
#else
    struct serial_s *uart_object = obj;
#endif

    /* Get object instance. */
    int instance = uart_object->instance;

    /* Only configure if instance owner has changed or an update is forced. */
    if ((uart_object != nordic_nrf5_uart_state[instance].owner) || (uart_object->update)) {

        /* Configure common setting. */
        nordic_nrf5_uart_configure_object(obj);

        /* Set new owner. */
        nordic_nrf5_uart_state[instance].owner = uart_object;
        uart_object->update = false;

#if DEVICE_SERIAL_ASYNCH
        /* Set asynchronous mode. */
        if (uart_object->rx_asynch == true) {

            nordic_nrf5_uart_configure_rx_asynch(instance);
        } else
#endif
        {
            /* Set non-asynchronous mode. */
            nordic_nrf5_uart_configure_rx(instance);
            nrf_uarte_task_trigger(nordic_nrf5_uart_register[instance],
                                   NRF_UARTE_TASK_STARTRX);
        }
    }
#if DEVICE_SERIAL_ASYNCH
    /* Owner hasn't changed but mode has. Reconfigure. */
    else if ((uart_object->rx_asynch == false) && (nordic_nrf5_uart_state[instance].rx_asynch == true)) {

        nordic_nrf5_uart_configure_rx(instance);
        nrf_uarte_task_trigger(nordic_nrf5_uart_register[instance],
                               NRF_UARTE_TASK_STARTRX);

    /* Owner hasn't changed but mode has. Reconfigure. */
    } else if ((uart_object->rx_asynch == true) && (nordic_nrf5_uart_state[instance].rx_asynch == false)) {

        nordic_nrf5_uart_configure_rx_asynch(instance);
    }
#endif
}

/***
 *      __  __ _              _   _    _          _                 _____ _____
 *     |  \/  | |            | | | |  | |   /\   | |          /\   |  __ \_   _|
 *     | \  / | |__   ___  __| | | |__| |  /  \  | |         /  \  | |__) || |
 *     | |\/| | '_ \ / _ \/ _` | |  __  | / /\ \ | |        / /\ \ |  ___/ | |
 *     | |  | | |_) |  __/ (_| | | |  | |/ ____ \| |____   / ____ \| |    _| |_
 *     |_|  |_|_.__/ \___|\__,_| |_|  |_/_/    \_\______| /_/    \_\_|   |_____|
 *
 *
 */

/** Initialize the serial peripheral. It sets the default parameters for serial
 *  peripheral, and configures its specifieds pins.
 *
 * Param obj The serial object
 * Param tx  The TX pin name
 * Param rx  The RX pin name
 */
void serial_init(serial_t *obj, PinName tx, PinName rx)
{
    MBED_ASSERT(obj);

#if DEVICE_SERIAL_ASYNCH
    struct serial_s *uart_object = &obj->serial;
#else
    struct serial_s *uart_object = obj;
#endif

    /* Only initialize on first call. */
    static bool first_init = true;
    if (first_init) {
        uint32_t ret;
        first_init = false;

        /* Initialize components that serial relies on. */
        if (!nrfx_gpiote_is_init()) {
            nrfx_gpiote_init();
        }

        /* Enable interrupts for SWI. */
        NVIC_SetVector(SWI0_EGU0_IRQn, (uint32_t) nordic_nrf5_uart_swi0);
        NRFX_IRQ_PRIORITY_SET(SWI0_EGU0_IRQn, APP_IRQ_PRIORITY_LOWEST);
        NRFX_IRQ_ENABLE(SWI0_EGU0_IRQn);

        /* Initialize FIFO buffer for UARTE0. */
        NRF_ATFIFO_INIT(nordic_nrf5_uart_fifo_0);
        nordic_nrf5_uart_state[0].fifo = nordic_nrf5_uart_fifo_0;

        /* Initialize owner to NULL. */
        nordic_nrf5_uart_state[0].owner = NULL;

        /* Allocate a PPI channel for flow control */
        ret = nrfx_ppi_channel_alloc(&nordic_nrf5_uart_state[0].ppi_rts);
        MBED_ASSERT(ret == NRF_SUCCESS);

        /* Clear RTS */
        nordic_nrf5_uart_state[0].rts = NRF_UART_PSEL_DISCONNECTED;

        /* Clear any old events and enable interrupts for UARTE0. */
        nrf_uarte_int_disable(nordic_nrf5_uart_register[0], 0xFFFFFFFF);

        NVIC_SetVector(UARTE0_UART0_IRQn, (uint32_t) nordic_nrf5_uart0_handler);
        NRFX_IRQ_PRIORITY_SET(UARTE0_UART0_IRQn, APP_IRQ_PRIORITY_HIGHEST);
        NRFX_IRQ_ENABLE(UARTE0_UART0_IRQn);

#if UART1_ENABLED
        /* Initialize FIFO buffer for UARTE1. */
        NRF_ATFIFO_INIT(nordic_nrf5_uart_fifo_1);
        nordic_nrf5_uart_state[1].fifo = nordic_nrf5_uart_fifo_1;

        /* Initialize owner to NULL. */
        nordic_nrf5_uart_state[1].owner = NULL;

        /* Allocate a PPI channel for flow control */
        ret = nrfx_ppi_channel_alloc(&nordic_nrf5_uart_state[1].ppi_rts);
        MBED_ASSERT(ret == NRF_SUCCESS);

        /* Clear RTS */
        nordic_nrf5_uart_state[1].rts = NRF_UART_PSEL_DISCONNECTED;

        /* Clear any old events and enable interrupts for UARTE1. */
        nrf_uarte_int_disable(nordic_nrf5_uart_register[1], 0xFFFFFFFF);

        NVIC_SetVector(UARTE1_IRQn, (uint32_t) nordic_nrf5_uart1_handler);
        NRFX_IRQ_PRIORITY_SET(UARTE1_IRQn, APP_IRQ_PRIORITY_HIGHEST);
        NRFX_IRQ_ENABLE(UARTE1_IRQn);
#endif
    }

    /* Get instance ID based on provided pins. */
    int instance = pin_instance_uart(tx, rx);

    uart_object->instance = instance;

    /* Increment usage counter for this instance. */
    nordic_nrf5_uart_state[instance].usage_counter++;

    /* Enable instance on first usage. */
    if (nordic_nrf5_uart_state[instance].usage_counter == 1) {

        nrf_uarte_enable(nordic_nrf5_uart_register[instance]);

        /* In order to support printing with interrupts disabled serial_putc
         * must busy wait on NRF_UARTE_EVENT_TXDRDY. This event cannot be set
         * manually but must be set by the UARTE module after a character has
         * been sent.
         *
         * The following code sends a dummy character into the void so that
         * NRF_UARTE_EVENT_TXDRDY is correctly set.
         */

        /* Ensure pins are disconnected. */
        nrf_uarte_txrx_pins_set(nordic_nrf5_uart_register[instance],
                                NRF_UART_PSEL_DISCONNECTED,
                                NRF_UART_PSEL_DISCONNECTED);

        /* Set maximum baud rate to minimize waiting. */
        nrf_uarte_baudrate_set(nordic_nrf5_uart_register[instance],
                               NRF_UARTE_BAUDRATE_1000000);

        /* Send character. */
        nrf_uarte_tx_buffer_set(nordic_nrf5_uart_register[instance],
                                &nordic_nrf5_uart_state[instance].tx_data,
                                1);
        nrf_uarte_event_clear(nordic_nrf5_uart_register[instance], NRF_UARTE_EVENT_ENDTX);
        nrf_uarte_task_trigger(nordic_nrf5_uart_register[instance], NRF_UARTE_TASK_STARTTX);

        /* Wait until NRF_UARTE_EVENT_TXDRDY is set before proceeding. */
        bool done = false;
        do {
            done = nrf_uarte_event_check(nordic_nrf5_uart_register[instance],
                                (nrf_uarte_event_t) NRF_UARTE_EVENT_TXDRDY);
        } while(done == false);
    }

    /* Store pins in serial object. */
    if (tx == NC) {

        uart_object->tx = NRF_UART_PSEL_DISCONNECTED;
    } else {

        uart_object->tx = tx;
    }

    if (rx == NC) {

        uart_object->rx = NRF_UART_PSEL_DISCONNECTED;
    } else {

        uart_object->rx = rx;
    }

    /* Set default parity, baud rate, and callback handler. */
    uart_object->parity = NRF_UART_PARITY_EXCLUDED;
    uart_object->baudrate = NRF_UART_BAUDRATE_9600;
    uart_object->cts = NRF_UART_PSEL_DISCONNECTED;
    uart_object->rts = NRF_UART_PSEL_DISCONNECTED;
    uart_object->hwfc = NRF_UART_HWFC_DISABLED;
    uart_object->handler = 0;

    /* The STDIO object is stored in this file. Set the flag once initialized. */
    if (obj == &stdio_uart) {
        stdio_uart_inited = 1;
    }

    /* Take ownership and configure UART. */
    uart_object->update = true;
    nordic_nrf5_serial_configure(obj);
}

/** Release the serial peripheral, not currently invoked. It requires further
 *  resource management.
 *
 * Param obj The serial object
 */
void serial_free(serial_t *obj)
{
    MBED_ASSERT(obj);

#if DEVICE_SERIAL_ASYNCH
    struct serial_s *uart_object = &obj->serial;
#else
    struct serial_s *uart_object = obj;
#endif

    int instance = uart_object->instance;

    /* Only consider disabling UARTE if number of users is not zero. */
    if (nordic_nrf5_uart_state[instance].usage_counter > 0) {

        /* Decrement usage counter for this instance. */
        nordic_nrf5_uart_state[instance].usage_counter--;

        /* Disable instance when not in use. */
        if (nordic_nrf5_uart_state[instance].usage_counter == 0) {

            nrf_uarte_disable(nordic_nrf5_uart_register[instance]);
        }
    }
}

/** Configure the baud rate
 *
 * Param obj      The serial object
 * Param baudrate The baud rate to be configured
 */
void serial_baud(serial_t *obj, int baudrate)
{
    MBED_ASSERT(obj);

#if DEVICE_SERIAL_ASYNCH
    struct serial_s *uart_object = &obj->serial;<commit_msg>Workaround to reset UARTE peripheral to be able to go into deep sleep<commit_after>/*
 * Copyright (c) 2013 Nordic Semiconductor ASA
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without modification,
 * are permitted provided that the following conditions are met:
 *
 *   1. Redistributions of source code must retain the above copyright notice, this list
 *      of conditions and the following disclaimer.
 *
 *   2. Redistributions in binary form, except as embedded into a Nordic Semiconductor ASA
 *      integrated circuit in a product or a software update for such product, 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.
 *
 *   3. Neither the name of Nordic Semiconductor ASA nor the names of its contributors may be
 *      used to endorse or promote products derived from this software without specific prior
 *      written permission.
 *
 *   4. This software, with or without modification, must only be used with a
 *      Nordic Semiconductor ASA integrated circuit.
 *
 *   5. Any software provided in binary or object form under this license must not be reverse
 *      engineered, decompiled, modified and/or disassembled.
 *
 * 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.
 *
 */

#if DEVICE_SERIAL

#include "hal/serial_api.h"

#include "nrf_uarte.h"
#include "nrfx_uarte.h"
#include "nrfx_uart.h"
#include "nrf_atfifo.h"
#include "app_util_platform.h"
#include "pinmap_ex.h"
#include "nrfx_gpiote.h"
#include "nrfx_ppi.h"
#include "PeripheralPins.h"

#include "platform/mbed_atomic.h"
#include "platform/mbed_critical.h"

#if UART0_ENABLED == 0
#error UART0 is disabled. DEVICE_SERIAL must also be disabled to continue.
#endif


/***
 *       _____             __ _                       _   _
 *      / ____|           / _(_)                     | | (_)
 *     | |     ___  _ __ | |_ _  __ _ _   _ _ __ __ _| |_ _  ___  _ __
 *     | |    / _ \| '_ \|  _| |/ _` | | | | '__/ _` | __| |/ _ \| '_ \
 *     | |___| (_) | | | | | | | (_| | |_| | | | (_| | |_| | (_) | | | |
 *      \_____\___/|_| |_|_| |_|\__, |\__,_|_|  \__,_|\__|_|\___/|_| |_|
 *                               __/ |
 *                              |___/
 */

/**
 * Default FIFO buffer size for UARTE0.
 */
#ifndef MBED_CONF_NORDIC_UART_0_FIFO_SIZE
#define MBED_CONF_NORDIC_UART_0_FIFO_SIZE 32
#endif

/**
 * Default FIFO buffer size for UARTE1.
 */
#ifndef MBED_CONF_NORDIC_UART_1_FIFO_SIZE
#define MBED_CONF_NORDIC_UART_1_FIFO_SIZE 32
#endif

/**
 * Internal short names.
 */
#define UART0_FIFO_BUFFER_SIZE  MBED_CONF_NORDIC_UART_0_FIFO_SIZE
#define UART1_FIFO_BUFFER_SIZE  MBED_CONF_NORDIC_UART_1_FIFO_SIZE
#define DMA_BUFFER_SIZE         1
#define NUMBER_OF_BANKS         2
#define FIFO_MIN                3

/***
 *      _______                   _       __
 *     |__   __|                 | |     / _|
 *        | |_   _ _ __   ___  __| | ___| |_ ___
 *        | | | | | '_ \ / _ \/ _` |/ _ \  _/ __|
 *        | | |_| | |_) |  __/ (_| |  __/ | \__ \
 *        |_|\__, | .__/ \___|\__,_|\___|_| |___/
 *            __/ | |
 *           |___/|_|
 */

/**
 * Missing event typedefs.
 */
typedef enum
{
    NRF_UARTE_EVENT_TXDRDY    = offsetof(NRF_UARTE_Type, EVENTS_TXDRDY),
} nrf_uarte_event_extra_t;


/**
 * Internal struct for storing each UARTE instance's state:
 *
 *  owner: pointer to serial object currently using instance.
 *  buffer: buffers assigned to EasyDMA.
 *  tx_data: 1 byte Tx buffer for blocking putc.
 *  tx_in_progress: mutex for atomic Tx.
 *  rx_in_progress: mutex for atomic Rx when using async API.
 *  tx_asynch: set synch or asynch mode for Tx.
 *  rx_asynch: set synch or asynch mode for Rx.
 *  callback_posted: flag for posting only one callback.
 *  active_bank: flag for buffer swapping.
 *  fifo: pointer to the FIFO buffer.
 */
typedef struct {
    struct serial_s *owner;
    uint8_t buffer[NUMBER_OF_BANKS][DMA_BUFFER_SIZE];
    uint32_t usage_counter;
    uint8_t tx_data;
    bool tx_in_progress;
    bool rx_in_progress;
    bool tx_asynch;
    bool rx_asynch;
    bool callback_posted;
    uint8_t active_bank;
    nrf_atfifo_t *fifo;
    uint32_t fifo_free_count;
    nrf_ppi_channel_t ppi_rts;
    nrfx_gpiote_pin_t rts;
    bool rx_suspended;
} nordic_uart_state_t;

/**
 * Turn Mbed HAL IRQ flags into maskable bit masks.
 */
typedef enum {
    NORDIC_TX_IRQ = (1 << 0),
    NORDIC_RX_IRQ = (1 << 1),
} nordic_irq_t;


/***
 *       _____ _       _           _  __      __        _       _     _
 *      / ____| |     | |         | | \ \    / /       (_)     | |   | |
 *     | |  __| | ___ | |__   __ _| |  \ \  / /_ _ _ __ _  __ _| |__ | | ___  ___
 *     | | |_ | |/ _ \| '_ \ / _` | |   \ \/ / _` | '__| |/ _` | '_ \| |/ _ \/ __|
 *     | |__| | | (_) | |_) | (_| | |    \  / (_| | |  | | (_| | |_) | |  __/\__ \
 *      \_____|_|\___/|_.__/ \__,_|_|     \/ \__,_|_|  |_|\__,_|_.__/|_|\___||___/
 *
 *
 */

/**
 * UARTE state. One for each instance.
 */
static nordic_uart_state_t nordic_nrf5_uart_state[NRFX_UARTE_ENABLED_COUNT] = { 0 };

/**
 * Array with UARTE register pointers for easy access.
 */
static NRF_UARTE_Type *nordic_nrf5_uart_register[NRFX_UARTE_ENABLED_COUNT] = {
    NRF_UARTE0,
#if UART1_ENABLED
    NRF_UARTE1,
#endif
};

/**
 * @brief      Create atomic fifo using macro. Macro defines static arrays
 *             for buffer and internal state.
 */
NRF_ATFIFO_DEF(nordic_nrf5_uart_fifo_0, uint8_t, UART0_FIFO_BUFFER_SIZE);

#if UART1_ENABLED
NRF_ATFIFO_DEF(nordic_nrf5_uart_fifo_1, uint8_t, UART1_FIFO_BUFFER_SIZE);
#endif

/**
 * SWI IRQ mask.
 */
static uint8_t nordic_nrf5_uart_swi_mask_tx_0 = 0;
static uint8_t nordic_nrf5_uart_swi_mask_rx_0 = 0;
#if UART1_ENABLED
static uint8_t nordic_nrf5_uart_swi_mask_tx_1 = 0;
static uint8_t nordic_nrf5_uart_swi_mask_rx_1 = 0;
#endif

/**
 * Global variables expected by mbed_retarget.cpp for STDOUT.
 */
int stdio_uart_inited = 0;
serial_t stdio_uart = { 0 };


/***
 *       _____        __ _                            _____       _                             _
 *      / ____|      / _| |                          |_   _|     | |                           | |
 *     | (___   ___ | |_| |___      ____ _ _ __ ___    | |  _ __ | |_ ___ _ __ _ __ _   _ _ __ | |_
 *      \___ \ / _ \|  _| __\ \ /\ / / _` | '__/ _ \   | | | '_ \| __/ _ \ '__| '__| | | | '_ \| __|
 *      ____) | (_) | | | |_ \ V  V / (_| | | |  __/  _| |_| | | | ||  __/ |  | |  | |_| | |_) | |_
 *     |_____/ \___/|_|  \__| \_/\_/ \__,_|_|  \___| |_____|_| |_|\__\___|_|  |_|   \__,_| .__/ \__|
 *                                                                                       | |
 *                                                                                       |_|
 */

/**
 * @brief      SWI interrupt handler for signaling RxIrq handler.
 *
 * @param[in]  instance  The instance
 */
static void nordic_nrf5_uart_callback_handler(uint32_t instance)
{
    /* Flag that no callback is posted. */
    nordic_nrf5_uart_state[instance].callback_posted = false;

    /* Check if callback handler is set and if event mask match. */
    uart_irq_handler callback = (uart_irq_handler) nordic_nrf5_uart_state[instance].owner->handler;
    uint32_t mask = nordic_nrf5_uart_state[instance].owner->mask;

    if (callback && (mask & NORDIC_RX_IRQ)) {

        /* Invoke callback function. */
        uint32_t context = nordic_nrf5_uart_state[instance].owner->context;
        callback(context, RxIrq);
    }
}

/**
 * @brief      SWI interrupt handler for when the Tx buffer has been transmitted.
 *
 * @param[in]  instance  The instance
 */
static void nordic_nrf5_uart_event_handler_endtx(int instance)
{
    /* Release mutex. As the owner this call is safe. */
    core_util_atomic_store_bool(&nordic_nrf5_uart_state[instance].tx_in_progress, false);

    /* Check if callback handler and Tx event mask is set. */
    uart_irq_handler callback = (uart_irq_handler) nordic_nrf5_uart_state[instance].owner->handler;
    uint32_t mask = nordic_nrf5_uart_state[instance].owner->mask;

    if (callback && (mask & NORDIC_TX_IRQ)) {

        /* Invoke callback function. */
        uint32_t context = nordic_nrf5_uart_state[instance].owner->context;
        callback(context, TxIrq);
    }
}

/**
 * @brief      Asynchronous event handler for when Tx DMA buffer has been sent.
 *
 * @param[in]  instance  The instance
 */
#if DEVICE_SERIAL_ASYNCH
static void nordic_nrf5_uart_event_handler_endtx_asynch(int instance)
{
    /* Set Tx done and reset Tx mode to be not asynchronous. */
    nordic_nrf5_uart_state[instance].tx_asynch = false;
    core_util_atomic_store_bool(&nordic_nrf5_uart_state[instance].tx_in_progress, false);

    /* Cast handler to callback function pointer. */
    void (*callback)(void) = (void (*)(void)) nordic_nrf5_uart_state[instance].owner->tx_handler;
    uint32_t mask = nordic_nrf5_uart_state[instance].owner->tx_mask;

    /* Signal error if event mask matches and event handler is set. */
    if (callback && (mask & SERIAL_EVENT_TX_COMPLETE)) {

        /* Store event value so it can be read back. */
        nordic_nrf5_uart_state[instance].owner->tx_event = SERIAL_EVENT_TX_COMPLETE;

        /* Signal callback handler. */
        callback();
    }
}
#endif

static void nordic_nrf5_uart_swi0(void)
{
    if (nordic_nrf5_uart_swi_mask_tx_0) {

        nordic_nrf5_uart_swi_mask_tx_0 = 0;

#if DEVICE_SERIAL_ASYNCH
        if (nordic_nrf5_uart_state[0].tx_asynch) {

            nordic_nrf5_uart_event_handler_endtx_asynch(0);
        } else
#endif
        {
            nordic_nrf5_uart_event_handler_endtx(0);
        }
    }

    if (nordic_nrf5_uart_swi_mask_rx_0) {

        nordic_nrf5_uart_swi_mask_rx_0 = 0;

        nordic_nrf5_uart_callback_handler(0);
    }


#if UART1_ENABLED
    if (nordic_nrf5_uart_swi_mask_tx_1) {

        nordic_nrf5_uart_swi_mask_tx_1 = 0;

#if DEVICE_SERIAL_ASYNCH
        if (nordic_nrf5_uart_state[1].tx_asynch) {

            nordic_nrf5_uart_event_handler_endtx_asynch(1);
        } else
#endif
        {
            nordic_nrf5_uart_event_handler_endtx(1);
        }
    }

    if (nordic_nrf5_uart_swi_mask_rx_1) {

        nordic_nrf5_uart_swi_mask_rx_1 = 0;

        nordic_nrf5_uart_callback_handler(1);
    }
#endif
}

/**
 * @brief      Trigger Tx SWI.
 *
 * @param[in]  instance  The instance.
 */
static void nordic_swi_tx_trigger(int instance)
{
    if (instance == 0) {

        nordic_nrf5_uart_swi_mask_tx_0 = 1;
        NVIC_SetPendingIRQ(SWI0_EGU0_IRQn);
    }
#if UART1_ENABLED
    else if (instance == 1) {

        nordic_nrf5_uart_swi_mask_tx_1 = 1;
        NVIC_SetPendingIRQ(SWI0_EGU0_IRQn);
    }
#endif
}

/**
 * @brief      Trigger Rx SWI.
 *
 * @param[in]  instance  The instance
 */
static void nordic_swi_rx_trigger(int instance)
{
    if (instance == 0) {

        nordic_nrf5_uart_swi_mask_rx_0 = 1;
        NVIC_SetPendingIRQ(SWI0_EGU0_IRQn);
    }
#if UART1_ENABLED
    else if (instance == 1) {

        nordic_nrf5_uart_swi_mask_rx_1 = 1;
        NVIC_SetPendingIRQ(SWI0_EGU0_IRQn);
    }
#endif
}

/***
 *      _    _         _____ _______   ______               _     _    _                 _ _
 *     | |  | |  /\   |  __ \__   __| |  ____|             | |   | |  | |               | | |
 *     | |  | | /  \  | |__) | | |    | |____   _____ _ __ | |_  | |__| | __ _ _ __   __| | | ___ _ __
 *     | |  | |/ /\ \ |  _  /  | |    |  __\ \ / / _ \ '_ \| __| |  __  |/ _` | '_ \ / _` | |/ _ \ '__|
 *     | |__| / ____ \| | \ \  | |    | |___\ V /  __/ | | | |_  | |  | | (_| | | | | (_| | |  __/ |
 *      \____/_/    \_\_|  \_\ |_|    |______\_/ \___|_| |_|\__| |_|  |_|\__,_|_| |_|\__,_|_|\___|_|
 *
 *
 */

/**
 * @brief      Event handler for when Rx buffer is full or buffer swap has been
 *             triggered by idle task.
 *
 *             Copy data from DMA buffer to FIFO buffer.
 *             Post callback if not already posted.
 *
 * @param[in]  instance  The instance
 */
static void nordic_nrf5_uart_event_handler_endrx(int instance)
{
    /* Read out active bank flag and swap DMA buffers. */
    uint8_t active_bank = nordic_nrf5_uart_state[instance].active_bank;
    nordic_nrf5_uart_state[instance].active_bank = active_bank ^ 0x01;

    /* Get number of bytes in DMA buffer. */
    uint32_t available = nrf_uarte_rx_amount_get(nordic_nrf5_uart_register[instance]);

    if (available > 0) {

        /* Copy data from DMA buffer to FIFO buffer. */
        for (size_t index = 0; index < available; index++) {

            /* Atomic FIFO can be used safely without disabling interrutps. */
            nrf_atfifo_item_put_t fifo_context;

            /* Get pointer to available space. */
            uint8_t *byte = (uint8_t *) nrf_atfifo_item_alloc(nordic_nrf5_uart_state[instance].fifo, &fifo_context);

            if (byte != NULL) {

                /* Copy 1 byte from DMA buffer and commit to FIFO buffer. */
                *byte = nordic_nrf5_uart_state[instance].buffer[active_bank][index];
                nrf_atfifo_item_put(nordic_nrf5_uart_state[instance].fifo, &fifo_context);
                core_util_atomic_decr_u32(&nordic_nrf5_uart_state[instance].fifo_free_count, 1);

            } else {

                /* Buffer overflow. */
                break;
            }
        }

        /* Signal callback through lower priority SWI if not already posted. */
        if (nordic_nrf5_uart_state[instance].callback_posted == false) {

            nordic_nrf5_uart_state[instance].callback_posted = true;
            nordic_swi_rx_trigger(instance);
        }
    }
}

/**
 * @brief      Event handler for when DMA has been armed with Rx buffer.
 *
 *             Arm Rx buffer with second buffer for optimal reception.
 *
 * @param[in]  instance  The instance
 */
static void nordic_nrf5_uart_event_handler_rxstarted(int instance)
{
    uint8_t next_bank = nordic_nrf5_uart_state[instance].active_bank ^ 0x01;

    nrf_uarte_rx_buffer_set(nordic_nrf5_uart_register[instance], nordic_nrf5_uart_state[instance].buffer[next_bank], DMA_BUFFER_SIZE);
    if (nordic_nrf5_uart_state[instance].rts != NRF_UART_PSEL_DISCONNECTED) {
        if (nordic_nrf5_uart_state[instance].fifo_free_count > FIFO_MIN) {
            /* Clear rts since we are ready to receive the next byte */
            nrfx_gpiote_clr_task_trigger(nordic_nrf5_uart_state[instance].rts);
        } else {
            /* Suspend reception since there isn't enough buffer space.
             * The function serial_getc will restart reception. */
            nordic_nrf5_uart_state[instance].rx_suspended = true;
        }
    }
}

#if DEVICE_SERIAL_ASYNCH
/**
 * @brief      Asynchronous event handler for when Rx DMA buffer is full.
 *
 * @param[in]  instance  The instance
 */
static void nordic_nrf5_uart_event_handler_endrx_asynch(int instance)
{
    /* Set Rx done and reset Rx mode to be not asynchronous. */
    nordic_nrf5_uart_state[instance].rx_asynch = false;
    core_util_atomic_store_bool(&nordic_nrf5_uart_state[instance].rx_in_progress, false);

    /* Cast handler to callback function pointer. */
    void (*callback)(void) = (void (*)(void)) nordic_nrf5_uart_state[instance].owner->rx_handler;
    uint32_t mask = nordic_nrf5_uart_state[instance].owner->rx_mask;

    /* Signal error if event mask matches and event handler is set. */
    if (callback && (mask & SERIAL_EVENT_RX_COMPLETE)) {

        /* Store event value so it can be read back. */
        nordic_nrf5_uart_state[instance].owner->rx_event = SERIAL_EVENT_RX_COMPLETE;

        /* Signal callback handler. */
        callback();
    }
}
#endif

/**
 * @brief      UARTE event handler.
 *
 *             Collect signals from UARTE0 and UARTE1 ISR and translate to instance.
 *
 * @param[in]  instance  The instance
 */
static void nordic_nrf5_uart_event_handler(int instance)
{
    /* DMA buffer is full or has been swapped out by idle timeout. */
    if (nrf_uarte_event_check(nordic_nrf5_uart_register[instance], NRF_UARTE_EVENT_ENDRX))
    {
        nrf_uarte_event_clear(nordic_nrf5_uart_register[instance], NRF_UARTE_EVENT_ENDRX);

#if DEVICE_SERIAL_ASYNCH
        /* Call appropriate event handler based on current mode. */
        if (nordic_nrf5_uart_state[instance].rx_asynch) {

            nordic_nrf5_uart_event_handler_endrx_asynch(instance);
        } else
#endif
        {
            nordic_nrf5_uart_event_handler_endrx(instance);
        }
    }

    /* Rx DMA buffer has been armed.
     *
     * Warning - Do not process NRF_UARTE_EVENT_RXSTARTED if NRF_UARTE_EVENT_ENDRX is pending.
     * NRF_UARTE_EVENT_RXSTARTED must be processed first or nordic_nrf5_uart_event_handler_rxstarted
     * will setup the wrong DMA buffer and cause data to be lost.
     */
    if (nrf_uarte_event_check(nordic_nrf5_uart_register[instance], NRF_UARTE_EVENT_RXSTARTED) &&
            !nrf_uarte_event_check(nordic_nrf5_uart_register[instance], NRF_UARTE_EVENT_ENDRX))
    {
        nrf_uarte_event_clear(nordic_nrf5_uart_register[instance], NRF_UARTE_EVENT_RXSTARTED);

        nordic_nrf5_uart_event_handler_rxstarted(instance);
    }

    /* Tx DMA buffer has been sent. */
    if (nrf_uarte_event_check(nordic_nrf5_uart_register[instance], NRF_UARTE_EVENT_ENDTX))
    {
        nrf_uarte_event_clear(nordic_nrf5_uart_register[instance], NRF_UARTE_EVENT_ENDTX);

        /* Use SWI to de-prioritize callback. */
        nordic_swi_tx_trigger(instance);
    }
}

/**
 * @brief      UARTE0 ISR.
 */
static void nordic_nrf5_uart0_handler(void)
{
    /* Call event handler with instance ID. */
    nordic_nrf5_uart_event_handler(0);
}

#if UART1_ENABLED
/**
 * @brief      UARTE1 ISR.
 */
static void nordic_nrf5_uart1_handler(void)
{
    /* Call event handler with instance ID. */
    nordic_nrf5_uart_event_handler(1);
}
#endif


/***
 *       _____             __ _                       _   _
 *      / ____|           / _(_)                     | | (_)
 *     | |     ___  _ __ | |_ _  __ _ _   _ _ __ __ _| |_ _  ___  _ __
 *     | |    / _ \| '_ \|  _| |/ _` | | | | '__/ _` | __| |/ _ \| '_ \
 *     | |___| (_) | | | | | | | (_| | |_| | | | (_| | |_| | (_) | | | |
 *      \_____\___/|_| |_|_| |_|\__, |\__,_|_|  \__,_|\__|_|\___/|_| |_|
 *                               __/ |
 *                              |___/
 */

/**
 * @brief      Configure UARTE based on serial object settings.
 *
 *             Common for both Rx and Tx.
 *
 * @param      obj   The object
 */
static void nordic_nrf5_uart_configure_object(serial_t *obj)
{
    MBED_ASSERT(obj);

#if DEVICE_SERIAL_ASYNCH
    struct serial_s *uart_object = &obj->serial;
#else
    struct serial_s *uart_object = obj;
#endif

    /* Configure Tx and Rx pins. */
    if (uart_object->tx != NRF_UART_PSEL_DISCONNECTED) {

        nrf_gpio_pin_set(uart_object->tx);
        nrf_gpio_cfg_output(uart_object->tx);
    }

    if (uart_object->rx != NRF_UART_PSEL_DISCONNECTED) {

        nrf_gpio_cfg_input(uart_object->rx, NRF_GPIO_PIN_NOPULL);
    }

    nrf_uarte_txrx_pins_set(nordic_nrf5_uart_register[uart_object->instance],
                            uart_object->tx,
                            uart_object->rx);

    /* Set hardware flow control pins. */
    if (uart_object->hwfc == NRF_UART_HWFC_ENABLED) {

        /* Check if pin is set before configuring it. */
        if (uart_object->cts != NRF_UART_PSEL_DISCONNECTED) {

            nrf_gpio_cfg_input(uart_object->cts, NRF_GPIO_PIN_NOPULL);
        }

        /* Only let UARTE module handle CTS, RTS is handled manually due to buggy UARTE logic. */
        nrf_uarte_hwfc_pins_set(nordic_nrf5_uart_register[uart_object->instance],
                                NRF_UART_PSEL_DISCONNECTED,
                                uart_object->cts);
    }

    /* Check if the rts pin changed */
    if (uart_object->rts != nordic_nrf5_uart_state[uart_object->instance].rts) {
        uint32_t ret;

        /* Disable the PPI interconnect */
        ret = nrfx_ppi_channel_disable(nordic_nrf5_uart_state[uart_object->instance].ppi_rts);
        MBED_ASSERT(ret == NRF_SUCCESS);

        /* Free flow control gpiote pin if it was previously set */
        if (nordic_nrf5_uart_state[uart_object->instance].rts != NRF_UART_PSEL_DISCONNECTED) {
            nrfx_gpiote_out_uninit((nrfx_gpiote_pin_t)uart_object->rts);
        }

        /* Allocate and enable flow control gpiote pin if it is being used */
        if (uart_object->rts != NRF_UART_PSEL_DISCONNECTED) {

            static const nrfx_gpiote_out_config_t config = {
                .init_state = NRF_GPIOTE_INITIAL_VALUE_HIGH,
                .task_pin   = true,
                .action     = NRF_GPIOTE_POLARITY_LOTOHI
            };

            /* Allocate gpiote channel */
            ret = nrfx_gpiote_out_init((nrfx_gpiote_pin_t)uart_object->rts, &config);
            if (ret == NRF_ERROR_INVALID_STATE) {
                /* Pin was previously set to GPIO so uninitialize it */
                nrfx_gpiote_out_uninit((nrfx_gpiote_pin_t)uart_object->rts);
                ret = nrfx_gpiote_out_init((nrfx_gpiote_pin_t)uart_object->rts, &config);
            }
            MBED_ASSERT(ret == NRF_SUCCESS);

            /* Set RTS high on the ENDRX event */
            ret = nrfx_ppi_channel_assign(nordic_nrf5_uart_state[uart_object->instance].ppi_rts,
                                          nrf_uarte_event_address_get(nordic_nrf5_uart_register[uart_object->instance], NRF_UARTE_EVENT_ENDRX),
                                          nrfx_gpiote_out_task_addr_get(uart_object->rts));
            MBED_ASSERT(ret == NRF_SUCCESS);

            ret = nrfx_ppi_channel_enable(nordic_nrf5_uart_state[uart_object->instance].ppi_rts);
            MBED_ASSERT(ret == NRF_SUCCESS);

            /* Enable gpiote task - rts pin can no longer be used as GPIO at this point */
            nrfx_gpiote_out_task_enable((nrfx_gpiote_pin_t)uart_object->rts);
        }

        nordic_nrf5_uart_state[uart_object->instance].rts = uart_object->rts;
    }

    /* Enable flow control and parity. */
    nrf_uarte_configure(nordic_nrf5_uart_register[uart_object->instance],
                        (nrf_uarte_parity_t) uart_object->parity,
                        (nrf_uarte_hwfc_t) uart_object->hwfc);

    /* Set baudrate. */
    nrf_uarte_baudrate_set(nordic_nrf5_uart_register[uart_object->instance],
                           (nrf_uarte_baudrate_t) uart_object->baudrate);
}

/**
 * @brief      Setup non-asynchronous reception.
 *
 * @param[in]  instance  The instance
 */
static void nordic_nrf5_uart_configure_rx(int instance)
{
    /* Disable interrupts during confiration. */
    nrf_uarte_int_disable(nordic_nrf5_uart_register[instance], NRF_UARTE_INT_RXSTARTED_MASK |
                                                               NRF_UARTE_INT_ENDRX_MASK);

    /* Clear FIFO buffer. */
    nrf_atfifo_clear(nordic_nrf5_uart_state[instance].fifo);
    nordic_nrf5_uart_state[instance].fifo_free_count = UART0_FIFO_BUFFER_SIZE;

    /* Clear Rx related events. */
    nrf_uarte_event_clear(nordic_nrf5_uart_register[instance], NRF_UARTE_EVENT_RXSTARTED);
    nrf_uarte_event_clear(nordic_nrf5_uart_register[instance], NRF_UARTE_EVENT_ENDRX);

    /* Enable shortcut between buffer full and begin reception on next buffer armed. */
    nrf_uarte_shorts_enable(nordic_nrf5_uart_register[instance], NRF_UARTE_SHORT_ENDRX_STARTRX);

    /* Reset bank flag. */
    nordic_nrf5_uart_state[instance].active_bank = 0;

    /* Arm first DMA buffer. */
    nrf_uarte_rx_buffer_set(nordic_nrf5_uart_register[instance],
                            nordic_nrf5_uart_state[instance].buffer[0],
                            DMA_BUFFER_SIZE);

    /* Set non-asynchronous mode. */
    nordic_nrf5_uart_state[instance].rx_asynch = false;

    /* Clear suspend condition */
    nordic_nrf5_uart_state[instance].rx_suspended = false;

    /* Enable interrupts again. */
    nrf_uarte_int_enable(nordic_nrf5_uart_register[instance], NRF_UARTE_INT_RXSTARTED_MASK |
                                                              NRF_UARTE_INT_ENDRX_MASK);
}

#if DEVICE_SERIAL_ASYNCH
/**
 * @brief      Setup asynchronous reception.
 *
 * @param[in]  instance  The instance
 */
static void nordic_nrf5_uart_configure_rx_asynch(int instance)
{
    /* Disable Rx related interrupts. */
    nrf_uarte_int_disable(nordic_nrf5_uart_register[instance], NRF_UARTE_INT_RXSTARTED_MASK |
                                                               NRF_UARTE_INT_ENDRX_MASK);

    /* Clear Rx related events. */
    nrf_uarte_event_clear(nordic_nrf5_uart_register[instance], NRF_UARTE_EVENT_RXSTARTED);
    nrf_uarte_event_clear(nordic_nrf5_uart_register[instance], NRF_UARTE_EVENT_ENDRX);

    /* Disable shortcut. Next Rx buffer must be manually started. */
    nrf_uarte_shorts_disable(nordic_nrf5_uart_register[instance], NRF_UARTE_SHORT_ENDRX_STARTRX);

    /* Set asynchronous mode. */
    nordic_nrf5_uart_state[instance].rx_asynch = true;

    /* Clear suspend condition */
    nordic_nrf5_uart_state[instance].rx_suspended = false;

    /* Enable Rx interrupt. */
    nrf_uarte_int_enable(nordic_nrf5_uart_register[instance], NRF_UARTE_INT_ENDRX_MASK);
}
#endif

/**
 * @brief      Main configuration function.
 *
 * @param      obj   The serial object
 */
static void nordic_nrf5_serial_configure(serial_t *obj)
{
    MBED_ASSERT(obj);

#if DEVICE_SERIAL_ASYNCH
    struct serial_s *uart_object = &obj->serial;
#else
    struct serial_s *uart_object = obj;
#endif

    /* Get object instance. */
    int instance = uart_object->instance;

    /* Only configure if instance owner has changed or an update is forced. */
    if ((uart_object != nordic_nrf5_uart_state[instance].owner) || (uart_object->update)) {

        /* Configure common setting. */
        nordic_nrf5_uart_configure_object(obj);

        /* Set new owner. */
        nordic_nrf5_uart_state[instance].owner = uart_object;
        uart_object->update = false;

#if DEVICE_SERIAL_ASYNCH
        /* Set asynchronous mode. */
        if (uart_object->rx_asynch == true) {

            nordic_nrf5_uart_configure_rx_asynch(instance);
        } else
#endif
        {
            /* Set non-asynchronous mode. */
            nordic_nrf5_uart_configure_rx(instance);
            nrf_uarte_task_trigger(nordic_nrf5_uart_register[instance],
                                   NRF_UARTE_TASK_STARTRX);
        }
    }
#if DEVICE_SERIAL_ASYNCH
    /* Owner hasn't changed but mode has. Reconfigure. */
    else if ((uart_object->rx_asynch == false) && (nordic_nrf5_uart_state[instance].rx_asynch == true)) {

        nordic_nrf5_uart_configure_rx(instance);
        nrf_uarte_task_trigger(nordic_nrf5_uart_register[instance],
                               NRF_UARTE_TASK_STARTRX);

    /* Owner hasn't changed but mode has. Reconfigure. */
    } else if ((uart_object->rx_asynch == true) && (nordic_nrf5_uart_state[instance].rx_asynch == false)) {

        nordic_nrf5_uart_configure_rx_asynch(instance);
    }
#endif
}

/***
 *      __  __ _              _   _    _          _                 _____ _____
 *     |  \/  | |            | | | |  | |   /\   | |          /\   |  __ \_   _|
 *     | \  / | |__   ___  __| | | |__| |  /  \  | |         /  \  | |__) || |
 *     | |\/| | '_ \ / _ \/ _` | |  __  | / /\ \ | |        / /\ \ |  ___/ | |
 *     | |  | | |_) |  __/ (_| | | |  | |/ ____ \| |____   / ____ \| |    _| |_
 *     |_|  |_|_.__/ \___|\__,_| |_|  |_/_/    \_\______| /_/    \_\_|   |_____|
 *
 *
 */

/** Initialize the serial peripheral. It sets the default parameters for serial
 *  peripheral, and configures its specifieds pins.
 *
 * Param obj The serial object
 * Param tx  The TX pin name
 * Param rx  The RX pin name
 */
void serial_init(serial_t *obj, PinName tx, PinName rx)
{
    MBED_ASSERT(obj);

#if DEVICE_SERIAL_ASYNCH
    struct serial_s *uart_object = &obj->serial;
#else
    struct serial_s *uart_object = obj;
#endif

    /* Only initialize on first call. */
    static bool first_init = true;
    if (first_init) {
        uint32_t ret;
        first_init = false;

        /* Initialize components that serial relies on. */
        if (!nrfx_gpiote_is_init()) {
            nrfx_gpiote_init();
        }

        /* Enable interrupts for SWI. */
        NVIC_SetVector(SWI0_EGU0_IRQn, (uint32_t) nordic_nrf5_uart_swi0);
        NRFX_IRQ_PRIORITY_SET(SWI0_EGU0_IRQn, APP_IRQ_PRIORITY_LOWEST);
        NRFX_IRQ_ENABLE(SWI0_EGU0_IRQn);

        /* Initialize FIFO buffer for UARTE0. */
        NRF_ATFIFO_INIT(nordic_nrf5_uart_fifo_0);
        nordic_nrf5_uart_state[0].fifo = nordic_nrf5_uart_fifo_0;

        /* Initialize owner to NULL. */
        nordic_nrf5_uart_state[0].owner = NULL;

        /* Allocate a PPI channel for flow control */
        ret = nrfx_ppi_channel_alloc(&nordic_nrf5_uart_state[0].ppi_rts);
        MBED_ASSERT(ret == NRF_SUCCESS);

        /* Clear RTS */
        nordic_nrf5_uart_state[0].rts = NRF_UART_PSEL_DISCONNECTED;

        /* Clear any old events and enable interrupts for UARTE0. */
        nrf_uarte_int_disable(nordic_nrf5_uart_register[0], 0xFFFFFFFF);

        NVIC_SetVector(UARTE0_UART0_IRQn, (uint32_t) nordic_nrf5_uart0_handler);
        NRFX_IRQ_PRIORITY_SET(UARTE0_UART0_IRQn, APP_IRQ_PRIORITY_HIGHEST);
        NRFX_IRQ_ENABLE(UARTE0_UART0_IRQn);

#if UART1_ENABLED
        /* Initialize FIFO buffer for UARTE1. */
        NRF_ATFIFO_INIT(nordic_nrf5_uart_fifo_1);
        nordic_nrf5_uart_state[1].fifo = nordic_nrf5_uart_fifo_1;

        /* Initialize owner to NULL. */
        nordic_nrf5_uart_state[1].owner = NULL;

        /* Allocate a PPI channel for flow control */
        ret = nrfx_ppi_channel_alloc(&nordic_nrf5_uart_state[1].ppi_rts);
        MBED_ASSERT(ret == NRF_SUCCESS);

        /* Clear RTS */
        nordic_nrf5_uart_state[1].rts = NRF_UART_PSEL_DISCONNECTED;

        /* Clear any old events and enable interrupts for UARTE1. */
        nrf_uarte_int_disable(nordic_nrf5_uart_register[1], 0xFFFFFFFF);

        NVIC_SetVector(UARTE1_IRQn, (uint32_t) nordic_nrf5_uart1_handler);
        NRFX_IRQ_PRIORITY_SET(UARTE1_IRQn, APP_IRQ_PRIORITY_HIGHEST);
        NRFX_IRQ_ENABLE(UARTE1_IRQn);
#endif
    }

    /* Get instance ID based on provided pins. */
    int instance = pin_instance_uart(tx, rx);

    uart_object->instance = instance;

    /* Increment usage counter for this instance. */
    nordic_nrf5_uart_state[instance].usage_counter++;

    /* Enable instance on first usage. */
    if (nordic_nrf5_uart_state[instance].usage_counter == 1) {

        nrf_uarte_enable(nordic_nrf5_uart_register[instance]);

        /* In order to support printing with interrupts disabled serial_putc
         * must busy wait on NRF_UARTE_EVENT_TXDRDY. This event cannot be set
         * manually but must be set by the UARTE module after a character has
         * been sent.
         *
         * The following code sends a dummy character into the void so that
         * NRF_UARTE_EVENT_TXDRDY is correctly set.
         */

        /* Ensure pins are disconnected. */
        nrf_uarte_txrx_pins_set(nordic_nrf5_uart_register[instance],
                                NRF_UART_PSEL_DISCONNECTED,
                                NRF_UART_PSEL_DISCONNECTED);

        /* Set maximum baud rate to minimize waiting. */
        nrf_uarte_baudrate_set(nordic_nrf5_uart_register[instance],
                               NRF_UARTE_BAUDRATE_1000000);

        /* Send character. */
        nrf_uarte_tx_buffer_set(nordic_nrf5_uart_register[instance],
                                &nordic_nrf5_uart_state[instance].tx_data,
                                1);
        nrf_uarte_event_clear(nordic_nrf5_uart_register[instance], NRF_UARTE_EVENT_ENDTX);
        nrf_uarte_task_trigger(nordic_nrf5_uart_register[instance], NRF_UARTE_TASK_STARTTX);

        /* Wait until NRF_UARTE_EVENT_TXDRDY is set before proceeding. */
        bool done = false;
        do {
            done = nrf_uarte_event_check(nordic_nrf5_uart_register[instance],
                                (nrf_uarte_event_t) NRF_UARTE_EVENT_TXDRDY);
        } while(done == false);
    }

    /* Store pins in serial object. */
    if (tx == NC) {

        uart_object->tx = NRF_UART_PSEL_DISCONNECTED;
    } else {

        uart_object->tx = tx;
    }

    if (rx == NC) {

        uart_object->rx = NRF_UART_PSEL_DISCONNECTED;
    } else {

        uart_object->rx = rx;
    }

    /* Set default parity, baud rate, and callback handler. */
    uart_object->parity = NRF_UART_PARITY_EXCLUDED;
    uart_object->baudrate = NRF_UART_BAUDRATE_9600;
    uart_object->cts = NRF_UART_PSEL_DISCONNECTED;
    uart_object->rts = NRF_UART_PSEL_DISCONNECTED;
    uart_object->hwfc = NRF_UART_HWFC_DISABLED;
    uart_object->handler = 0;

    /* The STDIO object is stored in this file. Set the flag once initialized. */
    if (obj == &stdio_uart) {
        stdio_uart_inited = 1;
    }

    /* Take ownership and configure UART. */
    uart_object->update = true;
    nordic_nrf5_serial_configure(obj);
}

/** Release the serial peripheral, not currently invoked. It requires further
 *  resource management.
 *
 * Param obj The serial object
 */
void serial_free(serial_t *obj)
{
    MBED_ASSERT(obj);

#if DEVICE_SERIAL_ASYNCH
    struct serial_s *uart_object = &obj->serial;
#else
    struct serial_s *uart_object = obj;
#endif

    int instance = uart_object->instance;

    /* Only consider disabling UARTE if number of users is not zero. */
    if (nordic_nrf5_uart_state[instance].usage_counter > 0) {

        /* Decrement usage counter for this instance. */
        nordic_nrf5_uart_state[instance].usage_counter--;

        /* Disable instance when not in use. */
        if (nordic_nrf5_uart_state[instance].usage_counter == 0) {

            nrf_uarte_disable(nordic_nrf5_uart_register[instance]);
            
            /* Turn NRF_UARTE0_BASE or NRF_UARTE1_BASE power off and on to reset peripheral. */
            if (instance == 0) {
                *(volatile uint32_t *)0x40002FFC = 0;
                *(volatile uint32_t *)0x40002FFC;
                *(volatile uint32_t *)0x40002FFC = 1;
            }
#if UART1_ENABLED
            else {
                *(volatile uint32_t *)0x40028FFC = 0;
                *(volatile uint32_t *)0x40028FFC;
                *(volatile uint32_t *)0x40028FFC = 1;
            }
#endif
            
        }
    }
}

/** Configure the baud rate
 *
 * Param obj      The serial object
 * Param baudrate The baud rate to be configured
 */
void serial_baud(serial_t *obj, int baudrate)
{
    MBED_ASSERT(obj);

#if DEVICE_SERIAL_ASYNCH
    struct serial_s *uart_object = &obj->serial;
{'fraction_non_alphanumeric': 0.10123043299320211, 'fraction_numerical': 0.011694080581297986, 'mean_word_length': 2.4468522232549037, 'pattern_counts': {'":': 0, '<': 13, '<?xml version=': 0, '>': 149, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 8, 'word_list_counts': {'cursed_substrings.json': 66, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '4938306', 'n_tokens_mistral': 25681, 'n_tokens_neox': 24986, 'n_words': 4569}