LCOV - code coverage report
Current view: top level - drivers/i2c - i2c_xilinx_axi.c (source / functions) Hit Total Coverage
Test: coverage.info Lines: 171 212 80.7 %
Date: 2025-01-07 17:56:07 Functions: 16 16 100.0 %
Legend: Lines: hit not hit | Branches: + taken - not taken # not executed Branches: 65 122 53.3 %

           Branch data     Line data    Source code
       1                 :            : /* SPDX-License-Identifier: Apache-2.0 */
       2                 :            : /*
       3                 :            :  * Copyright © 2023 Calian Ltd.  All rights reserved.
       4                 :            :  *
       5                 :            :  * Driver for the Xilinx AXI IIC Bus Interface.
       6                 :            :  * This is an FPGA logic core as described by Xilinx document PG090.
       7                 :            :  */
       8                 :            : 
       9                 :            : #include <errno.h>
      10                 :            : #include <zephyr/drivers/i2c.h>
      11                 :            : #include <zephyr/sys/util.h>
      12                 :            : #include <zephyr/logging/log.h>
      13                 :            : #include <zephyr/irq.h>
      14                 :            : 
      15                 :            : LOG_MODULE_REGISTER(i2c_xilinx_axi, CONFIG_I2C_LOG_LEVEL);
      16                 :            : 
      17                 :            : #include "i2c-priv.h"
      18                 :            : #include "i2c_xilinx_axi.h"
      19                 :            : 
      20                 :            : struct i2c_xilinx_axi_config {
      21                 :            :         mem_addr_t base;
      22                 :            :         void (*irq_config_func)(const struct device *dev);
      23                 :            :         /* Whether device has working dynamic read (broken prior to core rev. 2.1) */
      24                 :            :         bool dyn_read_working;
      25                 :            : };
      26                 :            : 
      27                 :            : struct i2c_xilinx_axi_data {
      28                 :            :         struct k_event irq_event;
      29                 :            :         /* Serializes between ISR and other calls */
      30                 :            :         struct k_spinlock lock;
      31                 :            :         /* Provides exclusion against multiple concurrent requests */
      32                 :            :         struct k_mutex mutex;
      33                 :            : 
      34                 :            : #if defined(CONFIG_I2C_TARGET)
      35                 :            :         struct i2c_target_config *target_cfg;
      36                 :            :         bool target_reading;
      37                 :            :         bool target_read_aborted;
      38                 :            :         bool target_writing;
      39                 :            : #endif
      40                 :            : };
      41                 :            : 
      42                 :          9 : static void i2c_xilinx_axi_reinit(const struct i2c_xilinx_axi_config *config)
      43                 :            : {
      44         [ -  + ]:          9 :         LOG_DBG("Controller reinit");
      45                 :          9 :         sys_write32(SOFTR_KEY, config->base + REG_SOFTR);
      46                 :          9 :         sys_write32(CR_TX_FIFO_RST, config->base + REG_CR);
      47                 :          9 :         sys_write32(CR_EN, config->base + REG_CR);
      48                 :          9 :         sys_write32(GIE_ENABLE, config->base + REG_GIE);
      49                 :          9 : }
      50                 :            : 
      51                 :            : #if defined(CONFIG_I2C_TARGET)
      52                 :            : 
      53                 :            : #define I2C_XILINX_AXI_TARGET_INTERRUPTS                                                           \
      54                 :            :         (ISR_ADDR_TARGET | ISR_NOT_ADDR_TARGET | ISR_RX_FIFO_FULL | ISR_TX_FIFO_EMPTY |            \
      55                 :            :          ISR_TX_ERR_TARGET_COMP)
      56                 :            : 
      57                 :            : static void i2c_xilinx_axi_target_setup(const struct i2c_xilinx_axi_config *config,
      58                 :            :                                         struct i2c_target_config *cfg)
      59                 :            : {
      60                 :            :         i2c_xilinx_axi_reinit(config);
      61                 :            : 
      62                 :            :         sys_write32(ISR_ADDR_TARGET, config->base + REG_IER);
      63                 :            :         sys_write32(cfg->address << 1, config->base + REG_ADR);
      64                 :            :         sys_write32(0, config->base + REG_RX_FIFO_PIRQ);
      65                 :            : }
      66                 :            : 
      67                 :            : static int i2c_xilinx_axi_target_register(const struct device *dev, struct i2c_target_config *cfg)
      68                 :            : {
      69                 :            :         const struct i2c_xilinx_axi_config *config = dev->config;
      70                 :            :         struct i2c_xilinx_axi_data *data = dev->data;
      71                 :            :         k_spinlock_key_t key;
      72                 :            :         int ret;
      73                 :            : 
      74                 :            :         if (cfg->flags & I2C_TARGET_FLAGS_ADDR_10_BITS) {
      75                 :            :                 /* Optionally supported in core, but not implemented in driver yet */
      76                 :            :                 return -EOPNOTSUPP;
      77                 :            :         }
      78                 :            : 
      79                 :            :         k_mutex_lock(&data->mutex, K_FOREVER);
      80                 :            :         key = k_spin_lock(&data->lock);
      81                 :            : 
      82                 :            :         if (data->target_cfg) {
      83                 :            :                 ret = -EBUSY;
      84                 :            :                 goto out_unlock;
      85                 :            :         }
      86                 :            : 
      87                 :            :         data->target_cfg = cfg;
      88                 :            :         i2c_xilinx_axi_target_setup(config, cfg);
      89                 :            :         ret = 0;
      90                 :            : 
      91                 :            : out_unlock:
      92                 :            :         k_spin_unlock(&data->lock, key);
      93                 :            :         LOG_DBG("Target register ret=%d", ret);
      94                 :            :         k_mutex_unlock(&data->mutex);
      95                 :            :         return ret;
      96                 :            : }
      97                 :            : 
      98                 :            : static int i2c_xilinx_axi_target_unregister(const struct device *dev, struct i2c_target_config *cfg)
      99                 :            : {
     100                 :            :         const struct i2c_xilinx_axi_config *config = dev->config;
     101                 :            :         struct i2c_xilinx_axi_data *data = dev->data;
     102                 :            :         k_spinlock_key_t key;
     103                 :            :         uint32_t int_enable;
     104                 :            :         int ret;
     105                 :            : 
     106                 :            :         k_mutex_lock(&data->mutex, K_FOREVER);
     107                 :            :         key = k_spin_lock(&data->lock);
     108                 :            : 
     109                 :            :         if (!data->target_cfg) {
     110                 :            :                 ret = -EINVAL;
     111                 :            :                 goto out_unlock;
     112                 :            :         }
     113                 :            : 
     114                 :            :         if (data->target_reading || data->target_writing) {
     115                 :            :                 ret = -EBUSY;
     116                 :            :                 goto out_unlock;
     117                 :            :         }
     118                 :            : 
     119                 :            :         data->target_cfg = NULL;
     120                 :            :         sys_write32(0, config->base + REG_ADR);
     121                 :            : 
     122                 :            :         sys_write32(CR_EN, config->base + REG_CR);
     123                 :            :         int_enable = sys_read32(config->base + REG_IER);
     124                 :            :         int_enable &= ~I2C_XILINX_AXI_TARGET_INTERRUPTS;
     125                 :            :         sys_write32(int_enable, config->base + REG_IER);
     126                 :            :         ret = 0;
     127                 :            : 
     128                 :            : out_unlock:
     129                 :            :         k_spin_unlock(&data->lock, key);
     130                 :            :         LOG_DBG("Target unregister ret=%d", ret);
     131                 :            :         k_mutex_unlock(&data->mutex);
     132                 :            :         return ret;
     133                 :            : }
     134                 :            : 
     135                 :            : static void i2c_xilinx_axi_target_isr(const struct i2c_xilinx_axi_config *config,
     136                 :            :                                       struct i2c_xilinx_axi_data *data, uint32_t *int_status,
     137                 :            :                                       uint32_t *ints_to_clear, uint32_t *int_enable)
     138                 :            : {
     139                 :            :         if (*int_status & ISR_ADDR_TARGET) {
     140                 :            :                 LOG_DBG("Addressed as target");
     141                 :            :                 *int_status &= ~ISR_ADDR_TARGET;
     142                 :            :                 *int_enable &= ~ISR_ADDR_TARGET;
     143                 :            :                 *int_enable |= ISR_NOT_ADDR_TARGET;
     144                 :            :                 *ints_to_clear |= ISR_NOT_ADDR_TARGET;
     145                 :            : 
     146                 :            :                 if (sys_read32(config->base + REG_SR) & SR_SRW) {
     147                 :            :                         uint8_t read_byte;
     148                 :            : 
     149                 :            :                         data->target_reading = true;
     150                 :            :                         *ints_to_clear |= ISR_TX_FIFO_EMPTY | ISR_TX_ERR_TARGET_COMP;
     151                 :            :                         *int_enable |= ISR_TX_FIFO_EMPTY | ISR_TX_ERR_TARGET_COMP;
     152                 :            :                         if ((*data->target_cfg->callbacks->read_requested)(data->target_cfg,
     153                 :            :                                                                            &read_byte)) {
     154                 :            :                                 LOG_DBG("target read_requested rejected");
     155                 :            :                                 data->target_read_aborted = true;
     156                 :            :                                 read_byte = 0xFF;
     157                 :            :                         }
     158                 :            :                         sys_write32(read_byte, config->base + REG_TX_FIFO);
     159                 :            :                 } else {
     160                 :            :                         data->target_writing = true;
     161                 :            :                         *int_enable |= ISR_RX_FIFO_FULL;
     162                 :            :                         if ((*data->target_cfg->callbacks->write_requested)(data->target_cfg)) {
     163                 :            :                                 uint32_t cr = sys_read32(config->base + REG_CR);
     164                 :            : 
     165                 :            :                                 LOG_DBG("target write_requested rejected");
     166                 :            :                                 cr |= CR_TXAK;
     167                 :            :                                 sys_write32(cr, config->base + REG_CR);
     168                 :            :                         }
     169                 :            :                 }
     170                 :            :         } else if (*int_status & ISR_NOT_ADDR_TARGET) {
     171                 :            :                 LOG_DBG("Not addressed as target");
     172                 :            :                 (*data->target_cfg->callbacks->stop)(data->target_cfg);
     173                 :            :                 data->target_reading = false;
     174                 :            :                 data->target_read_aborted = false;
     175                 :            :                 data->target_writing = false;
     176                 :            : 
     177                 :            :                 sys_write32(CR_EN, config->base + REG_CR);
     178                 :            :                 *int_status &= ~ISR_NOT_ADDR_TARGET;
     179                 :            :                 *int_enable &= ~I2C_XILINX_AXI_TARGET_INTERRUPTS;
     180                 :            :                 *int_enable |= ISR_ADDR_TARGET;
     181                 :            :                 *ints_to_clear |= ISR_ADDR_TARGET;
     182                 :            :         } else if (data->target_writing && (*int_status & ISR_RX_FIFO_FULL)) {
     183                 :            :                 *int_status &= ~ISR_RX_FIFO_FULL;
     184                 :            :                 const uint8_t written_byte =
     185                 :            :                         sys_read32(config->base + REG_RX_FIFO) & RX_FIFO_DATA_MASK;
     186                 :            : 
     187                 :            :                 if ((*data->target_cfg->callbacks->write_received)(data->target_cfg,
     188                 :            :                                                                    written_byte)) {
     189                 :            :                         uint32_t cr = sys_read32(config->base + REG_CR);
     190                 :            : 
     191                 :            :                         LOG_DBG("target write_received rejected");
     192                 :            :                         cr |= CR_TXAK;
     193                 :            :                         sys_write32(cr, config->base + REG_CR);
     194                 :            :                 }
     195                 :            :         } else if (data->target_reading && (*int_status & ISR_TX_ERR_TARGET_COMP)) {
     196                 :            :                 /* Controller has NAKed the last byte read, so no more to send.
     197                 :            :                  * Ignore TX FIFO empty so we don't write an extra byte.
     198                 :            :                  */
     199                 :            :                 LOG_DBG("target read completed");
     200                 :            :                 *int_status &= ~ISR_TX_ERR_TARGET_COMP;
     201                 :            :                 *int_enable &= ~ISR_TX_FIFO_EMPTY;
     202                 :            :                 *ints_to_clear |= ISR_TX_FIFO_EMPTY;
     203                 :            :         } else if (data->target_reading && (*int_status & ISR_TX_FIFO_EMPTY)) {
     204                 :            :                 *int_status &= ~ISR_TX_FIFO_EMPTY;
     205                 :            :                 uint8_t read_byte = 0xFF;
     206                 :            : 
     207                 :            :                 if (!data->target_read_aborted &&
     208                 :            :                    (*data->target_cfg->callbacks->read_processed)(data->target_cfg,
     209                 :            :                                                                   &read_byte)) {
     210                 :            :                         LOG_DBG("target read_processed rejected");
     211                 :            :                         data->target_read_aborted = true;
     212                 :            :                 }
     213                 :            :                 sys_write32(read_byte, config->base + REG_TX_FIFO);
     214                 :            :         }
     215                 :            : }
     216                 :            : #endif
     217                 :            : 
     218                 :          9 : static void i2c_xilinx_axi_isr(const struct device *dev)
     219                 :            : {
     220                 :          9 :         const struct i2c_xilinx_axi_config *config = dev->config;
     221                 :          9 :         struct i2c_xilinx_axi_data *data = dev->data;
     222                 :          9 :         const k_spinlock_key_t key = k_spin_lock(&data->lock);
     223                 :          9 :         uint32_t int_enable = sys_read32(config->base + REG_IER);
     224                 :          9 :         uint32_t int_status = sys_read32(config->base + REG_ISR) & int_enable;
     225                 :          9 :         uint32_t ints_to_clear = int_status;
     226                 :            : 
     227         [ -  + ]:          9 :         LOG_DBG("ISR called for 0x%08" PRIxPTR ", status 0x%02x", config->base, int_status);
     228                 :            : 
     229         [ -  + ]:          9 :         if (int_status & ISR_ARB_LOST) {
     230                 :            :                 /* Must clear MSMS before clearing interrupt */
     231                 :          0 :                 uint32_t cr = sys_read32(config->base + REG_CR);
     232                 :            : 
     233                 :          0 :                 cr &= ~CR_MSMS;
     234                 :          0 :                 sys_write32(cr, config->base + REG_CR);
     235                 :            :         }
     236                 :            : 
     237                 :            : #if defined(CONFIG_I2C_TARGET)
     238                 :            :         if (data->target_cfg && (int_status & I2C_XILINX_AXI_TARGET_INTERRUPTS)) {
     239                 :            :                 /* This clears events from int_status which are already handled */
     240                 :            :                 i2c_xilinx_axi_target_isr(config, data, &int_status, &ints_to_clear, &int_enable);
     241                 :            :         }
     242                 :            : #endif
     243                 :            : 
     244                 :            :         /* Mask any interrupts which have not already been handled separately */
     245                 :          9 :         sys_write32(int_enable & ~int_status, config->base + REG_IER);
     246                 :            :         /* Be careful, writing 1 to a bit that is not currently set in ISR will SET it! */
     247                 :          9 :         sys_write32(ints_to_clear & sys_read32(config->base + REG_ISR), config->base + REG_ISR);
     248                 :            : 
     249                 :          9 :         k_spin_unlock(&data->lock, key);
     250         [ +  - ]:          9 :         if (int_status) {
     251                 :          9 :                 k_event_post(&data->irq_event, int_status);
     252                 :            :         }
     253                 :          9 : }
     254                 :            : 
     255                 :          4 : static int i2c_xilinx_axi_configure(const struct device *dev, uint32_t dev_config)
     256                 :            : {
     257                 :          4 :         const struct i2c_xilinx_axi_config *config = dev->config;
     258                 :            : 
     259         [ -  + ]:          4 :         LOG_INF("Configuring %s at 0x%08" PRIxPTR, dev->name, config->base);
     260                 :          4 :         i2c_xilinx_axi_reinit(config);
     261                 :          4 :         return 0;
     262                 :            : }
     263                 :            : 
     264                 :          8 : static uint32_t i2c_xilinx_axi_wait_interrupt(const struct i2c_xilinx_axi_config *config,
     265                 :            :                                               struct i2c_xilinx_axi_data *data, uint32_t int_mask)
     266                 :            : {
     267                 :          8 :         const k_spinlock_key_t key = k_spin_lock(&data->lock);
     268                 :          8 :         const uint32_t int_enable = sys_read32(config->base + REG_IER) | int_mask;
     269                 :            :         uint32_t events;
     270                 :            : 
     271         [ -  + ]:          8 :         LOG_DBG("Set IER to 0x%02x", int_enable);
     272                 :          8 :         sys_write32(int_enable, config->base + REG_IER);
     273                 :          8 :         k_event_clear(&data->irq_event, int_mask);
     274                 :          8 :         k_spin_unlock(&data->lock, key);
     275                 :            : 
     276                 :          8 :         events = k_event_wait(&data->irq_event, int_mask, false, K_MSEC(100));
     277                 :            : 
     278         [ -  + ]:          8 :         LOG_DBG("Got ISR events 0x%02x", events);
     279         [ -  + ]:          8 :         if (!events) {
     280         [ #  # ]:          0 :                 LOG_ERR("Timeout waiting for ISR events 0x%02x, SR 0x%02x, ISR 0x%02x", int_mask,
     281                 :            :                         sys_read32(config->base + REG_SR), sys_read32(config->base + REG_ISR));
     282                 :            :         }
     283                 :          8 :         return events;
     284                 :            : }
     285                 :            : 
     286                 :         11 : static void i2c_xilinx_axi_clear_interrupt(const struct i2c_xilinx_axi_config *config,
     287                 :            :                                            struct i2c_xilinx_axi_data *data, uint32_t int_mask)
     288                 :            : {
     289                 :         11 :         const k_spinlock_key_t key = k_spin_lock(&data->lock);
     290                 :         11 :         const uint32_t int_status = sys_read32(config->base + REG_ISR);
     291                 :            : 
     292         [ +  + ]:         11 :         if (int_status & int_mask) {
     293                 :          5 :                 sys_write32(int_status & int_mask, config->base + REG_ISR);
     294                 :            :         }
     295                 :         11 :         k_spin_unlock(&data->lock, key);
     296                 :         11 : }
     297                 :            : 
     298                 :          5 : static int i2c_xilinx_axi_wait_rx_full(const struct i2c_xilinx_axi_config *config,
     299                 :            :                                        struct i2c_xilinx_axi_data *data, uint32_t read_bytes)
     300                 :            : {
     301                 :            :         uint32_t events;
     302                 :            : 
     303                 :          5 :         i2c_xilinx_axi_clear_interrupt(config, data, ISR_RX_FIFO_FULL);
     304         [ -  + ]:          5 :         if (!(sys_read32(config->base + REG_SR) & SR_RX_FIFO_EMPTY) &&
     305         [ #  # ]:          0 :             (sys_read32(config->base + REG_RX_FIFO_OCY) & RX_FIFO_OCY_MASK) + 1 >= read_bytes) {
     306         [ #  # ]:          0 :                 LOG_DBG("RX already full on checking, SR 0x%02x RXOCY 0x%02x",
     307                 :            :                         sys_read32(config->base + REG_SR),
     308                 :            :                         sys_read32(config->base + REG_RX_FIFO_OCY));
     309                 :          0 :                 return 0;
     310                 :            :         }
     311                 :          5 :         events = i2c_xilinx_axi_wait_interrupt(config, data, ISR_RX_FIFO_FULL | ISR_ARB_LOST);
     312         [ -  + ]:          5 :         if (!events) {
     313                 :          0 :                 return -ETIMEDOUT;
     314                 :            :         }
     315         [ -  + ]:          5 :         if (events & ISR_ARB_LOST) {
     316         [ #  # ]:          0 :                 LOG_ERR("Arbitration lost on RX");
     317                 :          0 :                 return -ENXIO;
     318                 :            :         }
     319                 :          5 :         return 0;
     320                 :            : }
     321                 :            : 
     322                 :          1 : static int i2c_xilinx_axi_read_nondyn(const struct i2c_xilinx_axi_config *config,
     323                 :            :                                       struct i2c_xilinx_axi_data *data, struct i2c_msg *msg,
     324                 :            :                                       uint16_t addr)
     325                 :            : {
     326                 :          1 :         uint8_t *read_ptr = msg->buf;
     327                 :          1 :         uint32_t bytes_left = msg->len;
     328                 :          1 :         uint32_t cr = CR_EN | CR_MSMS;
     329                 :            : 
     330         [ -  + ]:          1 :         if (!bytes_left) {
     331                 :          0 :                 return -EINVAL;
     332                 :            :         }
     333         [ -  + ]:          1 :         if (bytes_left == 1) {
     334                 :            :                 /* Set TXAK bit now, to NAK after the first byte is received */
     335                 :          0 :                 cr |= CR_TXAK;
     336                 :            :         }
     337                 :            : 
     338                 :            :         /**
     339                 :            :          * The Xilinx core's RX FIFO full logic seems rather broken in that the interrupt
     340                 :            :          * is triggered, and the I2C receive is throttled, only when the FIFO occupancy
     341                 :            :          * equals the PIRQ threshold, not when greater or equal. In the non-dynamic mode
     342                 :            :          * of operation, we need to stop the read prior to the last bytes being received
     343                 :            :          * from the target in order to set the TXAK bit and clear MSMS to terminate the
     344                 :            :          * receive properly.
     345                 :            :          * However, if we previously allowed multiple bytes into the RX FIFO, this requires
     346                 :            :          * reducing the PIRQ threshold to 0 (single byte) during the receive operation. This
     347                 :            :          * can cause the receive to unthrottle (since FIFO occupancy now exceeds PIRQ
     348                 :            :          * threshold) and depending on timing between the driver code and the core,
     349                 :            :          * this can cause the core to try to receive more data into the FIFO than desired
     350                 :            :          * and cause various unexpected results.
     351                 :            :          *
     352                 :            :          * To avoid this, we only receive one byte at a time in the non-dynamic mode.
     353                 :            :          * Dynamic mode doesn't have this issue as it provides the RX byte count to the
     354                 :            :          * controller specifically and the TXAK and MSMS bits are handled automatically.
     355                 :            :          */
     356                 :          1 :         sys_write32(0, config->base + REG_RX_FIFO_PIRQ);
     357                 :            : 
     358         [ -  + ]:          1 :         if (msg->flags & I2C_MSG_RESTART) {
     359                 :          0 :                 cr |= CR_RSTA;
     360                 :            : 
     361                 :          0 :                 sys_write32(cr, config->base + REG_CR);
     362                 :          0 :                 sys_write32((addr << 1) | I2C_MSG_READ, config->base + REG_TX_FIFO);
     363                 :            :         } else {
     364                 :          1 :                 sys_write32((addr << 1) | I2C_MSG_READ, config->base + REG_TX_FIFO);
     365                 :          1 :                 sys_write32(cr, config->base + REG_CR);
     366                 :            :         }
     367                 :            : 
     368         [ +  + ]:          4 :         while (bytes_left) {
     369                 :          3 :                 int ret = i2c_xilinx_axi_wait_rx_full(config, data, 1);
     370                 :            : 
     371         [ -  + ]:          3 :                 if (ret) {
     372                 :          0 :                         return ret;
     373                 :            :                 }
     374                 :            : 
     375         [ +  + ]:          3 :                 if (bytes_left == 2) {
     376                 :            :                         /* Set TXAK so the last byte is NAKed */
     377                 :          1 :                         cr |= CR_TXAK;
     378   [ +  +  +  - ]:          2 :                 } else if (bytes_left == 1 && (msg->flags & I2C_MSG_STOP)) {
     379                 :            :                         /* Before reading the last byte, clear MSMS to issue a stop if required */
     380                 :          1 :                         cr &= ~CR_MSMS;
     381                 :            :                 }
     382                 :          3 :                 cr &= ~CR_RSTA;
     383                 :          3 :                 sys_write32(cr, config->base + REG_CR);
     384                 :            : 
     385                 :          3 :                 *read_ptr++ = sys_read32(config->base + REG_RX_FIFO) & RX_FIFO_DATA_MASK;
     386                 :          3 :                 bytes_left--;
     387                 :            :         }
     388                 :          1 :         return 0;
     389                 :            : }
     390                 :            : 
     391                 :          2 : static int i2c_xilinx_axi_read_dyn(const struct i2c_xilinx_axi_config *config,
     392                 :            :                                    struct i2c_xilinx_axi_data *data, struct i2c_msg *msg,
     393                 :            :                                    uint16_t addr)
     394                 :            : {
     395                 :          2 :         uint8_t *read_ptr = msg->buf;
     396                 :          2 :         uint32_t bytes_left = msg->len;
     397                 :          2 :         uint32_t bytes_to_read = bytes_left;
     398                 :          2 :         uint32_t cr = CR_EN;
     399                 :          2 :         uint32_t len_word = bytes_left;
     400                 :            : 
     401   [ +  -  -  + ]:          2 :         if (!bytes_left || bytes_left > MAX_DYNAMIC_READ_LEN) {
     402                 :          0 :                 return -EINVAL;
     403                 :            :         }
     404         [ +  + ]:          2 :         if (msg->flags & I2C_MSG_RESTART) {
     405                 :          1 :                 cr |= CR_MSMS | CR_RSTA;
     406                 :            :         }
     407                 :          2 :         sys_write32(cr, config->base + REG_CR);
     408                 :            : 
     409         [ -  + ]:          2 :         if (bytes_to_read > FIFO_SIZE) {
     410                 :          0 :                 bytes_to_read = FIFO_SIZE;
     411                 :            :         }
     412                 :          2 :         sys_write32(bytes_to_read - 1, config->base + REG_RX_FIFO_PIRQ);
     413                 :          2 :         sys_write32((addr << 1) | I2C_MSG_READ | TX_FIFO_START, config->base + REG_TX_FIFO);
     414                 :            : 
     415         [ +  - ]:          2 :         if (msg->flags & I2C_MSG_STOP) {
     416                 :          2 :                 len_word |= TX_FIFO_STOP;
     417                 :            :         }
     418                 :          2 :         sys_write32(len_word, config->base + REG_TX_FIFO);
     419                 :            : 
     420         [ +  + ]:          4 :         while (bytes_left) {
     421                 :            :                 int ret;
     422                 :            : 
     423                 :          2 :                 bytes_to_read = bytes_left;
     424         [ -  + ]:          2 :                 if (bytes_to_read > FIFO_SIZE) {
     425                 :          0 :                         bytes_to_read = FIFO_SIZE;
     426                 :            :                 }
     427                 :            : 
     428                 :          2 :                 sys_write32(bytes_to_read - 1, config->base + REG_RX_FIFO_PIRQ);
     429                 :          2 :                 ret = i2c_xilinx_axi_wait_rx_full(config, data, bytes_to_read);
     430         [ -  + ]:          2 :                 if (ret) {
     431                 :          0 :                         return ret;
     432                 :            :                 }
     433                 :            : 
     434         [ +  + ]:          6 :                 while (bytes_to_read) {
     435                 :          4 :                         *read_ptr++ = sys_read32(config->base + REG_RX_FIFO) & RX_FIFO_DATA_MASK;
     436                 :          4 :                         bytes_to_read--;
     437                 :          4 :                         bytes_left--;
     438                 :            :                 }
     439                 :            :         }
     440                 :          2 :         return 0;
     441                 :            : }
     442                 :            : 
     443                 :          3 : static int i2c_xilinx_axi_wait_tx_done(const struct i2c_xilinx_axi_config *config,
     444                 :            :                                        struct i2c_xilinx_axi_data *data)
     445                 :            : {
     446                 :          3 :         const uint32_t finish_bits = ISR_BUS_NOT_BUSY | ISR_TX_FIFO_EMPTY;
     447                 :            : 
     448                 :          3 :         uint32_t events = i2c_xilinx_axi_wait_interrupt(
     449                 :            :                 config, data, finish_bits | ISR_TX_ERR_TARGET_COMP | ISR_ARB_LOST);
     450   [ +  -  -  + ]:          3 :         if (!(events & finish_bits) || (events & ~finish_bits)) {
     451         [ #  # ]:          0 :                 if (!events) {
     452                 :          0 :                         return -ETIMEDOUT;
     453                 :            :                 }
     454         [ #  # ]:          0 :                 if (events & ISR_ARB_LOST) {
     455         [ #  # ]:          0 :                         LOG_ERR("Arbitration lost on TX");
     456                 :          0 :                         return -EAGAIN;
     457                 :            :                 }
     458         [ #  # ]:          0 :                 LOG_ERR("TX received NAK");
     459                 :          0 :                 return -ENXIO;
     460                 :            :         }
     461                 :          3 :         return 0;
     462                 :            : }
     463                 :            : 
     464                 :         10 : static int i2c_xilinx_axi_wait_not_busy(const struct i2c_xilinx_axi_config *config,
     465                 :            :                                         struct i2c_xilinx_axi_data *data)
     466                 :            : {
     467         [ -  + ]:         10 :         if (sys_read32(config->base + REG_SR) & SR_BB) {
     468                 :          0 :                 uint32_t events = i2c_xilinx_axi_wait_interrupt(config, data, ISR_BUS_NOT_BUSY);
     469                 :            : 
     470         [ #  # ]:          0 :                 if (events != ISR_BUS_NOT_BUSY) {
     471         [ #  # ]:          0 :                         LOG_ERR("Bus stuck busy");
     472                 :          0 :                         i2c_xilinx_axi_reinit(config);
     473                 :          0 :                         return -EBUSY;
     474                 :            :                 }
     475                 :            :         }
     476                 :         10 :         return 0;
     477                 :            : }
     478                 :            : 
     479                 :          3 : static int i2c_xilinx_axi_write(const struct i2c_xilinx_axi_config *config,
     480                 :            :                                 struct i2c_xilinx_axi_data *data, const struct i2c_msg *msg,
     481                 :            :                                 uint16_t addr)
     482                 :            : {
     483                 :          3 :         const uint8_t *write_ptr = msg->buf;
     484                 :          3 :         uint32_t bytes_left = msg->len;
     485                 :          3 :         uint32_t cr = CR_EN | CR_TX;
     486                 :          3 :         uint32_t fifo_space = FIFO_SIZE - 1; /* account for address being written */
     487                 :            : 
     488         [ -  + ]:          3 :         if (msg->flags & I2C_MSG_RESTART) {
     489                 :          0 :                 cr |= CR_MSMS | CR_RSTA;
     490                 :            :         }
     491                 :            : 
     492                 :          3 :         i2c_xilinx_axi_clear_interrupt(config, data, ISR_TX_ERR_TARGET_COMP | ISR_ARB_LOST);
     493                 :            : 
     494                 :          3 :         sys_write32(cr, config->base + REG_CR);
     495                 :          3 :         sys_write32((addr << 1) | TX_FIFO_START, config->base + REG_TX_FIFO);
     496                 :            : 
     497                 :            :         /* TX FIFO empty detection is somewhat fragile because the status register
     498                 :            :          * TX_FIFO_EMPTY bit can be set prior to the transaction actually being
     499                 :            :          * complete, so we have to rely on the TX empty interrupt.
     500                 :            :          * However, delays in writing data to the TX FIFO could cause it
     501                 :            :          * to run empty in the middle of the process, causing us to get a spurious
     502                 :            :          * completion detection from the interrupt. Therefore we disable interrupts
     503                 :            :          * while the TX FIFO is being filled up to try to avoid this.
     504                 :            :          */
     505                 :            : 
     506         [ +  + ]:          6 :         while (bytes_left) {
     507                 :          3 :                 uint32_t bytes_to_send = bytes_left;
     508                 :          3 :                 const k_spinlock_key_t key = k_spin_lock(&data->lock);
     509                 :            :                 int ret;
     510                 :            : 
     511         [ -  + ]:          3 :                 if (bytes_to_send > fifo_space) {
     512                 :          0 :                         bytes_to_send = fifo_space;
     513                 :            :                 }
     514         [ +  + ]:          6 :                 while (bytes_to_send) {
     515                 :          3 :                         uint32_t write_word = *write_ptr++;
     516                 :            : 
     517   [ +  -  +  + ]:          3 :                         if (bytes_left == 1 && (msg->flags & I2C_MSG_STOP)) {
     518                 :          2 :                                 write_word |= TX_FIFO_STOP;
     519                 :            :                         }
     520                 :          3 :                         sys_write32(write_word, config->base + REG_TX_FIFO);
     521                 :          3 :                         bytes_to_send--;
     522                 :          3 :                         bytes_left--;
     523                 :            :                 }
     524                 :          3 :                 k_spin_unlock(&data->lock, key);
     525                 :          3 :                 i2c_xilinx_axi_clear_interrupt(config, data, ISR_TX_FIFO_EMPTY | ISR_BUS_NOT_BUSY);
     526                 :            : 
     527                 :          3 :                 ret = i2c_xilinx_axi_wait_tx_done(config, data);
     528         [ -  + ]:          3 :                 if (ret) {
     529                 :          0 :                         return ret;
     530                 :            :                 }
     531                 :          3 :                 fifo_space = FIFO_SIZE;
     532                 :            :         }
     533                 :          3 :         return 0;
     534                 :            : }
     535                 :            : 
     536                 :          5 : static int i2c_xilinx_axi_transfer(const struct device *dev, struct i2c_msg *msgs, uint8_t num_msgs,
     537                 :            :                                    uint16_t addr)
     538                 :            : {
     539                 :          5 :         const struct i2c_xilinx_axi_config *config = dev->config;
     540                 :          5 :         struct i2c_xilinx_axi_data *data = dev->data;
     541                 :            :         int ret;
     542                 :            : 
     543                 :          5 :         k_mutex_lock(&data->mutex, K_FOREVER);
     544                 :            : 
     545                 :          5 :         ret = i2c_xilinx_axi_wait_not_busy(config, data);
     546         [ -  + ]:          5 :         if (ret) {
     547                 :          0 :                 goto out_unlock;
     548                 :            :         }
     549                 :            : 
     550         [ -  + ]:          5 :         if (!num_msgs) {
     551                 :          0 :                 goto out_unlock;
     552                 :            :         }
     553                 :            : 
     554                 :            :         /**
     555                 :            :          * Reinitializing before each transfer shouldn't technically be needed, but
     556                 :            :          * seems to improve general reliability. The Linux driver also does this.
     557                 :            :          */
     558                 :          5 :         i2c_xilinx_axi_reinit(config);
     559                 :            : 
     560                 :            :         do {
     561         [ -  + ]:          6 :                 if (msgs->flags & I2C_MSG_ADDR_10_BITS) {
     562                 :            :                         /* Optionally supported in core, but not implemented in driver yet */
     563                 :          0 :                         ret = -EOPNOTSUPP;
     564                 :          0 :                         goto out_check_target;
     565                 :            :                 }
     566         [ +  + ]:          6 :                 if (msgs->flags & I2C_MSG_READ) {
     567   [ +  +  +  - ]:          3 :                         if (config->dyn_read_working && msgs->len <= MAX_DYNAMIC_READ_LEN) {
     568                 :          2 :                                 ret = i2c_xilinx_axi_read_dyn(config, data, msgs, addr);
     569                 :            :                         } else {
     570                 :          1 :                                 ret = i2c_xilinx_axi_read_nondyn(config, data, msgs, addr);
     571                 :            :                         }
     572                 :            :                 } else {
     573                 :          3 :                         ret = i2c_xilinx_axi_write(config, data, msgs, addr);
     574                 :            :                 }
     575   [ +  -  +  + ]:          6 :                 if (!ret && (msgs->flags & I2C_MSG_STOP)) {
     576                 :          5 :                         ret = i2c_xilinx_axi_wait_not_busy(config, data);
     577                 :            :                 }
     578         [ -  + ]:          6 :                 if (ret) {
     579                 :          0 :                         goto out_check_target;
     580                 :            :                 }
     581                 :          6 :                 msgs++;
     582                 :          6 :                 num_msgs--;
     583         [ +  + ]:          6 :         } while (num_msgs);
     584                 :            : 
     585                 :          5 : out_check_target:
     586                 :            : #if defined(CONFIG_I2C_TARGET)
     587                 :            :         /* If a target is registered, then ensure the controller gets put back
     588                 :            :          * into a suitable state to handle target transfers.
     589                 :            :          */
     590                 :            :         k_spinlock_key_t key = k_spin_lock(&data->lock);
     591                 :            : 
     592                 :            :         if (data->target_cfg) {
     593                 :            :                 i2c_xilinx_axi_target_setup(config, data->target_cfg);
     594                 :            :         }
     595                 :            :         k_spin_unlock(&data->lock, key);
     596                 :            : #endif
     597                 :            : 
     598                 :          5 : out_unlock:
     599                 :          5 :         k_mutex_unlock(&data->mutex);
     600                 :          5 :         return ret;
     601                 :            : }
     602                 :            : 
     603                 :          4 : static int i2c_xilinx_axi_init(const struct device *dev)
     604                 :            : {
     605                 :          4 :         const struct i2c_xilinx_axi_config *config = dev->config;
     606                 :          4 :         struct i2c_xilinx_axi_data *data = dev->data;
     607                 :            :         int error;
     608                 :            : 
     609                 :          4 :         k_event_init(&data->irq_event);
     610                 :          4 :         k_mutex_init(&data->mutex);
     611                 :            : 
     612                 :          4 :         error = i2c_xilinx_axi_configure(dev, I2C_MODE_CONTROLLER);
     613         [ -  + ]:          4 :         if (error) {
     614                 :          0 :                 return error;
     615                 :            :         }
     616                 :            : 
     617                 :          4 :         config->irq_config_func(dev);
     618                 :            : 
     619         [ -  + ]:          4 :         LOG_INF("initialized");
     620                 :          4 :         return 0;
     621                 :            : }
     622                 :            : 
     623                 :            : static const struct i2c_driver_api i2c_xilinx_axi_driver_api = {
     624                 :            :         .configure = i2c_xilinx_axi_configure,
     625                 :            :         .transfer = i2c_xilinx_axi_transfer,
     626                 :            : #if defined(CONFIG_I2C_TARGET)
     627                 :            :         .target_register = i2c_xilinx_axi_target_register,
     628                 :            :         .target_unregister = i2c_xilinx_axi_target_unregister,
     629                 :            : #endif
     630                 :            : };
     631                 :            : 
     632                 :            : #define I2C_XILINX_AXI_INIT(n, compat)                                                             \
     633                 :            :         static void i2c_xilinx_axi_config_func_##compat##_##n(const struct device *dev);           \
     634                 :            :                                                                                                    \
     635                 :            :         static const struct i2c_xilinx_axi_config i2c_xilinx_axi_config_##compat##_##n = {         \
     636                 :            :                 .base = DT_INST_REG_ADDR(n),                                                       \
     637                 :            :                 .irq_config_func = i2c_xilinx_axi_config_func_##compat##_##n,                      \
     638                 :            :                 .dyn_read_working = DT_INST_NODE_HAS_COMPAT(n, xlnx_xps_iic_2_1)};                 \
     639                 :            :                                                                                                    \
     640                 :            :         static struct i2c_xilinx_axi_data i2c_xilinx_axi_data_##compat##_##n;                      \
     641                 :            :                                                                                                    \
     642                 :            :         I2C_DEVICE_DT_INST_DEFINE(n, i2c_xilinx_axi_init, NULL,                                    \
     643                 :            :                                   &i2c_xilinx_axi_data_##compat##_##n,                             \
     644                 :            :                                   &i2c_xilinx_axi_config_##compat##_##n, POST_KERNEL,              \
     645                 :            :                                   CONFIG_I2C_INIT_PRIORITY, &i2c_xilinx_axi_driver_api);           \
     646                 :            :                                                                                                    \
     647                 :            :         static void i2c_xilinx_axi_config_func_##compat##_##n(const struct device *dev)            \
     648                 :            :         {                                                                                          \
     649                 :            :                 ARG_UNUSED(dev);                                                                   \
     650                 :            :                                                                                                    \
     651                 :            :                 IRQ_CONNECT(DT_INST_IRQN(n), DT_INST_IRQ(n, priority), i2c_xilinx_axi_isr,         \
     652                 :            :                             DEVICE_DT_INST_GET(n), 0);                                             \
     653                 :            :                                                                                                    \
     654                 :            :                 irq_enable(DT_INST_IRQN(n));                                                       \
     655                 :            :         }
     656                 :            : 
     657                 :            : #define DT_DRV_COMPAT xlnx_xps_iic_2_1
     658                 :          1 : DT_INST_FOREACH_STATUS_OKAY_VARGS(I2C_XILINX_AXI_INIT, DT_DRV_COMPAT)
     659                 :            : #undef DT_DRV_COMPAT
     660                 :            : #define DT_DRV_COMPAT xlnx_xps_iic_2_00_a
     661                 :          3 : DT_INST_FOREACH_STATUS_OKAY_VARGS(I2C_XILINX_AXI_INIT, DT_DRV_COMPAT)

Generated by: LCOV version 1.16-3-g92e2121