acassis commented on a change in pull request #2952:
URL: https://github.com/apache/incubator-nuttx/pull/2952#discussion_r585768582



##########
File path: arch/arm/src/rp2040/rp2040_i2c.c
##########
@@ -0,0 +1,832 @@
+/****************************************************************************
+ * arch/arm/src/rp2040/rp2040_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 <stdbool.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <debug.h>
+#include <assert.h>
+
+#include <nuttx/arch.h>
+#include <nuttx/i2c/i2c_master.h>
+
+#include <nuttx/irq.h>
+#include <arch/board/board.h>
+
+#include "chip.h"
+#include "arm_arch.h"
+#include "arm_internal.h"
+
+#include "rp2040_i2c.h"
+#include "hardware/rp2040_i2c.h"
+
+#ifdef CONFIG_RP2040_I2C
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+
+#define I2C_TIMEOUT  (20*1000/CONFIG_USEC_PER_TICK) /* 20 mS */
+
+#define I2C_DEFAULT_FREQUENCY 400000
+#define I2C_FIFO_MAX_SIZE          32
+
+#define I2C_INTR_ENABLE ((RP2040_I2C_IC_INTR_STAT_R_STOP_DET) | \
+                         (RP2040_I2C_IC_INTR_STAT_R_TX_ABRT)  | \
+                         (RP2040_I2C_IC_INTR_STAT_R_TX_OVER)  | \
+                         (RP2040_I2C_IC_INTR_STAT_R_RX_OVER)  | \
+                         (RP2040_I2C_IC_INTR_STAT_R_RX_UNDER))
+
+/****************************************************************************
+ * Private Data
+ ****************************************************************************/
+
+struct rp2040_i2cdev_s
+{
+  struct i2c_master_s dev;     /* Generic I2C device */
+  unsigned int     base;       /* Base address of registers */
+  uint16_t         irqid;      /* IRQ for this device */
+  int8_t           port;       /* Port number */
+  uint32_t         base_freq;  /* branch frequency */
+
+  sem_t            mutex;      /* Only one thread can access at a time */
+  sem_t            wait;       /* Place to wait for transfer completion */
+  struct wdog_s    timeout;    /* watchdog to timeout when bus hung */
+  uint32_t         frequency;  /* Current I2C frequency */
+  ssize_t          reg_buff_offset;
+  ssize_t          rw_size;
+
+  struct i2c_msg_s *msgs;
+
+  int              error;      /* Error status of each transfers */
+  int              refs;       /* Reference count */
+};
+
+#ifdef CONFIG_RP2040_I2C0
+static struct rp2040_i2cdev_s g_i2c0dev =
+{
+  .port = 0,
+  .base = RP2040_I2C0_BASE,
+  .irqid = RP2040_I2C0_IRQ,
+  .refs = 0,
+};
+#endif
+#ifdef CONFIG_RP2040_I2C1
+static struct rp2040_i2cdev_s g_i2c1dev =
+{
+  .port = 1,
+  .base = RP2040_I2C1_BASE,
+  .irqid = RP2040_I2C1_IRQ,
+  .refs = 0,
+};
+#endif
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+static inline int i2c_takesem(FAR sem_t *sem);
+static inline int i2c_givesem(FAR sem_t *sem);
+
+static inline uint32_t i2c_reg_read(struct rp2040_i2cdev_s *priv,
+                                    uint32_t offset);
+static inline void i2c_reg_write(struct rp2040_i2cdev_s *priv,
+                                 uint32_t offset,
+                                 uint32_t val);
+static inline void i2c_reg_rmw(struct rp2040_i2cdev_s *dev,
+                               uint32_t offset,
+                               uint32_t val, uint32_t mask);
+
+static int rp2040_i2c_disable(struct rp2040_i2cdev_s *priv);
+static void rp2040_i2c_enable(struct rp2040_i2cdev_s *priv);
+
+static int  rp2040_i2c_interrupt(int irq, FAR void *context, FAR void *arg);
+static void rp2040_i2c_timeout(wdparm_t arg);
+static void rp2040_i2c_setfrequency(struct rp2040_i2cdev_s *priv,
+                                   uint32_t frequency);
+static int  rp2040_i2c_transfer(FAR struct i2c_master_s *dev,
+                               FAR struct i2c_msg_s *msgs, int count);
+#ifdef CONFIG_I2C_RESET
+static int rp2040_i2c_reset(FAR struct i2c_master_s *dev);
+#endif
+
+/****************************************************************************
+ * Name: i2c_takesem
+ ****************************************************************************/
+
+static inline int i2c_takesem(FAR sem_t *sem)
+{
+  return nxsem_wait_uninterruptible(sem);
+}
+
+/****************************************************************************
+ * Name: i2c_givesem
+ ****************************************************************************/
+
+static inline int i2c_givesem(FAR sem_t *sem)
+{
+  return nxsem_post(sem);
+}
+
+/****************************************************************************
+ * I2C device operations
+ ****************************************************************************/
+
+struct i2c_ops_s rp2040_i2c_ops =
+{
+  .transfer = rp2040_i2c_transfer,
+#ifdef CONFIG_I2C_RESET
+  .reset = rp2040_i2c_reset,
+#endif
+};
+
+/****************************************************************************
+ * Name: rp2040_i2c_setfrequency
+ *
+ * Description:
+ *   Set the frequency for the next transfer
+ *
+ ****************************************************************************/
+
+static void rp2040_i2c_setfrequency(struct rp2040_i2cdev_s *priv,
+                                   uint32_t frequency)
+{
+  int32_t lcnt;
+  int32_t hcnt;
+  uint64_t lcnt64;
+  uint64_t hcnt64;
+  uint64_t speed;
+  uint64_t t_low;
+  uint64_t t_high;
+  uint32_t base = BOARD_PERI_FREQ;
+  uint32_t spklen;
+
+  ASSERT(base);
+
+  if ((priv->frequency == frequency) && (priv->base_freq == base))
+    {
+      return;
+    }
+
+  priv->frequency = frequency;
+  priv->base_freq = base;
+
+  base /= 1000;
+
+  if (frequency <= 100000)
+    {
+      t_low  = 4700000;
+      t_high = 4000000;
+    }
+  else if (frequency <= 400000)
+    {
+      t_low  = 1300000;
+      t_high = 600000;
+    }
+  else
+    {
+      t_low  = 500000;
+      t_high = 260000;
+    }
+
+  if (frequency > 100000)
+    {
+      if (base < 20032)
+        {
+          spklen = 1;
+        }
+      else if (base < 40064)
+        {
+          spklen = 2;
+        }
+      else
+        {
+          spklen = 3;
+        }
+    }
+  else
+    {
+      spklen = 1;
+    }
+
+  lcnt64 = (t_low + 6500ull / 20000ull) * base;
+  lcnt   = ((lcnt64 + 999999999ull) / 1000000000ull) - 1; /* ceil */
+  lcnt   = lcnt < 8 ? 8 : lcnt;
+
+  hcnt64 = (t_high - 6500ull) * base;
+  hcnt   = ((hcnt64 + 999999999ull) / 1000000000ull) - 6 - spklen; /* ceil */
+  hcnt   = hcnt < 6 ? 6 : hcnt;
+
+  speed =
+    1000000000000000000ull /
+    (((lcnt + 1) * 1000000000000ull +
+    (hcnt + 6 + spklen) * 1000000000000ull) / base +
+     20000ull / 1000ull * 1000000ull);
+
+  if (speed > (frequency * 1000ull))
+    {
+      uint64_t adj;
+      adj = ((1000000000000000000ull / (frequency * 1000ull)) -
+             (1000000000000000000ull / speed)) *
+            base;
+      hcnt += (adj + 999999999999ull) / 1000000000000ull;
+    }
+
+  /* use FS register in SS and FS mode */
+
+  i2c_reg_write(priv, RP2040_I2C_IC_FS_SCL_HCNT_OFFSET, hcnt);
+  i2c_reg_write(priv, RP2040_I2C_IC_FS_SCL_LCNT_OFFSET, lcnt);
+  i2c_reg_rmw(priv, RP2040_I2C_IC_CON_OFFSET,
+                    RP2040_I2C_IC_CON_SPEED_FAST,
+                    RP2040_I2C_IC_CON_SPEED_MASK);
+
+  i2c_reg_write(priv, RP2040_I2C_IC_FS_SPKLEN_OFFSET, spklen);
+}
+
+/****************************************************************************
+ * Name: rp2040_i2c_timeout
+ *
+ * Description:
+ *   Watchdog timer for timeout of I2C operation
+ *
+ ****************************************************************************/
+
+static void rp2040_i2c_timeout(wdparm_t arg)
+{
+  struct rp2040_i2cdev_s *priv = (struct rp2040_i2cdev_s *)arg;
+  irqstate_t flags             = enter_critical_section();
+
+  priv->error = -ENODEV;
+  i2c_givesem(&priv->wait);
+  leave_critical_section(flags);
+}
+
+/****************************************************************************
+ * Name: rp2040_i2c_drainrxfifo
+ *
+ * Description:
+ *   Receive I2C data
+ *
+ ****************************************************************************/
+
+static void rp2040_i2c_drainrxfifo(struct rp2040_i2cdev_s *priv)
+{
+  struct i2c_msg_s *msg = priv->msgs;
+  uint32_t status;
+  uint32_t dat;
+  ssize_t i;
+
+  DEBUGASSERT(msg != NULL);
+
+  status = i2c_reg_read(priv, RP2040_I2C_IC_STATUS_OFFSET);
+
+  for (i = 0; i < priv->rw_size && status & RP2040_I2C_IC_STATUS_RFNE; i++)
+    {
+      dat            = i2c_reg_read(priv, RP2040_I2C_IC_DATA_CMD_OFFSET);
+      msg->buffer[priv->reg_buff_offset + i] = dat & 0xff;
+      status         = i2c_reg_read(priv, RP2040_I2C_IC_STATUS_OFFSET);
+    }
+
+  priv->reg_buff_offset += priv->rw_size;
+}
+
+/****************************************************************************
+ * Name: rp2040_i2c_interrupt
+ *
+ * Description:
+ *   The I2C Interrupt Handler
+ *
+ ****************************************************************************/
+
+static int rp2040_i2c_interrupt(int irq, FAR void *context, FAR void *arg)
+{
+  FAR struct rp2040_i2cdev_s *priv = (FAR struct rp2040_i2cdev_s *)arg;
+  uint32_t state;
+  int ret;
+
+  state = i2c_reg_read(priv, RP2040_I2C_IC_INTR_STAT_OFFSET);
+
+  if (state & RP2040_I2C_IC_INTR_STAT_R_TX_ABRT)
+    {
+      i2c_reg_read(priv, RP2040_I2C_IC_CLR_TX_ABRT_OFFSET);
+      priv->error = -ENODEV;
+    }
+
+  if (state & RP2040_I2C_IC_INTR_STAT_R_TX_OVER)
+    {
+      i2c_reg_read(priv, RP2040_I2C_IC_CLR_TX_OVER_OFFSET);
+      priv->error = -EIO;
+    }
+
+  if (state & RP2040_I2C_IC_INTR_STAT_R_RX_OVER)
+    {
+      i2c_reg_read(priv, RP2040_I2C_IC_CLR_RX_OVER_OFFSET);
+      priv->error = -EIO;
+    }
+
+  if (state & RP2040_I2C_IC_INTR_STAT_R_RX_UNDER)
+    {
+      i2c_reg_read(priv, RP2040_I2C_IC_CLR_RX_UNDER_OFFSET);
+      priv->error = -EIO;
+    }
+
+  if (state & RP2040_I2C_IC_INTR_STAT_R_TX_EMPTY)
+    {
+      /* TX_EMPTY is automatically cleared by hardware
+       * when the buffer level goes above the threshold.
+       */
+
+      i2c_reg_rmw(priv, RP2040_I2C_IC_INTR_MASK_OFFSET,
+                  0, RP2040_I2C_IC_INTR_MASK_M_TX_EMPTY);
+    }
+
+  if (state & RP2040_I2C_IC_INTR_STAT_R_RX_FULL)
+    {
+      /* RX_FULL is automatically cleared by hardware
+       * when the buffer level goes below the threshold.
+       */
+
+      i2c_reg_rmw(priv, RP2040_I2C_IC_INTR_MASK_OFFSET,
+                  0, RP2040_I2C_IC_INTR_MASK_M_RX_FULL);
+      rp2040_i2c_drainrxfifo(priv);
+    }
+
+  if (state & RP2040_I2C_IC_INTR_STAT_R_STOP_DET)
+    {
+      i2c_reg_read(priv, RP2040_I2C_IC_CLR_STOP_DET_OFFSET);
+    }
+
+  if ((priv->error) || (state & RP2040_I2C_IC_INTR_STAT_R_TX_EMPTY) ||
+                       (state & RP2040_I2C_IC_INTR_STAT_R_RX_FULL))
+    {
+      /* Failure of wd_cancel() means that the timer expired.
+       * In this case, nxsem_post() has already been called.
+       * Therefore, call nxsem_post() only when wd_cancel() succeeds.
+       */
+
+      ret = wd_cancel(&priv->timeout);
+      if (ret == OK)
+        {
+          i2c_givesem(&priv->wait);
+        }
+    }
+
+  return OK;
+}
+
+/****************************************************************************
+ * Name: rp2040_i2c_receive
+ *
+ * Description:
+ *   Receive data from I2C bus.
+ *   Prohibit all interrupt because the STOP condition might happen
+ *   if the interrupt occurs when the writing request.
+ *   Actual receiving data is in RX_FULL interrupt handler.
+ *
+ * TODO : The argument "last" is not used.
+ ****************************************************************************/
+
+static int rp2040_i2c_receive(struct rp2040_i2cdev_s *priv, int last)
+{
+  struct i2c_msg_s *msg = priv->msgs;
+  int i;
+  int en;
+  ssize_t msg_length;
+  irqstate_t flags;
+
+  priv->reg_buff_offset = 0;
+
+  DEBUGASSERT(msg != NULL);
+
+  for (msg_length = msg->length; msg_length > 0; msg_length -= priv->rw_size)
+    {
+      if (msg_length <= I2C_FIFO_MAX_SIZE)
+        {
+          priv->rw_size = msg_length;
+          en = 1;
+        }
+      else
+        {
+          priv->rw_size = I2C_FIFO_MAX_SIZE;
+          en = 0;
+        }
+
+      /* update threshold value of the receive buffer */
+
+      i2c_reg_write(priv, RP2040_I2C_IC_RX_TL_OFFSET, priv->rw_size - 1);
+
+      for (i = 0; i < priv->rw_size - 1; i++)
+        {
+          i2c_reg_write(priv, RP2040_I2C_IC_DATA_CMD_OFFSET,
+                              RP2040_I2C_IC_DATA_CMD_CMD);
+        }
+
+      flags = enter_critical_section();
+      wd_start(&priv->timeout, I2C_TIMEOUT,
+               rp2040_i2c_timeout, (wdparm_t)priv);
+
+      /* Set stop flag for indicate the last data */
+
+      i2c_reg_write(priv, RP2040_I2C_IC_DATA_CMD_OFFSET,
+                          RP2040_I2C_IC_DATA_CMD_CMD |
+                          (en ? RP2040_I2C_IC_DATA_CMD_STOP : 0));
+
+      i2c_reg_rmw(priv, RP2040_I2C_IC_INTR_MASK_OFFSET,
+                  RP2040_I2C_IC_INTR_STAT_R_RX_FULL,
+                  RP2040_I2C_IC_INTR_STAT_R_RX_FULL);
+      leave_critical_section(flags);
+      i2c_takesem(&priv->wait);
+
+      if (priv->error != OK)
+        {
+          break;
+        }
+    }
+
+  return 0;
+}
+
+/****************************************************************************
+ * Name: rp2040_i2c_send
+ *
+ * Description:
+ *   Send data to I2C bus.
+ *
+ ****************************************************************************/
+
+static int rp2040_i2c_send(struct rp2040_i2cdev_s *priv, int last)
+{
+  struct i2c_msg_s *msg = priv->msgs;
+  ssize_t i;
+  irqstate_t flags;
+
+  DEBUGASSERT(msg != NULL);
+
+  for (i = 0; i < msg->length - 1; i++)
+    {
+      while (!(i2c_reg_read(priv, RP2040_I2C_IC_STATUS_OFFSET)
+               & RP2040_I2C_IC_STATUS_TFNF))
+        ;
+
+      i2c_reg_write(priv, RP2040_I2C_IC_DATA_CMD_OFFSET,
+                    (uint32_t)msg->buffer[i]);
+    }
+
+  while (!(i2c_reg_read(priv, RP2040_I2C_IC_STATUS_OFFSET)
+           & RP2040_I2C_IC_STATUS_TFNF))
+    ;
+
+  flags = enter_critical_section();
+  wd_start(&priv->timeout, I2C_TIMEOUT,
+           rp2040_i2c_timeout, (wdparm_t)priv);
+  i2c_reg_write(priv, RP2040_I2C_IC_DATA_CMD_OFFSET,
+                (uint32_t)msg->buffer[i] |
+                (last ? RP2040_I2C_IC_DATA_CMD_STOP : 0));
+
+  /* Enable TX_EMPTY interrupt for determine transfer done. */
+
+  i2c_reg_rmw(priv, RP2040_I2C_IC_INTR_MASK_OFFSET,
+              RP2040_I2C_IC_INTR_STAT_R_TX_EMPTY, 
RP2040_I2C_IC_INTR_STAT_R_TX_EMPTY);

Review comment:
       Please break this line to put the last parameter in the new line, this 
way it will pass in the CI test




----------------------------------------------------------------
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.

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


Reply via email to