LCOV - code coverage report
Current view: top level - drivers/i2c - i2c_cdns.c (source / functions) Hit Total Coverage
Test: coverage.info Lines: 244 335 72.8 %
Date: 2025-01-08 14:43:14 Functions: 21 23 91.3 %
Legend: Lines: hit not hit | Branches: + taken - not taken # not executed Branches: 76 156 48.7 %

           Branch data     Line data    Source code
       1                 :            : /*
       2                 :            :  * Copyright (c) 2024 Advanced Micro Devices, Inc.
       3                 :            :  *
       4                 :            :  * SPDX-License-Identifier: Apache-2.0
       5                 :            :  */
       6                 :            : 
       7                 :            : #include <zephyr/drivers/i2c.h>
       8                 :            : #include <zephyr/sys/util.h>
       9                 :            : #include <zephyr/logging/log.h>
      10                 :            : #include <zephyr/irq.h>
      11                 :            : 
      12                 :            : LOG_MODULE_REGISTER(i2c_cadence, CONFIG_I2C_LOG_LEVEL);
      13                 :            : 
      14                 :            : /* Register offsets for the I2C device. */
      15                 :            : #define CDNS_I2C_CR_OFFSET              0x00 /* Control Register, RW */
      16                 :            : #define CDNS_I2C_SR_OFFSET              0x04 /* Status Register, RO */
      17                 :            : #define CDNS_I2C_ADDR_OFFSET            0x08 /* I2C Address Register, RW */
      18                 :            : #define CDNS_I2C_DATA_OFFSET            0x0C /* I2C Data Register, RW */
      19                 :            : #define CDNS_I2C_ISR_OFFSET             0x10 /* IRQ Status Register, RW */
      20                 :            : #define CDNS_I2C_XFER_SIZE_OFFSET       0x14 /* Transfer Size Register, RW */
      21                 :            : #define CDNS_I2C_TIME_OUT_OFFSET        0x1C /* Time Out Register, RW */
      22                 :            : #define CDNS_I2C_IMR_OFFSET             0x20 /* IRQ Mask Register, RO */
      23                 :            : #define CDNS_I2C_IER_OFFSET             0x24 /* IRQ Enable Register, WO */
      24                 :            : #define CDNS_I2C_IDR_OFFSET             0x28 /* IRQ Disable Register, WO */
      25                 :            : 
      26                 :            : /* Control Register Bit mask definitions */
      27                 :            : #define CDNS_I2C_CR_HOLD                BIT(4) /* Hold the I2C Bus */
      28                 :            : #define CDNS_I2C_CR_ACK_EN              BIT(3) /* Enables or disables acknowledgment */
      29                 :            : #define CDNS_I2C_CR_NEA                 BIT(2) /* No Extended addressing */
      30                 :            : #define CDNS_I2C_CR_MS                  BIT(1) /* 0 = Slave Mode, 1 = Master Mode */
      31                 :            : #define CDNS_I2C_CR_RW                  BIT(0) /* Transfer Dir: 0 = Transmitter, 1 = Receiver */
      32                 :            : #define CDNS_I2C_CR_CLR_FIFO            BIT(6) /* Clears the FIFO on initialization */
      33                 :            : 
      34                 :            : /* Dividers for clock generation */
      35                 :            : #define CDNS_I2C_CR_DIVA_SHIFT          14U
      36                 :            : #define CDNS_I2C_CR_DIVA_MASK           ((uint32_t)3U << CDNS_I2C_CR_DIVA_SHIFT)
      37                 :            : #define CDNS_I2C_CR_DIVB_SHIFT          8U
      38                 :            : #define CDNS_I2C_CR_DIVB_MASK           ((uint32_t)0x3f << CDNS_I2C_CR_DIVB_SHIFT)
      39                 :            : 
      40                 :            : /* Master Enable Mask - master mode, acknowledgment, and no extended address */
      41                 :            : #define CDNS_I2C_CR_MASTER_EN_MASK      (CDNS_I2C_CR_NEA | \
      42                 :            :                                          CDNS_I2C_CR_ACK_EN | \
      43                 :            :                                          CDNS_I2C_CR_MS)
      44                 :            : 
      45                 :            : /* Slave Enable Mask - used for slave configuration */
      46                 :            : #define CDNS_I2C_CR_SLAVE_EN_MASK       ~CDNS_I2C_CR_MASTER_EN_MASK
      47                 :            : 
      48                 :            : /* Status Register Bit mask definitions */
      49                 :            : #define CDNS_I2C_SR_BA                  BIT(8) /* Bus is available */
      50                 :            : #define CDNS_I2C_SR_TXDV                BIT(6) /* Transmit data is valid */
      51                 :            : #define CDNS_I2C_SR_RXDV                BIT(5) /* Received data is valid */
      52                 :            : #define CDNS_I2C_SR_RXRW                BIT(3) /* Read or Write operation */
      53                 :            : 
      54                 :            : /*
      55                 :            :  * I2C Address Register Bit mask definitions
      56                 :            :  * Normal addressing mode uses [6:0] bits.
      57                 :            :  * Extended addressing mode uses [9:0] bits.
      58                 :            :  * A write access to this register always initiates a transfer if the I2C is in master mode.
      59                 :            :  */
      60                 :            : #define CDNS_I2C_ADDR_MASK              0x000003FFU /* I2C Address Mask */
      61                 :            : 
      62                 :            : /*
      63                 :            :  * I2C Interrupt Registers Bit mask definitions
      64                 :            :  * All the four interrupt registers (Status/Mask/Enable/Disable) have the same bit definitions.
      65                 :            :  */
      66                 :            : #define CDNS_I2C_IXR_ARB_LOST           BIT(9) /* Arbitration Lost Interrupt */
      67                 :            : #define CDNS_I2C_IXR_RX_UNF             BIT(7) /* RX FIFO Underflow Interrupt */
      68                 :            : #define CDNS_I2C_IXR_TX_OVF             BIT(6) /* TX FIFO Overflow Interrupt */
      69                 :            : #define CDNS_I2C_IXR_RX_OVF             BIT(5) /* RX FIFO Overflow Interrupt */
      70                 :            : #define CDNS_I2C_IXR_SLV_RDY            BIT(4) /* Slave Ready Interrupt */
      71                 :            : #define CDNS_I2C_IXR_TO                 BIT(3) /* Timeout Interrupt */
      72                 :            : #define CDNS_I2C_IXR_NACK               BIT(2) /* NACK Interrupt */
      73                 :            : #define CDNS_I2C_IXR_DATA               BIT(1) /* Data Interrupt */
      74                 :            : #define CDNS_I2C_IXR_COMP               BIT(0) /* Transfer Complete Interrupt */
      75                 :            : 
      76                 :            : /* All Interrupt Mask */
      77                 :            : #define CDNS_I2C_IXR_ALL_INTR_MASK      (CDNS_I2C_IXR_ARB_LOST | \
      78                 :            :                                          CDNS_I2C_IXR_RX_UNF | \
      79                 :            :                                          CDNS_I2C_IXR_TX_OVF | \
      80                 :            :                                          CDNS_I2C_IXR_RX_OVF | \
      81                 :            :                                          CDNS_I2C_IXR_SLV_RDY | \
      82                 :            :                                          CDNS_I2C_IXR_TO | \
      83                 :            :                                          CDNS_I2C_IXR_NACK | \
      84                 :            :                                          CDNS_I2C_IXR_DATA | \
      85                 :            :                                          CDNS_I2C_IXR_COMP)
      86                 :            : 
      87                 :            : /* Error Interrupt Mask */
      88                 :            : #define CDNS_I2C_IXR_ERR_INTR_MASK      (CDNS_I2C_IXR_ARB_LOST | \
      89                 :            :                                          CDNS_I2C_IXR_RX_UNF | \
      90                 :            :                                          CDNS_I2C_IXR_TX_OVF | \
      91                 :            :                                          CDNS_I2C_IXR_RX_OVF | \
      92                 :            :                                          CDNS_I2C_IXR_NACK)
      93                 :            : 
      94                 :            : /* Enabled Interrupt Mask */
      95                 :            : #define CDNS_I2C_ENABLED_INTR_MASK      (CDNS_I2C_IXR_ARB_LOST | \
      96                 :            :                                          CDNS_I2C_IXR_RX_UNF | \
      97                 :            :                                          CDNS_I2C_IXR_TX_OVF | \
      98                 :            :                                          CDNS_I2C_IXR_RX_OVF | \
      99                 :            :                                          CDNS_I2C_IXR_NACK | \
     100                 :            :                                          CDNS_I2C_IXR_DATA | \
     101                 :            :                                          CDNS_I2C_IXR_COMP)
     102                 :            : 
     103                 :            : /* Slave Interrupt Mask */
     104                 :            : #define CDNS_I2C_IXR_SLAVE_INTR_MASK    (CDNS_I2C_IXR_RX_UNF | \
     105                 :            :                                          CDNS_I2C_IXR_TX_OVF | \
     106                 :            :                                          CDNS_I2C_IXR_RX_OVF | \
     107                 :            :                                          CDNS_I2C_IXR_TO | \
     108                 :            :                                          CDNS_I2C_IXR_NACK | \
     109                 :            :                                          CDNS_I2C_IXR_DATA | \
     110                 :            :                                          CDNS_I2C_IXR_COMP)
     111                 :            : 
     112                 :            : /* System clock frequency for I2C ticks */
     113                 :            : #define CDNS_I2C_TICKS_PER_SEC          CONFIG_SYS_CLOCK_TICKS_PER_SEC
     114                 :            : 
     115                 :            : /* Default timeout ticks for I2C operations */
     116                 :            : #define CDNS_I2C_TIMEOUT_TICKS          CDNS_I2C_TICKS_PER_SEC
     117                 :            : 
     118                 :            : #define CDNS_I2C_FIFO_DEPTH_DEFAULT     16U /* Default FIFO depth for I2C operations */
     119                 :            : #define CDNS_I2C_MAX_TRANSFER_SIZE      255U /* Maximum transfer size for I2C data */
     120                 :            : 
     121                 :            : /* Default transfer size */
     122                 :            : #define CDNS_I2C_TRANSFER_SIZE_DEFAULT  (CDNS_I2C_MAX_TRANSFER_SIZE - 3U)
     123                 :            : 
     124                 :            : /* Maximum dividers for I2C clock */
     125                 :            : #define CDNS_I2C_DIVA_MAX               4U
     126                 :            : #define CDNS_I2C_DIVB_MAX               64U
     127                 :            : #define CDNS_I2C_CLK_DIV_FACTOR         22U
     128                 :            : 
     129                 :            : #define CDNS_I2C_TIMEOUT_MAX              0xFFU /* Maximum value for Timeout Register */
     130                 :            : #define CDNS_I2C_POLL_US                100000U /* Polling interval in microseconds */
     131                 :            : #define CDNS_I2C_TIMEOUT_US             500000U /* Timeout value for I2C operations */
     132                 :            : #define CDNS_I2C_BROKEN_HOLD_BIT        BIT(0)  /* Broken Hold - Cadence I2C controller ver 1.0 */
     133                 :            : 
     134                 :            : /* Event flag for I2C transfer completion */
     135                 :            : #define I2C_XFER_COMPLETION_EVENT       BIT(0)
     136                 :            : 
     137                 :            : /* I2C Speed Definitions */
     138                 :            : #define CDNS_I2C_SPEED_STANDARD_HZ      (100000UL) /* Standard I2C speed (100 kHz) */
     139                 :            : #define CDNS_I2C_SPEED_FAST_HZ          (400000UL) /* Fast I2C speed (400 kHz) */
     140                 :            : 
     141                 :            : #if defined(CONFIG_I2C_TARGET)
     142                 :            : /**
     143                 :            :  * enum cdns_i2c_mode - I2C Controller current operating mode
     144                 :            :  *
     145                 :            :  * @CDNS_I2C_MODE_SLAVE: I2C controller operating in slave mode
     146                 :            :  * @CDNS_I2C_MODE_MASTER: I2C Controller operating in master mode
     147                 :            :  */
     148                 :            : enum cdns_i2c_mode {
     149                 :            :         CDNS_I2C_MODE_SLAVE = 0,
     150                 :            :         CDNS_I2C_MODE_MASTER,
     151                 :            : };
     152                 :            : 
     153                 :            : /**
     154                 :            :  * enum cdns_i2c_slave_state - Slave state when I2C is operating in slave mode
     155                 :            :  *
     156                 :            :  * @CDNS_I2C_SLAVE_STATE_IDLE: I2C slave idle
     157                 :            :  * @CDNS_I2C_SLAVE_STATE_SEND: I2C slave sending data to master
     158                 :            :  * @CDNS_I2C_SLAVE_STATE_RECV: I2C slave receiving data from master
     159                 :            :  */
     160                 :            : enum cdns_i2c_slave_state {
     161                 :            :         CDNS_I2C_SLAVE_STATE_IDLE = 0,
     162                 :            :         CDNS_I2C_SLAVE_STATE_SEND,
     163                 :            :         CDNS_I2C_SLAVE_STATE_RECV,
     164                 :            : };
     165                 :            : #endif
     166                 :            : 
     167                 :            : /* Driver config */
     168                 :            : struct cdns_i2c_config {
     169                 :            :         void (*irq_config_func)(const struct device *dev);
     170                 :            : };
     171                 :            : 
     172                 :            : /**
     173                 :            :  * struct cdns_i2c_data - Cadence I2C device private data structure
     174                 :            :  * @membase: Base address of the I2C device.
     175                 :            :  * @ctrl_reg: Cached value of the control register.
     176                 :            :  * @input_clk: Input clock to I2C controller.
     177                 :            :  * @i2c_clk: Maximum I2C clock speed.
     178                 :            :  * @i2c_config: Configuration of I2C settings.
     179                 :            :  * @fifo_depth: The depth of the transfer FIFO.
     180                 :            :  * @transfer_size: The maximum number of bytes in one transfer.
     181                 :            :  * @bus_hold_flag: Flag used in repeated start for clearing HOLD bit.
     182                 :            :  * @broken_hold_flag: Flag for broken hold bit usage in r1p10.
     183                 :            :  * @xfer_done: Transfer complete event.
     184                 :            :  * @err_status: Error status in Interrupt Status Register.
     185                 :            :  * @p_msg: Message pointer for I2C communication.
     186                 :            :  * @p_send_buf: Pointer to transmit buffer.
     187                 :            :  * @p_recv_buf: Pointer to receive buffer.
     188                 :            :  * @send_count: Number of bytes still expected to send.
     189                 :            :  * @recv_count: Number of bytes still expected to receive.
     190                 :            :  * @curr_recv_count: Number of bytes to be received in current transfer.
     191                 :            :  * @bus_mutex: Mutex for bus access synchronization
     192                 :            :  * @ctrl_reg_diva_divb: Value of fields DIV_A and DIV_B from CR register.
     193                 :            :  * @slave: Registered slave instance.
     194                 :            :  * @dev_mode: I2C operating role (master/slave).
     195                 :            :  * @slave_state: I2C Slave state (idle/read/write).
     196                 :            :  */
     197                 :            : struct cdns_i2c_data {
     198                 :            :         mem_addr_t membase;
     199                 :            :         uint32_t ctrl_reg;
     200                 :            :         uint32_t input_clk;
     201                 :            :         uint32_t i2c_clk;
     202                 :            :         uint32_t i2c_config;
     203                 :            :         uint32_t fifo_depth;
     204                 :            :         uint32_t transfer_size;
     205                 :            :         uint32_t bus_hold_flag;
     206                 :            :         uint32_t broken_hold_flag;
     207                 :            : 
     208                 :            :         struct k_event xfer_done;
     209                 :            :         uint32_t err_status;
     210                 :            :         struct i2c_msg *p_msg;
     211                 :            :         uint8_t *p_send_buf;
     212                 :            :         uint8_t *p_recv_buf;
     213                 :            :         uint32_t send_count;
     214                 :            :         uint32_t recv_count;
     215                 :            :         uint32_t curr_recv_count;
     216                 :            : 
     217                 :            :         struct k_mutex bus_mutex;
     218                 :            : 
     219                 :            : #if defined(CONFIG_I2C_TARGET)
     220                 :            :         uint16_t ctrl_reg_diva_divb;
     221                 :            :         struct i2c_target_config *slave;
     222                 :            :         enum cdns_i2c_mode dev_mode;
     223                 :            :         enum cdns_i2c_slave_state slave_state;
     224                 :            : #endif
     225                 :            : };
     226                 :            : 
     227                 :            : /**
     228                 :            :  * cdns_i2c_writereg - Write a 32-bit value to a specific offset in the I2C register space.
     229                 :            :  * @i2c_bus: Pointer to the I2C data structure.
     230                 :            :  * @value: The 32-bit value to write to the register.
     231                 :            :  * @offset: The offset in the I2C register space where the value will be written.
     232                 :            :  */
     233                 :         52 : static inline void cdns_i2c_writereg(const struct cdns_i2c_data *i2c_bus,
     234                 :            :                                      uint32_t value, uintptr_t offset)
     235                 :            : {
     236                 :         52 :         uintptr_t reg_address = (uintptr_t)(i2c_bus->membase) + offset;
     237                 :            : 
     238                 :         52 :         sys_write32(value, reg_address);
     239                 :         52 : }
     240                 :            : 
     241                 :            : /**
     242                 :            :  * cdns_i2c_readreg - Read a 32-bit value from a specific offset in the I2C register space.
     243                 :            :  * @i2c_bus: Pointer to the I2C data structure.
     244                 :            :  * @offset: The offset in the I2C register space from which the value will be read.
     245                 :            :  *
     246                 :            :  * Return: The 32-bit value read from the register.
     247                 :            :  */
     248                 :         53 : static inline uint32_t cdns_i2c_readreg(const struct cdns_i2c_data *i2c_bus, uintptr_t offset)
     249                 :            : {
     250                 :         53 :         uintptr_t reg_address = (uintptr_t)(i2c_bus->membase) + offset;
     251                 :            : 
     252                 :         53 :         return sys_read32(reg_address);
     253                 :            : }
     254                 :            : 
     255                 :            : /**
     256                 :            :  * cdns_i2c_enable_peripheral - Enable the Cadence I2C controller
     257                 :            :  * @i2c_bus: Pointer to the device's private data structure for the I2C controller
     258                 :            :  */
     259                 :          2 : static void cdns_i2c_enable_peripheral(struct cdns_i2c_data *i2c_bus)
     260                 :            : {
     261                 :          2 :         cdns_i2c_writereg(i2c_bus, i2c_bus->ctrl_reg, CDNS_I2C_CR_OFFSET);
     262                 :            : 
     263                 :            :         /*
     264                 :            :          * Cadence I2C controller has a bug causing invalid reads after a timeout
     265                 :            :          * in master receiver mode. While the timeout feature is disabled,
     266                 :            :          * writing the max value to the timeout register reduces the issue.
     267                 :            :          */
     268                 :          2 :         cdns_i2c_writereg(i2c_bus, CDNS_I2C_TIMEOUT_MAX, CDNS_I2C_TIME_OUT_OFFSET);
     269                 :          2 : }
     270                 :            : 
     271                 :            : /**
     272                 :            :  * cdns_i2c_calc_divs - Calculate clock dividers for I2C frequency
     273                 :            :  * @f: Pointer to the I2C target frequency (input/output)
     274                 :            :  * @input_clk: Input clock frequency in Hz
     275                 :            :  * @a: Pointer to the first divider (output)
     276                 :            :  * @b: Pointer to the second divider (output)
     277                 :            :  *
     278                 :            :  * On entry, @f holds the target I2C frequency. On exit, @f will hold the
     279                 :            :  * actual I2C frequency generated by the calculated dividers.
     280                 :            :  *
     281                 :            :  * Return: 0 on success, negative errno otherwise.
     282                 :            :  */
     283                 :          2 : static int32_t cdns_i2c_calc_divs(uint32_t *f, uint32_t input_clk,
     284                 :            :                                   uint32_t *a, uint32_t *b)
     285                 :            : {
     286                 :          2 :         uint32_t fscl = *f;
     287                 :          2 :         uint32_t best_fscl = *f;
     288                 :            :         uint32_t actual_fscl, temp;
     289                 :            :         uint32_t div_a, div_b;
     290                 :          2 :         uint32_t calc_div_a = 0, calc_div_b = 0;
     291                 :            :         uint32_t last_error, current_error;
     292                 :          2 :         int32_t ret = 0;
     293                 :            : 
     294                 :            :         /* calculate initial estimate for divisor_a and divisor_b */
     295                 :          2 :         temp = input_clk / (CDNS_I2C_CLK_DIV_FACTOR * fscl);
     296                 :            : 
     297                 :            :         /* Check if the calculated value is out of range */
     298   [ +  -  -  + ]:          2 :         if ((temp == 0U) || (temp > (CDNS_I2C_DIVA_MAX * CDNS_I2C_DIVB_MAX))) {
     299                 :          0 :                 ret = -EINVAL;
     300                 :          0 :                 goto out;
     301                 :            :         }
     302                 :            : 
     303                 :            :         /* Initialize the last error to a large value (no error yet) */
     304                 :          2 :         last_error = UINT32_MAX;
     305                 :            : 
     306                 :            :         /* Iterate over possible values for divisor_a */
     307         [ +  + ]:         10 :         for (div_a = 0; div_a < CDNS_I2C_DIVA_MAX; div_a++) {
     308                 :            :                 /* Calculate the corresponding divisor_b for this div_a */
     309                 :          8 :                 div_b = DIV_ROUND_UP(input_clk, CDNS_I2C_CLK_DIV_FACTOR * fscl * (div_a + 1U));
     310                 :            : 
     311                 :            :                 /* Skip invalid values of div_b */
     312   [ +  -  -  + ]:          8 :                 if ((div_b < 1U) || (div_b > CDNS_I2C_DIVB_MAX)) {
     313                 :          0 :                         continue;
     314                 :            :                 }
     315                 :            : 
     316                 :            :                 /* Adjust div_b for zero-based indexing */
     317                 :          8 :                 div_b--;
     318                 :            : 
     319                 :            :                 /* Calculate the actual fscl based on the current divisors */
     320                 :          8 :                 actual_fscl = input_clk / (CDNS_I2C_CLK_DIV_FACTOR * (div_a + 1U) * (div_b + 1U));
     321                 :            : 
     322                 :            :                 /* Skip if the actual fscl exceeds the target fscl */
     323         [ -  + ]:          8 :                 if (actual_fscl > fscl) {
     324                 :          0 :                         continue;
     325                 :            :                 }
     326                 :            : 
     327                 :            :                 /* Calculate the error between the target fscl and the actual fscl */
     328                 :          8 :                 current_error = fscl - actual_fscl;
     329                 :            : 
     330                 :            :                 /* Update the best divisors if a smaller error is found */
     331         [ +  + ]:          8 :                 if (last_error > current_error) {
     332                 :          2 :                         calc_div_a = div_a;
     333                 :          2 :                         calc_div_b = div_b;
     334                 :          2 :                         best_fscl = actual_fscl;
     335                 :          2 :                         last_error = current_error;
     336                 :            :                 }
     337                 :            :         }
     338                 :            : 
     339                 :            :         /* Set the output values */
     340                 :          2 :         *a = calc_div_a;
     341                 :          2 :         *b = calc_div_b;
     342                 :          2 :         *f = best_fscl;
     343                 :            : 
     344                 :          2 : out:
     345                 :          2 :         return ret;
     346                 :            : }
     347                 :            : 
     348                 :            : /**
     349                 :            :  * cdns_i2c_setclk - Set the serial clock rate for the I2C device
     350                 :            :  * @i2c_bus: Pointer to the I2C data structure
     351                 :            :  * @clk_in: I2C clock input frequency in Hz
     352                 :            :  *
     353                 :            :  * This function sets the serial clock rate for the I2C device by configuring
     354                 :            :  * the clock divisors in the device's control register. The device must be idle
     355                 :            :  * (i.e., not actively transferring data) before calling this function.
     356                 :            :  *
     357                 :            :  * The clock rate is determined by the following formula:
     358                 :            :  *      Fscl = Fpclk / (22 * (divisor_a + 1) * (divisor_b + 1))
     359                 :            :  * Where:
     360                 :            :  *      - Fscl is the desired I2C clock rate
     361                 :            :  *      - Fpclk is the input clock frequency
     362                 :            :  *      - divisor_a and divisor_b are the calculated divisors to achieve the desired clock rate
     363                 :            :  *
     364                 :            :  * The serial clock rate cannot exceed the input clock divided by 22. Common I2C clock
     365                 :            :  * rates are 100 KHz and 400 KHz.
     366                 :            :  *
     367                 :            :  * Return: 0 on success, negative error code on failure
     368                 :            :  */
     369                 :          2 : static int32_t cdns_i2c_setclk(struct cdns_i2c_data *i2c_bus, uint32_t clk_in)
     370                 :            : {
     371                 :            :         uint32_t div_a, div_b;
     372                 :            :         uint32_t ctrl_reg;
     373                 :          2 :         int32_t ret = 0;
     374                 :          2 :         uint32_t fscl = i2c_bus->i2c_clk;
     375                 :            : 
     376                 :            :         /* Calculate the divider values */
     377                 :          2 :         ret = cdns_i2c_calc_divs(&fscl, clk_in, &div_a, &div_b);
     378         [ -  + ]:          2 :         if (ret != 0) {
     379                 :          0 :                 goto out;
     380                 :            :         }
     381                 :            : 
     382                 :            :         /* Set new divider values in the control register */
     383                 :          2 :         ctrl_reg = i2c_bus->ctrl_reg;
     384                 :          2 :         ctrl_reg &= ~(CDNS_I2C_CR_DIVA_MASK | CDNS_I2C_CR_DIVB_MASK);
     385                 :          2 :         ctrl_reg |= ((div_a << CDNS_I2C_CR_DIVA_SHIFT) |
     386                 :          2 :                      (div_b << CDNS_I2C_CR_DIVB_SHIFT));
     387                 :          2 :         i2c_bus->ctrl_reg = ctrl_reg;
     388                 :          2 :         cdns_i2c_writereg(i2c_bus, ctrl_reg, CDNS_I2C_CR_OFFSET);
     389                 :            : 
     390                 :            : #if defined(CONFIG_I2C_TARGET)
     391                 :            :         /* Save the divider values for switching between master and slave modes */
     392                 :            :         i2c_bus->ctrl_reg_diva_divb = (uint16_t)(uint32_t)(ctrl_reg & (CDNS_I2C_CR_DIVA_MASK |
     393                 :            :                                                                        CDNS_I2C_CR_DIVB_MASK));
     394                 :            : #endif
     395                 :            : 
     396                 :          2 : out:
     397                 :          2 :         return ret;
     398                 :            : }
     399                 :            : 
     400                 :            : /**
     401                 :            :  * cdns_i2c_configure - Configures the I2C bus speed and initializes the I2C peripheral
     402                 :            :  * @dev: Pointer to the device structure representing the I2C bus
     403                 :            :  * @dev_config: Configuration value containing the desired I2C bus speed
     404                 :            :  *
     405                 :            :  * This function configures the I2C bus speed based on the value provided in
     406                 :            :  * @dev_config, which can be either I2C_SPEED_STANDARD (100 KHz) or I2C_SPEED_FAST (400 KHz).
     407                 :            :  * It then sets the appropriate clock for the I2C bus, verifies the clock is valid, and
     408                 :            :  * initializes the I2C peripheral. The configuration is saved to the device's data structure.
     409                 :            :  *
     410                 :            :  * Return: 0 on success, -EIO on failure
     411                 :            :  */
     412                 :          1 : static int32_t cdns_i2c_configure(const struct device *dev, uint32_t dev_config)
     413                 :            : {
     414                 :          1 :         int32_t ret = 0;
     415                 :          1 :         struct cdns_i2c_data *i2c_bus = (struct cdns_i2c_data *)dev->data;
     416                 :          1 :         uint32_t speed = I2C_SPEED_GET(dev_config);
     417                 :            :         uint32_t i2c_speed;
     418                 :            : 
     419                 :          1 :         (void)k_mutex_lock(&i2c_bus->bus_mutex, K_FOREVER);
     420                 :            : 
     421                 :            :         /* Check requested I2C Speed */
     422         [ +  - ]:          1 :         if (speed == I2C_SPEED_STANDARD) {
     423                 :          1 :                 i2c_speed = CDNS_I2C_SPEED_STANDARD_HZ; /* 100 KHz */
     424         [ #  # ]:          0 :         } else if (speed == I2C_SPEED_FAST) {
     425                 :          0 :                 i2c_speed = CDNS_I2C_SPEED_FAST_HZ; /* 400 KHz */
     426                 :            :         } else {
     427         [ #  # ]:          0 :                 LOG_ERR("Unsupported I2C speed requested: %u", speed);
     428                 :          0 :                 ret = -EIO;
     429                 :          0 :                 goto out;
     430                 :            :         }
     431                 :            : 
     432                 :            :         /* Set the I2C clock */
     433                 :          1 :         i2c_bus->i2c_clk = i2c_speed;
     434                 :          1 :         ret = cdns_i2c_setclk(i2c_bus, i2c_bus->input_clk);
     435         [ -  + ]:          1 :         if (ret != 0) {
     436         [ #  # ]:          0 :                 LOG_ERR("Invalid SCL clock: %u Hz", i2c_bus->i2c_clk);
     437                 :          0 :                 ret = -EIO;
     438                 :          0 :                 goto out;
     439                 :            :         }
     440                 :            : 
     441                 :            :         /* Enable the I2C peripheral */
     442                 :          1 :         i2c_bus->ctrl_reg |= CDNS_I2C_CR_ACK_EN | CDNS_I2C_CR_NEA | CDNS_I2C_CR_MS;
     443                 :          1 :         cdns_i2c_enable_peripheral(i2c_bus);
     444                 :            : 
     445                 :            :         /* Save I2C host configuration */
     446                 :          1 :         i2c_bus->i2c_config = dev_config;
     447                 :            : 
     448                 :          1 : out:
     449                 :          1 :         (void)k_mutex_unlock(&i2c_bus->bus_mutex);
     450                 :            : 
     451                 :          1 :         return ret;
     452                 :            : }
     453                 :            : 
     454                 :            : /**
     455                 :            :  * cdns_i2c_get_config - Retrieve the saved I2C configuration.
     456                 :            :  * @dev: Pointer to the device structure.
     457                 :            :  * @dev_config: Pointer to a variable where the configuration will be stored.
     458                 :            :  *
     459                 :            :  * Return: 0 on success, or a negative error code on failure.
     460                 :            :  */
     461                 :          1 : static int32_t cdns_i2c_get_config(const struct device *dev, uint32_t *dev_config)
     462                 :            : {
     463                 :          1 :         int32_t ret = 0;
     464                 :          1 :         struct cdns_i2c_data *i2c_bus = (struct cdns_i2c_data *)dev->data;
     465                 :            : 
     466                 :            :         /* Check if the configuration is valid (non-zero) */
     467         [ -  + ]:          1 :         if (i2c_bus->i2c_config == 0U) {
     468                 :          0 :                 ret = -EINVAL;
     469                 :            :         } else {
     470                 :            :                 /* Return the saved configuration */
     471                 :          1 :                 *dev_config = i2c_bus->i2c_config;
     472                 :            :         }
     473                 :            : 
     474                 :          1 :         return ret;
     475                 :            : }
     476                 :            : 
     477                 :            : /**
     478                 :            :  * cdns_i2c_clear_bus_hold - Clear bus hold bit in the controller's register
     479                 :            :  * @i2c_bus: Pointer to the I2C controller driver data structure
     480                 :            :  */
     481                 :          5 : static void cdns_i2c_clear_bus_hold(struct cdns_i2c_data *i2c_bus)
     482                 :            : {
     483                 :          5 :         uint32_t reg = cdns_i2c_readreg(i2c_bus, CDNS_I2C_CR_OFFSET);
     484                 :            : 
     485         [ -  + ]:          5 :         if ((reg & CDNS_I2C_CR_HOLD) == CDNS_I2C_CR_HOLD) {
     486                 :          0 :                 cdns_i2c_writereg(i2c_bus, reg & ~CDNS_I2C_CR_HOLD, CDNS_I2C_CR_OFFSET);
     487                 :            :         }
     488                 :          5 : }
     489                 :            : 
     490                 :            : /**
     491                 :            :  * cdns_is_fifo_hold_quirk - Check if the FIFO hold quirk is triggered for I2C
     492                 :            :  * @i2c_bus: Pointer to the I2C controller driver data structure
     493                 :            :  * @hold_wrkaround: Boolean indicating if hold workarounds should be applied
     494                 :            :  *
     495                 :            :  * Return: True if the quirk condition is met, false otherwise.
     496                 :            :  */
     497                 :          8 : static inline bool cdns_is_fifo_hold_quirk(const struct cdns_i2c_data *i2c_bus,
     498                 :            :                                            bool hold_wrkaround)
     499                 :            : {
     500   [ -  +  -  - ]:          8 :         return (hold_wrkaround && (i2c_bus->curr_recv_count == (i2c_bus->fifo_depth + 1U)));
     501                 :            : }
     502                 :            : 
     503                 :            : #if defined(CONFIG_I2C_TARGET)
     504                 :            : /**
     505                 :            :  * cdns_i2c_set_mode - Set the I2C bus mode (master or slave)
     506                 :            :  * @mode: The mode to set (CDNS_I2C_MODE_MASTER or CDNS_I2C_MODE_SLAVE)
     507                 :            :  * @i2c_bus: Pointer to the I2C data structure
     508                 :            :  */
     509                 :            : static void cdns_i2c_set_mode(enum cdns_i2c_mode mode, struct cdns_i2c_data *i2c_bus)
     510                 :            : {
     511                 :            :         /* Disable all interrupts */
     512                 :            :         cdns_i2c_writereg(i2c_bus, CDNS_I2C_IXR_ALL_INTR_MASK, CDNS_I2C_IDR_OFFSET);
     513                 :            : 
     514                 :            :         /* Clear FIFO and reset transfer size */
     515                 :            :         cdns_i2c_writereg(i2c_bus, CDNS_I2C_CR_CLR_FIFO, CDNS_I2C_CR_OFFSET);
     516                 :            : 
     517                 :            :         /* Update the I2C device mode and state */
     518                 :            :         i2c_bus->dev_mode = mode;
     519                 :            :         i2c_bus->slave_state = CDNS_I2C_SLAVE_STATE_IDLE;
     520                 :            : 
     521                 :            :         if (mode == CDNS_I2C_MODE_MASTER) {
     522                 :            :                 /* Enable i2c master */
     523                 :            :                 cdns_i2c_writereg(i2c_bus, i2c_bus->ctrl_reg_diva_divb |
     524                 :            :                                   CDNS_I2C_CR_MASTER_EN_MASK,
     525                 :            :                                   CDNS_I2C_CR_OFFSET);
     526                 :            : 
     527                 :            :                 /* Allow time for master mode to stabilize */
     528                 :            :                 (void)k_usleep(120);
     529                 :            :         } else {
     530                 :            :                 /* Enable i2c slave */
     531                 :            :                 cdns_i2c_writereg(i2c_bus, i2c_bus->ctrl_reg_diva_divb &
     532                 :            :                                   CDNS_I2C_CR_SLAVE_EN_MASK,
     533                 :            :                                   CDNS_I2C_CR_OFFSET);
     534                 :            : 
     535                 :            :                 /* Configure the slave address */
     536                 :            :                 cdns_i2c_writereg(i2c_bus,
     537                 :            :                                   ((uint32_t)(i2c_bus->slave->address) & CDNS_I2C_ADDR_MASK),
     538                 :            :                                   CDNS_I2C_ADDR_OFFSET);
     539                 :            : 
     540                 :            :                 /* Enable slave send/receive interrupts */
     541                 :            :                 cdns_i2c_writereg(i2c_bus, CDNS_I2C_IXR_SLAVE_INTR_MASK, CDNS_I2C_IER_OFFSET);
     542                 :            :         }
     543                 :            : }
     544                 :            : 
     545                 :            : /**
     546                 :            :  * cdns_i2c_slave_rcv_data - Handle data reception for I2C slave mode
     547                 :            :  * @i2c_bus: Pointer to the I2C data structure
     548                 :            :  */
     549                 :            : static void cdns_i2c_slave_rcv_data(struct cdns_i2c_data *i2c_bus)
     550                 :            : {
     551                 :            :         uint8_t bytes;
     552                 :            :         uint8_t data;
     553                 :            :         const struct i2c_target_callbacks *i2c_slave_event = i2c_bus->slave->callbacks;
     554                 :            : 
     555                 :            :         /* Prepare backend for data reception */
     556                 :            :         if (i2c_bus->slave_state == CDNS_I2C_SLAVE_STATE_IDLE) {
     557                 :            :                 i2c_bus->slave_state = CDNS_I2C_SLAVE_STATE_RECV;
     558                 :            :                 if ((i2c_slave_event != NULL) &&
     559                 :            :                     (i2c_slave_event->write_requested != NULL)) {
     560                 :            :                         (void)i2c_slave_event->write_requested(i2c_bus->slave);
     561                 :            :                 }
     562                 :            :         }
     563                 :            : 
     564                 :            :         /* Fetch number of bytes to receive */
     565                 :            :         bytes = (uint8_t)cdns_i2c_readreg(i2c_bus, CDNS_I2C_XFER_SIZE_OFFSET);
     566                 :            : 
     567                 :            :         /* Read data and send it to backend */
     568                 :            :         while (bytes > 0U) {
     569                 :            :                 data = (uint8_t)cdns_i2c_readreg(i2c_bus, CDNS_I2C_DATA_OFFSET);
     570                 :            :                 if ((i2c_slave_event != NULL) &&
     571                 :            :                     (i2c_slave_event->write_requested != NULL)) {
     572                 :            :                         (void)i2c_slave_event->write_received(i2c_bus->slave, data);
     573                 :            :                 }
     574                 :            :                 bytes--;
     575                 :            :         }
     576                 :            : }
     577                 :            : 
     578                 :            : /**
     579                 :            :  * cdns_i2c_slave_send_data - Handle data transmission for I2C slave mode
     580                 :            :  * @i2c_bus: Pointer to the I2C data structure
     581                 :            :  */
     582                 :            : static void cdns_i2c_slave_send_data(struct cdns_i2c_data *i2c_bus)
     583                 :            : {
     584                 :            :         uint8_t data = 0U;
     585                 :            :         const struct i2c_target_callbacks *i2c_slave_event = i2c_bus->slave->callbacks;
     586                 :            : 
     587                 :            :         /* Prepare backend for data transmission */
     588                 :            :         if (i2c_bus->slave_state == CDNS_I2C_SLAVE_STATE_IDLE) {
     589                 :            :                 i2c_bus->slave_state = CDNS_I2C_SLAVE_STATE_SEND;
     590                 :            :                 if ((i2c_slave_event != NULL) &&
     591                 :            :                     (i2c_slave_event->read_requested != NULL)) {
     592                 :            :                         (void)i2c_slave_event->read_requested(i2c_bus->slave, &data);
     593                 :            :                 }
     594                 :            :         } else {
     595                 :            :                 if ((i2c_slave_event != NULL) &&
     596                 :            :                     (i2c_slave_event->read_processed != NULL)) {
     597                 :            :                         (void)i2c_slave_event->read_processed(i2c_bus->slave, &data);
     598                 :            :                 }
     599                 :            :         }
     600                 :            : 
     601                 :            :         /* Send data over the bus */
     602                 :            :         cdns_i2c_writereg(i2c_bus, data, CDNS_I2C_DATA_OFFSET);
     603                 :            : }
     604                 :            : 
     605                 :            : /**
     606                 :            :  * cdns_slave_handle_receive_interrupt - Handle the receive interrupt for I2C slave.
     607                 :            :  * @i2c_bus: Pointer to the I2C data structure.
     608                 :            :  * @isr_status: Interrupt status, indicating the cause of the interrupt.
     609                 :            :  */
     610                 :            : static void cdns_slave_handle_receive_interrupt(struct cdns_i2c_data *i2c_bus, uint32_t isr_status)
     611                 :            : {
     612                 :            :         const struct i2c_target_callbacks *i2c_slave_event = i2c_bus->slave->callbacks;
     613                 :            : 
     614                 :            :         /* Receive data from master */
     615                 :            :         if ((isr_status & CDNS_I2C_IXR_DATA) == CDNS_I2C_IXR_DATA) {
     616                 :            :                 cdns_i2c_slave_rcv_data(i2c_bus);
     617                 :            :         }
     618                 :            : 
     619                 :            :         /* Transfer complete, reset state */
     620                 :            :         if ((isr_status & CDNS_I2C_IXR_COMP) == CDNS_I2C_IXR_COMP) {
     621                 :            :                 cdns_i2c_slave_rcv_data(i2c_bus);  /* Ensure final reception */
     622                 :            :                 i2c_bus->slave_state = CDNS_I2C_SLAVE_STATE_IDLE;
     623                 :            :                 if ((i2c_slave_event != NULL) && (i2c_slave_event->stop != NULL)) {
     624                 :            :                         (void)i2c_slave_event->stop(i2c_bus->slave);
     625                 :            :                 }
     626                 :            :         }
     627                 :            : }
     628                 :            : 
     629                 :            : /**
     630                 :            :  * cdns_slave_handle_transmit_interrupt - Handle the transmit interrupt for I2C slave.
     631                 :            :  * @i2c_bus: Pointer to the I2C data structure.
     632                 :            :  * @isr_status: Interrupt status, indicating the cause of the interrupt.
     633                 :            :  */
     634                 :            : static void cdns_slave_handle_transmit_interrupt(struct cdns_i2c_data *i2c_bus, uint32_t isr_status)
     635                 :            : {
     636                 :            :         const struct i2c_target_callbacks *i2c_slave_event = i2c_bus->slave->callbacks;
     637                 :            : 
     638                 :            :         /* Send data to master */
     639                 :            :         if ((isr_status & CDNS_I2C_IXR_DATA) == CDNS_I2C_IXR_DATA) {
     640                 :            :                 cdns_i2c_slave_send_data(i2c_bus);
     641                 :            :         }
     642                 :            : 
     643                 :            :         /* Transfer complete, reset state */
     644                 :            :         if ((isr_status & CDNS_I2C_IXR_COMP) == CDNS_I2C_IXR_COMP) {
     645                 :            :                 i2c_bus->slave_state = CDNS_I2C_SLAVE_STATE_IDLE;
     646                 :            :                 if ((i2c_slave_event != NULL) && (i2c_slave_event->stop != NULL)) {
     647                 :            :                         (void)i2c_slave_event->stop(i2c_bus->slave);
     648                 :            :                 }
     649                 :            :         }
     650                 :            : }
     651                 :            : 
     652                 :            : /**
     653                 :            :  * cdns_slave_handle_error_interrupt - Handle the error interrupt for I2C slave.
     654                 :            :  * @i2c_bus: Pointer to the I2C data structure.
     655                 :            :  * @isr_status: Interrupt status, indicating the cause of the error.
     656                 :            :  */
     657                 :            : static void cdns_slave_handle_error_interrupt(struct cdns_i2c_data *i2c_bus, uint32_t isr_status)
     658                 :            : {
     659                 :            :         const struct i2c_target_callbacks *i2c_slave_event = i2c_bus->slave->callbacks;
     660                 :            : 
     661                 :            :         /* Master indicated xfer stop or FIFO underflow/overflow */
     662                 :            :         if ((isr_status & (CDNS_I2C_IXR_NACK | CDNS_I2C_IXR_RX_OVF |
     663                 :            :                            CDNS_I2C_IXR_RX_UNF | CDNS_I2C_IXR_TX_OVF)) != 0U) {
     664                 :            :                 i2c_bus->slave_state = CDNS_I2C_SLAVE_STATE_IDLE;
     665                 :            :                 if ((i2c_slave_event != NULL) && (i2c_slave_event->stop != NULL)) {
     666                 :            :                         (void)i2c_slave_event->stop(i2c_bus->slave);
     667                 :            :                 }
     668                 :            :                 /* Clear FIFO errors */
     669                 :            :                 cdns_i2c_writereg(i2c_bus, CDNS_I2C_CR_CLR_FIFO, CDNS_I2C_CR_OFFSET);
     670                 :            :         }
     671                 :            : }
     672                 :            : 
     673                 :            : /**
     674                 :            :  * cdns_i2c_slave_isr - Interrupt Service Routine for the I2C slave
     675                 :            :  * @i2c_bus: Pointer to the I2C data structure
     676                 :            :  *
     677                 :            :  * The function processes the interrupt status, handles the data transfer
     678                 :            :  * (send/receive), and clears any necessary interrupt flags.
     679                 :            :  */
     680                 :            : static void cdns_i2c_slave_isr(struct cdns_i2c_data *i2c_bus)
     681                 :            : {
     682                 :            :         uint32_t isr_status, i2c_status;
     683                 :            : 
     684                 :            :         /* Fetch and clear the interrupt status */
     685                 :            :         isr_status = cdns_i2c_readreg(i2c_bus, CDNS_I2C_ISR_OFFSET);
     686                 :            :         cdns_i2c_writereg(i2c_bus, isr_status, CDNS_I2C_ISR_OFFSET);
     687                 :            : 
     688                 :            :         /* Masked interrupts handling */
     689                 :            :         isr_status &= ~cdns_i2c_readreg(i2c_bus, CDNS_I2C_IMR_OFFSET);
     690                 :            : 
     691                 :            :         /* Fetch transfer mode (send/receive) */
     692                 :            :         i2c_status = cdns_i2c_readreg(i2c_bus, CDNS_I2C_SR_OFFSET);
     693                 :            : 
     694                 :            :         /* Handle data send/receive based on transfer mode */
     695                 :            :         if ((i2c_status & CDNS_I2C_SR_RXRW) == CDNS_I2C_SR_RXRW) {
     696                 :            :                 cdns_slave_handle_transmit_interrupt(i2c_bus, isr_status);
     697                 :            :         } else {
     698                 :            :                 cdns_slave_handle_receive_interrupt(i2c_bus, isr_status);
     699                 :            :         }
     700                 :            : 
     701                 :            :         /* Handle any errors (FIFO, NACK, overflow, underflow) */
     702                 :            :         cdns_slave_handle_error_interrupt(i2c_bus, isr_status);
     703                 :            : }
     704                 :            : #endif
     705                 :            : 
     706                 :            : /**
     707                 :            :  * cdns_i2c_master_handle_receive_interrupt - Handles I2C master receive interrupts
     708                 :            :  * @i2c_bus: Pointer to the I2C data structure
     709                 :            :  * @isr_status: Interrupt status, indicating the cause of the interrupt
     710                 :            :  */
     711                 :          3 : static void cdns_i2c_master_handle_receive_interrupt(struct cdns_i2c_data *i2c_bus,
     712                 :            :                                                      uint32_t isr_status)
     713                 :            : {
     714                 :            :         uint32_t transfer_size;
     715                 :            :         uint32_t xfer_size;
     716                 :            : 
     717                 :            :         /* Handle reception interrupt (data available or transfer complete) */
     718   [ -  +  -  - ]:          3 :         if (((isr_status & CDNS_I2C_IXR_COMP) == 0U) && ((isr_status & CDNS_I2C_IXR_DATA) == 0U)) {
     719                 :          0 :                 return;
     720                 :            :         }
     721                 :            : 
     722                 :            :         /* Receiving Data: Keep reading as long as data is available */
     723         [ +  + ]:          8 :         while ((cdns_i2c_readreg(i2c_bus, CDNS_I2C_SR_OFFSET) & CDNS_I2C_SR_RXDV) != 0U) {
     724                 :            :                 /* Ensure there's space to store received data */
     725         [ +  - ]:          5 :                 if (i2c_bus->recv_count > 0U) {
     726                 :          5 :                         *(i2c_bus->p_recv_buf) = (uint8_t)cdns_i2c_readreg(i2c_bus,
     727                 :            :                                                                            CDNS_I2C_DATA_OFFSET);
     728                 :          5 :                         i2c_bus->p_recv_buf++;
     729                 :          5 :                         i2c_bus->recv_count--;
     730                 :          5 :                         i2c_bus->curr_recv_count--;
     731                 :            :                 } else {
     732                 :            :                         /* Handle receive buffer overflow or unexpected condition */
     733         [ #  # ]:          0 :                         LOG_ERR("I2C receive buffer overflow. Transfer aborted!");
     734                 :          0 :                         i2c_bus->err_status |= CDNS_I2C_IXR_TO;
     735                 :          0 :                         break;
     736                 :            :                 }
     737                 :            : 
     738                 :            :                 /* Handle issues with receiving more data than expected */
     739         [ -  + ]:          5 :                 if (cdns_is_fifo_hold_quirk(i2c_bus,
     740                 :          5 :                                         i2c_bus->recv_count > i2c_bus->curr_recv_count)) {
     741                 :          0 :                         break;
     742                 :            :                 }
     743                 :            :         }
     744                 :            : 
     745                 :            :         /* Workaround for large data receive in case of FIFO space issues */
     746         [ -  + ]:          3 :         if (cdns_is_fifo_hold_quirk(i2c_bus, i2c_bus->recv_count > i2c_bus->curr_recv_count)) {
     747                 :          0 :                 transfer_size = i2c_bus->recv_count - i2c_bus->fifo_depth;
     748                 :            : 
     749         [ #  # ]:          0 :                 if (transfer_size > i2c_bus->transfer_size) {
     750                 :          0 :                         xfer_size = i2c_bus->transfer_size;
     751                 :            :                 } else {
     752                 :          0 :                         xfer_size = transfer_size;
     753                 :            :                 }
     754                 :            : 
     755                 :            :                 /* Busy-wait until FIFO has space for more data */
     756                 :          0 :                 while (cdns_i2c_readreg(i2c_bus, CDNS_I2C_XFER_SIZE_OFFSET) !=
     757         [ #  # ]:          0 :                        (i2c_bus->curr_recv_count - i2c_bus->fifo_depth)) {
     758                 :            :                 }
     759                 :            : 
     760                 :            :                 /* Update the transfer size for the next batch of data */
     761                 :          0 :                 cdns_i2c_writereg(i2c_bus, xfer_size, CDNS_I2C_XFER_SIZE_OFFSET);
     762                 :          0 :                 i2c_bus->curr_recv_count = xfer_size + i2c_bus->fifo_depth;
     763                 :            :         }
     764                 :            : 
     765                 :            :         /* Complete transfer if all data has been received and no more data is expected */
     766         [ +  - ]:          3 :         if (((isr_status & CDNS_I2C_IXR_COMP) == CDNS_I2C_IXR_COMP) &&
     767         [ +  - ]:          3 :             (i2c_bus->recv_count == 0U)) {
     768                 :            :                 /* Release bus hold if no longer needed */
     769         [ +  - ]:          3 :                 if (i2c_bus->bus_hold_flag == 0U) {
     770                 :          3 :                         cdns_i2c_clear_bus_hold(i2c_bus);
     771                 :            :                 }
     772                 :            :                 /* Notify completion of the transfer */
     773                 :          3 :                 (void)k_event_post(&i2c_bus->xfer_done, I2C_XFER_COMPLETION_EVENT);
     774                 :            :         }
     775                 :            : }
     776                 :            : 
     777                 :            : /**
     778                 :            :  * cdns_i2c_master_handle_transmit_interrupt - Handles I2C master transmit interrupts
     779                 :            :  * @i2c_bus: Pointer to the I2C data structure
     780                 :            :  * @isr_status: Interrupt status, indicating the cause of the interrupt
     781                 :            :  */
     782                 :          3 : static void cdns_i2c_master_handle_transmit_interrupt(struct cdns_i2c_data *i2c_bus,
     783                 :            :                                                       uint32_t isr_status)
     784                 :            : {
     785                 :            :         uint32_t avail_bytes;
     786                 :            :         uint32_t bytes_to_send;
     787                 :            : 
     788                 :            :         /* Handle transmission interrupt (data sent or transfer complete) */
     789         [ -  + ]:          3 :         if ((isr_status & CDNS_I2C_IXR_COMP) == 0U) {
     790                 :          0 :                 return;
     791                 :            :         }
     792                 :            : 
     793                 :            :         /* Sending data: Check if there is any data left to send */
     794         [ -  + ]:          3 :         if (i2c_bus->send_count > 0U) {
     795                 :            :                 /* Calculate how many bytes can be sent based on FIFO availability */
     796                 :          0 :                 avail_bytes = i2c_bus->fifo_depth - cdns_i2c_readreg(i2c_bus,
     797                 :            :                                                                      CDNS_I2C_XFER_SIZE_OFFSET);
     798                 :            : 
     799         [ #  # ]:          0 :                 if (i2c_bus->send_count > avail_bytes) {
     800                 :          0 :                         bytes_to_send = avail_bytes;
     801                 :            :                 } else {
     802                 :          0 :                         bytes_to_send = i2c_bus->send_count;
     803                 :            :                 }
     804                 :            : 
     805                 :            :                 /* Write data to the I2C data register */
     806         [ #  # ]:          0 :                 while (bytes_to_send > 0U) {
     807                 :          0 :                         cdns_i2c_writereg(i2c_bus, *(i2c_bus->p_send_buf), CDNS_I2C_DATA_OFFSET);
     808                 :          0 :                         i2c_bus->p_send_buf++;
     809                 :          0 :                         i2c_bus->send_count--;
     810                 :          0 :                         bytes_to_send--;
     811                 :            :                 }
     812                 :            :         } else {
     813                 :            :                 /* If there is no data to send, signal transfer completion */
     814                 :          3 :                 (void)k_event_post(&i2c_bus->xfer_done, I2C_XFER_COMPLETION_EVENT);
     815                 :            :         }
     816                 :            : 
     817                 :            :         /* Clear bus hold if no more data is pending */
     818   [ +  -  +  + ]:          3 :         if ((i2c_bus->send_count == 0U) && (i2c_bus->bus_hold_flag == 0U)) {
     819                 :          1 :                 cdns_i2c_clear_bus_hold(i2c_bus);
     820                 :            :         }
     821                 :            : }
     822                 :            : 
     823                 :            : /**
     824                 :            :  * cdns_i2c_master_isr - Interrupt handler for the I2C device in master role
     825                 :            :  * @i2c_bus: Pointer to I2C device private data structure
     826                 :            :  *
     827                 :            :  * This function handles various interrupt events including data received,
     828                 :            :  * transfer complete, and error interrupts for the I2C master role.
     829                 :            :  */
     830                 :          6 : static void cdns_i2c_master_isr(struct cdns_i2c_data *i2c_bus)
     831                 :            : {
     832                 :            :         uint32_t isr_status;
     833                 :            : 
     834                 :            :         /* Read the interrupt status register */
     835                 :          6 :         isr_status = cdns_i2c_readreg(i2c_bus, CDNS_I2C_ISR_OFFSET);
     836                 :            : 
     837                 :            :         /* Clear interrupt status */
     838                 :          6 :         cdns_i2c_writereg(i2c_bus, isr_status, CDNS_I2C_ISR_OFFSET);
     839                 :            : 
     840                 :            :         /* Update the error status based on interrupt flags */
     841                 :          6 :         i2c_bus->err_status = isr_status & CDNS_I2C_IXR_ERR_INTR_MASK;
     842                 :            : 
     843                 :            :         /* Handling NACK or arbitration lost interrupts */
     844         [ -  + ]:          6 :         if ((isr_status & (CDNS_I2C_IXR_NACK | CDNS_I2C_IXR_ARB_LOST)) != 0U) {
     845                 :          0 :                 (void)k_event_post(&i2c_bus->xfer_done, I2C_XFER_COMPLETION_EVENT);
     846                 :          0 :                 return;
     847                 :            :         }
     848                 :            : 
     849                 :            :         /* Handle reception interrupt */
     850         [ +  + ]:          6 :         if (i2c_bus->p_recv_buf != NULL) {
     851                 :          3 :                 cdns_i2c_master_handle_receive_interrupt(i2c_bus, isr_status);
     852                 :            :         }
     853                 :            : 
     854                 :            :         /* Handle transmission interrupt */
     855         [ +  + ]:          6 :         if (i2c_bus->p_recv_buf == NULL) {
     856                 :          3 :                 cdns_i2c_master_handle_transmit_interrupt(i2c_bus, isr_status);
     857                 :            :         }
     858                 :            : }
     859                 :            : 
     860                 :            : /**
     861                 :            :  * cdns_i2c_isr - Interrupt handler for the I2C controller
     862                 :            :  * @dev: Pointer to I2C device
     863                 :            :  *
     864                 :            :  * If the controller is in slave mode, the slave ISR is invoked.
     865                 :            :  * If the controller is in master mode, the master ISR is used.
     866                 :            :  */
     867                 :          6 : static void cdns_i2c_isr(const struct device *dev)
     868                 :            : {
     869                 :          6 :         struct cdns_i2c_data *i2c_bus = (struct cdns_i2c_data *)dev->data;
     870                 :            : 
     871                 :            : #if defined(CONFIG_I2C_TARGET)
     872                 :            :         /* Handle the interrupt for slave mode */
     873                 :            :         if (i2c_bus->dev_mode == CDNS_I2C_MODE_SLAVE) {
     874                 :            :                 cdns_i2c_slave_isr(i2c_bus);
     875                 :            :                 return;
     876                 :            :         }
     877                 :            : #endif
     878                 :            :         /* Handle the interrupt for master mode */
     879                 :          6 :         cdns_i2c_master_isr(i2c_bus);
     880                 :          6 : }
     881                 :            : 
     882                 :            : /**
     883                 :            :  * cdns_i2c_mrecv - Prepare and start a master receive operation
     884                 :            :  * @i2c_bus: Pointer to the I2C data structure
     885                 :            :  * @msg_addr: The address of the slave device to receive data from
     886                 :            :  */
     887                 :          3 : static void cdns_i2c_mrecv(struct cdns_i2c_data *i2c_bus, uint16_t msg_addr)
     888                 :            : {
     889                 :            :         uint32_t ctrl_reg;
     890                 :            :         uint32_t isr_status;
     891                 :          3 :         bool hold_clear = false;
     892                 :          3 :         bool irq_save = false;
     893                 :            :         uint32_t addr;
     894                 :            : 
     895                 :            :         /* Initialize the receive buffer and count */
     896                 :          3 :         i2c_bus->p_recv_buf = i2c_bus->p_msg->buf;
     897                 :          3 :         i2c_bus->recv_count = i2c_bus->p_msg->len;
     898                 :          3 :         i2c_bus->curr_recv_count = i2c_bus->recv_count;
     899                 :            : 
     900                 :            :         /* Prepare controller for master receive mode and clear FIFO */
     901                 :          3 :         ctrl_reg = cdns_i2c_readreg(i2c_bus, CDNS_I2C_CR_OFFSET);
     902                 :          3 :         ctrl_reg |= CDNS_I2C_CR_RW | CDNS_I2C_CR_CLR_FIFO;
     903                 :            : 
     904                 :            :         /* Check if the message size exceeds FIFO depth, hold the bus if true */
     905         [ -  + ]:          3 :         if (i2c_bus->recv_count > i2c_bus->fifo_depth) {
     906                 :          0 :                 ctrl_reg |= CDNS_I2C_CR_HOLD;
     907                 :            :         }
     908                 :            : 
     909                 :          3 :         cdns_i2c_writereg(i2c_bus, ctrl_reg, CDNS_I2C_CR_OFFSET);
     910                 :            : 
     911                 :            :         /* Clear the interrupts in interrupt status register */
     912                 :          3 :         isr_status = cdns_i2c_readreg(i2c_bus, CDNS_I2C_ISR_OFFSET);
     913                 :          3 :         cdns_i2c_writereg(i2c_bus, isr_status, CDNS_I2C_ISR_OFFSET);
     914                 :            : 
     915                 :            :         /* Set transfer size register and enable interrupts */
     916         [ -  + ]:          3 :         if ((i2c_bus->recv_count) > (i2c_bus->transfer_size)) {
     917                 :          0 :                 cdns_i2c_writereg(i2c_bus, i2c_bus->transfer_size,
     918                 :            :                                   CDNS_I2C_XFER_SIZE_OFFSET);
     919                 :          0 :                 i2c_bus->curr_recv_count = i2c_bus->transfer_size;
     920                 :            :         } else {
     921                 :          3 :                 cdns_i2c_writereg(i2c_bus, i2c_bus->recv_count, CDNS_I2C_XFER_SIZE_OFFSET);
     922                 :            :         }
     923                 :            : 
     924                 :            :         /* Determine whether to clear the hold bit based on conditions */
     925   [ +  -  +  - ]:          3 :         if ((i2c_bus->bus_hold_flag == 0U) && (i2c_bus->recv_count <= i2c_bus->fifo_depth)) {
     926         [ +  + ]:          3 :                 if ((ctrl_reg & CDNS_I2C_CR_HOLD) != 0U) {
     927                 :          2 :                         hold_clear = true;
     928                 :            : 
     929                 :            :                         /* Check if broken hold flag requires special handling */
     930         [ -  + ]:          2 :                         if ((i2c_bus->broken_hold_flag & CDNS_I2C_BROKEN_HOLD_BIT) != 0U) {
     931                 :          0 :                                 irq_save = true;
     932                 :            :                         }
     933                 :            :                 }
     934                 :            :         }
     935                 :            : 
     936                 :            :         /* Mask address and prepare for I2C communication */
     937                 :          3 :         addr = msg_addr;
     938                 :          3 :         addr &= CDNS_I2C_ADDR_MASK;
     939                 :            : 
     940                 :            :         /* Handle clearing of the hold bit */
     941         [ +  + ]:          3 :         if (hold_clear) {
     942                 :          2 :                 ctrl_reg &= ~CDNS_I2C_CR_HOLD;
     943                 :          2 :                 ctrl_reg &= ~CDNS_I2C_CR_CLR_FIFO;
     944                 :            : 
     945                 :            :                 /* If IRQ saving is necessary, disable interrupts for a safe register write */
     946         [ -  + ]:          2 :                 if (irq_save) {
     947                 :          0 :                         (void)irq_lock();
     948                 :            :                 }
     949                 :            : 
     950                 :            :                 /* Write the address and control register values */
     951                 :          2 :                 cdns_i2c_writereg(i2c_bus, addr, CDNS_I2C_ADDR_OFFSET);
     952                 :          2 :                 cdns_i2c_writereg(i2c_bus, ctrl_reg, CDNS_I2C_CR_OFFSET);
     953                 :            :                 /* Read back to ensure write completion */
     954                 :          2 :                 (void)cdns_i2c_readreg(i2c_bus, CDNS_I2C_CR_OFFSET);
     955                 :            : 
     956                 :            :                 /* Restore IRQ state if needed */
     957         [ -  + ]:          2 :                 if (irq_save) {
     958                 :          0 :                         irq_unlock(0);
     959                 :            :                 }
     960                 :            :         } else {
     961                 :            :                 /* Directly write the address if no need to clear the hold bit */
     962                 :          1 :                 cdns_i2c_writereg(i2c_bus, addr, CDNS_I2C_ADDR_OFFSET);
     963                 :            :         }
     964                 :            : 
     965                 :            :         /* Enable interrupts */
     966                 :          3 :         cdns_i2c_writereg(i2c_bus, CDNS_I2C_ENABLED_INTR_MASK, CDNS_I2C_IER_OFFSET);
     967                 :          3 : }
     968                 :            : 
     969                 :            : /**
     970                 :            :  * cdns_i2c_msend - Prepare and start a master send operation
     971                 :            :  * @i2c_bus: Pointer to the I2C data structure
     972                 :            :  * @msg_addr: I2C address of the slave to communicate with
     973                 :            :  */
     974                 :          3 : static void cdns_i2c_msend(struct cdns_i2c_data *i2c_bus, uint16_t msg_addr)
     975                 :            : {
     976                 :            :         uint32_t avail_bytes;
     977                 :            :         uint32_t bytes_to_send;
     978                 :            :         uint32_t ctrl_reg;
     979                 :            :         uint32_t isr_status;
     980                 :            : 
     981                 :            :         /* Initialize send buffer and update send count */
     982                 :          3 :         i2c_bus->p_recv_buf = NULL;
     983                 :          3 :         i2c_bus->p_send_buf = i2c_bus->p_msg->buf;
     984                 :          3 :         i2c_bus->send_count = i2c_bus->p_msg->len;
     985                 :            : 
     986                 :            :         /* Configure the controller in Master transmit mode and clear FIFO. */
     987                 :          3 :         ctrl_reg = cdns_i2c_readreg(i2c_bus, CDNS_I2C_CR_OFFSET);
     988                 :          3 :         ctrl_reg &= ~CDNS_I2C_CR_RW;
     989                 :          3 :         ctrl_reg |= CDNS_I2C_CR_CLR_FIFO;
     990                 :            : 
     991                 :            :         /* Check if the message size exceeds FIFO depth, hold the bus if true */
     992         [ -  + ]:          3 :         if (i2c_bus->send_count > i2c_bus->fifo_depth) {
     993                 :          0 :                 ctrl_reg |= CDNS_I2C_CR_HOLD;
     994                 :            :         }
     995                 :          3 :         cdns_i2c_writereg(i2c_bus, ctrl_reg, CDNS_I2C_CR_OFFSET);
     996                 :            : 
     997                 :            :         /* Clear any previous interrupt flags */
     998                 :          3 :         isr_status = cdns_i2c_readreg(i2c_bus, CDNS_I2C_ISR_OFFSET);
     999                 :          3 :         cdns_i2c_writereg(i2c_bus, isr_status, CDNS_I2C_ISR_OFFSET);
    1000                 :            : 
    1001                 :            :         /* Calculate available FIFO space and determine how many bytes to send */
    1002                 :          3 :         avail_bytes = i2c_bus->fifo_depth - cdns_i2c_readreg(i2c_bus, CDNS_I2C_XFER_SIZE_OFFSET);
    1003                 :          3 :         bytes_to_send = (i2c_bus->send_count > avail_bytes) ? avail_bytes : i2c_bus->send_count;
    1004                 :            : 
    1005                 :            :         /* Send data to FIFO until all bytes are transmitted */
    1006         [ +  + ]:          6 :         while (bytes_to_send > 0U) {
    1007                 :          3 :                 cdns_i2c_writereg(i2c_bus, (*(i2c_bus->p_send_buf)), CDNS_I2C_DATA_OFFSET);
    1008                 :          3 :                 (i2c_bus->p_send_buf)++;
    1009                 :          3 :                 i2c_bus->send_count--;
    1010                 :          3 :                 bytes_to_send--;
    1011                 :            :         }
    1012                 :            : 
    1013                 :            :         /* Clear the 'hold bus' flag if there's no more data and it's the last message */
    1014   [ +  +  +  - ]:          3 :         if ((i2c_bus->bus_hold_flag == 0U) && (i2c_bus->send_count == 0U)) {
    1015                 :          1 :                 cdns_i2c_clear_bus_hold(i2c_bus);
    1016                 :            :         }
    1017                 :            : 
    1018                 :            :         /* Set the slave address to trigger operation. */
    1019                 :          3 :         cdns_i2c_writereg(i2c_bus, ((uint32_t)msg_addr & CDNS_I2C_ADDR_MASK),
    1020                 :            :                                    CDNS_I2C_ADDR_OFFSET);
    1021                 :            : 
    1022                 :            :         /* Enable interrupts after data transmission starts */
    1023                 :          3 :         cdns_i2c_writereg(i2c_bus, CDNS_I2C_ENABLED_INTR_MASK, CDNS_I2C_IER_OFFSET);
    1024                 :          3 : }
    1025                 :            : 
    1026                 :            : /**
    1027                 :            :  * cdns_i2c_master_reset - Reset the I2C master interface
    1028                 :            :  * @i2c_bus: Pointer to the i2c driver instance
    1029                 :            :  *
    1030                 :            :  * This function performs a full reset of the I2C master interface
    1031                 :            :  * The reset ensures that the interface is returned to a known idle state.
    1032                 :            :  */
    1033                 :          0 : static void cdns_i2c_master_reset(struct cdns_i2c_data *i2c_bus)
    1034                 :            : {
    1035                 :            :         uint32_t regval;
    1036                 :            : 
    1037                 :            :         /* Disable the interrupts */
    1038                 :          0 :         cdns_i2c_writereg(i2c_bus, CDNS_I2C_IXR_ALL_INTR_MASK, CDNS_I2C_IDR_OFFSET);
    1039                 :            : 
    1040                 :            :         /* Clear the hold bit and flush FIFOs */
    1041                 :          0 :         regval = cdns_i2c_readreg(i2c_bus, CDNS_I2C_CR_OFFSET);
    1042                 :          0 :         regval &= ~CDNS_I2C_CR_HOLD;
    1043                 :          0 :         regval |= CDNS_I2C_CR_CLR_FIFO;
    1044                 :          0 :         cdns_i2c_writereg(i2c_bus, regval, CDNS_I2C_CR_OFFSET);
    1045                 :            : 
    1046                 :            :         /* Reset transfer count register to zero */
    1047                 :          0 :         cdns_i2c_writereg(i2c_bus, 0, CDNS_I2C_XFER_SIZE_OFFSET);
    1048                 :            : 
    1049                 :            :         /* Clear the interrupt status register */
    1050                 :          0 :         regval = cdns_i2c_readreg(i2c_bus, CDNS_I2C_ISR_OFFSET);
    1051                 :          0 :         cdns_i2c_writereg(i2c_bus, regval, CDNS_I2C_ISR_OFFSET);
    1052                 :            : 
    1053                 :            :         /* Clear the status register */
    1054                 :          0 :         regval = cdns_i2c_readreg(i2c_bus, CDNS_I2C_SR_OFFSET);
    1055                 :          0 :         cdns_i2c_writereg(i2c_bus, regval, CDNS_I2C_SR_OFFSET);
    1056                 :          0 : }
    1057                 :            : 
    1058                 :            : /**
    1059                 :            :  * cdns_i2c_process_msg - Processes an I2C message on the specified I2C bus
    1060                 :            :  * @i2c_bus: Pointer to the I2C data structure
    1061                 :            :  * @msg: Pointer to the I2C message to be processed
    1062                 :            :  * @addr: The 7-bit or 10-bit I2C address of the slave device
    1063                 :            :  *
    1064                 :            :  * Return: 0 on success, negative error code on failure.
    1065                 :            :  */
    1066                 :          6 : static int32_t cdns_i2c_process_msg(struct cdns_i2c_data *i2c_bus, struct i2c_msg *msg,
    1067                 :            :                                     uint16_t addr)
    1068                 :            : {
    1069                 :          6 :         int32_t ret = 0;
    1070                 :            :         uint32_t reg;
    1071                 :            :         k_timeout_t msg_timeout;
    1072                 :            :         uint32_t events;
    1073                 :            : 
    1074                 :            :         /* Initialize message processing state */
    1075                 :          6 :         i2c_bus->p_msg = msg;
    1076                 :          6 :         i2c_bus->err_status = 0U;
    1077                 :          6 :         (void)k_event_clear(&i2c_bus->xfer_done, (uint32_t)I2C_XFER_COMPLETION_EVENT);
    1078                 :            : 
    1079                 :            :         /* Handle 10-bit addressing mode */
    1080                 :          6 :         reg = cdns_i2c_readreg(i2c_bus, CDNS_I2C_CR_OFFSET);
    1081         [ -  + ]:          6 :         if ((msg->flags & I2C_MSG_ADDR_10_BITS) != 0U) {
    1082                 :            :                 /* Enable 10-bit address mode if not already enabled */
    1083         [ #  # ]:          0 :                 if ((reg & CDNS_I2C_CR_NEA) == CDNS_I2C_CR_NEA) {
    1084                 :          0 :                         cdns_i2c_writereg(i2c_bus,
    1085                 :            :                                         reg & ~CDNS_I2C_CR_NEA,
    1086                 :            :                                         CDNS_I2C_CR_OFFSET);
    1087                 :            :                 }
    1088                 :            :         } else {
    1089                 :            :                 /* Disable 10-bit address mode if currently enabled */
    1090         [ -  + ]:          6 :                 if ((reg & CDNS_I2C_CR_NEA) == 0U) {
    1091                 :          0 :                         cdns_i2c_writereg(i2c_bus,
    1092                 :          0 :                                         reg | CDNS_I2C_CR_NEA,
    1093                 :            :                                         CDNS_I2C_CR_OFFSET);
    1094                 :            :                 }
    1095                 :            :         }
    1096                 :            : 
    1097                 :            :         /* Handle read/write flag and perform the appropriate action */
    1098         [ +  + ]:          6 :         if ((msg->flags & I2C_MSG_READ) != 0U) {
    1099                 :          3 :                 cdns_i2c_mrecv(i2c_bus, addr); /* Receive data */
    1100                 :            :         } else {
    1101                 :          3 :                 cdns_i2c_msend(i2c_bus, addr); /* Send data */
    1102                 :            :         }
    1103                 :            : 
    1104                 :            :         /* Calculate the minimal timeout based on message length */
    1105                 :          6 :         msg_timeout.ticks = (((k_ticks_t)(msg->len) * 8)*(CDNS_I2C_TICKS_PER_SEC)) /
    1106                 :          6 :                              ((k_ticks_t)(i2c_bus->i2c_clk));
    1107                 :          6 :         msg_timeout.ticks += (CDNS_I2C_TICKS_PER_SEC / 2);
    1108         [ +  - ]:          6 :         if (msg_timeout.ticks < CDNS_I2C_TIMEOUT_TICKS) {
    1109                 :          6 :                 msg_timeout.ticks = CDNS_I2C_TIMEOUT_TICKS;
    1110                 :            :         }
    1111                 :            : 
    1112                 :            :         /* Wait for the completion signal or timeout */
    1113                 :          6 :         events = k_event_wait(&i2c_bus->xfer_done, (uint32_t)I2C_XFER_COMPLETION_EVENT,
    1114                 :            :                               false, msg_timeout);
    1115         [ -  + ]:          6 :         if ((events & I2C_XFER_COMPLETION_EVENT) == 0U) {
    1116                 :            :                 /* Timeout occurred, reset the master */
    1117                 :          0 :                 cdns_i2c_master_reset(i2c_bus);
    1118                 :          0 :                 ret = -ETIMEDOUT;
    1119                 :          0 :                 goto out;
    1120                 :            :         }
    1121                 :            : 
    1122                 :            :         /* Disable interrupt masking for the current transfer */
    1123                 :          6 :         cdns_i2c_writereg(i2c_bus, CDNS_I2C_IXR_ALL_INTR_MASK, CDNS_I2C_IDR_OFFSET);
    1124                 :            : 
    1125                 :            :         /* If it is bus arbitration error, try again */
    1126         [ +  - ]:          6 :         if ((i2c_bus->err_status & CDNS_I2C_IXR_ARB_LOST) == CDNS_I2C_IXR_ARB_LOST) {
    1127                 :          0 :                 ret = -EAGAIN;
    1128                 :            :         }
    1129                 :            : 
    1130                 :          6 : out:
    1131                 :          6 :         return ret;
    1132                 :            : }
    1133                 :            : 
    1134                 :            : /**
    1135                 :            :  * cdns_i2c_wait_for_bus_free - Wait for the I2C bus to become free.
    1136                 :            :  * @i2c_bus: Pointer to the I2C data structure that holds bus state information.
    1137                 :            :  * @timeout_us: Maximum time (in microseconds) to wait for the bus to become free.
    1138                 :            :  *
    1139                 :            :  * This function waits for the I2C bus to become idle. It checks the bus state
    1140                 :            :  * register periodically until the bus is free or the timeout occurs.
    1141                 :            :  *
    1142                 :            :  * Return: true if the bus is free within the timeout, false otherwise.
    1143                 :            :  */
    1144                 :          4 : static bool cdns_i2c_wait_for_bus_free(struct cdns_i2c_data *i2c_bus, uint32_t timeout_us)
    1145                 :            : {
    1146                 :          4 :         bool ret_flag = false;
    1147                 :            :         uint32_t reg;
    1148                 :            : 
    1149                 :            :         /* Poll until the bus is free or the timeout is reached */
    1150         [ +  - ]:          4 :         while (timeout_us > 0U) {
    1151                 :          4 :                 reg = cdns_i2c_readreg(i2c_bus, CDNS_I2C_SR_OFFSET);
    1152         [ +  - ]:          4 :                 if ((reg & CDNS_I2C_SR_BA) == 0U) {
    1153                 :            :                         /* Bus Available (BA) bit is cleared, the bus is free */
    1154                 :          4 :                         ret_flag = true;
    1155                 :          4 :                         break;
    1156                 :            :                 }
    1157                 :            : 
    1158                 :            :                 /* Wait for a small period before checking again */
    1159                 :          0 :                 (void)k_usleep((int32_t)CDNS_I2C_POLL_US);
    1160                 :          0 :                 timeout_us -= CDNS_I2C_POLL_US;
    1161                 :            :         }
    1162                 :            : 
    1163         [ -  + ]:          4 :         if (timeout_us == 0U) {
    1164                 :            :                 /* Timeout reached, bus not available */
    1165                 :          0 :                 ret_flag = false;
    1166                 :            :         }
    1167                 :            : 
    1168                 :          4 :         return ret_flag;
    1169                 :            : }
    1170                 :            : 
    1171                 :            : /**
    1172                 :            :  * cdns_i2c_master_handle_repeated_start - Handle repeated start during I2C master transfer
    1173                 :            :  * @i2c_bus: Pointer to the I2C data structure that holds bus state information
    1174                 :            :  * @msgs: Array of I2C messages to be processed
    1175                 :            :  * @num_msgs: Number of messages in the @msgs array
    1176                 :            :  *
    1177                 :            :  * Return: 0 on success, or a negative error code on failure
    1178                 :            :  */
    1179                 :          2 : static int32_t cdns_i2c_master_handle_repeated_start(struct cdns_i2c_data *i2c_bus,
    1180                 :            :                                                      struct i2c_msg *msgs, uint8_t num_msgs)
    1181                 :            : {
    1182                 :          2 :         int32_t ret = 0;
    1183                 :            :         uint32_t reg;
    1184                 :            :         uint32_t count;
    1185                 :          2 :         bool hold_quirk = false;
    1186                 :          2 :         struct i2c_msg *msg_ptr = msgs;
    1187                 :            : 
    1188                 :            :         /* Handle potential quirks with bus holding on hardware using Controller Version 1.0 */
    1189         [ -  + ]:          2 :         if ((i2c_bus->broken_hold_flag & CDNS_I2C_BROKEN_HOLD_BIT) == CDNS_I2C_BROKEN_HOLD_BIT) {
    1190                 :          0 :                 hold_quirk = true;
    1191                 :            :         }
    1192                 :            : 
    1193                 :            :         /* Check if the first message is a read, then no repeated start can follow */
    1194   [ +  -  -  + ]:          2 :         for (count = 0; ((count < ((uint32_t)num_msgs - 1U)) && hold_quirk); count++) {
    1195         [ #  # ]:          0 :                 if ((msg_ptr[count].flags & I2C_MSG_READ) != 0U) {
    1196         [ #  # ]:          0 :                         LOG_ERR("Can't do repeated start after a receive message");
    1197                 :          0 :                         ret = -EOPNOTSUPP;
    1198                 :          0 :                         goto out;
    1199                 :            :                 }
    1200                 :            :         }
    1201                 :            : 
    1202                 :            :         /* Set the hold flag and register */
    1203                 :          2 :         i2c_bus->bus_hold_flag = 1;
    1204                 :          2 :         reg = cdns_i2c_readreg(i2c_bus, CDNS_I2C_CR_OFFSET);
    1205                 :          2 :         reg |= CDNS_I2C_CR_HOLD;
    1206                 :          2 :         cdns_i2c_writereg(i2c_bus, reg, CDNS_I2C_CR_OFFSET);
    1207                 :            : 
    1208                 :          2 : out:
    1209                 :          2 :         return ret;
    1210                 :            : }
    1211                 :            : 
    1212                 :            : /**
    1213                 :            :  * cdns_i2c_master_handle_transfer_error - Handle errors during I2C master transfer.
    1214                 :            :  * @i2c_bus: Pointer to the I2C data structure
    1215                 :            :  *
    1216                 :            :  * Return: -EIO or -ENXIO.
    1217                 :            :  */
    1218                 :          0 : static int32_t cdns_i2c_master_handle_transfer_error(struct cdns_i2c_data *i2c_bus)
    1219                 :            : {
    1220                 :            :         int32_t ret;
    1221                 :            : 
    1222                 :            :         /* Perform a reset of the I2C master to clear the error condition */
    1223                 :          0 :         cdns_i2c_master_reset(i2c_bus);
    1224                 :            : 
    1225         [ #  # ]:          0 :         if ((i2c_bus->err_status & CDNS_I2C_IXR_NACK) != 0U) {
    1226                 :          0 :                 ret = -ENXIO; /* No device found (NACK) */
    1227                 :            :         } else {
    1228                 :          0 :                 ret = -EIO; /* General I/O error */
    1229                 :            :         }
    1230                 :            : 
    1231                 :          0 :         return ret;
    1232                 :            : }
    1233                 :            : 
    1234                 :            : /**
    1235                 :            :  * cdns_i2c_master_transfer - Performs an I2C master transfer using the Cadence I2C controller.
    1236                 :            :  * @dev: Pointer to the device structure representing the I2C controller.
    1237                 :            :  * @msgs: Array of I2C message structures representing the messages to be sent/received.
    1238                 :            :  * @num_msgs: Number of messages in the msgs array.
    1239                 :            :  * @addr: The 7-bit or 10-bit I2C address of the slave device.
    1240                 :            :  *
    1241                 :            :  * Return: 0 on success, negative error code on failure.
    1242                 :            :  */
    1243                 :          4 : static int32_t cdns_i2c_master_transfer(const struct device *dev, struct i2c_msg *msgs,
    1244                 :            :                                         uint8_t num_msgs, uint16_t addr)
    1245                 :            : {
    1246                 :          4 :         struct cdns_i2c_data *i2c_bus = (struct cdns_i2c_data *)dev->data;
    1247                 :          4 :         int32_t ret = 0;
    1248                 :            :         uint32_t count;
    1249                 :          4 :         struct i2c_msg *msg_ptr = msgs;
    1250                 :            : 
    1251                 :            : #if defined(CONFIG_I2C_TARGET)
    1252                 :            :         bool change_role = false;
    1253                 :            : #endif
    1254                 :            : 
    1255                 :          4 :         (void)k_mutex_lock(&i2c_bus->bus_mutex, K_FOREVER);
    1256                 :            : 
    1257                 :            : #if defined(CONFIG_I2C_TARGET)
    1258                 :            :         /* Switch to master mode if operating in slave mode */
    1259                 :            :         if (i2c_bus->dev_mode == CDNS_I2C_MODE_SLAVE) {
    1260                 :            :                 if (i2c_bus->slave_state != CDNS_I2C_SLAVE_STATE_IDLE) {
    1261                 :            :                         ret = -EAGAIN;
    1262                 :            :                         goto out;
    1263                 :            :                 }
    1264                 :            : 
    1265                 :            :                 /* Switch mode to master and set flag to switch back to slave after transfer */
    1266                 :            :                 cdns_i2c_set_mode(CDNS_I2C_MODE_MASTER, i2c_bus);
    1267                 :            :                 change_role = true;
    1268                 :            :         }
    1269                 :            : #endif
    1270                 :            : 
    1271                 :            :         /* Wait for the bus to be free */
    1272         [ -  + ]:          4 :         if (cdns_i2c_wait_for_bus_free(i2c_bus, CDNS_I2C_TIMEOUT_US) == false) {
    1273                 :          0 :                 ret = -EAGAIN;
    1274                 :          0 :                 goto out;
    1275                 :            :         }
    1276                 :            : 
    1277                 :            :         /* Handle repeated start for multiple messages */
    1278         [ +  + ]:          4 :         if (num_msgs > 1U) {
    1279                 :          2 :                 ret = cdns_i2c_master_handle_repeated_start(i2c_bus, msgs, num_msgs);
    1280         [ -  + ]:          2 :                 if (ret != 0) {
    1281                 :          0 :                         goto out;
    1282                 :            :                 }
    1283                 :            :         }
    1284                 :            : 
    1285                 :            :         /* Process each message individually */
    1286         [ +  + ]:         10 :         for (count = 0; count < num_msgs; count++) {
    1287                 :            :                 /* Reset hold flag for the last message */
    1288         [ +  + ]:          6 :                 if (count == ((uint32_t)num_msgs - 1U)) {
    1289                 :          4 :                         i2c_bus->bus_hold_flag = 0;
    1290                 :            :                 }
    1291                 :            : 
    1292                 :            :                 /* Process the current message */
    1293                 :          6 :                 ret = cdns_i2c_process_msg(i2c_bus, msg_ptr, addr);
    1294         [ -  + ]:          6 :                 if (ret != 0) {
    1295                 :          0 :                         goto out;
    1296                 :            :                 }
    1297                 :            : 
    1298                 :            :                 /* Handle any errors during the transfer */
    1299         [ -  + ]:          6 :                 if ((i2c_bus->err_status) != 0U) {
    1300                 :          0 :                         ret = cdns_i2c_master_handle_transfer_error(i2c_bus);
    1301         [ #  # ]:          0 :                         if (ret != 0) {
    1302                 :          0 :                                 goto out;
    1303                 :            :                         }
    1304                 :            :                 }
    1305                 :            : 
    1306                 :          6 :                 msg_ptr++;
    1307                 :            :         }
    1308                 :            : 
    1309                 :          4 : out:
    1310                 :            : #if defined(CONFIG_I2C_TARGET)
    1311                 :            :         /* Switch back to slave mode if the role was changed */
    1312                 :            :         if (change_role) {
    1313                 :            :                 cdns_i2c_set_mode(CDNS_I2C_MODE_SLAVE, i2c_bus);
    1314                 :            :         }
    1315                 :            : #endif
    1316                 :          4 :         (void)k_mutex_unlock(&i2c_bus->bus_mutex);
    1317                 :            : 
    1318                 :          4 :         return ret;
    1319                 :            : }
    1320                 :            : 
    1321                 :            : #if defined(CONFIG_I2C_TARGET)
    1322                 :            : /**
    1323                 :            :  * cdns_i2c_target_register - Registers the device as an I2C target (slave).
    1324                 :            :  * @dev: Pointer to the device structure representing the I2C bus.
    1325                 :            :  * @slave_cfg: Configuration settings for the I2C target (slave).
    1326                 :            :  *
    1327                 :            :  * Return: 0 on success, or a negative error code.
    1328                 :            :  */
    1329                 :            : static int32_t cdns_i2c_target_register(const struct device *dev,
    1330                 :            :                                         struct i2c_target_config *slave_cfg)
    1331                 :            : {
    1332                 :            :         struct cdns_i2c_data *i2c_bus = (struct cdns_i2c_data *)dev->data;
    1333                 :            : 
    1334                 :            :         /* Return busy error if a slave is already configured */
    1335                 :            :         if (i2c_bus->slave != NULL) {
    1336                 :            :                 return -EBUSY;
    1337                 :            :         }
    1338                 :            : 
    1339                 :            :         /* Check for unsupported 10-bit address mode */
    1340                 :            :         if ((slave_cfg->flags & I2C_TARGET_FLAGS_ADDR_10_BITS) == I2C_TARGET_FLAGS_ADDR_10_BITS) {
    1341                 :            :                 return -EAFNOSUPPORT;
    1342                 :            :         }
    1343                 :            : 
    1344                 :            :         (void)k_mutex_lock(&i2c_bus->bus_mutex, K_FOREVER);
    1345                 :            : 
    1346                 :            :         /* Store the slave configuration */
    1347                 :            :         i2c_bus->slave = slave_cfg;
    1348                 :            : 
    1349                 :            :         /* Enable I2C slave mode */
    1350                 :            :         cdns_i2c_set_mode(CDNS_I2C_MODE_SLAVE, i2c_bus);
    1351                 :            : 
    1352                 :            :         (void)k_mutex_unlock(&i2c_bus->bus_mutex);
    1353                 :            : 
    1354                 :            :         return 0;
    1355                 :            : }
    1356                 :            : 
    1357                 :            : /**
    1358                 :            :  * cdns_i2c_target_unregister - Unregisters the device as an I2C target (slave).
    1359                 :            :  * @dev: Pointer to the device structure representing the I2C bus.
    1360                 :            :  * @cfg: Pointer to the I2C target configuration, which is ignored in this function.
    1361                 :            :  *
    1362                 :            :  * Return: 0 on success.
    1363                 :            :  */
    1364                 :            : static int32_t cdns_i2c_target_unregister(const struct device *dev, struct i2c_target_config *cfg)
    1365                 :            : {
    1366                 :            :         struct cdns_i2c_data *i2c_bus = (struct cdns_i2c_data *)dev->data;
    1367                 :            :         (void)cfg;
    1368                 :            : 
    1369                 :            :         (void)k_mutex_lock(&i2c_bus->bus_mutex, K_FOREVER);
    1370                 :            : 
    1371                 :            :         /* Clear slave information */
    1372                 :            :         i2c_bus->slave = NULL;
    1373                 :            : 
    1374                 :            :         /* Switch to I2C master mode */
    1375                 :            :         cdns_i2c_set_mode(CDNS_I2C_MODE_MASTER, i2c_bus);
    1376                 :            : 
    1377                 :            :         (void)k_mutex_unlock(&i2c_bus->bus_mutex);
    1378                 :            : 
    1379                 :            :         return 0;
    1380                 :            : }
    1381                 :            : #endif
    1382                 :            : 
    1383                 :            : /**
    1384                 :            :  * cdns_i2c_init - Initialize the Cadence I2C controller
    1385                 :            :  * @dev: Pointer to the device
    1386                 :            :  *
    1387                 :            :  * Return: 0 on success, negative error code on failure.
    1388                 :            :  */
    1389                 :          1 : static int32_t cdns_i2c_init(const struct device *dev)
    1390                 :            : {
    1391                 :          1 :         const struct cdns_i2c_config *config = (const struct cdns_i2c_config *)dev->config;
    1392                 :          1 :         struct cdns_i2c_data *i2c_bus = (struct cdns_i2c_data *)dev->data;
    1393                 :            :         int32_t ret;
    1394                 :            : 
    1395                 :          1 :         (void)k_mutex_init(&i2c_bus->bus_mutex);
    1396                 :          1 :         k_event_init(&i2c_bus->xfer_done);
    1397                 :            : 
    1398                 :            : #if defined(CONFIG_I2C_TARGET)
    1399                 :            :         /* Set initial mode to master and confligure slave states */
    1400                 :            :         i2c_bus->dev_mode = CDNS_I2C_MODE_MASTER;
    1401                 :            :         i2c_bus->slave_state = CDNS_I2C_SLAVE_STATE_IDLE;
    1402                 :            :         i2c_bus->slave = NULL;
    1403                 :            :         i2c_bus->ctrl_reg_diva_divb = 0;
    1404                 :            : #endif
    1405                 :            : 
    1406                 :            :         /* Configure the control reg flags, transfer size */
    1407                 :          1 :         i2c_bus->ctrl_reg = CDNS_I2C_CR_ACK_EN | CDNS_I2C_CR_NEA | CDNS_I2C_CR_MS;
    1408                 :          1 :         i2c_bus->transfer_size = CDNS_I2C_TRANSFER_SIZE_DEFAULT;
    1409                 :            : 
    1410                 :            :         /* Set the I2C clock frequency */
    1411                 :          1 :         ret = cdns_i2c_setclk(i2c_bus, i2c_bus->input_clk);
    1412         [ -  + ]:          1 :         if (ret != 0) {
    1413         [ #  # ]:          0 :                 LOG_ERR("Invalid SCL clock: %u Hz", i2c_bus->i2c_clk);
    1414                 :          0 :                 ret = -EINVAL;
    1415                 :          0 :                 goto out;
    1416                 :            :         }
    1417                 :          1 :         i2c_bus->i2c_config = I2C_MODE_CONTROLLER | i2c_bus->i2c_clk;
    1418                 :            : 
    1419                 :            :         /* Configure IRQ */
    1420                 :          1 :         config->irq_config_func(dev);
    1421                 :            : 
    1422                 :            :         /* Enable the I2C peripheral */
    1423                 :          1 :         cdns_i2c_enable_peripheral(i2c_bus);
    1424                 :            : 
    1425         [ -  + ]:          1 :         LOG_INF("%u KHz mmio %08lx", i2c_bus->i2c_clk/1000U, i2c_bus->membase);
    1426                 :            : 
    1427                 :          1 : out:
    1428                 :          1 :         return ret;
    1429                 :            : }
    1430                 :            : 
    1431                 :            : /* I2C driver API structure for the Cadence I2C controller */
    1432                 :            : static const struct i2c_driver_api cdns_i2c_driver_api = {
    1433                 :            :         .configure = cdns_i2c_configure,
    1434                 :            :         .get_config = cdns_i2c_get_config,
    1435                 :            :         .transfer = cdns_i2c_master_transfer,
    1436                 :            : #if defined(CONFIG_I2C_TARGET)
    1437                 :            :         .target_register = cdns_i2c_target_register,
    1438                 :            :         .target_unregister = cdns_i2c_target_unregister,
    1439                 :            : #endif
    1440                 :            : };
    1441                 :            : 
    1442                 :            : /* CDNS_I2C_BROKEN_HOLD_COMPAT - Handles bus holding quirk based on the device's compatibility */
    1443                 :            : #define CDNS_I2C_BROKEN_HOLD_COMPAT(n) \
    1444                 :            :         COND_CODE_1(DT_INST_NODE_HAS_COMPAT(n, cdns_i2c_r1p10), (CDNS_I2C_BROKEN_HOLD_BIT), (0U))
    1445                 :            : 
    1446                 :            : #define CADENCE_I2C_INIT(n, compat)                                                         \
    1447                 :            :         static void cdns_i2c_config_func_##compat##_##n(const struct device *dev);          \
    1448                 :            :                                                                                             \
    1449                 :            :         static const struct cdns_i2c_config cdns_i2c_config_##compat##_##n = {              \
    1450                 :            :                 .irq_config_func = cdns_i2c_config_func_##compat##_##n,                     \
    1451                 :            :         };                                                                                  \
    1452                 :            :                                                                                             \
    1453                 :            :         static struct cdns_i2c_data cdns_i2c_data_##compat##_##n = {                        \
    1454                 :            :                 .membase = DT_INST_REG_ADDR(n),                                             \
    1455                 :            :                 .input_clk = DT_INST_PROP_BY_PHANDLE(n, clocks, clock_frequency),           \
    1456                 :            :                 .i2c_clk = DT_INST_PROP_OR(n, clock_frequency, CDNS_I2C_SPEED_STANDARD_HZ), \
    1457                 :            :                 .fifo_depth = DT_INST_PROP_OR(n, fifo_depth, CDNS_I2C_FIFO_DEPTH_DEFAULT),  \
    1458                 :            :                 .broken_hold_flag = CDNS_I2C_BROKEN_HOLD_COMPAT(n),                         \
    1459                 :            :         };                                                                                  \
    1460                 :            :                                                                                             \
    1461                 :            :         I2C_DEVICE_DT_INST_DEFINE(n, cdns_i2c_init, NULL,                                   \
    1462                 :            :                                 &cdns_i2c_data_##compat##_##n,                                  \
    1463                 :            :                                 &cdns_i2c_config_##compat##_##n, POST_KERNEL,                   \
    1464                 :            :                                 CONFIG_I2C_INIT_PRIORITY, &cdns_i2c_driver_api);        \
    1465                 :            :                                                                                             \
    1466                 :            :         static void cdns_i2c_config_func_##compat##_##n(const struct device *dev)           \
    1467                 :            :         {                                                                                   \
    1468                 :            :                 ARG_UNUSED(dev);                                                            \
    1469                 :            :                                                                                             \
    1470                 :            :                 IRQ_CONNECT(DT_INST_IRQN(n), DT_INST_IRQ(n, priority), cdns_i2c_isr,        \
    1471                 :            :                             DEVICE_DT_INST_GET(n), 0);                                      \
    1472                 :            :                                                                                             \
    1473                 :            :                 irq_enable(DT_INST_IRQN(n));                                                \
    1474                 :            :         }
    1475                 :            : 
    1476                 :            : #define DT_DRV_COMPAT cdns_i2c_r1p10
    1477                 :            : DT_INST_FOREACH_STATUS_OKAY_VARGS(CADENCE_I2C_INIT, DT_DRV_COMPAT)
    1478                 :            : 
    1479                 :            : #undef DT_DRV_COMPAT
    1480                 :            : #define DT_DRV_COMPAT cdns_i2c_r1p14
    1481                 :          1 : DT_INST_FOREACH_STATUS_OKAY_VARGS(CADENCE_I2C_INIT, DT_DRV_COMPAT)

Generated by: LCOV version 1.16-3-g92e2121