GD32-MCU commented on code in PR #7884:
URL: https://github.com/apache/nuttx/pull/7884#discussion_r1051311608


##########
arch/arm/src/gd32f4/gd32f4xx_i2c.c:
##########
@@ -0,0 +1,2731 @@
+/****************************************************************************
+ * arch/arm/src/gd32f4/gd32f4xx_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.
+ *
+ ****************************************************************************/
+
+/* Supports:
+ *  - Master operation, 100 kHz (standard) and 400 kHz (full speed)
+ * TODO:
+ *  - Multiple instances (shared bus)
+ *  - Interrupt based operation
+ *
+ * Structure naming:
+ *  - Device: structure as defined by the nuttx/i2c/i2c.h
+ *  - Instance: represents each individual access to the I2C driver, obtained
+ *      by the i2c_init(); it extends the Device structure from the
+ *      nuttx/i2c/i2c.h;
+ *      Instance points to OPS, to common I2C Hardware private data and
+ *      contains its own private data, as frequency, address, mode of
+ *      operation (in the future)
+ *  - Private: Private data of an I2C Hardware
+ *
+ * TODO
+ *  - Check for all possible deadlocks (as BUSY='1' I2C needs to be reset in
+ *    hardware using the I2C_CTL0_SRESET)
+ *  - SMBus support (hardware layer timings are already supported) and add
+ *    SMBA gpio pin
+ *  - Slave support with multiple addresses (on multiple instances):
+ *      - 2 x 7-bit address or
+ *      - 1 x 10 bit addresses + 1 x 7 bit address (for the slave in
+ *        Dual-Address mode)
+ *      - plus the broadcast address (general call)
+ *  - Multi-master support
+ */
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+
+#include <sys/types.h>
+#include <inttypes.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <stdbool.h>
+#include <assert.h>
+#include <errno.h>
+#include <debug.h>
+
+#include <nuttx/arch.h>
+#include <nuttx/irq.h>
+#include <nuttx/clock.h>
+#include <nuttx/semaphore.h>
+#include <nuttx/i2c/i2c_master.h>
+
+#include <arch/board/board.h>
+
+#include "arm_internal.h"
+
+#include "chip.h"
+#include "gd32f4xx.h"
+#include "gd32f4xx_i2c.h"
+
+/* At least one I2C peripheral must be enabled */
+
+#if defined(CONFIG_GD32F4_I2C0) || defined(CONFIG_GD32F4_I2C1) || \
+    defined(CONFIG_GD32F4_I2C2)
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+
+/* Configuration ************************************************************/
+
+/* CONFIG_I2C_POLLED may be set so that I2C interrupts will not be used.
+ * Instead, CPU-intensive polling will be used.
+ */
+
+/* Interrupt wait timeout in seconds and milliseconds */
+
+#if !defined(CONFIG_GD32F4_I2C_TIMEOSEC) && !defined(CONFIG_GD32F4_I2C_TIMEOMS)
+#  define CONFIG_GD32F4_I2C_TIMEOSEC 0
+#  define CONFIG_GD32F4_I2C_TIMEOMS  500   /* Default is 500 milliseconds */
+#elif !defined(CONFIG_GD32F4_I2C_TIMEOSEC)
+#  define CONFIG_GD32F4_I2C_TIMEOSEC 0     /* User provided milliseconds */
+#elif !defined(CONFIG_GD32F4_I2C_TIMEOMS)
+#  define CONFIG_GD32F4_I2C_TIMEOMS  0     /* User provided seconds */
+#endif
+
+/* Interrupt wait time timeout in system timer ticks */
+
+#ifndef CONFIG_GD32F4_I2CTIMEOTICKS
+#  define CONFIG_GD32F4_I2CTIMEOTICKS \
+    (SEC2TICK(CONFIG_GD32F4_I2C_TIMEOSEC) + \
+     MSEC2TICK(CONFIG_GD32F4_I2C_TIMEOMS))
+#endif
+
+#ifndef CONFIG_GD32F4_I2C_DYNTIMEO_STARTSTOP
+#  define CONFIG_GD32F4_I2C_DYNTIMEO_STARTSTOP 
TICK2USEC(CONFIG_GD32F4_I2CTIMEOTICKS)
+#endif
+
+/* Macros to convert a I2C pin to a GPIO output */
+
+#define I2C_OUTPUT (GPIO_CFG_MODE_OUTPUT | GPIO_CFG_PUPD_NONE | GPIO_CFG_OD |\
+                    GPIO_CFG_SPEED_50MHZ | GPIO_CFG_OUTPUT_SET)
+
+#define MKI2C_OUTPUT(p) (((p) & (GPIO_CFG_PORT_MASK | GPIO_CFG_PIN_MASK)) |\
+                         I2C_OUTPUT)
+
+/* I2C DMA priority */
+
+#ifdef CONFIG_GD32F4_I2C_DMA
+
+#  error "Now I2C DMA has not ready"
+
+# if defined(CONFIG_I2C_DMAPRIO)
+#   if (CONFIG_I2C_DMAPRIO & ~DMA_CHXCTL_PRIO_MASK) != 0
+#     error "Illegal value for CONFIG_I2C_DMAPRIO"
+#   endif
+#   define I2C_DMA_PRIO     CONFIG_I2C_DMAPRIO
+# else
+#   define I2C_DMA_PRIO     DMA_PRIO_HIGH_SELECT
+# endif
+#endif
+
+/* Debug ********************************************************************/
+
+/* I2C event trace logic.  NOTE:  trace uses the internal, non-standard,
+ * low-level debug interface syslog() but does not require that any other
+ * debug is enabled.
+ */
+
+#ifndef CONFIG_I2C_TRACE
+#  define gd32_i2c_tracereset(p)
+#  define gd32_i2c_tracenew(p,s)
+#  define gd32_i2c_traceevent(p,e,a)
+#  define gd32_i2c_tracedump(p)
+#endif
+
+#ifndef CONFIG_I2C_NTRACE
+#  define CONFIG_I2C_NTRACE 32
+#endif
+
+/****************************************************************************
+ * Private Types
+ ****************************************************************************/
+
+/* Interrupt state */
+
+enum gd32_intstate_e
+{
+  INTSTATE_IDLE = 0,      /* No I2C activity */
+  INTSTATE_WAITING,       /* Waiting for completion of interrupt activity */
+  INTSTATE_DONE,          /* Interrupt activity complete */
+};
+
+/* Trace events */
+
+enum gd32_trace_e
+{
+  I2CEVENT_NONE = 0,
+  I2CEVENT_STATE_ERROR,
+  I2CEVENT_ISR_SHUTDOWN,
+  I2CEVENT_ISR_CALL,
+  I2CEVENT_ISR_EMPTY_CALL,
+  I2CEVENT_MSG_HANDLING,
+  I2CEVENT_POLL_NOT_READY,
+  I2CEVENT_EMPTY_MSG,
+  I2CEVENT_START,
+  I2CEVENT_SENDADDR,
+  I2CEVENT_ADDRESS_ACKED,
+  I2CEVENT_ADDRESS_NACKED,
+  I2CEVENT_NACK,
+  I2CEVENT_READ,
+  I2CEVENT_READ_ERROR,
+  I2CEVENT_ADDRESS_ACKED_READ_1,
+  I2CEVENT_ADDRESS_ACKED_READ_2,
+  I2CEVENT_WRITE_TO_DR,
+  I2CEVENT_WRITE_STOP,
+  I2CEVENT_WRITE_RESTART,
+  I2CEVENT_WRITE_NO_RESTART,
+  I2CEVENT_WRITE_ERROR,
+  I2CEVENT_WRITE_FLAG_ERROR,
+  I2CEVENT_TC_RESTART,
+  I2CEVENT_TC_NO_RESTART,
+  I2CEVENT_ERROR
+};
+
+/* Trace data */
+
+struct gd32_trace_s
+{
+  uint32_t status;             /* I2C 32-bit STAT0|STAT1 status */
+  uint32_t count;              /* Interrupt count when status change */
+  enum gd32_intstate_e event;  /* Last event that occurred with this status */
+  uint32_t parm;               /* Parameter associated with the event */
+  clock_t time;                /* First of event or first status */
+};
+
+/* I2C Device hardware configuration */
+
+struct gd32_i2c_config_s
+{
+  uint32_t i2c_base;          /* I2C base address */
+  uint32_t scl_pin;           /* GPIO configuration for SCL as SCL */
+  uint32_t sda_pin;           /* GPIO configuration for SDA as SDA */
+#ifndef CONFIG_I2C_POLLED
+  uint32_t event_irq;         /* Event IRQ */
+  uint32_t error_irq;         /* Error IRQ */
+#endif
+};
+
+/* I2C Device Private Data */
+
+struct gd32_i2c_priv_s
+{
+  /* Standard I2C operations */
+
+  const struct i2c_ops_s *ops;
+
+  /* Port configuration */
+
+  const struct gd32_i2c_config_s *config;
+
+  int refs;                    /* Reference count */
+  sem_t sem_excl;              /* Mutual exclusion semaphore */
+#ifndef CONFIG_I2C_POLLED
+  sem_t sem_isr;               /* Interrupt wait semaphore */
+#endif
+  volatile uint8_t intstate;   /* Interrupt handshake (see enum 
gd32_intstate_e) */
+
+  uint8_t msgc;                /* Message count */
+  struct i2c_msg_s *msgv;      /* Message list */
+  uint8_t *ptr;                /* Current message buffer */
+  uint32_t frequency;          /* Current I2C frequency */
+  volatile int dcnt;           /* Current message length */
+  uint16_t flags;              /* Current message flags */
+  bool check_addr_ack;         /* Flag to signal if on next interrupt address 
has ACKed */
+
+  /* I2C trace support */
+
+#ifdef CONFIG_I2C_TRACE
+  int tndx;                    /* Trace array index */
+  clock_t start_time;          /* Time when the trace was started */
+
+  /* The actual trace data */
+
+  struct gd32_trace_s trace[CONFIG_I2C_NTRACE];
+#endif
+
+  uint32_t status;             /* End of transfer STAT0|STAT1 status */
+
+  /* I2C DMA support */
+
+#ifdef CONFIG_GD32F4_I2C_DMA
+  DMA_HANDLE      txdma;       /* TX DMA handle */
+  DMA_HANDLE      rxdma;       /* RX DMA handle */
+  uint8_t         txch;        /* TX DMA channel number */
+  uint8_t         rxch;        /* RX DMA channel number */
+#endif
+};
+
+/****************************************************************************
+ * Private Function Prototypes
+ ****************************************************************************/
+
+/* Clock */
+
+static void            gd32_i2c_periph_reset(uint32_t i2cbase);
+static void            gd32_i2c_clock_enable(uint32_t i2cbase);
+static void            gd32_i2c_clock_disable(uint32_t i2cbase);
+
+static inline uint16_t gd32_i2c_getreg(struct gd32_i2c_priv_s *priv,
+                                       uint8_t offset);
+static inline void     gd32_i2c_putreg(struct gd32_i2c_priv_s *priv,
+                                       uint8_t offset, uint16_t value);
+static inline void     gd32_i2c_modifyreg(struct gd32_i2c_priv_s *priv,
+                                          uint8_t offset, uint16_t clearbits,
+                                          uint16_t setbits);
+
+#ifdef CONFIG_GD32F4_I2C_DYNTIMEO
+static uint32_t    gd32_i2c_toticks(int msgc, struct i2c_msg_s *msgs);
+#endif /* CONFIG_GD32F4_I2C_DYNTIMEO */
+
+static inline int  gd32_i2c_sem_waitdone(struct gd32_i2c_priv_s *priv);
+static inline void gd32_i2c_sem_waitstop(struct gd32_i2c_priv_s *priv);
+static inline void gd32_i2c_sem_post(struct gd32_i2c_priv_s *priv);
+static inline void gd32_i2c_sem_init(struct gd32_i2c_priv_s *priv);
+static inline void gd32_i2c_sem_destroy(struct gd32_i2c_priv_s *priv);
+
+#ifdef CONFIG_I2C_TRACE
+static void  gd32_i2c_tracereset(struct gd32_i2c_priv_s *priv);
+static void  gd32_i2c_tracenew(struct gd32_i2c_priv_s *priv,
+                               uint32_t status);
+static void  gd32_i2c_traceevent(struct gd32_i2c_priv_s *priv,
+                                 enum gd32_trace_e event, uint32_t parm);
+static void  gd32_i2c_tracedump(struct gd32_i2c_priv_s *priv);
+#endif /* CONFIG_I2C_TRACE */
+
+static void        gd32_i2c_setclock(struct gd32_i2c_priv_s *priv,
+                                     uint32_t frequency);
+static inline void gd32_i2c_sendstart(struct gd32_i2c_priv_s *priv);
+static inline void gd32_i2c_clrstart(struct gd32_i2c_priv_s *priv);
+static inline void gd32_i2c_sendstop(struct gd32_i2c_priv_s *priv);
+static inline
+uint32_t           gd32_i2c_getstatus(struct gd32_i2c_priv_s *priv);
+
+static int         gd32_i2c_isr_process(struct gd32_i2c_priv_s *priv);
+
+#ifndef CONFIG_I2C_POLLED
+static int         gd32_i2c_isr(int irq, void *context, void *arg);
+#endif /* !CONFIG_I2C_POLLED */
+
+static int         gd32_i2c_init(struct gd32_i2c_priv_s *priv);
+static int         gd32_i2c_deinit(struct gd32_i2c_priv_s *priv);
+static int         gd32_i2c_transfer(struct i2c_master_s *dev,
+                                     struct i2c_msg_s *msgs, int count);
+#ifdef CONFIG_I2C_RESET
+static int         gd32_i2c_reset(struct i2c_master_s *dev);
+#endif
+
+/* DMA support */
+
+#ifdef CONFIG_GD32F4_I2C_DMA
+static void        gd32_i2c_dmarxcallback(DMA_HANDLE handle,
+                                          uint8_t status, void *arg);
+static void        gd32_i2c_dmatxcallback(DMA_HANDLE handle,
+                                          uint8_t status, void *arg);
+#endif
+
+/****************************************************************************
+ * Private Data
+ ****************************************************************************/
+
+/* I2C interface */
+
+static const struct i2c_ops_s gd32_i2c_ops =
+{
+  .transfer = gd32_i2c_transfer,
+#ifdef CONFIG_I2C_RESET
+  .reset  = gd32_i2c_reset
+#endif
+};
+
+/* I2C device structures */
+
+#ifdef CONFIG_GD32F4_I2C0
+static const struct gd32_i2c_config_s gd32_i2c0_config =
+{
+  .i2c_base   = GD32_I2C0,
+  .scl_pin    = GPIO_I2C0_SCL,
+  .sda_pin    = GPIO_I2C0_SDA,
+#ifndef CONFIG_I2C_POLLED
+  .event_irq  = GD32_IRQ_I2C0_EV,
+  .error_irq  = GD32_IRQ_I2C0_ER
+#endif
+};
+
+static struct gd32_i2c_priv_s gd32_i2c0_priv =
+{
+  .ops        = &gd32_i2c_ops,
+  .config     = &gd32_i2c0_config,
+  .refs       = 0,
+  .intstate   = INTSTATE_IDLE,
+  .msgc       = 0,
+  .msgv       = NULL,
+  .ptr        = NULL,
+  .dcnt       = 0,
+  .flags      = 0,
+  .status     = 0,
+#ifdef CONFIG_GD32F4_I2C_DMA
+  .rxch       = DMA_REQ_I2C0_RX,
+  .txch       = DMA_REQ_I2C0_TX,
+#endif
+};
+#endif
+
+#ifdef CONFIG_GD32F4_I2C1
+static const struct gd32_i2c_config_s gd32_i2c1_config =
+{
+  .i2c_base   = GD32_I2C1,
+  .scl_pin    = GPIO_I2C1_SCL,
+  .sda_pin    = GPIO_I2C1_SDA,
+#ifndef CONFIG_I2C_POLLED
+  .event_irq  = GD32_IRQ_I2C1_EV,
+  .error_irq  = GD32_IRQ_I2C1_ER
+#endif
+};
+
+static struct gd32_i2c_priv_s gd32_i2c1_priv =
+{
+  .ops        = &gd32_i2c_ops,
+  .config     = &gd32_i2c1_config,
+  .refs       = 0,
+  .intstate   = INTSTATE_IDLE,
+  .msgc       = 0,
+  .msgv       = NULL,
+  .ptr        = NULL,
+  .dcnt       = 0,
+  .flags      = 0,
+  .status     = 0,
+#ifdef CONFIG_GD32F4_I2C_DMA
+  .rxch       = DMA_REQ_I2C1_RX,
+  .txch       = DMA_REQ_I2C1_TX,
+#endif
+};
+#endif
+
+#ifdef CONFIG_GD32F4_I2C2
+static const struct gd32_i2c_config_s gd32_i2c2_config =
+{
+  .i2c_base   = GD32_I2C2,
+  .scl_pin    = GPIO_I2C2_SCL,
+  .sda_pin    = GPIO_I2C2_SDA,
+#ifndef CONFIG_I2C_POLLED
+  .event_irq  = GD32_IRQ_I2C2_EV,
+  .error_irq  = GD32_IRQ_I2C2_ER
+#endif
+};
+
+static struct gd32_i2c_priv_s gd32_i2c2_priv =
+{
+  .ops        = &gd32_i2c_ops,
+  .config     = &gd32_i2c2_config,
+  .refs       = 0,
+  .intstate   = INTSTATE_IDLE,
+  .msgc       = 0,
+  .msgv       = NULL,
+  .ptr        = NULL,
+  .dcnt       = 0,
+  .flags      = 0,
+  .status     = 0,
+#ifdef CONFIG_GD32F4_I2C_DMA
+  .rxch       = DMA_REQ_I2C2_RX,
+  .txch       = DMA_REQ_I2C2_TX,
+#endif
+};
+#endif
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: gd32_i2c_periph_reset
+ *
+ * Description:
+ *   Reset the I2C.
+ *
+ ****************************************************************************/
+
+static void gd32_i2c_periph_reset(uint32_t i2cbase)
+{
+  uint32_t rcu_rst;
+  uint32_t regaddr;
+
+  /* Determine which I2C to configure */
+
+  switch (i2cbase)
+    {
+    default:
+      return;
+#ifdef CONFIG_GD32F4_I2C0
+    case GD32_I2C0:
+      rcu_rst = RCU_APB1RST_I2C0RST;
+      regaddr = GD32_RCU_APB1RST;
+      break;
+#endif
+#ifdef CONFIG_GD32F4_I2C1
+    case GD32_I2C1:
+      rcu_rst = RCU_APB1RST_I2C1RST;
+      regaddr = GD32_RCU_APB1RST;
+      break;
+#endif
+#ifdef CONFIG_GD32F4_I2C2
+    case GD32_I2C2:
+      rcu_rst = RCU_APB1RST_I2C2RST;
+      regaddr = GD32_RCU_APB1RST;
+      break;
+#endif
+    }
+
+  /* Enable APB 1/2 reset for I2C */
+
+  modifyreg32(regaddr, 0, rcu_rst);
+
+  /* Disable APB 1/2 reset for I2C */
+
+  modifyreg32(regaddr, rcu_rst, 0);
+}
+
+/****************************************************************************
+ * Name: gd32_i2c_clock_enable
+ *
+ * Description:
+ *   Enable I2C clock
+ ****************************************************************************/
+
+static void gd32_i2c_clock_enable(uint32_t i2cbase)
+{
+  uint32_t rcu_en;
+  uint32_t regaddr;
+
+  /* Determine which I2C to configure */
+
+  switch (i2cbase)
+    {
+    default:
+      return;
+#ifdef CONFIG_GD32F4_I2C0
+    case GD32_I2C0:
+      rcu_en = RCU_APB1EN_I2C0EN;
+      regaddr = GD32_RCU_APB1EN;
+      break;
+#endif
+#ifdef CONFIG_GD32F4_I2C1
+    case GD32_I2C1:
+      rcu_en = RCU_APB1EN_I2C1EN;
+      regaddr = GD32_RCU_APB1EN;
+      break;
+#endif
+#ifdef CONFIG_GD32F4_I2C2
+    case GD32_I2C2:
+      rcu_en = RCU_APB1EN_I2C2EN;
+      regaddr = GD32_RCU_APB1EN;
+      break;
+#endif
+    }
+
+  /* Enable APB 1/2 clock for I2C */
+
+  modifyreg32(regaddr, 0, rcu_en);
+}
+
+/****************************************************************************
+ * Name: gd32_i2c_clock_disable
+ *
+ * Description:
+ *   Dinable I2C clock
+ ****************************************************************************/
+
+static void gd32_i2c_clock_disable(uint32_t i2cbase)
+{
+  uint32_t rcu_en;
+  uint32_t regaddr;
+
+  /* Determine which I2C to configure */
+
+  switch (i2cbase)
+    {
+    default:
+      return;
+#ifdef CONFIG_GD32F4_I2CI0
+    case GD32_I2C0:
+      rcu_en = RCU_APB1EN_I2C0EN;
+      regaddr = GD32_RCU_APB1EN;
+      break;
+#endif
+#ifdef CONFIG_GD32F4_I2C1
+    case GD32_I2C1:
+      rcu_en = RCU_APB1EN_I2C1EN;
+      regaddr = GD32_RCU_APB1EN;
+      break;
+#endif
+#ifdef CONFIG_GD32F4_I2C2
+    case GD32_I2C2:
+      rcu_en = RCU_APB1EN_I2C2EN;
+      regaddr = GD32_RCU_APB1EN;
+      break;
+#endif
+    }
+
+  /* Disable APB 1/2 clock for I2C */
+
+  modifyreg32(regaddr, rcu_en, 0);
+}
+
+/****************************************************************************
+ * Name: gd32_i2c_getreg
+ *
+ * Description:
+ *   Get a 16-bit register value by offset
+ *
+ ****************************************************************************/
+
+static inline uint16_t gd32_i2c_getreg(struct gd32_i2c_priv_s *priv,
+                                      uint8_t offset)
+{
+  return getreg16(priv->config->i2c_base + offset);
+}
+
+/****************************************************************************
+ * Name: gd32_i2c_putreg
+ *
+ * Description:
+ *  Put a 16-bit register value by offset
+ *
+ ****************************************************************************/
+
+static inline void gd32_i2c_putreg(struct gd32_i2c_priv_s *priv,
+                                  uint8_t offset, uint16_t value)
+{
+  putreg16(value, priv->config->i2c_base + offset);
+}
+
+/****************************************************************************
+ * Name: gd32_i2c_modifyreg
+ *
+ * Description:
+ *   Modify a 16-bit register value by offset
+ *
+ ****************************************************************************/
+
+static inline void gd32_i2c_modifyreg(struct gd32_i2c_priv_s *priv,
+                                      uint8_t offset, uint16_t clearbits,
+                                      uint16_t setbits)
+{
+  modifyreg16(priv->config->i2c_base + offset, clearbits, setbits);
+}
+
+/****************************************************************************
+ * Name: gd32_i2c_toticks
+ *
+ * Description:
+ *   Return a micro-second delay based on the number of bytes left to be
+ *   processed.
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_GD32F4_I2C_DYNTIMEO
+static uint32_t gd32_i2c_toticks(int msgc, struct i2c_msg_s *msgs)
+{
+  size_t bytecount = 0;
+  int i;
+
+  /* Count the number of bytes left to process */
+
+  for (i = 0; i < msgc; i++)
+    {
+      bytecount += msgs[i].length;
+    }
+
+  /* Then return a number of microseconds based on a user provided scaling
+   * factor.
+   */
+
+  return USEC2TICK(CONFIG_GD32F4_I2C_DYNTIMEO_USECPERBYTE * bytecount);
+}
+#endif
+
+/****************************************************************************
+ * Name: gd32_i2c_sem_waitdone
+ *
+ * Description:
+ *   Wait for a transfer to complete
+ *
+ ****************************************************************************/
+
+#ifndef CONFIG_I2C_POLLED
+static inline int gd32_i2c_sem_waitdone(struct gd32_i2c_priv_s *priv)
+{
+  irqstate_t flags;
+  uint32_t regval;
+  int ret;
+
+  flags = enter_critical_section();
+
+  /* Enable I2C interrupts */
+
+  regval  = gd32_i2c_getreg(priv, GD32_I2C_CTL1_OFFSET);
+  regval |= (I2C_CTL1_ERRIE | I2C_CTL1_EVIE);
+  gd32_i2c_putreg(priv, GD32_I2C_CTL1_OFFSET, regval);
+
+  /* Signal the interrupt handler that we are waiting.  NOTE:  Interrupts
+   * are currently disabled but will be temporarily re-enabled below when
+   * nxsem_tickwait_uninterruptible() sleeps.
+   */
+
+  priv->intstate = INTSTATE_WAITING;
+  do
+    {
+      /* Wait until either the transfer is complete or the timeout expires */
+
+#ifdef CONFIG_GD32F4_I2C_DYNTIMEO
+      ret = nxsem_tickwait_uninterruptible(&priv->sem_isr,
+                           gd32_i2c_toticks(priv->msgc, priv->msgv));
+#else
+      ret = nxsem_tickwait_uninterruptible(&priv->sem_isr,
+                                           CONFIG_GD32F4_I2C_TIMEOTICKS);
+#endif
+      if (ret < 0)
+        {
+          /* Break out of the loop on irrecoverable errors.  This would
+           * include timeouts and mystery errors reported by
+           * nxsem_tickwait_uninterruptible.
+           */
+
+          break;
+        }
+    }
+
+  /* Loop until the interrupt level transfer is complete. */
+
+  while (priv->intstate != INTSTATE_DONE);
+
+  /* Set the interrupt state back to IDLE */
+
+  priv->intstate = INTSTATE_IDLE;
+
+  /* Disable I2C interrupts */
+
+  regval  = gd32_i2c_getreg(priv, GD32_I2C_CTL1_OFFSET);
+  regval &= ~I2C_CTL1_INTS_MASK;
+  gd32_i2c_putreg(priv, GD32_I2C_CTL1_OFFSET, regval);
+
+  leave_critical_section(flags);
+  return ret;
+}
+#else
+static inline int gd32_i2c_sem_waitdone(struct gd32_i2c_priv_s *priv)
+{
+  clock_t timeout;
+  clock_t start;
+  clock_t elapsed;
+  int ret;
+
+  /* Get the timeout value */
+
+#ifdef CONFIG_GD32F4_I2C_DYNTIMEO
+  timeout = gd32_i2c_toticks(priv->msgc, priv->msgv);
+#else
+  timeout = CONFIG_GD32F4_I2C_TIMEOTICKS;
+#endif
+
+  /* Signal the interrupt handler that we are waiting.  NOTE:  Interrupts
+   * are currently disabled but will be temporarily re-enabled below when
+   * nxsem_tickwait_uninterruptible() sleeps.
+   */
+
+  priv->intstate = INTSTATE_WAITING;
+  start = clock_systime_ticks();
+
+  do
+    {
+      /* Calculate the elapsed time */
+
+      elapsed = clock_systime_ticks() - start;
+
+      /* Poll by simply calling the timer interrupt handler until it
+       * reports that it is done.
+       */
+
+      gd32_i2c_isr_process(priv);
+    }
+
+  /* Loop until the transfer is complete. */
+
+  while (priv->intstate != INTSTATE_DONE && elapsed < timeout);
+
+  i2cinfo("intstate: %d elapsed: %ld threshold: %ld status: %08" PRIx32 "\n",
+          priv->intstate, (long)elapsed, (long)timeout, priv->status);
+
+  /* Set the interrupt state back to IDLE */
+
+  ret = priv->intstate == INTSTATE_DONE ? OK : -ETIMEDOUT;
+  priv->intstate = INTSTATE_IDLE;
+  return ret;
+}
+#endif
+
+/****************************************************************************
+ * Name: gd32_i2c_sem_waitstop
+ *
+ * Description:
+ *   Wait for a STOP to complete
+ *
+ ****************************************************************************/
+
+static inline void gd32_i2c_sem_waitstop(struct gd32_i2c_priv_s *priv)
+{
+  clock_t start;
+  clock_t elapsed;
+  clock_t timeout;
+  uint32_t ctl0;
+  uint32_t stat0;
+
+  /* Select a timeout */
+
+#ifdef CONFIG_GD32F4_I2C_DYNTIMEO
+  timeout = USEC2TICK(CONFIG_GD32F4_I2C_DYNTIMEO_STARTSTOP);
+#else
+  timeout = CONFIG_GD32F4_I2C_TIMEOTICKS;
+#endif
+
+  /* Wait as stop might still be in progress; but stop might also
+   * be set because of a timeout error: "The [STOP] bit is set and
+   * cleared by software, cleared by hardware when a Stop condition is
+   * detected, set by hardware when a timeout error is detected."
+   */
+
+  start = clock_systime_ticks();
+  do
+    {
+      /* Calculate the elapsed time */
+
+      elapsed = clock_systime_ticks() - start;
+
+      /* Check for STOP condition */
+
+      ctl0 = gd32_i2c_getreg(priv, GD32_I2C_CTL0_OFFSET);
+      if ((ctl0 & I2C_CTL0_STOP) == 0)
+        {
+          return;
+        }
+
+      /* Check for timeout error */
+
+      stat0 = gd32_i2c_getreg(priv, GD32_I2C_STAT0_OFFSET);
+      if ((stat0 & I2C_STAT0_SMBTO) != 0)
+        {
+          return;
+        }
+    }
+
+  /* Loop until the stop is complete or a timeout occurs. */
+
+  while (elapsed < timeout);
+
+  /* If we get here then a timeout occurred with the STOP condition
+   * still pending.
+   */
+
+  i2cinfo("Timeout with CTL0: %04" PRIx32 " STAT0: %04" PRIx32 "\n", \
+          ctl0, stat0);
+}
+
+/****************************************************************************
+ * Name: gd32_i2c_sem_post
+ *
+ * Description:
+ *   Release the mutual exclusion semaphore
+ *
+ ****************************************************************************/
+
+static inline void gd32_i2c_sem_post(struct gd32_i2c_priv_s *priv)
+{
+  nxsem_post(&priv->sem_excl);
+}
+
+/****************************************************************************
+ * Name: gd32_i2c_sem_init
+ *
+ * Description:
+ *   Initialize semaphores
+ *
+ ****************************************************************************/
+
+static inline void gd32_i2c_sem_init(struct gd32_i2c_priv_s *priv)
+{
+  nxsem_init(&priv->sem_excl, 0, 1);
+
+#ifndef CONFIG_I2C_POLLED
+  /* This semaphore is used for signaling and, hence, should not have
+   * priority inheritance enabled.
+   */
+
+  nxsem_init(&priv->sem_isr, 0, 0);
+  nxsem_set_protocol(&priv->sem_isr, SEM_PRIO_NONE);
+#endif
+}
+
+/****************************************************************************
+ * Name: gd32_i2c_sem_destroy
+ *
+ * Description:
+ *   Destroy semaphores.
+ *
+ ****************************************************************************/
+
+static inline void gd32_i2c_sem_destroy(struct gd32_i2c_priv_s *priv)
+{
+  nxsem_destroy(&priv->sem_excl);
+#ifndef CONFIG_I2C_POLLED
+  nxsem_destroy(&priv->sem_isr);
+#endif
+}
+
+/****************************************************************************
+ * Name: gd32_i2c_trace*
+ *
+ * Description:
+ *   I2C trace instrumentation
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_I2C_TRACE
+static void gd32_i2c_traceclear(struct gd32_i2c_priv_s *priv)
+{
+  struct gd32_trace_s *trace = &priv->trace[priv->tndx];
+
+  trace->status = 0;              /* I2C 32-bit STAT1|STAT0 status */
+  trace->count  = 0;              /* Interrupt count when status change */
+  trace->event  = I2CEVENT_NONE;  /* Last event that occurred with
+                                   * this status */
+  trace->parm   = 0;              /* Parameter associated with the event */
+  trace->time   = 0;              /* Time of first status or event */
+}
+
+static void gd32_i2c_tracereset(struct gd32_i2c_priv_s *priv)
+{
+  /* Reset the trace info for a new data collection */
+
+  priv->tndx       = 0;
+  priv->start_time = clock_systime_ticks();
+  gd32_i2c_traceclear(priv);
+}
+
+static void gd32_i2c_tracenew(struct gd32_i2c_priv_s *priv,
+                               uint32_t status)
+{
+  struct gd32_trace_s *trace = &priv->trace[priv->tndx];
+
+  /* Is the current entry uninitialized?  Has the status changed? */
+
+  if (trace->count == 0 || status != trace->status)
+    {
+      /* Yes.. Was it the status changed?  */
+
+      if (trace->count != 0)
+        {
+          /* Yes.. bump up the trace index
+           * (unless we are out of trace entries)
+           */
+
+          if (priv->tndx >= (CONFIG_I2C_NTRACE - 1))
+            {
+              i2cerr("ERROR: Trace table overflow\n");
+              return;
+            }
+
+          priv->tndx++;
+          trace = &priv->trace[priv->tndx];
+        }
+
+      /* Initialize the new trace entry */
+
+      gd32_i2c_traceclear(priv);
+      trace->status = status;
+      trace->count  = 1;
+      trace->time   = clock_systime_ticks();
+    }
+  else
+    {
+      /* Just increment the count of times that we have seen this status */
+
+      trace->count++;
+    }
+}
+
+static void gd32_i2c_traceevent(struct gd32_i2c_priv_s *priv,
+                                 enum gd32_trace_e event, uint32_t parm)
+{
+  struct gd32_trace_s *trace;
+
+  if (event != I2CEVENT_NONE)
+    {
+      trace = &priv->trace[priv->tndx];
+
+      /* Initialize the new trace entry */
+
+      trace->event  = event;
+      trace->parm   = parm;
+
+      /* Bump up the trace index (unless we are out of trace entries) */
+
+      if (priv->tndx >= (CONFIG_I2C_NTRACE - 1))
+        {
+          i2cerr("ERROR: Trace table overflow\n");
+          return;
+        }
+
+      priv->tndx++;
+      gd32_i2c_traceclear(priv);
+    }
+}
+
+static void gd32_i2c_tracedump(struct gd32_i2c_priv_s *priv)
+{
+  struct gd32_trace_s *trace;
+  int i;
+
+  syslog(LOG_DEBUG, "Elapsed time: %ld\n",
+         (long)(clock_systime_ticks() - priv->start_time));
+
+  for (i = 0; i < priv->tndx; i++)
+    {
+      trace = &priv->trace[i];
+      syslog(LOG_DEBUG,
+             "%2d. STATUS: %08x COUNT: %3d EVENT: %2d PARM: %08x TIME: %d\n",
+             i + 1, trace->status, trace->count, trace->event, trace->parm,
+             (int)(trace->time - priv->start_time));
+    }
+}
+#endif /* CONFIG_I2C_TRACE */
+
+/****************************************************************************
+ * Name: gd32_i2c_setclock
+ *
+ * Description:
+ *   Set the I2C clock
+ *
+ ****************************************************************************/
+
+static void gd32_i2c_setclock(struct gd32_i2c_priv_s *priv,
+                               uint32_t frequency)
+{
+  uint16_t ctl0;
+  uint16_t ckfg;
+  uint16_t trise;
+  uint16_t freq_mhz;
+  uint16_t speed;
+
+  /* Has the I2C bus frequency changed? */
+
+  if (frequency != priv->frequency)
+    {
+      /* Disable the selected I2C peripheral to configure TRISE */
+
+      ctl0 = gd32_i2c_getreg(priv, GD32_I2C_CTL0_OFFSET);
+      gd32_i2c_putreg(priv, GD32_I2C_CTL0_OFFSET, ctl0 & ~I2C_CTL0_I2CEN);
+
+      /* Update timing and control registers */
+
+      freq_mhz = (uint16_t)(GD32_PCLK1_FREQUENCY / 1000000);
+      ckfg = 0;
+
+      /* Configure speed in standard mode */
+
+      if (frequency <= 100000)
+        {
+          /* Standard mode speed calculation */
+
+          speed = (uint16_t)(GD32_PCLK1_FREQUENCY / (frequency << 1));
+
+          /* The CKCFG fault must be >= 4 */
+
+          if (speed < 4)
+            {
+              /* Set the minimum allowed value */
+
+              speed = 4;
+            }
+
+          ckfg |= speed;
+
+          /* Set Maximum Rise Time for standard mode */
+
+          trise = freq_mhz + 1;
+        }
+
+      /* Configure speed in fast mode */
+
+      else /* (frequency <= 400000) */
+        {
+          /* Fast mode speed calculation with Tlow/Thigh = 16/9 */
+
+#ifdef CONFIG_GD32F4_I2C_DUTY16_9
+          speed = (uint16_t)(GD32_PCLK1_FREQUENCY / (frequency * 25));
+
+          /* Set DUTY and fast speed bits */
+
+          ckfg |= (I2C_CKCFG_DTCY | I2C_CKCFG_FAST);
+#else
+          /* Fast mode speed calculation with Tlow/Thigh = 2 */
+
+          speed = (uint16_t)(GD32_PCLK1_FREQUENCY / (frequency * 3));
+
+          /* Set fast speed bit */
+
+          ckfg |= I2C_CKCFG_FAST;
+#endif
+
+          /* Verify that the CKCFG speed value is nonzero */
+
+          if (speed < 1)
+            {
+              /* Set the minimum allowed value */
+
+              speed = 1;
+            }
+
+          ckfg |= speed;
+
+          /* Set Maximum Rise Time for fast mode */
+
+          trise = (uint16_t)(((freq_mhz * 300) / 1000) + 1);
+        }
+
+      /* Write the new values of the CKCFG and TRISE registers */
+
+      gd32_i2c_putreg(priv, GD32_I2C_CKCFG_OFFSET, ckfg);
+      gd32_i2c_putreg(priv, GD32_I2C_RT_OFFSET, trise);
+
+      /* Re-enable the peripheral (or not) */
+
+      gd32_i2c_putreg(priv, GD32_I2C_CTL0_OFFSET, ctl0);
+
+      /* Save the new I2C frequency */
+
+      priv->frequency = frequency;
+    }
+}
+
+/****************************************************************************
+ * Name: gd32_i2c_sendstart
+ *
+ * Description:
+ *   Send the START conditions/force Master mode
+ *
+ ****************************************************************************/
+
+static inline void gd32_i2c_sendstart(struct gd32_i2c_priv_s *priv)
+{
+  /* Generate START */
+
+  gd32_i2c_modifyreg(priv, GD32_I2C_CTL0_OFFSET,
+                     0, I2C_CTL0_START);
+}
+
+/****************************************************************************
+ * Name: gd32_i2c_clrstart
+ *
+ * Description:
+ *   Clear the STOP, START or PECTRANS condition on certain error
+ *   recovery steps.
+ *
+ ****************************************************************************/
+
+static inline void gd32_i2c_clrstart(struct gd32_i2c_priv_s *priv)
+{
+  /* "Note: When the STOP, START or PECTRANS bit is set, the software must
+   *  not perform any write access to I2C_CTL0 before this bit is
+   *  cleared by hardware. Otherwise there is a risk of setting a
+   *  second STOP, START or PECTRANS request."
+   *
+   * "The [STOP] bit is set and cleared by software, cleared by hardware
+   *  when a Stop condition is detected, set by hardware when a timeout
+   *  error is detected.
+   *
+   * "This [START] bit is set and cleared by software and cleared by hardware
+   *  when start is sent or I2CEN=0." The bit must be cleared by software if
+   *  the START is never sent.
+   *
+   * "This [PECTRANS] bit is set and cleared by software, and cleared by
+   *  hardware when PECTRANS is transferred or by a START or Stop condition
+   *  or when I2CEN=0."
+   */
+
+  gd32_i2c_modifyreg(priv, GD32_I2C_CTL0_OFFSET,
+                     I2C_CTL0_START | I2C_CTL0_STOP | I2C_CTL0_PECTRANS, 0);
+}
+
+/****************************************************************************
+ * Name: gd32_i2c_sendstop
+ *
+ * Description:
+ *   Send the STOP conditions
+ *
+ ****************************************************************************/
+
+static inline void gd32_i2c_sendstop(struct gd32_i2c_priv_s *priv)
+{
+  gd32_i2c_modifyreg(priv, GD32_I2C_CTL0_OFFSET, I2C_CTL0_ACKEN, \
+                     I2C_CTL0_STOP);
+}
+
+/****************************************************************************
+ * Name: gd32_i2c_getstatus
+ *
+ * Description:
+ *   Get 32-bit status (STAT0 and STAT1 combined)
+ *
+ ****************************************************************************/
+
+static inline uint32_t gd32_i2c_getstatus(struct gd32_i2c_priv_s *priv)
+{
+  uint32_t status = gd32_i2c_getreg(priv, GD32_I2C_STAT0_OFFSET);
+  status |= (gd32_i2c_getreg(priv, GD32_I2C_STAT1_OFFSET) << 16);
+  return status;
+}
+
+/****************************************************************************
+ * Name: gd32_i2c_isr_process
+ *
+ * Description:
+ *  Common Interrupt Service Routine
+ *
+ ****************************************************************************/
+
+static int gd32_i2c_isr_process(struct gd32_i2c_priv_s *priv)
+{
+  uint32_t status;
+#ifndef CONFIG_I2C_POLLED
+  uint32_t regval;
+#endif
+#ifdef CONFIG_GD32F4_I2C_DMA
+  uint16_t ctl1;
+#endif
+
+  i2cinfo("I2C ISR called\n");
+
+  /* Get state of the I2C controller (register STAT0 only)
+   *
+   * Get control register STAT0 only as reading both STAT0 and STAT1
+   * clears the ADDR flag(possibly others) causing the hardware to
+   * advance to the next state without the proper action being taken.
+   */
+
+  status = gd32_i2c_getreg(priv, GD32_I2C_STAT0_OFFSET);
+
+  /* Update private version of the state */
+
+  priv->status = status;
+
+  /* Check if this is a new transmission so to set up the
+   * trace table accordingly.
+   */
+
+  gd32_i2c_tracenew(priv, status);
+  gd32_i2c_traceevent(priv, I2CEVENT_ISR_CALL, 0);
+
+  /* Messages handling (1/2)
+   *
+   * Message handling should only operate when a message has been completely
+   * sent and after the ISR had the chance to run to set bits after the last
+   * written/read byte, i.e. priv->dcnt == -1. This is also the case in when
+   * the ISR is called for the first time. This can seen in
+   * gd32_i2c_process() before entering the gd32_i2c_sem_waitdone() waiting
+   * process.
+   *
+   * Message handling should only operate when:
+   *     - A message has been completely sent and there are still messages
+   *       to send(i.e. msgc > 0).
+   *     - After the ISR had the chance to run to set start bit or
+   *       termination flags after the last written/read byte(after last byte
+   *       dcnt=0, msg handling dcnt = -1).
+   *
+   * When the ISR is called for the first time the same conditions hold.
+   * This can seen in gd32_i2c_process() before entering the
+   * gd32_i2c_sem_waitdone() waiting process.
+   */
+
+#ifdef CONFIG_GD32F4_I2C_DMA
+  /* If ISR gets called (ex. polling mode) while DMA is still in
+   * progress, we should just return and let the DMA finish.
+   */
+
+  ctl1 = gd32_i2c_getreg(priv, GD32_I2C_CTL1_OFFSET);
+  if ((ctl1 & I2C_CTL1_DMAON) != 0)
+    {
+#ifdef CONFIG_DEBUG_I2C_INFO
+      size_t left = gd32_dma_tansnum_get(priv->rxdma);
+
+      i2cinfo("DMA in progress: %ld [bytes] remainining. Returning.\n",
+              left);
+#endif
+      return OK;
+    }
+#endif
+
+  if (priv->dcnt == -1 && priv->msgc > 0)
+    {
+      /* Any new message should begin with "Start" condition
+       * However there were 2 situations where that was not true
+       * Situation 1:
+       *    Next message continue transmission sequence of previous message
+       *
+       * Situation 2: If an error is injected that looks like a STOP the
+       * interrupt will be reentered with some status that will be incorrect.
+       * This will ensure that the error handler will clear the interrupt
+       * enables and return the error to the waiting task.
+       */
+
+      if (((priv->msgv[0].flags & I2C_M_NOSTART) != 0 &&
+           (status & I2C_STAT0_TBE) == 0) ||
+          ((priv->msgv[0].flags & I2C_M_NOSTART) == 0 &&
+           (status & I2C_STAT0_SBSEND) == 0))
+        {
+#if defined(CONFIG_GD32F4_I2C_DMA) || defined(CONFIG_I2C_POLLED)
+          return OK;
+#else
+          priv->status |= I2C_STAT0_SMBTO;
+          goto state_error;
+#endif
+        }
+
+      i2cinfo("Switch to new message\n");
+
+      /* Get current message to process data and copy to private structure */
+
+      priv->ptr           = priv->msgv->buffer;   /* Copy buffer to private 
struct */
+      priv->dcnt          = priv->msgv->length;   /* Set counter of current 
msg length */
+      priv->flags         = priv->msgv->flags;    /* Copy flags to private 
struct */

Review Comment:
   Thanks,I will modify according to comments.



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: commits-unsubscr...@nuttx.apache.org

For queries about this service, please contact Infrastructure at:
us...@infra.apache.org


Reply via email to