You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@nuttx.apache.org by GitBox <gi...@apache.org> on 2021/03/02 14:08:36 UTC

[GitHub] [incubator-nuttx] yunkya2 opened a new pull request #2952: arm/rp2040: Add I2C driver support

yunkya2 opened a new pull request #2952:
URL: https://github.com/apache/incubator-nuttx/pull/2952


   ## Summary
   Add I2C driver for Raspberry Pi Pico.
   
   ## Impact
   rp2040 only
   
   ## Testing
   New config `raspberrypi-pico:ssd1306` is added to test I2C OLED display (SSD1306).
   Graphic samples nx, nxhello, nxdemo, nxlines are working with this config.
   


----------------------------------------------------------------
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:
users@infra.apache.org



[GitHub] [incubator-nuttx] acassis commented on a change in pull request #2952: arm/rp2040: Add I2C driver support

Posted by GitBox <gi...@apache.org>.
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:
users@infra.apache.org



[GitHub] [incubator-nuttx] yunkya2 commented on a change in pull request #2952: arm/rp2040: Add I2C driver support

Posted by GitBox <gi...@apache.org>.
yunkya2 commented on a change in pull request #2952:
URL: https://github.com/apache/incubator-nuttx/pull/2952#discussion_r585956470



##########
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:
       Oops. I overlooked the style error before commit. Thanks.




----------------------------------------------------------------
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:
users@infra.apache.org



[GitHub] [incubator-nuttx] acassis merged pull request #2952: arm/rp2040: Add I2C driver support

Posted by GitBox <gi...@apache.org>.
acassis merged pull request #2952:
URL: https://github.com/apache/incubator-nuttx/pull/2952


   


----------------------------------------------------------------
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:
users@infra.apache.org



[GitHub] [incubator-nuttx] acassis commented on pull request #2952: arm/rp2040: Add I2C driver support

Posted by GitBox <gi...@apache.org>.
acassis commented on pull request #2952:
URL: https://github.com/apache/incubator-nuttx/pull/2952#issuecomment-789079779


   Hi @yunkya2 please fix it:
   Error: /home/runner/work/incubator-nuttx/incubator-nuttx/nuttx/arch/arm/src/rp2040/rp2040_i2c.c:515:86: error: Long line found


----------------------------------------------------------------
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:
users@infra.apache.org



[GitHub] [incubator-nuttx] acassis commented on a change in pull request #2952: arm/rp2040: Add I2C driver support

Posted by GitBox <gi...@apache.org>.
acassis commented on a change in pull request #2952:
URL: https://github.com/apache/incubator-nuttx/pull/2952#discussion_r585611438



##########
File path: boards/arm/rp2040/common/src/rp2040_i2cdev.c
##########
@@ -0,0 +1,68 @@
+/****************************************************************************
+ * boards/arm/rp2040/common/src/rp2040_i2cdev.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 <stdio.h>
+#include <debug.h>
+#include <errno.h>
+
+#include "rp2040_i2c.h"
+
+/****************************************************************************
+ * Public Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: board_i2cdev_initialize
+ *
+ * Description:
+ *   Initialize and register i2c driver for the specified i2c port
+ *
+ ****************************************************************************/
+
+int board_i2cdev_initialize(int port)
+{
+  int ret;
+  FAR struct i2c_master_s *i2c;
+
+  _info("Initializing /dev/i2c%d..\n", port);

Review comment:
       Please change to i2cinfo()

##########
File path: boards/arm/rp2040/common/src/rp2040_i2cdev.c
##########
@@ -0,0 +1,68 @@
+/****************************************************************************
+ * boards/arm/rp2040/common/src/rp2040_i2cdev.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 <stdio.h>
+#include <debug.h>
+#include <errno.h>
+
+#include "rp2040_i2c.h"
+
+/****************************************************************************
+ * Public Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: board_i2cdev_initialize
+ *
+ * Description:
+ *   Initialize and register i2c driver for the specified i2c port
+ *
+ ****************************************************************************/
+
+int board_i2cdev_initialize(int port)
+{
+  int ret;
+  FAR struct i2c_master_s *i2c;
+
+  _info("Initializing /dev/i2c%d..\n", port);
+
+  /* Initialize i2c device */
+
+  i2c = rp2040_i2cbus_initialize(port);
+  if (!i2c)
+    {
+      _err("ERROR: Failed to initialize i2c%d.\n", port);

Review comment:
       please change to i2cerr()

##########
File path: boards/arm/rp2040/common/src/rp2040_i2cdev.c
##########
@@ -0,0 +1,68 @@
+/****************************************************************************
+ * boards/arm/rp2040/common/src/rp2040_i2cdev.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 <stdio.h>
+#include <debug.h>
+#include <errno.h>
+
+#include "rp2040_i2c.h"
+
+/****************************************************************************
+ * Public Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: board_i2cdev_initialize
+ *
+ * Description:
+ *   Initialize and register i2c driver for the specified i2c port
+ *
+ ****************************************************************************/
+
+int board_i2cdev_initialize(int port)
+{
+  int ret;
+  FAR struct i2c_master_s *i2c;
+
+  _info("Initializing /dev/i2c%d..\n", port);
+
+  /* Initialize i2c device */
+
+  i2c = rp2040_i2cbus_initialize(port);
+  if (!i2c)
+    {
+      _err("ERROR: Failed to initialize i2c%d.\n", port);
+      return -ENODEV;
+    }
+
+  ret = i2c_register(i2c, port);
+  if (ret < 0)
+    {
+      _err("ERROR: Failed to register i2c%d: %d\n", port, ret);

Review comment:
       Ditto




----------------------------------------------------------------
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:
users@infra.apache.org



[GitHub] [incubator-nuttx] yunkya2 commented on a change in pull request #2952: arm/rp2040: Add I2C driver support

Posted by GitBox <gi...@apache.org>.
yunkya2 commented on a change in pull request #2952:
URL: https://github.com/apache/incubator-nuttx/pull/2952#discussion_r585621510



##########
File path: boards/arm/rp2040/common/src/rp2040_i2cdev.c
##########
@@ -0,0 +1,68 @@
+/****************************************************************************
+ * boards/arm/rp2040/common/src/rp2040_i2cdev.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 <stdio.h>
+#include <debug.h>
+#include <errno.h>
+
+#include "rp2040_i2c.h"
+
+/****************************************************************************
+ * Public Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: board_i2cdev_initialize
+ *
+ * Description:
+ *   Initialize and register i2c driver for the specified i2c port
+ *
+ ****************************************************************************/
+
+int board_i2cdev_initialize(int port)
+{
+  int ret;
+  FAR struct i2c_master_s *i2c;
+
+  _info("Initializing /dev/i2c%d..\n", port);

Review comment:
       I got it.




----------------------------------------------------------------
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:
users@infra.apache.org