/*
 * Copyright (c) 2021 - 2025, Nordic Semiconductor ASA
 * All rights reserved.
 *
 * SPDX-License-Identifier: BSD-3-Clause
 *
 * 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 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 the copyright holder nor the names of its
 *    contributors may be used to endorse or promote products derived from this
 *    software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 */

#ifndef NRFY_TWIM_H__
#define NRFY_TWIM_H__

#include <nrfx.h>
#include <nrf_erratas.h>
#include <hal/nrf_twim.h>

#ifdef __cplusplus
extern "C" {
#endif

typedef struct nrfy_twim_xfer_desc_t nrfy_twim_xfer_desc_t;

NRFY_STATIC_INLINE void __nrfy_internal_twim_event_enabled_clear(NRF_TWIM_Type *  p_reg,
                                                                 uint32_t         mask,
                                                                 nrf_twim_event_t event);

NRFY_STATIC_INLINE bool __nrfy_internal_twim_event_handle(NRF_TWIM_Type *  p_reg,
                                                          uint32_t         mask,
                                                          nrf_twim_event_t event,
                                                          uint32_t *       p_evt_mask);

NRFY_STATIC_INLINE
uint32_t __nrfy_internal_twim_events_process(NRF_TWIM_Type *               p_reg,
                                             uint32_t                      mask,
                                             nrfy_twim_xfer_desc_t const * p_xfer);

/**
 * @defgroup nrfy_twim TWIM HALY
 * @{
 * @ingroup nrf_twim
 * @brief   Hardware access layer with cache and barrier support for managing the TWIM peripheral.
 */

#if NRF_TWIM_HAS_ARRAY_LIST || defined(__NRFX_DOXYGEN__)
/** @refhal{NRF_TWIM_HAS_ARRAY_LIST} */
#define NRFY_TWIM_HAS_ARRAY_LIST 1
#else
#define NRFY_TWIM_HAS_ARRAY_LIST 0
#endif

/** @brief TWIM pins configuration structure. */
typedef struct
{
    uint32_t scl_pin; ///< SCL pin number.
    uint32_t sda_pin; ///< SDA pin number.
} nrfy_twim_pins_t;

/** @brief TWIM configuration structure. */
typedef struct
{
    nrfy_twim_pins_t     pins;          ///< TWIM pins configuation.
    nrf_twim_frequency_t frequency;     ///< TWIM frequency.
    bool                 skip_psel_cfg; ///< Skip pin selection configuration.
                                        /**< When set to true, the driver does not modify
                                         *   pin select registers in the peripheral.
                                         *   Those registers are supposed to be set up
                                         *   externally before the driver is initialized.
                                         *   @note When both GPIO configuration and pin
                                         *   selection are to be skipped, the structure
                                         *   fields that specify pins can be omitted,
                                         *   as they are ignored anyway. */
} nrfy_twim_config_t;

/** @brief Structure describing a TWIM transfer. */
struct nrfy_twim_xfer_desc_t
{
    uint8_t * p_buffer; ///< Pointer to transferred data.
    size_t    length;   ///< Number of bytes transferred.
};

/**
 * @brief Function for configuring the TWIM.
 *
 * @param[in] p_reg    Pointer to the structure of registers of the peripheral.
 * @param[in] p_config Pointer to the peripheral configuration structure.
 */
NRFY_STATIC_INLINE void nrfy_twim_periph_configure(NRF_TWIM_Type *            p_reg,
                                                   nrfy_twim_config_t const * p_config)
{
    if (!p_config->skip_psel_cfg)
    {
        nrf_twim_pins_set(p_reg, p_config->pins.scl_pin, p_config->pins.sda_pin);
    }
    nrf_twim_frequency_set(p_reg, p_config->frequency);
    nrf_barrier_w();
}

/**
 * @brief Function for initializing the specified TWIM interrupts.
 *
 * @param[in] p_reg        Pointer to the structure of registers of the peripheral.
 * @param[in] mask         Mask of interrupts to be initialized.
 * @param[in] irq_priority Interrupt priority.
 * @param[in] enable       True if interrupts associated with the event mask are to be enabled, false otherwise.
 */
NRFY_STATIC_INLINE void nrfy_twim_int_init(NRF_TWIM_Type * p_reg,
                                           uint32_t        mask,
                                           uint8_t         irq_priority,
                                           bool            enable)
{
    __nrfy_internal_twim_event_enabled_clear(p_reg, mask, NRF_TWIM_EVENT_TXSTARTED);
    __nrfy_internal_twim_event_enabled_clear(p_reg, mask, NRF_TWIM_EVENT_RXSTARTED);
    __nrfy_internal_twim_event_enabled_clear(p_reg, mask, NRF_TWIM_EVENT_LASTTX);
    __nrfy_internal_twim_event_enabled_clear(p_reg, mask, NRF_TWIM_EVENT_LASTRX);
    __nrfy_internal_twim_event_enabled_clear(p_reg, mask, NRF_TWIM_EVENT_STOPPED);
    __nrfy_internal_twim_event_enabled_clear(p_reg, mask, NRF_TWIM_EVENT_SUSPENDED);
    __nrfy_internal_twim_event_enabled_clear(p_reg, mask, NRF_TWIM_EVENT_ERROR);
    nrf_barrier_w();

    NRFX_IRQ_PRIORITY_SET(nrfx_get_irq_number(p_reg), irq_priority);
    NRFX_IRQ_ENABLE(nrfx_get_irq_number(p_reg));
    if (enable)
    {
        nrf_twim_int_enable(p_reg, mask);
    }
    nrf_barrier_w();
}

/**
 * @brief Function for uninitializing the TWIM interrupts.
 *
 * @param[in] p_reg Pointer to the structure of registers of the peripheral.
 */
NRFY_STATIC_INLINE void nrfy_twim_int_uninit(NRF_TWIM_Type * p_reg)
{
    NRFX_IRQ_DISABLE(nrfx_get_irq_number(p_reg));
    nrf_barrier_w();
}

/**
 * @brief Function for processing the specified TWIM events.
 *
 * @param[in] p_reg  Pointer to the structure of registers of the peripheral.
 * @param[in] mask   Mask of events to be processed, created by @ref NRFY_EVENT_TO_INT_BITMASK();
 * @param[in] p_xfer Pointer to the structure containing buffer associated with the last reception.
 *                   Can be NULL.
 *
 * @return Mask of events that were generated and processed.
 *         To be checked against the result of @ref NRFY_EVENT_TO_INT_BITMASK().
 */
NRFY_STATIC_INLINE uint32_t nrfy_twim_events_process(NRF_TWIM_Type *               p_reg,
                                                     uint32_t                      mask,
                                                     nrfy_twim_xfer_desc_t const * p_xfer)
{
    uint32_t evt_mask = __nrfy_internal_twim_events_process(p_reg, mask, p_xfer);
    nrf_barrier_w();
    return evt_mask;
}

/**
 * @brief Function for setting the TWIM transaction buffer.
 *
 * @param[in] p_reg  Pointer to the structure of registers of the peripheral.
 * @param[in] p_xfer Pointer to the structure containing transaction buffer.
 */
NRFY_STATIC_INLINE void nrfy_twim_tx_buffer_set(NRF_TWIM_Type *               p_reg,
                                                nrfy_twim_xfer_desc_t const * p_xfer)
{
    if (p_xfer->p_buffer)
    {
        NRFY_CACHE_WB(p_xfer->p_buffer, p_xfer->length);
    }
    nrf_twim_tx_buffer_set(p_reg, p_xfer->p_buffer, p_xfer->length);
    nrf_barrier_w();
}

/**
 * @brief Function for setting the TWIM reception buffer.
 *
 * @param[in] p_reg  Pointer to the structure of registers of the peripheral.
 * @param[in] p_xfer Pointer to the structure containing reception buffer.
 */
NRFY_STATIC_INLINE void nrfy_twim_rx_buffer_set(NRF_TWIM_Type *               p_reg,
                                                nrfy_twim_xfer_desc_t const * p_xfer)
{
    nrf_twim_rx_buffer_set(p_reg, p_xfer->p_buffer, p_xfer->length);
    nrf_barrier_w();
}

/**
 * @brief Function for starting TWIM transaction.
 *
 * @param[in] p_reg  Pointer to the structure of registers of the peripheral.
 * @param[in] p_xfer Pointer to the structure containing transaction buffer.
 */
NRFY_STATIC_INLINE void nrfy_twim_tx_start(NRF_TWIM_Type *               p_reg,
                                           nrfy_twim_xfer_desc_t const * p_xfer)
{
    nrf_twim_task_trigger(p_reg, NRF_TWIM_TASK_STARTTX);
    if (p_xfer)
    {
        if (p_xfer->length == 0)
        {
            nrf_twim_task_trigger(p_reg, NRF_TWIM_TASK_STOP);
        }
        nrf_barrier_w();
        uint32_t mask = NRFY_EVENT_TO_INT_BITMASK(NRF_TWIM_EVENT_SUSPENDED) |
                        NRFY_EVENT_TO_INT_BITMASK(NRF_TWIM_EVENT_STOPPED) |
                        NRFY_EVENT_TO_INT_BITMASK(NRF_TWIM_EVENT_ERROR);
        uint32_t evt_mask = 0;
        while (!(evt_mask & (NRFY_EVENT_TO_INT_BITMASK(NRF_TWIM_EVENT_SUSPENDED) |
                             NRFY_EVENT_TO_INT_BITMASK(NRF_TWIM_EVENT_STOPPED))))
        {
            evt_mask = __nrfy_internal_twim_events_process(p_reg, mask, p_xfer);
            if (evt_mask & NRFY_EVENT_TO_INT_BITMASK(NRF_TWIM_EVENT_ERROR))
            {
                bool lasttx_triggered = __nrfy_internal_twim_events_process(p_reg,
                                                NRFY_EVENT_TO_INT_BITMASK(NRF_TWIM_EVENT_LASTTX),
                                                NULL);
                nrf_barrier_rw();
                uint32_t shorts_mask = nrf_twim_shorts_get(p_reg);
                nrf_barrier_r();

                if (!(lasttx_triggered && (shorts_mask & NRF_TWIM_SHORT_LASTTX_STOP_MASK)))
                {
                    // Unless LASTTX event arrived and LASTTX_STOP shortcut is active,
                    // triggering of STOP task in case of error has to be done manually.
                    nrf_twim_task_trigger(p_reg, NRF_TWIM_TASK_RESUME);
                    nrf_twim_task_trigger(p_reg, NRF_TWIM_TASK_STOP);
                    nrf_barrier_w();

                    // Mark transmission as not finished yet,
                    // as STOPPED event is expected to arrive.
                    // If LASTTX_SUSPENDED shortcut is active,
                    // NACK has been received on last byte sent
                    // and SUSPENDED event happened to be checked before ERROR,
                    // transmission will be marked as finished.
                    // In such case this flag has to be overwritten.
                    evt_mask = 0;
                }

                if (lasttx_triggered && (shorts_mask & NRF_TWIM_SHORT_LASTTX_SUSPEND_MASK))
                {
                    // When STOP task was triggered just before SUSPEND task has taken effect,
                    // SUSPENDED event may not arrive.
                    // However if SUSPENDED arrives it always arrives after ERROR.
                    // Therefore SUSPENDED has to be cleared
                    // so it does not cause premature termination of busy loop
                    // waiting for STOPPED event to arrive.
                    (void)__nrfy_internal_twim_events_process(p_reg,
                                            NRFY_EVENT_TO_INT_BITMASK(NRF_TWIM_EVENT_SUSPENDED),
                                            p_xfer);
                    // Mark transmission as not finished yet,
                    // for same reasons as above.
                    evt_mask = 0;
                }
            }
        }
    }
    nrf_barrier_w();
}

/**
 * @brief Function for starting TWIM reception.
 *
 * @param[in] p_reg  Pointer to the structure of registers of the peripheral.
 * @param[in] p_xfer Pointer to the structure containing reception buffer.
 */
NRFY_STATIC_INLINE void nrfy_twim_rx_start(NRF_TWIM_Type *               p_reg,
                                           nrfy_twim_xfer_desc_t const * p_xfer)
{
    nrf_twim_task_trigger(p_reg, NRF_TWIM_TASK_STARTRX);
    if (p_xfer)
    {
        if (p_xfer->length == 0)
        {
            nrf_twim_task_trigger(p_reg, NRF_TWIM_TASK_STOP);
        }

        nrf_barrier_w();
        uint32_t mask = NRFY_EVENT_TO_INT_BITMASK(NRF_TWIM_EVENT_SUSPENDED) |
                        NRFY_EVENT_TO_INT_BITMASK(NRF_TWIM_EVENT_STOPPED) |
                        NRFY_EVENT_TO_INT_BITMASK(NRF_TWIM_EVENT_ERROR);
        uint32_t evt_mask = 0;
        while (!(evt_mask & (NRFY_EVENT_TO_INT_BITMASK(NRF_TWIM_EVENT_SUSPENDED) |
                             NRFY_EVENT_TO_INT_BITMASK(NRF_TWIM_EVENT_STOPPED))))
        {
            evt_mask = __nrfy_internal_twim_events_process(p_reg, mask, p_xfer);
            if (evt_mask & NRFY_EVENT_TO_INT_BITMASK(NRF_TWIM_EVENT_ERROR))
            {
                // triggering of STOP task in case of error has to be done manually.
                nrf_twim_task_trigger(p_reg, NRF_TWIM_TASK_STOP);
                nrf_barrier_w();
            }
        }
    }
    nrf_barrier_w();
}

/**
 * @brief Function for aborting the ongoing TWIM transaction.
 *
 * @param[in] p_reg  Pointer to the structure of registers of the peripheral.
 * @param[in] p_xfer Pointer to the structure containing reception buffer
 *                   if the abort is to be blocking. NULL for non-blocking operation.
 */
NRFY_STATIC_INLINE void nrfy_twim_abort(NRF_TWIM_Type * p_reg, nrfy_twim_xfer_desc_t const * p_xfer)
{
    nrf_twim_task_trigger(p_reg, NRF_TWIM_TASK_STOP);
    if (p_xfer)
    {
        nrf_barrier_w();
        uint32_t evt_mask = NRFY_EVENT_TO_INT_BITMASK(NRF_TWIM_EVENT_STOPPED);
        while (!__nrfy_internal_twim_events_process(p_reg, evt_mask, p_xfer))
        {}
    }
    nrf_barrier_w();
}

/**
 * @brief Function for getting TWIM pins configuration.
 *
 * @param[in]  p_reg  Pointer to the structure of registers of the peripheral.
 * @param[out] p_pins Pointer to the structure to be filled with TWIM pins configuration.
 */
NRFY_STATIC_INLINE void nrfy_twim_pins_get(NRF_TWIM_Type const * p_reg,
                                           nrfy_twim_pins_t *    p_pins)
{
    nrf_barrier_rw();
    p_pins->scl_pin = nrf_twim_scl_pin_get(p_reg);
    p_pins->sda_pin = nrf_twim_sda_pin_get(p_reg);
    nrf_barrier_r();
}

/**
 * @brief Function for disabling TWIM with all interrupts and shortcuts.
 *
 * @param[in] p_reg Pointer to the structure of registers of the peripheral.
 */
NRFY_STATIC_INLINE void nrfy_twim_stop(NRF_TWIM_Type * p_reg)
{
    nrf_twim_int_disable(p_reg, NRF_TWIM_ALL_INTS_MASK);
    nrf_twim_shorts_disable(p_reg, NRF_TWIM_ALL_SHORTS_MASK);
#if NRF52_ERRATA_89_ENABLE_WORKAROUND
    if (nrf52_errata_89())
    {
        *(volatile uint32_t *)((uint8_t *)p_reg + 0x500UL) = 9;
    }
#endif
    nrf_twim_disable(p_reg);
    nrf_barrier_w();
}

#if NRFY_TWIM_HAS_ARRAY_LIST
/**
 * @brief Function for enabling or disabling the TX list feature.
 *
 * @param[in] p_reg  Pointer to the structure of registers of the peripheral.
 * @param[in] enable True if TX list feature is to be enabled, false otherwise.
 */
NRFY_STATIC_INLINE void nrfy_twim_tx_list_set(NRF_TWIM_Type * p_reg, bool enable)
{
    if (enable)
    {
        nrf_twim_tx_list_enable(p_reg);
    }
    else
    {
        nrf_twim_tx_list_disable(p_reg);
    }
    nrf_barrier_w();
}

/**
 * @brief Function for enabling or disabling the RX list feature.
 *
 * @param[in] p_reg  Pointer to the structure of registers of the peripheral.
 * @param[in] enable True if RX list feature is to be enabled, false otherwise.
 */
NRFY_STATIC_INLINE void nrfy_twim_rx_list_set(NRF_TWIM_Type * p_reg, bool enable)
{
    if (enable)
    {
        nrf_twim_rx_list_enable(p_reg);
    }
    else
    {
        nrf_twim_rx_list_disable(p_reg);
    }
    nrf_barrier_w();
}
#endif

/**
 * @brief Function for setting the TWIM pins configuration.
 *
 * @param[in]  p_reg  Pointer to the structure of registers of the peripheral.
 * @param[out] p_pins Pointer to the TWIM pin configurartion structure.
 */
NRFY_STATIC_INLINE void nrfy_twim_pins_set(NRF_TWIM_Type *          p_reg,
                                           nrfy_twim_pins_t const * p_pins)
{
    nrf_twim_pins_set(p_reg, p_pins->scl_pin, p_pins->sda_pin);
    nrf_barrier_w();
}

/** @refhal{nrf_twim_task_trigger} */
NRFY_STATIC_INLINE void nrfy_twim_task_trigger(NRF_TWIM_Type * p_reg,
                                               nrf_twim_task_t task)
{
    nrf_twim_task_trigger(p_reg, task);
    nrf_barrier_w();
}

/** @refhal{nrf_twim_task_address_get} */
NRFY_STATIC_INLINE uint32_t nrfy_twim_task_address_get(NRF_TWIM_Type const * p_reg,
                                                       nrf_twim_task_t       task)
{
    return nrf_twim_task_address_get(p_reg, task);
}

/** @refhal{nrf_twim_event_clear} */
NRFY_STATIC_INLINE void nrfy_twim_event_clear(NRF_TWIM_Type *  p_reg,
                                              nrf_twim_event_t event)
{
    nrf_twim_event_clear(p_reg, event);
    nrf_barrier_w();
}

/** @refhal{nrf_twim_event_check} */
NRFY_STATIC_INLINE bool nrfy_twim_event_check(NRF_TWIM_Type const * p_reg,
                                              nrf_twim_event_t      event)
{
    nrf_barrier_r();
    bool check = nrf_twim_event_check(p_reg, event);
    nrf_barrier_r();
    return check;
}

/** @refhal{nrf_twim_event_address_get} */
NRFY_STATIC_INLINE uint32_t nrfy_twim_event_address_get(NRF_TWIM_Type const * p_reg,
                                                        nrf_twim_event_t      event)
{
    return nrf_twim_event_address_get(p_reg, event);
}

/** @refhal{nrf_twim_shorts_enable} */
NRFY_STATIC_INLINE void nrfy_twim_shorts_enable(NRF_TWIM_Type * p_reg,
                                                uint32_t        mask)
{
    nrf_twim_shorts_enable(p_reg, mask);
    nrf_barrier_w();
}

/** @refhal{nrf_twim_shorts_disable} */
NRFY_STATIC_INLINE void nrfy_twim_shorts_disable(NRF_TWIM_Type * p_reg,
                                                 uint32_t        mask)
{
    nrf_twim_shorts_disable(p_reg, mask);
    nrf_barrier_w();
}

/** @refhal{nrf_twim_int_enable} */
NRFY_STATIC_INLINE void nrfy_twim_int_enable(NRF_TWIM_Type * p_reg,
                                             uint32_t        mask)
{
    nrf_twim_int_enable(p_reg, mask);
    nrf_barrier_w();
}

/** @refhal{nrf_twim_int_disable} */
NRFY_STATIC_INLINE void nrfy_twim_int_disable(NRF_TWIM_Type * p_reg,
                                              uint32_t        mask)
{
    nrf_twim_int_disable(p_reg, mask);
    nrf_barrier_w();
}

/** @refhal{nrf_twim_int_enable_check} */
NRFY_STATIC_INLINE uint32_t nrfy_twim_int_enable_check(NRF_TWIM_Type const * p_reg, uint32_t mask)
{
    nrf_barrier_rw();
    uint32_t check = nrf_twim_int_enable_check(p_reg, mask);
    nrf_barrier_r();
    return check;
}

#if defined(DPPI_PRESENT) || defined(__NRFX_DOXYGEN__)
/** @refhal{nrf_twim_subscribe_set} */
NRFY_STATIC_INLINE void nrfy_twim_subscribe_set(NRF_TWIM_Type * p_reg,
                                                nrf_twim_task_t task,
                                                uint8_t         channel)
{
    nrf_twim_subscribe_set(p_reg, task, channel);
    nrf_barrier_w();
}

/** @refhal{nrf_twim_subscribe_clear} */
NRFY_STATIC_INLINE void nrfy_twim_subscribe_clear(NRF_TWIM_Type * p_reg,
                                                  nrf_twim_task_t task)
{
    nrf_twim_subscribe_clear(p_reg, task);
    nrf_barrier_w();

}

/** @refhal{nrf_twim_publish_set} */
NRFY_STATIC_INLINE void nrfy_twim_publish_set(NRF_TWIM_Type *  p_reg,
                                              nrf_twim_event_t event,
                                              uint8_t          channel)
{
    nrf_twim_publish_set(p_reg, event, channel);
    nrf_barrier_w();
}

/** @refhal{nrf_twim_publish_clear} */
NRFY_STATIC_INLINE void nrfy_twim_publish_clear(NRF_TWIM_Type *  p_reg,
                                                nrf_twim_event_t event)
{
    nrf_twim_publish_clear(p_reg, event);
    nrf_barrier_w();
}
#endif // defined(DPPI_PRESENT) || defined(__NRFX_DOXYGEN__)

/** @refhal{nrf_twim_enable} */
NRFY_STATIC_INLINE void nrfy_twim_enable(NRF_TWIM_Type * p_reg)
{
    nrf_twim_enable(p_reg);
    nrf_barrier_w();
}

/** @refhal{nrf_twim_disable} */
NRFY_STATIC_INLINE void nrfy_twim_disable(NRF_TWIM_Type * p_reg)
{
    nrf_twim_disable(p_reg);
    nrf_barrier_w();
}

/** @refhal{nrf_twim_scl_pin_get} */
NRFY_STATIC_INLINE uint32_t nrfy_twim_scl_pin_get(NRF_TWIM_Type const * p_reg)
{
    nrf_barrier_rw();
    uint32_t scl_pin = nrf_twim_scl_pin_get(p_reg);
    nrf_barrier_r();
    return scl_pin;
}

/** @refhal{nrf_twim_sda_pin_get} */
NRFY_STATIC_INLINE uint32_t nrfy_twim_sda_pin_get(NRF_TWIM_Type const * p_reg)
{
    nrf_barrier_rw();
    uint32_t sda_pin = nrf_twim_sda_pin_get(p_reg);
    nrf_barrier_r();
    return sda_pin;
}

/** @refhal{nrf_twim_frequency_set} */
NRFY_STATIC_INLINE void nrfy_twim_frequency_set(NRF_TWIM_Type *      p_reg,
                                                nrf_twim_frequency_t frequency)
{
    nrf_twim_frequency_set(p_reg, frequency);
    nrf_barrier_w();
}

/** @refhal{nrf_twim_errorsrc_get_and_clear} */
NRFY_STATIC_INLINE uint32_t nrfy_twim_errorsrc_get_and_clear(NRF_TWIM_Type * p_reg)
{
    nrf_barrier_rw();
    uint32_t errorsrc = nrf_twim_errorsrc_get_and_clear(p_reg);
    nrf_barrier_rw();
    return errorsrc;
}

/** @refhal{nrf_twim_address_set} */
NRFY_STATIC_INLINE void nrfy_twim_address_set(NRF_TWIM_Type * p_reg,
                                              uint8_t         address)
{
    nrf_twim_address_set(p_reg, address);
    nrf_barrier_w();
}

/** @refhal{nrf_twim_shorts_set} */
NRFY_STATIC_INLINE void nrfy_twim_shorts_set(NRF_TWIM_Type * p_reg,
                                             uint32_t        mask)
{
    nrf_twim_shorts_set(p_reg, mask);
    nrf_barrier_w();
}

/** @refhal{nrf_twim_shorts_get} */
NRFY_STATIC_INLINE uint32_t nrfy_twim_shorts_get(NRF_TWIM_Type const * p_reg)
{
    nrf_barrier_rw();
    uint32_t shorts = nrf_twim_shorts_get(p_reg);
    nrf_barrier_r();
    return shorts;
}

/** @refhal{nrf_twim_txd_amount_get} */
NRFY_STATIC_INLINE size_t nrfy_twim_txd_amount_get(NRF_TWIM_Type const * p_reg)
{
    nrf_barrier_r();
    size_t amount = nrf_twim_txd_amount_get(p_reg);
    nrf_barrier_r();
    return amount;
}

/** @refhal{nrf_twim_rxd_amount_get} */
NRFY_STATIC_INLINE size_t nrfy_twim_rxd_amount_get(NRF_TWIM_Type const * p_reg)
{
    nrf_barrier_r();
    size_t amount = nrf_twim_rxd_amount_get(p_reg);
    nrf_barrier_r();
    return amount;
}

#if NRFY_TWIM_HAS_ARRAY_LIST
/** @refhal{nrf_twim_tx_list_enable} */
NRFY_STATIC_INLINE void nrfy_twim_tx_list_enable(NRF_TWIM_Type * p_reg)
{
    nrf_twim_tx_list_enable(p_reg);
    nrf_barrier_w();
}

/** @refhal{nrf_twim_tx_list_disable} */
NRFY_STATIC_INLINE void nrfy_twim_tx_list_disable(NRF_TWIM_Type * p_reg)
{
    nrf_twim_tx_list_disable(p_reg);
    nrf_barrier_w();
}

/** @refhal{nrf_twim_rx_list_enable} */
NRFY_STATIC_INLINE void nrfy_twim_rx_list_enable(NRF_TWIM_Type * p_reg)
{
    nrf_twim_rx_list_enable(p_reg);
    nrf_barrier_w();
}

/** @refhal{nrf_twim_rx_list_disable} */
NRFY_STATIC_INLINE void nrfy_twim_rx_list_disable(NRF_TWIM_Type * p_reg)
{
    nrf_twim_rx_list_disable(p_reg);
    nrf_barrier_w();
}
#endif

/** @} */

NRFY_STATIC_INLINE void __nrfy_internal_twim_event_enabled_clear(NRF_TWIM_Type *  p_reg,
                                                                 uint32_t         mask,
                                                                 nrf_twim_event_t event)
{
    if (mask & NRFY_EVENT_TO_INT_BITMASK(event))
    {
        nrf_twim_event_clear(p_reg, event);
    }
}

NRFY_STATIC_INLINE bool __nrfy_internal_twim_event_handle(NRF_TWIM_Type *  p_reg,
                                                          uint32_t         mask,
                                                          nrf_twim_event_t event,
                                                          uint32_t *       p_evt_mask)
{
    if ((mask & NRFY_EVENT_TO_INT_BITMASK(event)) && nrf_twim_event_check(p_reg, event))
    {
        nrf_twim_event_clear(p_reg, event);
        if (p_evt_mask)
        {
            *p_evt_mask |= NRFY_EVENT_TO_INT_BITMASK(event);
        }
        return true;
    }
    return false;
}

NRFY_STATIC_INLINE
uint32_t __nrfy_internal_twim_events_process(NRF_TWIM_Type *               p_reg,
                                             uint32_t                      mask,
                                             nrfy_twim_xfer_desc_t const * p_xfer)
{
    uint32_t evt_mask = 0;

    nrf_barrier_r();
    (void)__nrfy_internal_twim_event_handle(p_reg, mask, NRF_TWIM_EVENT_SUSPENDED, &evt_mask);
    (void)__nrfy_internal_twim_event_handle(p_reg, mask, NRF_TWIM_EVENT_STOPPED, &evt_mask);
    (void)__nrfy_internal_twim_event_handle(p_reg, mask, NRF_TWIM_EVENT_ERROR, &evt_mask);
    (void)__nrfy_internal_twim_event_handle(p_reg, mask, NRF_TWIM_EVENT_TXSTARTED, &evt_mask);
    (void)__nrfy_internal_twim_event_handle(p_reg, mask, NRF_TWIM_EVENT_RXSTARTED, &evt_mask);
    (void)__nrfy_internal_twim_event_handle(p_reg, mask, NRF_TWIM_EVENT_LASTTX, &evt_mask);
    (void)__nrfy_internal_twim_event_handle(p_reg, mask, NRF_TWIM_EVENT_LASTRX, &evt_mask);

    if (p_xfer && (mask & NRFY_EVENT_TO_INT_BITMASK(NRF_TWIM_EVENT_STOPPED)))
    {
        NRFY_CACHE_INV(p_xfer->p_buffer, p_xfer->length);
    }
    else if (p_xfer && (mask & NRFY_EVENT_TO_INT_BITMASK(NRF_TWIM_EVENT_LASTRX)))
    {
        NRFY_CACHE_INV(p_xfer->p_buffer, p_xfer->length);
    }
    nrf_barrier_w();
    return evt_mask;
}

#ifdef __cplusplus
}
#endif

#endif // NRFY_TWIM_H__
