Newer
Older
mbed-os / targets / TARGET_ARM_SSG / TARGET_MUSCA_S1 / device / drivers / i2c_ip6510_drv.c
@Gabor Toth Gabor Toth on 10 Sep 2020 42 KB Add platform support to Musca S1
/*
 * Copyright (c) 2018-2020 Arm Limited
 *
 * SPDX-License-Identifier: Apache-2.0
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

/**
 * \file i2c_ip6510_drv.c
 * \brief Driver for IP6510 I2C controller.
 */

#include "i2c_ip6510_drv.h"

#include <stdint.h>
#include <stdbool.h>
#include "cmsis_compiler.h"

/*******************************************************************************
 * Cadence IP6510 I2C device specific definitions based on:
 *      I2C Controller IP User Guide
 *      Part Number:            IP6510
 *      IP Version Number:      r115_f01
 *      User Guide Revision:    1.20
 *
 ******************************************************************************/

/** Setter bit manipulation macro */
#define SET_BIT(WORD, BIT_INDEX) ((WORD) |= (1U << (BIT_INDEX)))
/** Clearing bit manipulation macro */
#define CLR_BIT(WORD, BIT_INDEX) ((WORD) &= ~(1U << (BIT_INDEX)))
/** Getter bit manipulation macro */
#define GET_BIT(WORD, BIT_INDEX) (bool)(((WORD) & (1U << (BIT_INDEX))))
/** Clear-and-Set bit manipulation macro */
#define ASSIGN_BIT(WORD, BIT_INDEX, VALUE) \
            (WORD = ((WORD & ~(1U << (BIT_INDEX))) | (VALUE << (BIT_INDEX))))

/** Getter bit-field manipulation macro */
#define GET_BIT_FIELD(WORD, BIT_MASK, BIT_OFFSET) \
            ((WORD & BIT_MASK) >> BIT_OFFSET)

/** Clear-and-Set bit-field manipulation macro */
#define ASSIGN_BIT_FIELD(WORD, BIT_MASK, BIT_OFFSET, VALUE) \
            (WORD = ((WORD & ~(BIT_MASK)) | ((VALUE << BIT_OFFSET) & BIT_MASK)))

#define BITMASK(width) ((1u<<(width))-1)

/**
 * \brief I2C (IP6510) register map structure
 */
struct i2c_ip6510_reg_map_t {
    volatile uint32_t control_reg;
        /*!< Offset: 0x00 (R/W) Control Register */
    volatile uint32_t status_reg;
        /*!< Offset: 0x04 (R/ ) Status Register */
    volatile uint32_t address_reg;
        /*!< Offset: 0x08 (R/W) I2C Address Register */
    volatile uint32_t data_reg;
        /*!< Offset: 0x0C (R/W) I2C Data Register */
    volatile uint32_t irq_status_reg;
        /*!< Offset: 0x10 (R/ ) Interrupt Status Register */
    volatile uint32_t transfer_size_reg;
        /*!< Offset: 0x14 (R/W) Transfer Size Register */
    volatile uint32_t slave_monitor_pause_reg;
        /*!< Offset: 0x18 (R/W) Slave Monitor Pause Register */
    volatile uint32_t time_out_reg;
        /*!< Offset: 0x1C (R/W) Time Out Register */
    volatile uint32_t irq_mask_reg;
        /*!< Offset: 0x20 (R/ ) Interrupt Mask Register */
    volatile uint32_t irq_enable_reg;
        /*!< Offset: 0x24 ( /W) Interrupt Enable Register */
    volatile uint32_t irq_disable_reg;
        /*!< Offset: 0x28 ( /W) Interrupt Disable Register */
    volatile uint32_t glitch_filter_control_reg;
        /*!< Offset: 0x2C (R/W) Glitch Filter Control Register */
};

#define I2C_IP6510_I2CCR_RW_OFF         (0u)
        /*!< Control Register Read/Write bit field offset */
#define I2C_IP6510_I2CCR_MS_OFF         (1u)
        /*!< Control Register Master/Slave bit field offset */
#define I2C_IP6510_I2CCR_NEA_OFF        (2u)
        /*!< Control Register Normal/Extended address bit field offset */
#define I2C_IP6510_I2CCR_ACKEN_OFF      (3u)
        /*!< Control Register Acknowledge Enable bit field offset */
#define I2C_IP6510_I2CCR_HOLD_OFF       (4u)
        /*!< Control Register Hold mode bit field offset */
#define I2C_IP6510_I2CCR_SLVMON_OFF     (5u)
        /*!< Control Register Slave Monitor mode bit field offset */
#define I2C_IP6510_I2CCR_CLRFIFO_OFF    (6u)
        /*!< Control Register Clear FIFO bit field offset */
#define I2C_IP6510_I2CCR_DIV_B_OFF      (8u)
        /*!< Control Register Divisor B bit field offset */
#define I2C_IP6510_I2CCR_DIV_A_OFF      (14u)
        /*!< Control Register Divisor A bit field offset */

#define I2C_IP6510_I2CSR_RXRW_OFF       (3u)
        /*!< Status Register RX read/write flag bit field offset*/
#define I2C_IP6510_I2CSR_RXDV_OFF       (5u)
        /*!< Status Register Receiver Data Valid bit field offset */
#define I2C_IP6510_I2CSR_TXDV_OFF       (6u)
        /*!< Status Register Transmitter Data Valid bit field offset */
#define I2C_IP6510_I2CSR_BA_OFF         (8u)
        /*!< Status Register Bus Active bit field offset */


/* Bit mask to determine the I2C address mode, if at least one of the upper
 * 3bits (from 10bits) is used (set to 1) than it is a 10bit I2C address */
#define I2C_10BIT_ADDR_MASK                 (0x0380u)

/* Valid range of I2C addresses based on specification */
#define I2C_7BIT_ADDR_LOWEST                (0x8u)
#define I2C_10BIT_ADDR_HIGHEST              (0x3FFu)

/* The default value is based on the Cadence I2C IP User Guide */
#define I2C_TIMEOUT_REG_DEFAULT_VALUE       (0x001Fu)

/* The frequency of the inner clock_enable signal is 22 times faster than the
 * required SCL output frequency */
#define I2C_SPEED_100KHZ_CLOCK_EN_FREQ      (2200000u)
#define I2C_SPEED_400KHZ_CLOCK_EN_FREQ      (8800000u)

/* In case of slave mode with FIFO implemented, to support the minimum
 * SCL period specification for fast-mode (400kHz), the device must be
 * programmed to run at higher frequency. The value is based on the
 * Cadence I2C IP User Guide and the clock divisor calculation method */
#define I2C_SPEED_MAX_SUPP_CLOCK_EN_FREQ    (13200000u)

/* Mask for Divisor A field in the Control Register */
#define I2C_IP6510_I2CCR_DIV_A_WIDTH    (2u)
#define I2C_IP6510_I2CCR_DIV_A_MASK     (BITMASK(I2C_IP6510_I2CCR_DIV_A_WIDTH) \
                                        << I2C_IP6510_I2CCR_DIV_A_OFF)

/* Mask for Divisor B field in the Control Register */
#define I2C_IP6510_I2CCR_DIV_B_WIDTH    (6u)
#define I2C_IP6510_I2CCR_DIV_B_MASK     (BITMASK(I2C_IP6510_I2CCR_DIV_B_WIDTH) \
                                        << I2C_IP6510_I2CCR_DIV_B_OFF)

/* The maximum value of stage A clock divider is 3.*/
#define I2C_IP6510_DIVISOR_A_MAX_VALUE      (3u)

/* The maximum value of stage B clock divider is 63.*/
#define I2C_IP6510_DIVISOR_B_MAX_VALUE      (63u)

/* The maximum number of received bytes in one (not combined) transfer
 * is 0xF (due to Transfer Size Register constraints). */
#define I2C_RECEIVED_BYTE_MAX_NUMBER        (15u)


/**
 * \brief Sets the direction of the I2C transfer.
 *
 * \param[in] p_i2c         I2C (IP6510) register map structure.
 *                          \ref i2c_ip6510_reg_map_t
 * \param[in] transf_dir    Required direction of the I2C transfer.
 *                          \ref i2c_ip6510_transf_dir_t
 */
 static void set_transfer_dir(struct i2c_ip6510_reg_map_t *p_i2c,
                                enum i2c_ip6510_transf_dir_t transf_dir)
{
    if(transf_dir == I2C_IP6510_TRANSMITTER) {
        CLR_BIT(p_i2c->control_reg, I2C_IP6510_I2CCR_RW_OFF);

    } else {
        /* I2C_IP6510_RECEIVER */
        SET_BIT(p_i2c->control_reg, I2C_IP6510_I2CCR_RW_OFF);
    }
}

/**
 * \brief Determines and sets the I2C addressing mode based on the address.
 *
 * \param[in] p_i2c         I2C (IP6510) register map structure.
 *                          \ref i2c_ip6510_reg_map_t
 * \param[in] addr          I2C device address (7bits or 10bits).
 *
 * \return Returns with the determined addressing mode.
 * \ref i2c_ip6510_addr_mode_t
 */
static enum i2c_ip6510_addr_mode_t set_address_mode(
                                struct i2c_ip6510_reg_map_t *p_i2c,
                                uint16_t addr)
{
    /* If at least one of the upper 3bits (from 10bits) is used (set to 1)
     * than it is a 10bit I2C address */
    if(addr & I2C_10BIT_ADDR_MASK) {
        /* Using I2C Extended Address mode */
        CLR_BIT(p_i2c->control_reg, I2C_IP6510_I2CCR_NEA_OFF);
        return I2C_IP6510_EXT_ADDR_MODE;

    } else {
        /* Using I2C Normal Address mode */
        SET_BIT(p_i2c->control_reg, I2C_IP6510_I2CCR_NEA_OFF);
        return I2C_IP6510_NOR_ADDR_MODE;
    }
}

/**
 * \brief Enables Slave Monitor mode (in master mode only).
 *
 * \param[in] p_i2c         I2C (IP6510) register map structure.
 *                          \ref i2c_ip6510_reg_map_t
 */
static void enable_slave_monitor(struct i2c_ip6510_reg_map_t *p_i2c)
{
    SET_BIT(p_i2c->control_reg, I2C_IP6510_I2CCR_SLVMON_OFF);
}

/**
 * \brief Disables Slave Monitor mode (in master mode only).
 *
 * \param[in] p_i2c         I2C (IP6510) register map structure.
 *                          \ref i2c_ip6510_reg_map_t
 */
static void disable_slave_monitor(struct i2c_ip6510_reg_map_t *p_i2c)
{
    CLR_BIT(p_i2c->control_reg, I2C_IP6510_I2CCR_SLVMON_OFF);
}

/**
 * \brief Check if Slave Monitor mode is enabled.
 *
 * \param[in] p_i2c         I2C (IP6510) register map structure.
 *                          \ref i2c_ip6510_reg_map_t
 *
 * \return Returns bool, true if Slave Monitor is enabled, false otherwise
 */
static bool is_slave_monitor_enabled(const struct i2c_ip6510_reg_map_t *p_i2c)
{
    return GET_BIT(p_i2c->control_reg, I2C_IP6510_I2CCR_SLVMON_OFF);
}

/**
 * \brief Configures the clock dividers (A and B) with the given values.
 *
 * \param[in] p_i2c         I2C (IP6510) register map structure.
 *                          \ref i2c_ip6510_reg_map_t
 * \param[in] div_a         Value of divisor stage A.
 * \param[in] div_b         Value of divisor stage B.
 */
static void set_divisor(struct i2c_ip6510_reg_map_t *p_i2c,
                                uint32_t div_a, uint32_t div_b)
{
    ASSIGN_BIT_FIELD(p_i2c->control_reg,
                     I2C_IP6510_I2CCR_DIV_A_MASK,
                     I2C_IP6510_I2CCR_DIV_A_OFF,
                     div_a);

    ASSIGN_BIT_FIELD(p_i2c->control_reg,
                     I2C_IP6510_I2CCR_DIV_B_MASK,
                     I2C_IP6510_I2CCR_DIV_B_OFF,
                     div_b);
}

/**
 * \brief Enables transmission of ACK.
 *
 * \param[in] p_i2c         I2C (IP6510) register map structure.
 *                          \ref i2c_ip6510_reg_map_t
 */
static void ack_enable(struct i2c_ip6510_reg_map_t *p_i2c)
{
    SET_BIT(p_i2c->control_reg, I2C_IP6510_I2CCR_ACKEN_OFF);
}

/**
 * \brief Disables transmission of ACK.
 *
 * \param[in] p_i2c         I2C (IP6510) register map structure.
 *                          \ref i2c_ip6510_reg_map_t
 */
static void ack_disable(struct i2c_ip6510_reg_map_t *p_i2c)
{
    CLR_BIT(p_i2c->control_reg, I2C_IP6510_I2CCR_ACKEN_OFF);
}

/**
 * \brief Enables Hold mode.
 *
 * \param[in] p_i2c         I2C (IP6510) register map structure.
 *                          \ref i2c_ip6510_reg_map_t
 */
static void hold_enable(struct i2c_ip6510_reg_map_t *p_i2c)
{
    SET_BIT(p_i2c->control_reg, I2C_IP6510_I2CCR_HOLD_OFF);
}

/**
 * \brief Disables Hold mode.
 *
 * \param[in] p_i2c         I2C (IP6510) register map structure.
 *                          \ref i2c_ip6510_reg_map_t
 */
static void hold_disable(struct i2c_ip6510_reg_map_t *p_i2c)
{
    CLR_BIT(p_i2c->control_reg, I2C_IP6510_I2CCR_HOLD_OFF);
}

/**
 * \brief Clears FIFO (and clears the Transfer Size register).
 *
 * \param[in] p_i2c         I2C (IP6510) register map structure.
 *                          \ref i2c_ip6510_reg_map_t
 */
static void clr_fifo(struct i2c_ip6510_reg_map_t *p_i2c)
{
    __DMB();
    SET_BIT(p_i2c->control_reg, I2C_IP6510_I2CCR_CLRFIFO_OFF);
    __DSB();
}

/**
 * \brief Clears the specified I2C interrupts.
 *
 * \param[in] p_i2c     I2C (IP6510) register map structure.
 *                      \ref i2c_ip6510_reg_map_t
 * \param[in] mask      Bit mask for clearing interrupts. Values from
 *                      \ref i2c_ip6510_intr_t could be used to create the mask.
 */
static void clr_irq(struct i2c_ip6510_reg_map_t *p_i2c, uint32_t mask)
{
    p_i2c->irq_status_reg = mask;
}

/**
 * \brief Configures the address register with the given address.
 *
 * \param[in] p_i2c     I2C (IP6510) register map structure.
 *                      \ref i2c_ip6510_reg_map_t
 * \param[in] addr      I2C device address (7bits or 10bits).
 */
static void set_address(struct i2c_ip6510_reg_map_t *p_i2c, uint16_t addr)
{
    __DMB();
    p_i2c->address_reg = (uint32_t)addr;
    __DSB();
}

/**
 * \brief Checks if new and valid data is available to be read.
 *
 * \param[in] p_i2c     I2C (IP6510) register map structure.
 *                      \ref i2c_ip6510_reg_map_t
 *
 * \return Returns bool, true if received data is available, false otherwise.
 */
static bool is_rx_data_ready(const struct i2c_ip6510_reg_map_t *p_i2c)
{
    return GET_BIT(p_i2c->status_reg, I2C_IP6510_I2CSR_RXDV_OFF);
}

/**
 * \brief Checks if there is still data to be transmitted.
 *
 * \param[in] p_i2c     I2C (IP6510) register map structure.
 *                      \ref i2c_ip6510_reg_map_t
 *
 * \return  Returns bool, true if there is still data to be transmitted,
 *          false otherwise.
 */
static bool is_tx_data_waiting(const struct i2c_ip6510_reg_map_t *p_i2c)
{
    return GET_BIT(p_i2c->status_reg, I2C_IP6510_I2CSR_TXDV_OFF);
}

/**
 * \brief Reads a data byte from the FIFO.
 *
 * \param[in] p_i2c     I2C (IP6510) register map structure.
 *                      \ref i2c_ip6510_reg_map_t
 *
 * \return Returns the received data byte.
 */
static uint8_t rx_byte(const struct i2c_ip6510_reg_map_t *p_i2c)
{
    return (uint8_t)(p_i2c->data_reg);
}

/**
 * \brief Writes a data byte into the FIFO to be transmitted.
 *
 * \param[in] p_i2c     I2C (IP6510) register map structure.
 *                      \ref i2c_ip6510_reg_map_t
 * \param[in] data      Data byte to be transmitted.
 */
static void tx_byte(struct i2c_ip6510_reg_map_t *p_i2c, uint8_t data)
{
    __DMB();
    p_i2c->data_reg = (uint32_t)data;
    __DSB();
}

/**
 * \brief Checks if I2C transfer is successful.
 *
 * \param[in] p_i2c         I2C (IP6510) register map structure.
 *                          \ref i2c_ip6510_reg_map_t
 *
 * \return Returns bool, true if transfer is successful, false otherwise.
 */
static bool is_transfer_complete(const struct i2c_ip6510_reg_map_t *p_i2c)
{
    return GET_BIT(p_i2c->irq_status_reg, I2C_IP6510_INTR_COMP_OFF);
}

/**
 * \brief Clears the complete interrupt status register.
 *
 * \param[in] p_i2c         I2C (IP6510) register map structure.
 *                          \ref i2c_ip6510_reg_map_t
 */
static void clear_irq_status_reg(struct i2c_ip6510_reg_map_t *p_i2c)
{
    p_i2c->irq_status_reg = I2C_IP6510_ALL_INTR_MASK;
}

/**
 * \brief Sets the Transfer Size register with the given value.
 *
 * \param[in] p_i2c         I2C (IP6510) register map structure.
 *                          \ref i2c_ip6510_reg_map_t
 * \param[in] byte_num      New Transfer Size register value.
 */
static void set_transfer_size(struct i2c_ip6510_reg_map_t *p_i2c,
                              uint32_t byte_num)
{
    p_i2c->transfer_size_reg = byte_num;
}

/**
 * \brief Returns the value of the Transfer Size register.
 *
 * \param[in] p_i2c         I2C (IP6510) register map structure.
 *                          \ref i2c_ip6510_reg_map_t
 */
static uint32_t get_transfer_size(struct i2c_ip6510_reg_map_t *p_i2c)
{
    return p_i2c->transfer_size_reg;
}

/**
 * \brief Writes the default values into the registers where it is applicable.
 *
 * \param[in] p_i2c         I2C (IP6510) register map structure.
 *                          \ref i2c_ip6510_reg_map_t
 */
static void reset_regs(struct i2c_ip6510_reg_map_t *p_i2c)
{
    p_i2c->control_reg                  = 0U;
    p_i2c->address_reg                  = 0U;
    p_i2c->transfer_size_reg            = 0U;
    p_i2c->slave_monitor_pause_reg      = 0U;
    p_i2c->time_out_reg                 = I2C_TIMEOUT_REG_DEFAULT_VALUE;
    p_i2c->glitch_filter_control_reg    = 0U;
}

/**
 * \brief Sets the I2C interface mode.
 *
 * \param[in] dev       I2C (IP6510) device structure \ref i2c_ip6510_dev_t
 * \param[in] mode      I2C interface mode \ref i2c_ip6510_device_mode_t
 *
 * \note This function doesn't check if dev is NULL.
 */
static void set_device_mode(struct i2c_ip6510_dev_t *dev,
                                enum i2c_ip6510_device_mode_t mode)
{
    struct i2c_ip6510_reg_map_t *p_i2c =
            (struct i2c_ip6510_reg_map_t*)dev->cfg->base;

    ASSIGN_BIT(p_i2c->control_reg, I2C_IP6510_I2CCR_MS_OFF, mode);

    dev->data->mode = mode;
}

enum i2c_ip6510_error_t i2c_ip6510_init(struct i2c_ip6510_dev_t *dev,
                                        uint32_t sys_clk)
{
    enum i2c_ip6510_error_t err = I2C_IP6510_ERR_NONE;

    struct i2c_ip6510_reg_map_t *p_i2c =
            (struct i2c_ip6510_reg_map_t*)dev->cfg->base;

    if(dev->data->state != I2C_IP6510_UNINITIALIZED) {
        return I2C_IP6510_ERR_ALREADY_INIT;
    }

    if(sys_clk == 0U) {
        return I2C_IP6510_ERR_INVALID_ARG;
    }

    i2c_ip6510_disable_irq(dev, I2C_IP6510_ALL_INTR_MASK);

    clear_irq_status_reg(p_i2c);
    clr_fifo(p_i2c);

    /* Updating the system clock frequency */
    dev->data->sys_clk = sys_clk;

    set_device_mode(dev, dev->cfg->default_mode);
    err = i2c_ip6510_set_speed(dev, dev->cfg->default_bus_speed);

    if(err != I2C_IP6510_ERR_NONE) {
        return err;
    }

    dev->data->state = I2C_IP6510_INITIALIZED;

    return I2C_IP6510_ERR_NONE;
}

void i2c_ip6510_uninit(struct i2c_ip6510_dev_t *dev)
{
    struct i2c_ip6510_reg_map_t *p_i2c =
                (struct i2c_ip6510_reg_map_t*)dev->cfg->base;

    i2c_ip6510_disable_irq(dev, I2C_IP6510_ALL_INTR_MASK);

    clear_irq_status_reg(p_i2c);

    reset_regs(p_i2c);

    clr_fifo(p_i2c);

    dev->data->state = I2C_IP6510_UNINITIALIZED;
}

void i2c_ip6510_hold_enable(const struct i2c_ip6510_dev_t *dev)
{
    struct i2c_ip6510_reg_map_t *p_i2c =
                    (struct i2c_ip6510_reg_map_t*)dev->cfg->base;
    hold_enable(p_i2c);
}

void i2c_ip6510_hold_disable(const struct i2c_ip6510_dev_t *dev)
{
    struct i2c_ip6510_reg_map_t *p_i2c =
                     (struct i2c_ip6510_reg_map_t*)dev->cfg->base;
    hold_disable(p_i2c);
}

enum i2c_ip6510_state_t i2c_ip6510_get_state(const struct i2c_ip6510_dev_t *dev)
{
    return dev->data->state;
}

enum i2c_ip6510_device_mode_t i2c_ip6510_get_device_mode(
                                const struct i2c_ip6510_dev_t *dev)
{
    return dev->data->mode;
}

enum i2c_ip6510_error_t i2c_ip6510_set_speed(struct i2c_ip6510_dev_t *dev,
                                enum i2c_ip6510_speed_t speed)
{
    uint32_t div_a = 0U;
    uint32_t div_b = 0U;
    uint32_t clock_enable_freq = 0U;

    struct i2c_ip6510_reg_map_t *p_i2c =
            (struct i2c_ip6510_reg_map_t*)dev->cfg->base;

    if(dev->data->sys_clk == 0U) {
        /* Invalid system clock frequency */
        return I2C_IP6510_ERR;
    }

    /* The required frequency of SCL output is 22 times slower than the
     * frequency of the inner clock_enable signal. */
    switch(speed) {

    case I2C_IP6510_SPEED_100KHZ:
        clock_enable_freq = I2C_SPEED_100KHZ_CLOCK_EN_FREQ;
        dev->data->bus_speed = I2C_IP6510_SPEED_100KHZ;
        break;

    case I2C_IP6510_SPEED_400KHZ:
        clock_enable_freq = I2C_SPEED_400KHZ_CLOCK_EN_FREQ;
        dev->data->bus_speed = I2C_IP6510_SPEED_400KHZ;
        break;

    case I2C_IP6510_SPEED_MAX_SUPPORTED:
        /* In case of slave mode with FIFO implemented, to support the minimum
         * SCL period specification for fast-mode (400kHz), the device must be
         * programmed to run at higher frequency. */
        clock_enable_freq = I2C_SPEED_MAX_SUPP_CLOCK_EN_FREQ;
        dev->data->bus_speed = I2C_IP6510_SPEED_MAX_SUPPORTED;
        break;

    default:
        return I2C_IP6510_ERR_INVALID_ARG;
    }

    /* Calculating the value of stage A and B clock dividers */
    div_a = 0U;
    div_b = (uint32_t)(dev->data->sys_clk / clock_enable_freq);

    if(div_b > I2C_IP6510_DIVISOR_B_MAX_VALUE) {

        /* (div_a+1)x(div_b+1) must be equal to former div_b value */
        div_a = I2C_IP6510_DIVISOR_A_MAX_VALUE;
        div_b = (uint32_t)(div_b+1U) >> I2C_IP6510_I2CCR_DIV_A_WIDTH;
    }

    set_divisor(p_i2c, div_a, div_b);

    return I2C_IP6510_ERR_NONE;
}

enum i2c_ip6510_speed_t i2c_ip6510_get_speed(const struct i2c_ip6510_dev_t *dev)
{
    return dev->data->bus_speed;
}

enum i2c_ip6510_bus_state_t i2c_ip6510_get_bus_status(
                                const struct i2c_ip6510_dev_t *dev)
{
    struct i2c_ip6510_reg_map_t *p_i2c =
            (struct i2c_ip6510_reg_map_t*)dev->cfg->base;

    return (GET_BIT(p_i2c->status_reg, I2C_IP6510_I2CSR_BA_OFF)) ?
            I2C_IP6510_BUS_ACTIVE : I2C_IP6510_BUS_INACTIVE;
}

int i2c_ip6510_get_transfer_size(const struct i2c_ip6510_dev_t *dev)
{
    struct i2c_ip6510_reg_map_t *p_i2c =
                (struct i2c_ip6510_reg_map_t*)dev->cfg->base;

    return p_i2c->transfer_size_reg;
}

uint32_t i2c_ip6510_get_irq_status(const struct i2c_ip6510_dev_t *dev)
{
    struct i2c_ip6510_reg_map_t *p_i2c =
            (struct i2c_ip6510_reg_map_t*)dev->cfg->base;

    return p_i2c->irq_status_reg;
}

uint32_t i2c_ip6510_get_irq_mask(const struct i2c_ip6510_dev_t *dev)
{
    struct i2c_ip6510_reg_map_t *p_i2c =
                (struct i2c_ip6510_reg_map_t*)dev->cfg->base;

    return p_i2c->irq_mask_reg;
}

void i2c_ip6510_clear_irq(struct i2c_ip6510_dev_t *dev, uint32_t mask)
{
    struct i2c_ip6510_reg_map_t *p_i2c =
                (struct i2c_ip6510_reg_map_t*)dev->cfg->base;

    clr_irq(p_i2c, mask);
}

void i2c_ip6510_enable_irq(struct i2c_ip6510_dev_t *dev, uint32_t mask)
{
    struct i2c_ip6510_reg_map_t *p_i2c =
                (struct i2c_ip6510_reg_map_t*)dev->cfg->base;

    p_i2c->irq_enable_reg = mask;
}

void i2c_ip6510_disable_irq(struct i2c_ip6510_dev_t *dev, uint32_t mask)
{
    struct i2c_ip6510_reg_map_t *p_i2c =
                (struct i2c_ip6510_reg_map_t*)dev->cfg->base;

    p_i2c->irq_disable_reg = mask;
}

void i2c_ip6510_set_timeout(struct i2c_ip6510_dev_t *dev,
                                uint8_t interval)
{
    struct i2c_ip6510_reg_map_t *p_i2c =
                (struct i2c_ip6510_reg_map_t*)dev->cfg->base;

    p_i2c->time_out_reg = (uint32_t)interval;
}

void i2c_ip6510_set_slave_monitor_pause_interval(struct i2c_ip6510_dev_t *dev,
                            uint8_t interval)
{
    struct i2c_ip6510_reg_map_t *p_i2c =
                (struct i2c_ip6510_reg_map_t*)dev->cfg->base;

    p_i2c->slave_monitor_pause_reg = (uint32_t)interval;
}

uint8_t i2c_ip6510_get_slave_monitor_pause_interval(
                            struct i2c_ip6510_dev_t *dev)
{
    struct i2c_ip6510_reg_map_t *p_i2c =
                (struct i2c_ip6510_reg_map_t*)dev->cfg->base;

    return (uint8_t)(p_i2c->slave_monitor_pause_reg);
}

void i2c_ip6510_set_glitch_filter_length(struct i2c_ip6510_dev_t *dev,
                                uint32_t length)
{
    struct i2c_ip6510_reg_map_t *p_i2c =
                (struct i2c_ip6510_reg_map_t*)dev->cfg->base;

    p_i2c->glitch_filter_control_reg = length;
}

uint8_t i2c_ip6510_get_glitch_filter_length(const struct i2c_ip6510_dev_t *dev)
{
    struct i2c_ip6510_reg_map_t *p_i2c =
                (struct i2c_ip6510_reg_map_t*)dev->cfg->base;

    return    (uint8_t)(p_i2c->glitch_filter_control_reg);
}

enum i2c_ip6510_error_t i2c_ip6510_monitor_slave(struct i2c_ip6510_dev_t *dev,
                                uint16_t addr)
{
    struct i2c_ip6510_reg_map_t *p_i2c =
                (struct i2c_ip6510_reg_map_t*)dev->cfg->base;

    if(i2c_ip6510_get_state(dev) != I2C_IP6510_INITIALIZED) {
        return I2C_IP6510_ERR_NOT_INIT;
    }

    clear_irq_status_reg(p_i2c);
    set_address_mode(p_i2c, addr);
    set_transfer_dir(p_i2c, I2C_IP6510_TRANSMITTER);
    enable_slave_monitor(p_i2c);

    /* Initiates slave addressing */
    set_address(p_i2c, addr);

    /* Slave Monitor Mode gets disabled if given slave is present */
    while(is_slave_monitor_enabled(p_i2c) ||
            !(GET_BIT(p_i2c->irq_status_reg, I2C_IP6510_INTR_SLVRDY_OFF))) {

        if(GET_BIT(p_i2c->irq_status_reg, I2C_IP6510_INTR_NACK_OFF)) {
            disable_slave_monitor(p_i2c);
            return I2C_IP6510_ERR_NACK;
        }
    }
    return I2C_IP6510_ERR_NONE;
}

/**
 * \brief Initiates transfer and writes the given number of data bytes to send.
 *
 * \param[in] p_i2c         I2C (IP6510) register map structure.
 *                          \ref i2c_ip6510_reg_map_t
 * \param[in] addr          I2C device address (7 bits or 10 bits).
 * \param[in] tx_data       Data buffer pointer to write.
 * \param[in] tx_length     Data buffer length (number of bytes to write).
 * \param[out] write_cntr   Number of bytes written.
 *
 * \return Returns error code as specified in \ref i2c_ip6510_error_t
 */
static enum i2c_ip6510_error_t tx_transfer(
                                struct i2c_ip6510_reg_map_t *p_i2c,
                                uint16_t addr,
                                const uint8_t *tx_data,
                                uint32_t *tx_length)
{
    uint32_t write_cntr = *tx_length;

    ack_enable(p_i2c);
    clear_irq_status_reg(p_i2c);
    set_address_mode(p_i2c, addr);
    set_transfer_dir(p_i2c, I2C_IP6510_TRANSMITTER);

    /* Initiates I2C transfer */
    set_address(p_i2c, addr);

    while(write_cntr) {

        tx_byte(p_i2c, *tx_data);

        /* If the sent data byte or slave addressing is not acknowledged */
        if(GET_BIT(p_i2c->irq_status_reg, I2C_IP6510_INTR_NACK_OFF)) {
            *tx_length -= write_cntr;
            return I2C_IP6510_ERR_NACK;

        } else if(GET_BIT(p_i2c->irq_status_reg, I2C_IP6510_INTR_TXOVF_OFF)) {
            /* In case of FIFO overflow the last written byte is ignored and
             * the Data Register access must be repeated. */
            clr_irq(p_i2c, I2C_IP6510_INTR_TXOVF_MASK);
            continue;
        }

        tx_data++;
        write_cntr--;
    }
    *tx_length -= write_cntr;

    return I2C_IP6510_ERR_NONE;
}

enum i2c_ip6510_error_t i2c_ip6510_master_write(struct i2c_ip6510_dev_t *dev,
                                uint16_t addr,
                                const uint8_t *tx_data,
                                bool stop,
                                uint32_t *tx_length)
{
    enum i2c_ip6510_error_t err = I2C_IP6510_ERR_NONE;

    struct i2c_ip6510_reg_map_t *p_i2c =
            (struct i2c_ip6510_reg_map_t*)dev->cfg->base;

    if(*tx_length == 0U) {
        return I2C_IP6510_ERR_INVALID_ARG;
    }

    if(i2c_ip6510_get_state(dev) != I2C_IP6510_INITIALIZED) {
        return I2C_IP6510_ERR_NOT_INIT;
    }

    clr_fifo(p_i2c);

    /* Prevents the I2C interface from generating a STOP condition */
    hold_enable(p_i2c);

    /* Initiates transfer and transmits the given number of data bytes */
    err = tx_transfer(p_i2c, addr, tx_data, tx_length);

    if(err != I2C_IP6510_ERR_NONE) {
        hold_disable(p_i2c);

        return err;
    }

    while(!is_transfer_complete(p_i2c)) {

        /* If the last transmitted byte did not get an ACK */
        if(GET_BIT(p_i2c->irq_status_reg, I2C_IP6510_INTR_NACK_OFF)) {
            return I2C_IP6510_ERR_NACK;
        }
    }

    /* If enabled, the host can terminate the transfer with disabled hold mode
     */
    if (stop) {
        hold_disable(p_i2c);
    }

    return I2C_IP6510_ERR_NONE;
}

/**
 * \brief Initiates transfer and reads the received data bytes.
 *
 * \param[in] p_i2c         I2C (IP6510) register map structure.
 *                          \ref i2c_ip6510_reg_map_t
 * \param[in] addr          I2C device address (7 bits or 10 bits).
 * \param[in] rx_data       Buffer pointer to store the read data.
 * \param[in] rx_length     Buffer length (number of bytes to read).
 * \param[out] read_cntr    Number of bytes read.
 *
 * \return Returns error code as specified in \ref i2c_ip6510_error_t
 */
static enum i2c_ip6510_error_t rx_transfer_combined(
                                struct i2c_ip6510_reg_map_t *p_i2c,
                                uint16_t addr,
                                uint8_t *rx_data,
                                uint32_t *rx_length)
{
    uint32_t read_cntr = *rx_length;
    uint32_t part_rx_length = (read_cntr > I2C_RECEIVED_BYTE_MAX_NUMBER) ?
                                    I2C_RECEIVED_BYTE_MAX_NUMBER : read_cntr;

    /* The I2C controller handles the Acknowledge generation. And Acknowledge
     * has to be enabled. */
    ack_enable(p_i2c);
    clear_irq_status_reg(p_i2c);
    set_address_mode(p_i2c, addr);
    set_transfer_dir(p_i2c, I2C_IP6510_RECEIVER);

    /* Initiates I2C transfer */
    set_address(p_i2c, addr);
    set_transfer_size(p_i2c, part_rx_length);

    while(read_cntr) {

        /* The maximum number of received bytes in one transfer is limited. If
         * the read length is more than the maximum number of bytes that can be
         * received in one transfer, the transfer size register has to be
         * adjusted before reaching 0. If the transfer size register reaches
         * zero, the I2C controller terminates the transfer before completion.
         */
        if (get_transfer_size(p_i2c) == 1U) {
            part_rx_length = (read_cntr > I2C_RECEIVED_BYTE_MAX_NUMBER) ?
                                    I2C_RECEIVED_BYTE_MAX_NUMBER : read_cntr;

            set_transfer_size(p_i2c, part_rx_length);
        }

        /* Waiting for new valid data to read */
        while(!is_rx_data_ready(p_i2c)) {

            /* If the previous slave addressing is not acknowledged */
            if(GET_BIT(p_i2c->irq_status_reg, I2C_IP6510_INTR_NACK_OFF)) {
                *rx_length -= read_cntr;
                return I2C_IP6510_ERR_NACK;

            } else if(GET_BIT(p_i2c->irq_status_reg,
                      I2C_IP6510_INTR_RXOVF_OFF)) {

                *rx_length -= read_cntr;
                /* In case of Receive Overflow return with error code */
                return I2C_IP6510_ERR_RXOVF;
            }
        }

        *rx_data = rx_byte(p_i2c);

        rx_data++;
        read_cntr--;
    }
    *rx_length -= read_cntr;

    return I2C_IP6510_ERR_NONE;
}

enum i2c_ip6510_error_t i2c_ip6510_master_read(struct i2c_ip6510_dev_t *dev,
                                uint16_t addr,
                                uint8_t *rx_data,
                                bool stop,
                                uint32_t *rx_length)
{
    enum i2c_ip6510_error_t err = I2C_IP6510_ERR_NONE;

    struct i2c_ip6510_reg_map_t *p_i2c =
            (struct i2c_ip6510_reg_map_t*)dev->cfg->base;

    if(*rx_length == 0U) {
        return I2C_IP6510_ERR_INVALID_ARG;
    }

    if(i2c_ip6510_get_state(dev) != I2C_IP6510_INITIALIZED) {
        return I2C_IP6510_ERR_NOT_INIT;
    }

    clr_fifo(p_i2c);

    /* Receive overflow can be avoided and combined read transfer can be
     * realized if Hold Mode is enabled */
    hold_enable(p_i2c);

    /* It reads the given number of received bytes from the FIFO
     * (in a combined transfer if necessary) */
    err = rx_transfer_combined(p_i2c, addr, rx_data, rx_length);

    if(err != I2C_IP6510_ERR_NONE) {
        hold_disable(p_i2c);

        return err;
    }

    /* If enabled, the host can terminate the transfer with disabled hold mode
     */
    if (stop) {
        hold_disable(p_i2c);
    }

    /* Waiting for the whole transfer to be complete */
    while(!is_transfer_complete(p_i2c)) {
        /* If there is more, unexpected data byte received due to
         * any kind of error. */
        if(is_rx_data_ready(p_i2c)) {
            return I2C_IP6510_ERR;
        }
        if(GET_BIT(p_i2c->irq_status_reg, I2C_IP6510_INTR_RXOVF_OFF)) {
            return I2C_IP6510_ERR_RXOVF;
        }
    }

    return I2C_IP6510_ERR_NONE;
}

enum i2c_ip6510_error_t i2c_ip6510_master_write_read(
                                struct i2c_ip6510_dev_t *dev,
                                uint16_t addr,
                                const uint8_t *tx_data,
                                uint8_t *rx_data,
                                uint32_t *tx_length,
                                uint32_t *rx_length)
{
    enum i2c_ip6510_error_t err = I2C_IP6510_ERR_NONE;

    struct i2c_ip6510_reg_map_t *p_i2c =
            (struct i2c_ip6510_reg_map_t*)dev->cfg->base;

    if((*tx_length == 0U) || (*rx_length == 0U)) {
        return I2C_IP6510_ERR_INVALID_ARG;
    }

    if(i2c_ip6510_get_state(dev) != I2C_IP6510_INITIALIZED) {
        return I2C_IP6510_ERR_NOT_INIT;
    }

    /* Prevents the I2C interface from generating a STOP condition */
    hold_enable(p_i2c);

    /* Initiates transfer and transmits the given number of data bytes */
    err = tx_transfer(p_i2c, addr, tx_data, tx_length);

    if(err != I2C_IP6510_ERR_NONE) {
        hold_disable(p_i2c);
        return err;
    }

    while(!is_transfer_complete(p_i2c)) {

        /* If the last transmitted byte did not get an ACK */
        if(GET_BIT(p_i2c->irq_status_reg, I2C_IP6510_INTR_NACK_OFF)) {
            hold_disable(p_i2c);
            return I2C_IP6510_ERR_NACK;
        }
    }

    clr_irq(p_i2c, I2C_IP6510_INTR_COMP_MASK);

    /* Write transaction is complete, continue with read transaction */

    /* The host does not need to ACK only 1 byte,
     * it generates a STOP condition after reception */
    if(*rx_length == 1U) {
        ack_disable(p_i2c);
    }

    /* It reads the given number of received bytes from the FIFO
     * (in a combined transfer if necessary) */
    err = rx_transfer_combined(p_i2c, addr, rx_data, rx_length);

    if(err != I2C_IP6510_ERR_NONE) {
        hold_disable(p_i2c);
        return err;
    }

    /* The host can terminate the transfer with disabled hold mode */
    hold_disable(p_i2c);

    /* Waiting for the whole combined transfer to be complete */
    while(!is_transfer_complete(p_i2c)) {

        if(GET_BIT(p_i2c->irq_status_reg, I2C_IP6510_INTR_RXOVF_OFF)) {
            return I2C_IP6510_ERR_RXOVF;
        }
    }

    return I2C_IP6510_ERR_NONE;
}

enum i2c_ip6510_error_t i2c_ip6510_master_byte_write(
                                const struct i2c_ip6510_dev_t *dev,
                                uint16_t addr,
                                const uint8_t *tx_data,
                                bool set_addr)
{
    struct i2c_ip6510_reg_map_t *p_i2c =
            (struct i2c_ip6510_reg_map_t*)dev->cfg->base;

    if(i2c_ip6510_get_state(dev) != I2C_IP6510_INITIALIZED) {
        return I2C_IP6510_ERR_NOT_INIT;
    }

    clear_irq_status_reg(p_i2c);

    if(set_addr == true) {
        clr_fifo(p_i2c);
        set_address_mode(p_i2c, addr);
        set_transfer_dir(p_i2c, I2C_IP6510_TRANSMITTER);
    }
    ack_enable(p_i2c);

    /* Write the data byte to the FIFO */
    tx_byte(p_i2c, *tx_data);

    /* If the first data byte was written, only then set the address */
    if(set_addr == true) {
        /* Initiates I2C transfer */
        set_address(p_i2c, addr);
    }

    /* If the sent data byte or slave addressing is not acknowledged */
    if(GET_BIT(p_i2c->irq_status_reg, I2C_IP6510_INTR_NACK_OFF)) {
        return I2C_IP6510_ERR_NACK;
    } else if(GET_BIT(p_i2c->irq_status_reg, I2C_IP6510_INTR_TXOVF_OFF)) {
        /* In case of FIFO overflow the last written byte is ignored */
        clr_irq(p_i2c, I2C_IP6510_INTR_TXOVF_MASK);
        return I2C_IP6510_ERR_TXOVF;
    }

    /* Waiting for the written byte to be transmitted */
    while(!is_transfer_complete(p_i2c)) {
        /* If the last transmitted byte did not get an ACK */
        if(GET_BIT(p_i2c->irq_status_reg, I2C_IP6510_INTR_NACK_OFF)) {
            return I2C_IP6510_ERR_NACK;
        }
    }

    return I2C_IP6510_ERR_NONE;
}

enum i2c_ip6510_error_t i2c_ip6510_master_byte_read(
                                const struct i2c_ip6510_dev_t *dev,
                                uint16_t addr,
                                bool last_byte,
                                bool set_addr,
                                uint8_t *rx_data)
{
    struct i2c_ip6510_reg_map_t *p_i2c =
            (struct i2c_ip6510_reg_map_t*)dev->cfg->base;

    if(i2c_ip6510_get_state(dev) != I2C_IP6510_INITIALIZED) {
        return I2C_IP6510_ERR_NOT_INIT;
    }

    clear_irq_status_reg(p_i2c);

    if(last_byte) {
        /* This is the last byte to receive */
        ack_disable(p_i2c);
        set_transfer_size(p_i2c, 1U);
    } else {
        /* There will be more then one data bytes */
        ack_enable(p_i2c);
        set_transfer_size(p_i2c, I2C_RECEIVED_BYTE_MAX_NUMBER);
    }

    /* If its the first read, the I2C controller has to be configured */
    if (set_addr){
        clr_fifo(p_i2c);
        set_address_mode(p_i2c, addr);
        set_transfer_dir(p_i2c, I2C_IP6510_RECEIVER);
        set_address(p_i2c, addr);
    }

    /* Waiting for new valid data to read */
    while(!is_rx_data_ready(p_i2c)) {

        /* If the previous slave addressing is not acknowledged */
        if(GET_BIT(p_i2c->irq_status_reg, I2C_IP6510_INTR_NACK_OFF)) {
            return I2C_IP6510_ERR_NACK;

        } else if(GET_BIT(p_i2c->irq_status_reg,
                  I2C_IP6510_INTR_RXOVF_OFF)) {
            /* In case of Receive Overflow return with error code */
            return I2C_IP6510_ERR_RXOVF;
        }
    }

    /* Read the data byte from the FIFO */
    *rx_data = rx_byte(p_i2c);

    return I2C_IP6510_ERR_NONE;
}

enum i2c_ip6510_error_t i2c_ip6510_set_slave_mode(
                                struct i2c_ip6510_dev_t *dev,
                                uint16_t addr)
{
    enum i2c_ip6510_error_t err = I2C_IP6510_ERR_NONE;

    struct i2c_ip6510_reg_map_t *p_i2c =
            (struct i2c_ip6510_reg_map_t*)dev->cfg->base;

    /* Checking whether the address is a valid 7/10bit I2C slave address */
    if((addr < I2C_7BIT_ADDR_LOWEST) || (addr > I2C_10BIT_ADDR_HIGHEST)) {
        return I2C_IP6510_ERR_INVALID_ARG;
    }

    if(i2c_ip6510_get_state(dev) != I2C_IP6510_INITIALIZED) {
        return I2C_IP6510_ERR_NOT_INIT;
    }

    /* I2C speed must be set to the fastest SCL frequency supported */
    err = i2c_ip6510_set_speed(dev, I2C_IP6510_SPEED_MAX_SUPPORTED);

    if(err != I2C_IP6510_ERR_NONE) {
        return err;
    }

    set_device_mode(dev, I2C_IP6510_SLAVE_MODE);
    clear_irq_status_reg(p_i2c);
    ack_enable(p_i2c);
    hold_enable(p_i2c);
    set_address_mode(p_i2c, addr);
    set_address(p_i2c, addr);

    return I2C_IP6510_ERR_NONE;
}

void i2c_ip6510_set_master_mode(struct i2c_ip6510_dev_t *dev)
{
    set_device_mode(dev, I2C_IP6510_MASTER_MODE);
}

enum i2c_ip6510_transf_dir_t i2c_ip6510_get_slave_tranf_dir(
                                struct i2c_ip6510_dev_t *dev)
{
    struct i2c_ip6510_reg_map_t *p_i2c =
            (struct i2c_ip6510_reg_map_t*)dev->cfg->base;

    return (GET_BIT(p_i2c->status_reg, I2C_IP6510_I2CSR_RXRW_OFF)) ?
            I2C_IP6510_TRANSMITTER : I2C_IP6510_RECEIVER;
}

enum i2c_ip6510_error_t i2c_ip6510_slave_write(struct i2c_ip6510_dev_t *dev,
                                const uint8_t *tx_data,
                                uint32_t *tx_length)
{
    uint32_t write_cntr = *tx_length;

    struct i2c_ip6510_reg_map_t *p_i2c =
            (struct i2c_ip6510_reg_map_t*)dev->cfg->base;

    if(write_cntr == 0U) {
        return I2C_IP6510_ERR_INVALID_ARG;
    }

    if(i2c_ip6510_get_state(dev) != I2C_IP6510_INITIALIZED) {
        return I2C_IP6510_ERR_NOT_INIT;
    }

    clr_fifo(p_i2c);
    ack_enable(p_i2c);
    hold_enable(p_i2c);

    /* The amount of data to be transferred is a system known parameter */
    while(write_cntr) {

        /* If the previously sent data byte is not acknowledged */
        if(GET_BIT(p_i2c->irq_status_reg, I2C_IP6510_INTR_NACK_OFF)) {

            hold_disable(p_i2c);
            clr_fifo(p_i2c);
            *tx_length -= write_cntr;
            return I2C_IP6510_ERR_NACK;
        }

        tx_byte(p_i2c, *tx_data);

        /* In case of FIFO overflow the last written byte is ignored and
         * the Data Register access must be repeated. */
        if(GET_BIT(p_i2c->irq_status_reg, I2C_IP6510_INTR_TXOVF_OFF)) {

            clr_irq(p_i2c, I2C_IP6510_INTR_TXOVF_MASK);
            continue;
        }

        tx_data++;
        write_cntr--;
    }
    *tx_length -= write_cntr;

    hold_disable(p_i2c);

    /* Waiting for the last byte to be transmitted from the FIFO */
    while(is_tx_data_waiting(p_i2c)) {

        /* If the transfer is terminated by the Master before completion */
        if(GET_BIT(p_i2c->irq_status_reg, I2C_IP6510_INTR_NACK_OFF) &&
                is_tx_data_waiting(p_i2c)) {

            clr_fifo(p_i2c);
            return I2C_IP6510_ERR_NACK;
        }

        /* DATA interrupt flag is set when there are only 2 bytes left in
         * the FIFO, so at this point there is still data in the FIFO to be
         * transmitted and DATA flag should be cleared for further
         * examination. */
        if(GET_BIT(p_i2c->irq_status_reg, I2C_IP6510_INTR_DATA_OFF)) {

            clr_irq(p_i2c, I2C_IP6510_INTR_DATA_MASK);
        }
    }

    clr_fifo(p_i2c);

    while(!is_transfer_complete(p_i2c)) {

        /* If DATA interrupt flag is set while the TXDV flag in the Status
         * Register is cleared the Master wants to continue the transfer. */
        if(GET_BIT(p_i2c->irq_status_reg, I2C_IP6510_INTR_DATA_OFF) &&
                !is_tx_data_waiting(p_i2c)) {

            return I2C_IP6510_ERR;
        }
    }

    return I2C_IP6510_ERR_NONE;
}

enum i2c_ip6510_error_t i2c_ip6510_slave_read(struct i2c_ip6510_dev_t *dev,
                                uint8_t *rx_data,
                                uint32_t *rx_length)
{
    uint32_t read_cntr = *rx_length;

    struct i2c_ip6510_reg_map_t *p_i2c =
            (struct i2c_ip6510_reg_map_t*)dev->cfg->base;

    if(read_cntr == 0U) {
        return I2C_IP6510_ERR_INVALID_ARG;
    }

    if(i2c_ip6510_get_state(dev) != I2C_IP6510_INITIALIZED) {
        return I2C_IP6510_ERR_NOT_INIT;
    }

    ack_enable(p_i2c);

    /* Receive overflow can be avoided if Hold Mode is enabled */
    hold_enable(p_i2c);

    while(read_cntr) {

        while(!is_rx_data_ready(p_i2c)) {

            /* With HOLD enabled the overflow conditions are avoided */
            if(GET_BIT(p_i2c->irq_status_reg, I2C_IP6510_INTR_RXOVF_OFF)) {
                hold_disable(p_i2c);
                *rx_length -= read_cntr;
                return I2C_IP6510_ERR_RXOVF;
            }
        }

        *rx_data = rx_byte(p_i2c);

        rx_data++;
        read_cntr--;
    }
    *rx_length -= read_cntr;

    hold_disable(p_i2c);

    /* Waiting for the master to terminate the transfer */
    while(!is_transfer_complete(p_i2c)) {

        if(GET_BIT(p_i2c->irq_status_reg, I2C_IP6510_INTR_RXOVF_OFF)) {
            return I2C_IP6510_ERR_RXOVF;

        } else if(is_rx_data_ready(p_i2c)) {
            /* If there are more, unexpected data bytes received from the Master
             */
            return I2C_IP6510_ERR;
        }
    }

    return I2C_IP6510_ERR_NONE;
}