This is an automated email from the ASF dual-hosted git repository.

xiaoxiang pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/nuttx.git


The following commit(s) were added to refs/heads/master by this push:
     new cd4fdf27c5 arch/xmc4 : i2c driver Added lower half i2c driver
cd4fdf27c5 is described below

commit cd4fdf27c5ada07c012d6c04feb3fece00252f7b
Author: adriendesp <adrien.despro...@gmail.com>
AuthorDate: Tue Jul 23 17:05:04 2024 +0200

    arch/xmc4 : i2c driver
    Added lower half i2c driver
---
 arch/arm/src/xmc4/CMakeLists.txt |    4 +
 arch/arm/src/xmc4/Make.defs      |    4 +
 arch/arm/src/xmc4/xmc4_i2c.c     | 1063 ++++++++++++++++++++++++++++++++++++++
 arch/arm/src/xmc4/xmc4_i2c.h     |    1 -
 4 files changed, 1071 insertions(+), 1 deletion(-)

diff --git a/arch/arm/src/xmc4/CMakeLists.txt b/arch/arm/src/xmc4/CMakeLists.txt
index d46b1a2dbf..faa35c3ec2 100644
--- a/arch/arm/src/xmc4/CMakeLists.txt
+++ b/arch/arm/src/xmc4/CMakeLists.txt
@@ -51,6 +51,10 @@ if(CONFIG_XMC4_USCI_SPI)
   list(APPEND SRCS xmc4_spi.c)
 endif()
 
+if(CONFIG_XMC4_USCI_I2C)
+  list(APPEND SRCS xmc4_i2c.c)
+endif()
+
 if(CONFIG_XMC4_PWM)
   list(APPEND SRCS xmc4_pwm.c)
 endif()
diff --git a/arch/arm/src/xmc4/Make.defs b/arch/arm/src/xmc4/Make.defs
index e7cb6d3b22..832d3c82e2 100644
--- a/arch/arm/src/xmc4/Make.defs
+++ b/arch/arm/src/xmc4/Make.defs
@@ -52,6 +52,10 @@ ifeq ($(CONFIG_XMC4_USCI_SPI),y)
 CHIP_CSRCS += xmc4_spi.c
 endif
 
+ifeq ($(CONFIG_XMC4_USCI_I2C),y)
+CHIP_CSRCS += xmc4_i2c.c
+endif
+
 ifeq ($(CONFIG_XMC4_ECAT),y)
 CHIP_CSRCS += xmc4_ecat.c
 endif
diff --git a/arch/arm/src/xmc4/xmc4_i2c.c b/arch/arm/src/xmc4/xmc4_i2c.c
new file mode 100644
index 0000000000..a7cd996afc
--- /dev/null
+++ b/arch/arm/src/xmc4/xmc4_i2c.c
@@ -0,0 +1,1063 @@
+/*****************************************************************************
+ * arch/arm/src/xmc4/xmc4_i2c.c
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.  The
+ * ASF licenses this file to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the
+ * License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ *****************************************************************************/
+
+/*****************************************************************************
+ * Included Files
+ *****************************************************************************/
+
+#include <nuttx/config.h>
+
+#include <sys/types.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <assert.h>
+#include <errno.h>
+#include <debug.h>
+
+#include <arch/board/board.h>
+#include <nuttx/mutex.h>
+#include <nuttx/irq.h>
+
+#include "arm_internal.h"
+#include "chip.h"
+
+#include "hardware/xmc4_pinmux.h"
+#include "hardware/xmc4_memorymap.h"
+#include "hardware/xmc4_usic.h"
+#include "xmc4_i2c.h"
+#include "xmc4_usic.h"
+#include "xmc4_gpio.h"
+
+/* At least one I2C peripheral must be enabled */
+
+#if defined(CONFIG_XMC4_USCI_I2C)
+
+/*****************************************************************************
+ * Pre-processor Definitions
+ *****************************************************************************/
+
+#define XMC_I2C_CLOCK_OVERSAMPLING_STANDARD (10U)
+#define XMC_I2C_CLOCK_OVERSAMPLING_FAST (25U)
+#define XMC_I2C_MAX_SPEED_STANDARD 100000UL
+#define XMC_I2C_MAX_SPEED_FAST 400000
+#define I2C_DEFAULT_FREQUENCY 400000
+
+#define I2C_INPUT_DX_SDA (0U) /* I2C uses DX0 input stage as SDA */
+#define I2C_INPUT_DX_SCL (1U) /* I2C uses DX1 input stage as SCL */
+
+#define XMC_I2C_TDF_MASTER_SEND (0U)
+#define XMC_I2C_TDF_SLAVE_SEND (1U << 8)
+#define XMC_I2C_TDF_MASTER_RECEIVE_ACK (2U << 8)
+#define XMC_I2C_TDF_MASTER_RECEIVE_NACK (3U << 8)
+#define XMC_I2C_TDF_MASTER_START (4U << 8)
+#define XMC_I2C_TDF_MASTER_RESTART (5U << 8)
+#define XMC_I2C_TDF_MASTER_STOP (6U << 8)
+
+#define XMC_I2C_CMD_WRITE (0U)
+#define XMC_I2C_CMD_READ (1U)
+
+#define I2C_WORDLENGTH (7U) /* 8 bits word length */
+#define I2C_TRM_MODE (3U)   /* Shift and transfert config */
+
+#define I2C_TDV_SET (1U) /* A transmission of data in TBUF \
+                          * can be started if TDV = 1 */
+
+/*****************************************************************************
+ * Private Types
+ *****************************************************************************/
+
+typedef enum xmc_usic_ch_input
+{
+  XMC_USIC_CH_INPUT_DX0, /* DX0 input */
+  XMC_USIC_CH_INPUT_DX1, /* DX1 input */
+  XMC_USIC_CH_INPUT_DX2, /* DX2 input */
+  XMC_USIC_CH_INPUT_DX3, /* DX3 input */
+  XMC_USIC_CH_INPUT_DX4, /* DX4 input */
+  XMC_USIC_CH_INPUT_DX5  /* DX5 input */
+} xmc_usic_ch_input_t;
+
+typedef enum xmc_usic_ch_source
+{
+  XMC_USIC_CH_INPUT_DXNA, /* DXnA source signal */
+  XMC_USIC_CH_INPUT_DXNB, /* DXnB source signal */
+  XMC_USIC_CH_INPUT_DXNC, /* DXnC source signal */
+  XMC_USIC_CH_INPUT_DXND, /* DXnD source signal */
+  XMC_USIC_CH_INPUT_DXNE, /* DXnE source signal */
+  XMC_USIC_CH_INPUT_DXNF, /* DXnF source signal */
+  XMC_USIC_CH_INPUT_DXNG, /* DXnG source signal */
+  XMC_USIC_CH_INPUT_DXN1  /* Signal always high */
+} xmc_usic_ch_source_t;
+
+/* This structure represents the state of one i2c channel */
+
+struct xmc4_i2cdev_s
+{
+  const struct i2c_ops_s *ops;       /* Generic I2C device operation */
+  const uint32_t base;               /* Base address of registers */
+  const enum usic_channel_e channel; /* USIC channel */
+
+  uint32_t frequency; /* Current I2C frequency */
+
+  xmc_usic_ch_source_t dx0_sda; /* Source signal of SDA */
+  xmc_usic_ch_source_t dx1_scl; /* Source signal of SCL */
+  uint32_t sda_gpio;            /* GPIO config of SDA */
+  uint32_t scl_gpio;            /* GPIO config of SCL */
+
+  mutex_t lock; /* Only one thread can access at a time */
+  int refs;     /* Reference count */
+};
+
+/*****************************************************************************
+ * Private Function Prototypes
+ *****************************************************************************/
+
+static inline void xmc4_i2c_putreg32(struct xmc4_i2cdev_s *priv,
+                                     uint32_t offset,
+                                     uint32_t value);
+static inline uint32_t xmc4_i2c_getreg32(struct xmc4_i2cdev_s *priv,
+                                         uint32_t offset);
+static inline void xmc4_i2c_modifyreg32(struct xmc4_i2cdev_s *priv,
+                                        uint32_t offset,
+                                        uint32_t clearbits,
+                                        uint32_t setbits);
+
+static int i2c_set_baudrate(struct xmc4_i2cdev_s *priv);
+static int i2c_set_input_source(struct xmc4_i2cdev_s *priv);
+static void i2c_start_channel(struct xmc4_i2cdev_s *priv);
+static void i2c_stop_channel(struct xmc4_i2cdev_s *priv);
+static bool i2c_get_status_flag(struct xmc4_i2cdev_s *priv,
+                                const uint32_t flag_mask);
+static int i2c_clear_status_flag(struct xmc4_i2cdev_s *priv,
+                                  const uint32_t flag_mask);
+static bool i2c_get_transmit_buffer_status(struct xmc4_i2cdev_s *priv);
+static void i2c_fill_transmit_buffer(struct xmc4_i2cdev_s *priv,
+                                      const uint32_t data);
+static uint8_t i2c_get_receive_buffer(struct xmc4_i2cdev_s *priv);
+
+static void i2c_start(struct xmc4_i2cdev_s *priv,
+                      const uint16_t addr,
+                      const bool read,
+                      const bool restart);
+static void i2c_stop(struct xmc4_i2cdev_s *priv);
+static void i2c_master_transmit(struct xmc4_i2cdev_s *priv,
+                                const uint8_t data);
+static void i2c_master_ack(struct xmc4_i2cdev_s *priv);
+static void i2c_master_nack(struct xmc4_i2cdev_s *priv);
+static void i2c_wait_for_slave_ack(struct xmc4_i2cdev_s *priv);
+static void i2c_wait_for_received_data_ready(struct xmc4_i2cdev_s *priv);
+
+static int i2c_initialize(struct xmc4_i2cdev_s *priv);
+static int i2c_uninitialize(struct xmc4_i2cdev_s *priv);
+
+static int i2c_transfer(struct i2c_master_s *dev,
+                        struct i2c_msg_s *msgs,
+                        int count);
+#ifdef CONFIG_I2C_RESET
+static int i2c_reset(struct i2c_master_s *dev);
+#endif
+/*****************************************************************************
+ * Private Data
+ *****************************************************************************/
+
+struct i2c_ops_s xmc4_i2c_ops =
+{
+  .transfer = i2c_transfer,
+#ifdef CONFIG_I2C_RESET
+  .reset = i2c_reset,
+#endif
+};
+
+#ifdef CONFIG_XMC4_I2C0
+static struct xmc4_i2cdev_s g_i2c0 =
+{
+  .ops = &xmc4_i2c_ops,
+  .base = XMC4_USIC0_CH0_BASE,
+  .channel = USIC0_CHAN0,
+  .dx0_sda = BOARD_I2C0_SDA_DX, /* See i2c_set_input_source */
+  .dx1_scl = BOARD_I2C0_SCL_DX, /* See i2c_set_input_source */
+  .sda_gpio = GPIO_I2C0_SDA,    /* See i2c_set_input_source */
+  .scl_gpio = GPIO_I2C0_SCL,    /* See i2c_set_input_source */
+  .frequency = (uint32_t)I2C_DEFAULT_FREQUENCY,
+  .lock = NXMUTEX_INITIALIZER,
+  .refs = 0,
+};
+#endif
+
+#ifdef CONFIG_XMC4_I2C1
+static struct xmc4_i2cdev_s g_i2c1 =
+{
+  .ops = &xmc4_i2c_ops,
+  .base = XMC4_USIC0_CH1_BASE,
+  .channel = USIC0_CHAN1,
+  .dx0_sda = BOARD_I2C1_SDA_DX, /* See i2c_set_input_source */
+  .dx1_scl = BOARD_I2C1_SCL_DX, /* See i2c_set_input_source */
+  .sda_gpio = GPIO_I2C1_SDA,    /* See i2c_set_input_source */
+  .scl_gpio = GPIO_I2C1_SCL,    /* See i2c_set_input_source */
+  .frequency = (uint32_t)I2C_DEFAULT_FREQUENCY,
+  .lock = NXMUTEX_INITIALIZER,
+  .refs = 0,
+};
+#endif
+
+#ifdef CONFIG_XMC4_I2C2
+static struct xmc4_i2cdev_s g_i2c2 =
+{
+  .ops = &xmc4_i2c_ops,
+  .base = XMC4_USIC1_CH0_BASE,
+  .channel = USIC1_CHAN0,
+  .dx0_sda = BOARD_I2C2_SDA_DX, /* See i2c_set_input_source */
+  .dx1_scl = BOARD_I2C2_SCL_DX, /* See i2c_set_input_source */
+  .sda_gpio = GPIO_I2C2_SDA,    /* See i2c_set_input_source */
+  .scl_gpio = GPIO_I2C2_SCL,    /* See i2c_set_input_source */
+  .frequency = (uint32_t)I2C_DEFAULT_FREQUENCY,
+  .lock = NXMUTEX_INITIALIZER,
+  .refs = 0,
+};
+#endif
+
+#ifdef CONFIG_XMC4_I2C3
+static struct xmc4_i2cdev_s g_i2c3 =
+{
+  .ops = &xmc4_i2c_ops,
+  .base = XMC4_USIC1_CH1_BASE,
+  .channel = USIC1_CHAN1,
+  .dx0_sda = BOARD_I2C3_SDA_DX, /* See i2c_set_input_source */
+  .dx1_scl = BOARD_I2C3_SCL_DX, /* See i2c_set_input_source */
+  .sda_gpio = GPIO_I2C3_SDA,    /* See i2c_set_input_source */
+  .scl_gpio = GPIO_I2C3_SCL,    /* See i2c_set_input_source */
+  .frequency = (uint32_t)I2C_DEFAULT_FREQUENCY,
+  .lock = NXMUTEX_INITIALIZER,
+  .refs = 0,
+};
+#endif
+
+#ifdef CONFIG_XMC4_I2C4
+static struct xmc4_i2cdev_s g_i2c4 =
+{
+  .ops = &xmc4_i2c_ops,
+  .base = XMC4_USIC2_CH0_BASE,
+  .channel = USIC2_CHAN0,
+  .dx0_sda = BOARD_I2C4_SDA_DX, /* See i2c_set_input_source */
+  .dx1_scl = BOARD_I2C4_SCL_DX, /* See i2c_set_input_source */
+  .sda_gpio = GPIO_I2C4_SDA,    /* See i2c_set_input_source */
+  .scl_gpio = GPIO_I2C4_SCL,    /* See i2c_set_input_source */
+  .frequency = (uint32_t)I2C_DEFAULT_FREQUENCY,
+  .lock = NXMUTEX_INITIALIZER,
+  .refs = 0,
+};
+#endif
+
+#ifdef CONFIG_XMC4_I2C5
+static struct xmc4_i2cdev_s g_i2c5 =
+{
+  .ops = &xmc4_i2c_ops,
+  .base = XMC4_USIC2_CH1_BASE,
+  .channel = USIC2_CHAN1,
+  .dx0_sda = BOARD_I2C5_SDA_DX, /* See i2c_set_input_source */
+  .dx1_scl = BOARD_I2C5_SCL_DX, /* See i2c_set_input_source */
+  .sda_gpio = GPIO_I2C5_SDA,    /* See i2c_set_input_source */
+  .scl_gpio = GPIO_I2C5_SCL,    /* See i2c_set_input_source */
+  .frequency = (uint32_t)I2C_DEFAULT_FREQUENCY,
+  .lock = NXMUTEX_INITIALIZER,
+  .refs = 0,
+};
+#endif
+
+/*****************************************************************************
+ * Private Function
+ *****************************************************************************/
+
+/* I2C register related functions */
+
+static inline void xmc4_i2c_putreg32(struct xmc4_i2cdev_s *priv,
+                                     uint32_t offset,
+                                     uint32_t value)
+{
+  putreg32(value, priv->base + offset);
+}
+
+static inline uint32_t xmc4_i2c_getreg32(struct xmc4_i2cdev_s *priv,
+                                         uint32_t offset)
+{
+  return getreg32(priv->base + offset);
+}
+
+static inline void xmc4_i2c_modifyreg32(struct xmc4_i2cdev_s *priv,
+                                 uint32_t offset,
+                                 uint32_t clearbits,
+                                 uint32_t setbits)
+{
+  modifyreg32(priv->base + offset, clearbits, setbits);
+}
+
+/*****************************************************************************
+ * Name: i2c_initialize
+ *
+ * Description:
+ *   Initialize USIC channel for I2C operations.
+ *
+ * Returned Value:
+ *   Zero on success; a negated errno value on failure
+ *
+ *****************************************************************************/
+
+static int i2c_initialize(struct xmc4_i2cdev_s *priv)
+{
+  int ret;
+
+  ret = xmc4_enable_usic_channel(priv->channel);
+
+  /* Data format configuration */
+
+  uint32_t sctr = ((uint32_t)I2C_TRM_MODE << (uint32_t)USIC_SCTR_TRM_SHIFT) |
+                ((uint32_t)I2C_WORDLENGTH << (uint32_t)USIC_SCTR_WLE_SHIFT) |
+                  (uint32_t)USIC_SCTR_FLE_MASK | /* unlimited data flow */
+                  (uint32_t)USIC_SCTR_SDIR |     /* MSB shifted first */
+                  (uint32_t)USIC_SCTR_PDL;       /* Passive Data Level */
+  xmc4_i2c_putreg32(priv, XMC4_USIC_SCTR_OFFSET, sctr);
+
+  /* Set baudrate */
+
+  ret |= i2c_set_baudrate(priv);
+
+  /* Enable transfer buffer */
+
+  uint32_t tcsr = ((uint32_t)I2C_TDV_SET << (uint32_t)USIC_TCSR_TDEN_SHIFT) |
+                                            (uint32_t)USIC_TCSR_TDSSM;
+  xmc4_i2c_putreg32(priv, XMC4_USIC_TCSR_OFFSET, tcsr);
+
+  /* Clear status flags */
+
+  ret |= i2c_clear_status_flag(priv, 0x1ffff);
+
+  /* Disable parity generation */
+
+  xmc4_i2c_putreg32(priv, XMC4_USIC_CCR_OFFSET, 0);
+
+  /* Set DX0m and DX1n as input sources for SDA & SCL */
+
+  ret |= i2c_set_input_source(priv);
+
+  /* Set USIC channel in I2C mode */
+
+  i2c_start_channel(priv);
+
+  /* Configure signal outputs pin */
+
+  xmc4_gpio_config((gpioconfig_t)priv->sda_gpio); /* GPIO_I2C_SDA is DOUT0 */
+  xmc4_gpio_config((gpioconfig_t)priv->scl_gpio); /* GPIO_I2C_SCL is SCLKOUT */
+
+  return ret;
+}
+
+/*****************************************************************************
+ * Name: i2c_uninitialize
+ *
+ * Description:
+ *   Reset USIC channel.
+ *
+ * Returned Value:
+ *   Zero on success; a negated errno value on failure
+ *
+ *****************************************************************************/
+
+static int i2c_uninitialize(struct xmc4_i2cdev_s *priv)
+{
+  xmc4_i2c_putreg32(priv, XMC4_USIC_PSCR_OFFSET, 0);
+  xmc4_i2c_putreg32(priv, XMC4_USIC_PCR_OFFSET, 0);
+  xmc4_i2c_putreg32(priv, XMC4_USIC_CCR_OFFSET, 0);
+
+  return xmc4_disable_usic_channel(priv->channel);
+}
+
+/*****************************************************************************
+ * Name: i2c_set_baudrate
+ *
+ * Description:
+ *   Set the baudrate of the USIC channel. Baudrate should be in [100-400k]Hz.
+ *   Baudrate can't be changed while the USIC is running.
+ *
+ * Returned Value:
+ *   Zero on success; a negated errno value on failure
+ *
+ *****************************************************************************/
+
+static int i2c_set_baudrate(struct xmc4_i2cdev_s *priv)
+{
+  int ret;
+
+  if (priv->frequency <= (uint32_t)XMC_I2C_MAX_SPEED_STANDARD)
+    {
+      xmc4_i2c_modifyreg32(priv, XMC4_USIC_PCR_OFFSET, USIC_PCR_IICMODE_STIM,
+                          ~USIC_PCR_IICMODE_STIM);
+
+      ret = xmc4_usic_baudrate(priv->channel, priv->frequency,
+                              XMC_I2C_CLOCK_OVERSAMPLING_STANDARD);
+    }
+  else if (priv->frequency <= (uint32_t)XMC_I2C_MAX_SPEED_FAST)
+    {
+      xmc4_i2c_modifyreg32(priv, XMC4_USIC_PCR_OFFSET, USIC_PCR_IICMODE_STIM,
+                          USIC_PCR_IICMODE_STIM);
+
+      ret = xmc4_usic_baudrate(priv->channel, priv->frequency,
+                              XMC_I2C_CLOCK_OVERSAMPLING_FAST);
+    }
+  else
+    {
+      ret = -EINVAL;
+      i2cerr("Selected baudrate isn't supported : [100-400k]Hz\n");
+    }
+
+  return ret;
+}
+
+/*****************************************************************************
+ * Name: i2c_set_input_source
+ *
+ * Description:
+ *   Sets the input source for I2C. Defines the input stage for the
+ *   corresponding input line. BOARD_I2Cx_SDA_DX, BOARD_I2Cx_SCL_DX,
+ *   GPIO_I2Cx_SDA and GPIO_I2Cx_SCL must be defined in board.h
+ *
+ * Returned Value:
+ *   Zero on success; a negated errno value on failure
+ *
+ *****************************************************************************/
+
+static int i2c_set_input_source(struct xmc4_i2cdev_s *priv)
+{
+  /* Configure I2C pins and input sources.
+   *
+   * NOTE that the board must provide the definitions in the board.h header
+   * file of the form like:  GPIO_I2Cx_SDA and GPIO_I2Cx_SCL where x is
+   * the I2C port number (U0C0 = 0, U0C1 = 1, U1C0 = 3 ... U2C1 = 5)
+   *
+   * In additional, the board.h must provide the definition of
+   * BOARD_I2Cx_SDA_DX and BOARD_I2Cx_SCL_DX which indicates which input
+   * source signal is selected, (0=DX_A, 1=DX_B, ... 6=DX_G).
+   * Use enum uart_dx_e.
+   */
+
+  if ((priv->dx0_sda > XMC_USIC_CH_INPUT_DXN1) ||
+      (priv->dx1_scl > XMC_USIC_CH_INPUT_DXN1))
+    {
+      return -EINVAL;
+    }
+
+  /* Set DX0 config */
+
+  xmc4_i2c_modifyreg32(priv, XMC4_USIC_DX0CR_OFFSET,
+                       USIC_DXCR_INSW, USIC_DXCR_DSEN);
+
+  /* Set DX0 source [G:A] */
+
+  xmc4_i2c_modifyreg32(priv, XMC4_USIC_DX0CR_OFFSET,
+                       USIC_DXCR_DSEL_MASK, USIC_DXCR_DSEL_DX(priv->dx0_sda));
+
+  /* Set DX1 config */
+
+  xmc4_i2c_modifyreg32(priv, XMC4_USIC_DX1CR_OFFSET,
+                       USIC_DXCR_INSW, USIC_DXCR_DSEN);
+
+  /* Set DX1 source [G:A] */
+
+  xmc4_i2c_modifyreg32(priv, XMC4_USIC_DX1CR_OFFSET,
+                       USIC_DXCR_DSEL_MASK, USIC_DXCR_DSEL_DX(priv->dx1_scl));
+
+  return OK;
+}
+
+/*****************************************************************************
+ * Name: i2c_start_channel
+ *
+ * Description:
+ *   Leave USIC channel from idle mode and set I2C mode.
+ *
+ *****************************************************************************/
+
+static void i2c_start_channel(struct xmc4_i2cdev_s *priv)
+{
+  /* Sets the USIC input operation mode to I2C mode using CCR register. */
+
+  xmc4_i2c_modifyreg32(priv, XMC4_USIC_CCR_OFFSET,
+                       USIC_CCR_MODE_MASK, USIC_CCR_MODE_I2C);
+}
+
+/*****************************************************************************
+ * Name: i2c_stop_channel
+ *
+ * Description:
+ *   Wait for current transmit to end and set the USIC channel in idle mode.
+ *
+ *****************************************************************************/
+
+static void i2c_stop_channel(struct xmc4_i2cdev_s *priv)
+{
+  /* Sets the USIC input operation mode to idle mode using CCR register. */
+
+  while (i2c_get_transmit_buffer_status(priv) == 1)
+    {
+      /* wait for transmit to end */
+    }
+
+  xmc4_i2c_modifyreg32(priv, XMC4_USIC_CCR_OFFSET, USIC_CCR_MODE_MASK, 0);
+}
+
+/*****************************************************************************
+ * Name: i2c_get_status_flag
+ *
+ * Description:
+ *   Return the flags from PSR register.
+ *
+ * Returned Value:
+ *   FALSE if selected flags are 0
+ *   else TRUE if one of the selected flags isn't 0.
+ *
+ *****************************************************************************/
+
+static bool i2c_get_status_flag(struct xmc4_i2cdev_s *priv,
+                             const uint32_t flag_mask)
+{
+  uint32_t psr = xmc4_i2c_getreg32(priv, XMC4_USIC_PSR_OFFSET);
+
+  return ((psr & flag_mask) != 0);
+}
+
+/*****************************************************************************
+ * Name: i2c_clear_status_flag
+ *
+ * Description:
+ *   Clear the flags from PSR register by writting in PSCR register.
+ *
+ * Returned Value:
+ *   Zero on success; a negated errno value on failure
+ *
+ *****************************************************************************/
+
+static int i2c_clear_status_flag(struct xmc4_i2cdev_s *priv,
+                          const uint32_t flag_mask)
+{
+  if (flag_mask > 0x1ffff)
+    {
+      return -EINVAL;
+    }
+
+  xmc4_i2c_modifyreg32(priv, XMC4_USIC_PSCR_OFFSET, 0, flag_mask);
+
+  return OK;
+}
+
+/*****************************************************************************
+ * Name: i2c_get_transmit_buffer_status
+ *
+ * Description:
+ *   Get the status of the transmit buffer.
+ *
+ * Returned Value:
+ *   ZERO if idle, else ONE if busy.
+ *
+ *****************************************************************************/
+
+static bool i2c_get_transmit_buffer_status(struct xmc4_i2cdev_s *priv)
+{
+  uint32_t tbuf_stat = xmc4_i2c_getreg32(priv, XMC4_USIC_TCSR_OFFSET);
+
+  return (tbuf_stat & (uint32_t)USIC_TCSR_TDV);
+}
+
+/*****************************************************************************
+ * Name: i2c_fill_transmit_buffer
+ *
+ * Description:
+ *   Wait for transmit buffer to be ready and fill the transmit buffer
+ *   with given word data.
+ *   TODO : enable FIFO mechanism so you don't have to wait.
+ *
+ *****************************************************************************/
+
+static void i2c_fill_transmit_buffer(struct xmc4_i2cdev_s *priv,
+                                      const uint32_t data)
+{
+  /* Check FIFO size */
+
+  uint32_t tbctr = xmc4_i2c_getreg32(priv, XMC4_USIC_TBCTR_OFFSET);
+
+  if ((tbctr & USIC_TBCTR_SIZE_MASK) == 0)
+    {
+      /* FIFO mechanism is disabled */
+
+      while (i2c_get_transmit_buffer_status(priv) == 1)
+        {
+          /* check TDV, wait until TBUF is ready */
+        }
+
+      /* Clear Status Flag */
+
+      i2c_clear_status_flag(priv, (uint32_t)USIC_PSR_IICMODE_TBIF);
+
+      /* Put data in TBUF[0] */
+
+      xmc4_i2c_putreg32(priv, XMC4_USIC_TBUF_OFFSET, data);
+    }
+  else
+    {
+      /* Put data in IN[0] */
+
+      xmc4_i2c_putreg32(priv, XMC4_USIC_IN_OFFSET, data);
+    }
+}
+
+/*****************************************************************************
+ * Name: i2c_get_receive_buffer
+ *
+ * Description:
+ *   Get the data word from receive buffer.
+ *
+ * Returned Value:
+ *   Recived data word in uint8_t.
+ *
+ *****************************************************************************/
+
+static uint8_t i2c_get_receive_buffer(struct xmc4_i2cdev_s *priv)
+{
+  uint8_t retval;
+
+  /* Check FIFO size */
+
+  uint32_t rbctr = xmc4_i2c_getreg32(priv, XMC4_USIC_RBCTR_OFFSET);
+  if ((rbctr & USIC_RBCTR_SIZE_SHIFT) == 0U)
+    {
+      retval = (uint8_t)xmc4_i2c_getreg32(priv, XMC4_USIC_RBUF_OFFSET);
+    }
+  else
+    {
+      retval = (uint8_t)xmc4_i2c_getreg32(priv, XMC4_USIC_OUTR_OFFSET);
+    }
+
+  return retval;
+}
+
+/*****************************************************************************
+ * Name: i2c_start
+ *
+ * Description:
+ *   Start and send an I2C frame with the given slave adress.
+ *   Send the following on bus : [START/RESTART A6 A5 A4 A3 A2 A1 A0 R/W]
+ *
+ *****************************************************************************/
+
+static void i2c_start(struct xmc4_i2cdev_s *priv,
+               const uint16_t addr,
+               const bool read,
+               const bool restart)
+{
+  uint32_t sent_data = (addr << 1);
+
+  /* Add start transition */
+
+  if (restart)
+    {
+      sent_data |= XMC_I2C_TDF_MASTER_RESTART;
+    }
+  else
+    {
+      sent_data |= XMC_I2C_TDF_MASTER_START;
+    }
+
+  if (read)
+    {
+      sent_data |= 0x01;
+    }
+
+  i2c_fill_transmit_buffer(priv, sent_data);
+}
+
+/*****************************************************************************
+ * Name: i2c_stop
+ *
+ * Description:
+ *   Stop an I2C frame.
+ *   Send the following on bus : [STOP]
+ *
+ *****************************************************************************/
+
+static void i2c_stop(struct xmc4_i2cdev_s *priv)
+{
+  uint32_t buff_data = (uint32_t)XMC_I2C_TDF_MASTER_STOP;
+  i2c_fill_transmit_buffer(priv, buff_data);
+}
+
+/*****************************************************************************
+ * Name: i2c_master_transmit
+ *
+ * Description:
+ *   Transmit data on bus.
+ *   Send the following on bus : [D7 D6 D5 D4 D3 D2 D1 D0]
+ *
+ *****************************************************************************/
+
+static void i2c_master_transmit(struct xmc4_i2cdev_s *priv,
+                                const uint8_t data)
+{
+  uint32_t buff_data = (uint32_t)(data | (uint32_t)XMC_I2C_TDF_MASTER_SEND);
+  i2c_fill_transmit_buffer(priv, buff_data);
+}
+
+/*****************************************************************************
+ * Name: i2c_master_ack
+ *
+ * Description:
+ *   Answer with a master acknowledge.
+ *   Send the following on bus : [ACK]
+ *
+ *****************************************************************************/
+
+static void i2c_master_ack(struct xmc4_i2cdev_s *priv)
+{
+  uint32_t buff_data = (uint32_t)XMC_I2C_TDF_MASTER_RECEIVE_ACK;
+  i2c_fill_transmit_buffer(priv, buff_data);
+}
+
+/*****************************************************************************
+ * Name: i2c_master_nack
+ *
+ * Description:
+ *   Answer with a master not-acknowledge.
+ *   Send the following on bus : [NACK]
+ *
+ *****************************************************************************/
+
+static void i2c_master_nack(struct xmc4_i2cdev_s *priv)
+{
+  uint32_t buff_data = (uint32_t)XMC_I2C_TDF_MASTER_RECEIVE_NACK;
+  i2c_fill_transmit_buffer(priv, buff_data);
+}
+
+/*****************************************************************************
+ * Name: i2c_wait_for_slave_ack
+ *
+ * Description:
+ *   Wait for the slave on bus to acknowledge.
+ *
+ *****************************************************************************/
+
+static void i2c_wait_for_slave_ack(struct xmc4_i2cdev_s *priv)
+{
+  while (i2c_get_status_flag(priv, (uint32_t)USIC_PSR_IICMODE_ACK) == false)
+    {
+      /* Wait for ACK */
+    }
+
+  i2c_clear_status_flag(priv, (uint32_t)USIC_PSR_IICMODE_ACK);
+}
+
+/*****************************************************************************
+ * Name: i2c_wait_for_received_data_ready
+ *
+ * Description:
+ *   Wait for the received data buffer to be ready to be read.
+ *
+ *****************************************************************************/
+
+static void i2c_wait_for_received_data_ready(struct xmc4_i2cdev_s *priv)
+{
+  while (i2c_get_status_flag(priv, (uint32_t)(USIC_PSR_IICMODE_RIF |
+                                              USIC_PSR_IICMODE_AIF)) == false)
+    {
+      /* Wait for RIF */
+    }
+
+  i2c_clear_status_flag(priv, (uint32_t)(USIC_PSR_IICMODE_RIF |
+                                         USIC_PSR_IICMODE_AIF));
+}
+
+/*****************************************************************************
+ * Name: i2c_transfer
+ *
+ * Description:
+ *   Generic I2C transfer.
+ *
+ * Returned Value:
+ *   Zero on success; a negated errno value on failure
+ *
+ *****************************************************************************/
+
+static int i2c_transfer(struct i2c_master_s *dev,
+                        struct i2c_msg_s *msgs,
+                        int count)
+{
+  struct xmc4_i2cdev_s *priv = (struct xmc4_i2cdev_s *)dev;
+
+  int ret = OK;
+
+  bool previous_msg_no_stop = false;
+
+  /* Get exclusive access to the I2C bus */
+
+  nxmutex_lock(&priv->lock);
+
+  for (int i = 0; i < count; i++)
+    {
+      /* Check if frequency must be changed */
+
+      if (msgs[i].frequency != priv->frequency)
+        {
+          priv->frequency = msgs[i].frequency;
+
+          if (!previous_msg_no_stop)
+            {
+              i2c_stop_channel(priv);
+
+              i2c_set_baudrate(priv);
+
+              i2c_start_channel(priv);
+            }
+          else
+            {
+              i2cerr("Can't update frequency between Start & Stop symbols\n");
+              nxmutex_unlock(&priv->lock);
+              return -EINVAL;
+            }
+        }
+
+      /* R/W bit */
+
+      bool read = ((msgs[i].flags & I2C_M_READ) != 0);
+
+      /* Enter critical section to avoid interrupts during i2c transfert */
+
+      irqstate_t state = enter_critical_section();
+
+      /* Check if you should start or restart a new i2c frame */
+
+      if ((msgs[i].flags & I2C_M_NOSTART) == 0)
+        {
+          if (previous_msg_no_stop)
+            {
+              /* Repeated start */
+
+              i2c_start(priv, msgs[i].addr, read, true);
+              i2c_wait_for_slave_ack(priv);
+            }
+          else
+            {
+              /*  Normal start */
+
+              i2c_start(priv, msgs[i].addr, read, false);
+              i2c_wait_for_slave_ack(priv);
+            }
+        }
+
+      /* Parse buffer data */
+
+      for (int j = 0; j < (msgs[i].length); j++)
+        {
+          if (read)
+            {
+              /* Answer ack (or nack if last buffer) */
+
+              if (j == ((msgs[i].length) - 1))
+                {
+                  i2c_master_nack(priv);
+                }
+              else
+                {
+                  i2c_master_ack(priv);
+                }
+
+              i2c_wait_for_received_data_ready(priv);
+
+              /* Save received data */
+
+              msgs[i].buffer[j] = i2c_get_receive_buffer(priv);
+            }
+          else
+            {
+              i2c_master_transmit(priv, msgs[i].buffer[j]);
+              i2c_wait_for_slave_ack(priv);
+            }
+        }
+
+      /* Check if you should not stop the i2c frame
+      * msg has NO_STOP or next msg has NO_START flag
+      */
+
+      bool message_has_no_stop = ((msgs[i].flags & I2C_M_NOSTOP) != 0);
+
+      bool next_message_has_no_start = false;
+
+      if (i < count)
+        {
+          next_message_has_no_start =
+                                ((msgs[i + 1].flags & I2C_M_NOSTART) != 0);
+        }
+
+      bool should_stop = !(message_has_no_stop || next_message_has_no_start);
+
+      /* TODO : Should you stop when last msg and overide flags ? */
+
+      if (should_stop)
+        {
+          i2c_stop(priv);
+        }
+      else
+        {
+          previous_msg_no_stop = true;
+        }
+    }
+
+  leave_critical_section(state);
+  nxmutex_unlock(&priv->lock);
+  return ret;
+}
+
+#ifdef CONFIG_I2C_RESET
+int i2c_reset(struct i2c_master_s *dev)
+{
+#error not implemented
+}
+#endif
+
+/*****************************************************************************
+ * Public Functions
+ *****************************************************************************/
+
+/*****************************************************************************
+ * Name: xmc4_i2cbus_initialize
+ *
+ * Description:
+ *   Initialise an I2C device
+ *
+ *****************************************************************************/
+
+struct i2c_master_s *xmc4_i2cbus_initialize(int port)
+{
+  struct xmc4_i2cdev_s *priv = NULL;
+
+  switch (port)
+    {
+#ifdef CONFIG_XMC4_I2C0
+      case 0:
+      {
+        priv = (struct xmc4_i2cdev_s *)&g_i2c0;
+        break;
+      }
+#endif
+
+#ifdef CONFIG_XMC4_I2C1
+      case 1:
+      {
+        priv = (struct xmc4_i2cdev_s *)&g_i2c1;
+        break;
+      }
+#endif
+
+#ifdef CONFIG_XMC4_I2C2
+      case 2:
+      {
+        priv = (struct xmc4_i2cdev_s *)&g_i2c2;
+        break;
+      }
+#endif
+
+#ifdef CONFIG_XMC4_I2C3
+      case 3:
+      {
+        priv = (struct xmc4_i2cdev_s *)&g_i2c3;
+        break;
+      }
+#endif
+
+#ifdef CONFIG_XMC4_I2C4
+      case 4:
+      {
+        priv = (struct xmc4_i2cdev_s *)&g_i2c4;
+        break;
+      }
+#endif
+
+#ifdef CONFIG_XMC4_I2C5
+      case 5:
+      {
+        priv = (struct xmc4_i2cdev_s *)&g_i2c5;
+        break;
+      }
+#endif
+
+      default:
+      {
+        i2cerr("No such i2c module.\n");
+        return NULL;
+      }
+    }
+
+  nxmutex_lock(&priv->lock);
+
+  if (priv->refs++ == 0)
+    {
+      int ret = i2c_initialize(priv);
+      if (ret < 0)
+        {
+          nxmutex_unlock(&priv->lock);
+          return NULL;
+        }
+    }
+
+  nxmutex_unlock(&priv->lock);
+
+  return (struct i2c_master_s *)priv;
+}
+
+/*****************************************************************************
+ * Name: xmc4_i2cbus_uninitialize
+ *
+ * Description:
+ *   Uninitialise an I2C device
+ *
+ *****************************************************************************/
+
+int xmc4_i2cbus_uninitialize(struct i2c_master_s *dev)
+{
+  struct xmc4_i2cdev_s *priv = (struct xmc4_i2cdev_s *)dev;
+
+  if (priv->refs == 0)
+    {
+      return ERROR;
+    }
+
+  nxmutex_lock(&priv->lock);
+
+  if (--priv->refs)
+    {
+      nxmutex_unlock(&priv->lock);
+      kmm_free(dev);
+      return OK;
+    }
+
+  i2c_uninitialize(priv);
+
+  nxmutex_unlock(&priv->lock);
+
+  kmm_free(dev);
+
+  return OK;
+}
+
+#endif /* CONFIG_XMC4_USCI_I2C */
diff --git a/arch/arm/src/xmc4/xmc4_i2c.h b/arch/arm/src/xmc4/xmc4_i2c.h
index f79d46514d..c910f03877 100644
--- a/arch/arm/src/xmc4/xmc4_i2c.h
+++ b/arch/arm/src/xmc4/xmc4_i2c.h
@@ -27,7 +27,6 @@
 
 #include <nuttx/config.h>
 #include <nuttx/i2c/i2c_master.h>
-#include "hardware/xmc4_i2c.h"
 
 /****************************************************************************
  * Public Function Prototypes

Reply via email to