You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@nuttx.apache.org by xi...@apache.org on 2021/11/02 09:10:14 UTC

[incubator-nuttx] branch master updated: mpfs: i2c: Add support for adaptive I2C bus frequency

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

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


The following commit(s) were added to refs/heads/master by this push:
     new a16a9f8  mpfs: i2c: Add support for adaptive I2C bus frequency
a16a9f8 is described below

commit a16a9f80e2e9ad67573041a795cb7c29caec982c
Author: Jani Paalijarvi <ja...@unikie.com>
AuthorDate: Wed Oct 27 12:22:00 2021 +0300

    mpfs: i2c: Add support for adaptive I2C bus frequency
    
    Select the closest possible frequency which is smaller
    than or equal to requested in I2C msg
---
 arch/risc-v/src/mpfs/mpfs_i2c.c           | 194 ++++++++++++++++++++++++------
 boards/risc-v/mpfs/icicle/include/board.h |   1 +
 2 files changed, 155 insertions(+), 40 deletions(-)

diff --git a/arch/risc-v/src/mpfs/mpfs_i2c.c b/arch/risc-v/src/mpfs/mpfs_i2c.c
index 83eea82..74886ff 100755
--- a/arch/risc-v/src/mpfs/mpfs_i2c.c
+++ b/arch/risc-v/src/mpfs/mpfs_i2c.c
@@ -89,9 +89,22 @@ typedef enum mpfs_i2c_clock_divider
   MPFS_I2C_PCLK_DIV_960,
   MPFS_I2C_PCLK_DIV_120,
   MPFS_I2C_PCLK_DIV_60,
-  MPFS_I2C_BCLK_DIV_8
+  MPFS_I2C_BCLK_DIV_8, /* FPGA generated BCLK */
+  MPFS_I2C_NUMBER_OF_DIVIDERS
 } mpfs_i2c_clk_div_t;
 
+static const uint32_t mpfs_i2c_frequencies[MPFS_I2C_NUMBER_OF_DIVIDERS] =
+{
+  MPFS_MSS_APB_AHB_CLK / 256,
+  MPFS_MSS_APB_AHB_CLK / 224,
+  MPFS_MSS_APB_AHB_CLK / 192,
+  MPFS_MSS_APB_AHB_CLK / 160,
+  MPFS_MSS_APB_AHB_CLK / 960,
+  MPFS_MSS_APB_AHB_CLK / 120,
+  MPFS_MSS_APB_AHB_CLK / 60,
+  MPFS_FPGA_BCLK / 8
+};
+
 static int mpfs_i2c_transfer(struct i2c_master_s *dev,
                              struct i2c_msg_s *msgs,
                              int count);
@@ -115,7 +128,7 @@ struct mpfs_i2c_priv_s
   uintptr_t              hw_base;     /* I2C bus base address */
   uint16_t               plic_irq;    /* Platform PLIC irq */
   struct i2c_msg_s       *msgv;       /* Message list */
-  mpfs_i2c_clk_div_t     bus_divider; /* Bus divider */
+  uint32_t               frequency;   /* Current I2C frequency */
 
   uint8_t                msgid;       /* Current message ID */
   ssize_t                bytes;       /* Processed data bytes */
@@ -135,6 +148,8 @@ struct mpfs_i2c_priv_s
   uint16_t               rx_idx;      /* Currently accessed index */
 
   mpfs_i2c_status_t      status;      /* Bus driver status */
+
+  bool                   initialized; /* Bus initialization status */
 };
 
 #ifdef CONFIG_MPFS_I2C0
@@ -145,7 +160,7 @@ static struct mpfs_i2c_priv_s g_mpfs_i2c0_lo_priv =
   .hw_base        = MPFS_I2C0_LO_BASE,
   .plic_irq       = MPFS_IRQ_I2C0_MAIN,
   .msgv           = NULL,
-  .bus_divider    = MPFS_I2C_PCLK_DIV_256,
+  .frequency      = 0,
   .ser_address    = 0x21,
   .target_addr    = 0,
   .refs           = 0,
@@ -153,7 +168,8 @@ static struct mpfs_i2c_priv_s g_mpfs_i2c0_lo_priv =
   .tx_idx         = 0,
   .rx_size        = 0,
   .rx_idx         = 0,
-  .status         = MPFS_I2C_SUCCESS
+  .status         = MPFS_I2C_SUCCESS,
+  .initialized    = false
 };
 #endif /* CONFIG_MPFS_I2C0 */
 
@@ -165,7 +181,7 @@ static struct mpfs_i2c_priv_s g_mpfs_i2c1_lo_priv =
   .hw_base        = MPFS_I2C1_LO_BASE,
   .plic_irq       = MPFS_IRQ_I2C1_MAIN,
   .msgv           = NULL,
-  .bus_divider    = MPFS_I2C_PCLK_DIV_256,
+  .frequency      = 0,
   .ser_address    = 0x21,
   .target_addr    = 0,
   .refs           = 0,
@@ -173,10 +189,14 @@ static struct mpfs_i2c_priv_s g_mpfs_i2c1_lo_priv =
   .tx_idx         = 0,
   .rx_size        = 0,
   .rx_idx         = 0,
-  .status         = MPFS_I2C_SUCCESS
+  .status         = MPFS_I2C_SUCCESS,
+  .initialized    = false
 };
 #endif /* CONFIG_MPFS_I2C1 */
 
+static int mpfs_i2c_setfrequency(struct mpfs_i2c_priv_s *priv,
+                                  uint32_t frequency);
+
 /****************************************************************************
  * Private Functions
  ****************************************************************************/
@@ -224,56 +244,62 @@ static void mpfs_restore_interrupts(irqstate_t primask)
  * Parameters:
  *   priv          - Pointer to the internal driver state structure.
  *
+ * Returned Value:
+ *   Zero (OK) is returned on success. A negated errno value is returned on
+ *   failure.
+ *
  ****************************************************************************/
 
-static void mpfs_i2c_init(struct mpfs_i2c_priv_s *priv)
+static int mpfs_i2c_init(struct mpfs_i2c_priv_s *priv)
 {
-  uint32_t clock_speed;
   uint32_t primask;
 
-  primask = mpfs_disable_interrupts();
-
-  if (priv->id == 0)
+  if (!priv->initialized)
     {
-      modifyreg32(MPFS_SYSREG_SOFT_RESET_CR, 0, SYSREG_SOFT_RESET_CR_I2C0);
+      primask = mpfs_disable_interrupts();
 
-      modifyreg32(MPFS_SYSREG_SOFT_RESET_CR, SYSREG_SOFT_RESET_CR_I2C0, 0);
+      if (priv->id == 0)
+        {
+          modifyreg32(MPFS_SYSREG_SOFT_RESET_CR,
+                      0, SYSREG_SOFT_RESET_CR_I2C0);
 
-      modifyreg32(MPFS_SYSREG_SUBBLK_CLOCK_CR, 0,
-                  SYSREG_SUBBLK_CLOCK_CR_I2C0);
-    }
-  else
-    {
-      modifyreg32(MPFS_SYSREG_SOFT_RESET_CR, 0, SYSREG_SOFT_RESET_CR_I2C1);
+          modifyreg32(MPFS_SYSREG_SOFT_RESET_CR,
+                      SYSREG_SOFT_RESET_CR_I2C0, 0);
 
-      modifyreg32(MPFS_SYSREG_SOFT_RESET_CR, SYSREG_SOFT_RESET_CR_I2C1, 0);
+          modifyreg32(MPFS_SYSREG_SUBBLK_CLOCK_CR,
+                      0, SYSREG_SUBBLK_CLOCK_CR_I2C0);
+        }
+      else
+        {
+          modifyreg32(MPFS_SYSREG_SOFT_RESET_CR,
+                      0, SYSREG_SOFT_RESET_CR_I2C1);
 
-      modifyreg32(MPFS_SYSREG_SUBBLK_CLOCK_CR, 0,
-                  SYSREG_SUBBLK_CLOCK_CR_I2C1);
-    }
+          modifyreg32(MPFS_SYSREG_SOFT_RESET_CR,
+                      SYSREG_SOFT_RESET_CR_I2C1, 0);
 
-  clock_speed = priv->bus_divider;
+          modifyreg32(MPFS_SYSREG_SUBBLK_CLOCK_CR,
+                      0, SYSREG_SUBBLK_CLOCK_CR_I2C1);
+        }
 
-  /* Update the clock divider */
+      /* Divider is zero after I2C reset */
 
-  modifyreg32(MPFS_I2C_CTRL,
-              MPFS_I2C_CTRL_CR2_MASK |
-              MPFS_I2C_CTRL_CR1_MASK |
-              MPFS_I2C_CTRL_CR0_MASK,
-              (((clock_speed >> 2u) & 0x01u) << MPFS_I2C_CTRL_CR2) |
-              (((clock_speed >> 1u) & 0x01u) << MPFS_I2C_CTRL_CR1) |
-              (((clock_speed & 0x01u) << MPFS_I2C_CTRL_CR0)));
+      priv->frequency = mpfs_i2c_frequencies[0];
 
-  /* This is our own address, not the target chip */
+      /* This is our own address, not the target chip */
 
-  putreg32(priv->ser_address, MPFS_I2C_ADDR);
+      putreg32(priv->ser_address, MPFS_I2C_ADDR);
 
-  /* Enable i2c bus */
+      /* Enable i2c bus */
 
-  modifyreg32(MPFS_I2C_CTRL, MPFS_I2C_CTRL_ENS1_MASK,
-              MPFS_I2C_CTRL_ENS1_MASK);
+      modifyreg32(MPFS_I2C_CTRL, MPFS_I2C_CTRL_ENS1_MASK,
+                  MPFS_I2C_CTRL_ENS1_MASK);
 
-  mpfs_restore_interrupts(primask);
+      priv->initialized = true;
+
+      mpfs_restore_interrupts(primask);
+    }
+
+  return OK;
 }
 
 /****************************************************************************
@@ -291,6 +317,11 @@ static void mpfs_i2c_deinit(struct mpfs_i2c_priv_s *priv)
 {
   up_disable_irq(priv->plic_irq);
   irq_detach(priv->plic_irq);
+
+  modifyreg32(MPFS_I2C_CTRL, MPFS_I2C_CTRL_ENS1_MASK,
+              ~MPFS_I2C_CTRL_ENS1_MASK);
+
+  priv->initialized = false;
 }
 
 /****************************************************************************
@@ -662,6 +693,12 @@ static int mpfs_i2c_transfer(struct i2c_master_s *dev,
             }
         }
 
+      ret = mpfs_i2c_setfrequency(priv, msgs[i].frequency);
+      if (ret != OK)
+        {
+          break;
+        }
+
       i2cinfo("Sending message %" PRIu8 "...\n", priv->msgid);
 
       mpfs_i2c_sendstart(priv);
@@ -713,11 +750,20 @@ static int mpfs_i2c_transfer(struct i2c_master_s *dev,
 static int mpfs_i2c_reset(struct i2c_master_s *dev)
 {
   struct mpfs_i2c_priv_s *priv = (struct mpfs_i2c_priv_s *)dev;
+  int ret;
 
   DEBUGASSERT(priv != NULL);
 
   up_disable_irq(priv->plic_irq);
-  mpfs_i2c_init(priv);
+
+  priv->initialized = false;
+
+  ret = mpfs_i2c_init(priv);
+  if (ret != OK)
+    {
+      up_enable_irq(priv->plic_irq);
+      return ret;
+    }
 
   priv->tx_size = 0;
   priv->tx_idx  = 0;
@@ -731,6 +777,69 @@ static int mpfs_i2c_reset(struct i2c_master_s *dev)
 #endif
 
 /****************************************************************************
+ * Name: mpfs_i2c_setfrequency
+ *
+ * Description:
+ *   Sets the closest possible frequency for the next transfers.
+ *
+ * Input Parameters:
+ *   priv      - Pointer to the internal driver state structure.
+ *   frequency - Requested frequency
+ *
+ * Returned Value:
+ *   Zero (OK) on success;
+ *
+ ****************************************************************************/
+
+static int mpfs_i2c_setfrequency(struct mpfs_i2c_priv_s *priv,
+                                  uint32_t frequency)
+{
+  uint32_t new_freq = 0;
+  uint32_t clock_div = 0;
+
+  if (priv->frequency != frequency)
+    {
+      /* Select the closest possible frequency
+       * which is smaller than or equal to requested
+       */
+
+      for (uint8_t i = 0; i < MPFS_I2C_NUMBER_OF_DIVIDERS; i++)
+        {
+          if (frequency >= mpfs_i2c_frequencies[i]
+              && mpfs_i2c_frequencies[i] > new_freq)
+            {
+              new_freq = mpfs_i2c_frequencies[i];
+              clock_div = i;
+            }
+        }
+
+      if (new_freq == 0)
+        {
+          i2cerr("ERROR: Requested frequency %" PRIu32 " for I2C bus %" PRIu8
+                 " is not supported by hardware.\n", frequency, priv->id);
+          return -EINVAL;
+        }
+
+      i2cinfo("Changing I2Cbus %" PRIu8 " clock speed to %" PRIu32
+              " (div %" PRIx32 ")\n", priv->id, new_freq, clock_div);
+
+      /* Update the clock divider */
+
+      modifyreg32(MPFS_I2C_CTRL,
+                  MPFS_I2C_CTRL_CR2_MASK |
+                  MPFS_I2C_CTRL_CR1_MASK |
+                  MPFS_I2C_CTRL_CR0_MASK,
+                  (((clock_div >> 2u) & 0x01u) << MPFS_I2C_CTRL_CR2) |
+                  (((clock_div >> 1u) & 0x01u) << MPFS_I2C_CTRL_CR1) |
+                  (((clock_div & 0x01u) << MPFS_I2C_CTRL_CR0)));
+
+      priv->frequency = frequency;
+    }
+
+  return OK;
+}
+
+/****************************************************************************
  * Public Functions
  ****************************************************************************/
 
@@ -794,7 +903,12 @@ struct i2c_master_s *mpfs_i2cbus_initialize(int port)
     }
 
   mpfs_i2c_sem_init(priv);
-  mpfs_i2c_init(priv);
+  ret = mpfs_i2c_init(priv);
+  if (ret != OK)
+    {
+      leave_critical_section(flags);
+      return NULL;
+    }
 
   leave_critical_section(flags);
 
diff --git a/boards/risc-v/mpfs/icicle/include/board.h b/boards/risc-v/mpfs/icicle/include/board.h
index 680915d..a7ec4d2 100755
--- a/boards/risc-v/mpfs/icicle/include/board.h
+++ b/boards/risc-v/mpfs/icicle/include/board.h
@@ -45,6 +45,7 @@
 #define MPFS_MSS_RTC_TOGGLE_CLK      (1000000UL)
 #define MPFS_MSS_AXI_CLK           (300000000UL)
 #define MPFS_MSS_APB_AHB_CLK       (150000000UL)
+#define MPFS_FPGA_BCLK               (3000000UL)
 
 /* LED definitions **********************************************************/