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

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


The following commit(s) were added to refs/heads/master by this push:
     new 923dc37a3b risc-v/bl808: Add I2C driver
923dc37a3b is described below

commit 923dc37a3b2e89d8468539640f98e85816fd5fd5
Author: Henry Rovner <rovnerhe...@gmail.com>
AuthorDate: Fri Aug 2 07:24:12 2024 -0700

    risc-v/bl808: Add I2C driver
    
    This change implements a driver with support for all four I2C blocks on the 
BL808.
---
 Documentation/platforms/risc-v/bl808/index.rst   |   2 +-
 arch/risc-v/include/bl808/irq.h                  |   4 +
 arch/risc-v/src/bl808/Kconfig                    |  92 +++
 arch/risc-v/src/bl808/Make.defs                  |   1 +
 arch/risc-v/src/bl808/bl808_gpio.h               |   7 +-
 arch/risc-v/src/bl808/bl808_i2c.c                | 848 +++++++++++++++++++++++
 arch/risc-v/src/bl808/bl808_i2c.h                |  70 ++
 arch/risc-v/src/bl808/bl808_irq.c                |  46 +-
 arch/risc-v/src/bl808/hardware/bl808_glb.h       |   9 +
 arch/risc-v/src/bl808/hardware/bl808_i2c.h       | 117 ++++
 arch/risc-v/src/bl808/hardware/bl808_memorymap.h |   4 +
 arch/risc-v/src/bl808/hardware/bl808_mm_glb.h    |  20 +-
 boards/risc-v/bl808/ox64/src/bl808_appinit.c     |  24 +
 13 files changed, 1234 insertions(+), 10 deletions(-)

diff --git a/Documentation/platforms/risc-v/bl808/index.rst 
b/Documentation/platforms/risc-v/bl808/index.rst
index dfb9b3c981..97c97aef9b 100644
--- a/Documentation/platforms/risc-v/bl808/index.rst
+++ b/Documentation/platforms/risc-v/bl808/index.rst
@@ -48,7 +48,7 @@ DMA          No
 EMAC         No
 GPADC        Yes
 GPIO         Yes
-I2C          No
+I2C          Yes
 I2S          No
 PWM          No
 SPI          Yes
diff --git a/arch/risc-v/include/bl808/irq.h b/arch/risc-v/include/bl808/irq.h
index 3c0dbbdb84..16af49b973 100644
--- a/arch/risc-v/include/bl808/irq.h
+++ b/arch/risc-v/include/bl808/irq.h
@@ -54,6 +54,8 @@
 /* D0 IRQs ******************************************************************/
 
 #define BL808_IRQ_UART3 (RISCV_IRQ_SEXT + BL808_IRQ_NUM_BASE + 4)
+#define BL808_IRQ_I2C2 (RISCV_IRQ_SEXT + BL808_IRQ_NUM_BASE + 5)
+#define BL808_IRQ_I2C3 (RISCV_IRQ_SEXT + BL808_IRQ_NUM_BASE + 6)
 #define BL808_IRQ_SPI1 (RISCV_IRQ_SEXT + BL808_IRQ_NUM_BASE + 7)
 #define BL808_IRQ_D0_IPC (RISCV_IRQ_SEXT + BL808_IRQ_NUM_BASE + 38)
 #define BL808_IRQ_TIMER1_CH0 (RISCV_IRQ_SEXT + BL808_IRQ_NUM_BASE + 61)
@@ -68,8 +70,10 @@
 #define BL808_IRQ_UART0 (RISCV_IRQ_SEXT + BL808_IRQ_NUM_BASE + 
BL808_M0_IRQ_OFFSET + 28)
 #define BL808_IRQ_UART1 (RISCV_IRQ_SEXT + BL808_IRQ_NUM_BASE + 
BL808_M0_IRQ_OFFSET + 29)
 #define BL808_IRQ_UART2 (RISCV_IRQ_SEXT + BL808_IRQ_NUM_BASE + 
BL808_M0_IRQ_OFFSET + 30)
+#define BL808_IRQ_I2C0 (RISCV_IRQ_SEXT + BL808_IRQ_NUM_BASE + 
BL808_M0_IRQ_OFFSET + 32)
 #define BL808_IRQ_TIMER0_CH0 (RISCV_IRQ_SEXT + BL808_IRQ_NUM_BASE + 
BL808_M0_IRQ_OFFSET + 36)
 #define BL808_IRQ_TIMER0_CH1 (RISCV_IRQ_SEXT + BL808_IRQ_NUM_BASE + 
BL808_M0_IRQ_OFFSET + 37)
 #define BL808_IRQ_WDT0 (RISCV_IRQ_SEXT + BL808_IRQ_NUM_BASE + 
BL808_M0_IRQ_OFFSET + 38)
+#define BL808_IRQ_I2C1 (RISCV_IRQ_SEXT + BL808_IRQ_NUM_BASE + 
BL808_M0_IRQ_OFFSET + 39)
 
 #endif /* __ARCH_RISCV_INCLUDE_BL808_IRQ_H */
diff --git a/arch/risc-v/src/bl808/Kconfig b/arch/risc-v/src/bl808/Kconfig
index d23cb0e060..dcf06528e5 100644
--- a/arch/risc-v/src/bl808/Kconfig
+++ b/arch/risc-v/src/bl808/Kconfig
@@ -106,6 +106,98 @@ config BL808_GPADC_SCAN_ORD11
 
 endmenu
 
+menuconfig BL808_I2C0
+       bool "I2C 0"
+       default n
+       select I2C
+        select I2C_DRIVER
+        select ARCH_HAVE_I2CRESET
+
+if BL808_I2C0
+
+comment "Refer to datasheet for valid pin assignments"
+
+config BL808_I2C0_SCL
+       int "SCL Pin"
+       default 6
+       range 0 45
+
+config BL808_I2C0_SDA
+       int "SDA Pin"
+       default 7
+       range 0 45
+
+endif
+
+menuconfig BL808_I2C1
+       bool "I2C 1"
+       default n
+       select I2C
+        select I2C_DRIVER
+        select ARCH_HAVE_I2CRESET
+
+if BL808_I2C1
+
+comment "Refer to datasheet for valid pin assignments"
+
+config BL808_I2C1_SCL
+       int "SCL Pin"
+       default 6
+       range 0 45
+
+config BL808_I2C1_SDA
+       int "SDA Pin"
+       default 7
+       range 0 45
+
+endif
+
+menuconfig BL808_I2C2
+       bool "I2C 2"
+       default n
+       select I2C
+        select I2C_DRIVER
+        select ARCH_HAVE_I2CRESET
+
+if BL808_I2C2
+
+comment "Refer to datasheet for valid pin assignments"
+
+config BL808_I2C2_SCL
+       int "SCL Pin"
+       default 6
+       range 0 45
+
+config BL808_I2C2_SDA
+       int "SDA Pin"
+       default 7
+       range 0 45
+
+endif
+
+menuconfig BL808_I2C3
+       bool "I2C 3"
+       default n
+       select I2C
+        select I2C_DRIVER
+        select ARCH_HAVE_I2CRESET
+
+if BL808_I2C3
+
+comment "Refer to datasheet for valid pin assignments"
+
+config BL808_I2C3_SCL
+       int "SCL Pin"
+       default 6
+       range 0 45
+
+config BL808_I2C3_SDA
+       int "SDA Pin"
+       default 7
+       range 0 45
+
+endif
+
 config BL808_UART0
        bool "UART 0"
        default n
diff --git a/arch/risc-v/src/bl808/Make.defs b/arch/risc-v/src/bl808/Make.defs
index d81c3781c9..8f42b6d084 100644
--- a/arch/risc-v/src/bl808/Make.defs
+++ b/arch/risc-v/src/bl808/Make.defs
@@ -31,3 +31,4 @@ CHIP_CSRCS  = bl808_start.c bl808_irq_dispatch.c bl808_irq.c
 CHIP_CSRCS += bl808_timerisr.c bl808_allocateheap.c
 CHIP_CSRCS += bl808_gpio.c bl808_mm_init.c bl808_pgalloc.c bl808_serial.c
 CHIP_CSRCS += bl808_gpadc.c bl808_spi.c bl808_timer.c bl808_wdt.c
+CHIP_CSRCS += bl808_i2c.c
diff --git a/arch/risc-v/src/bl808/bl808_gpio.h 
b/arch/risc-v/src/bl808/bl808_gpio.h
index eab60845cd..f3e618076e 100644
--- a/arch/risc-v/src/bl808/bl808_gpio.h
+++ b/arch/risc-v/src/bl808/bl808_gpio.h
@@ -58,8 +58,8 @@
 
 #define GPIO_MODE_SHIFT  (10)                     /* Bit 10: Port Mode */
 #define GPIO_MODE_MASK   (1 << GPIO_MODE_SHIFT)
-#  define GPIO_INPUT     (1 << GPIO_MODE_SHIFT)  /* Input Enable */
-#  define GPIO_OUTPUT    (0 << GPIO_MODE_SHIFT)  /* Output Enable */
+#define GPIO_INPUT     (1 << GPIO_MODE_SHIFT)  /* Input Enable */
+#define GPIO_OUTPUT    (0 << GPIO_MODE_SHIFT)  /* Output Enable */
 
 /* Input/Output pull-ups/downs:
  *
@@ -116,6 +116,7 @@
 #define GPIO_FUNC_SDH    (0  << GPIO_FUNC_SHIFT)  /* SDH */
 #define GPIO_FUNC_SPI0   (1  << GPIO_FUNC_SHIFT)  /* SPI0 */
 #define GPIO_FUNC_FLASH  (2  << GPIO_FUNC_SHIFT)  /* Flash */
+#define GPIO_FUNC_I2C0   (5  << GPIO_FUNC_SHIFT)  /* I2C0 */
 #define GPIO_FUNC_I2C1   (6  << GPIO_FUNC_SHIFT)  /* I2C1 */
 #define GPIO_FUNC_UART   (7  << GPIO_FUNC_SHIFT)  /* UART */
 #define GPIO_FUNC_CAM    (9  << GPIO_FUNC_SHIFT)  /* CSI */
@@ -123,6 +124,8 @@
 #define GPIO_FUNC_SWGPIO (11 << GPIO_FUNC_SHIFT)  /* Software GPIO */
 #define GPIO_FUNC_PWM0   (16 << GPIO_FUNC_SHIFT)  /* PWM0 */
 #define GPIO_FUNC_SPI1   (18 << GPIO_FUNC_SHIFT)  /* SPI1 */
+#define GPIO_FUNC_I2C2   (19  << GPIO_FUNC_SHIFT) /* I2C2 */
+#define GPIO_FUNC_I2C3   (20  << GPIO_FUNC_SHIFT) /* I2C3 */
 #define GPIO_FUNC_JTAG_D0 (27 << GPIO_FUNC_SHIFT) /* JTAG */
 
 /****************************************************************************
diff --git a/arch/risc-v/src/bl808/bl808_i2c.c 
b/arch/risc-v/src/bl808/bl808_i2c.c
new file mode 100644
index 0000000000..1b7feaef7d
--- /dev/null
+++ b/arch/risc-v/src/bl808/bl808_i2c.c
@@ -0,0 +1,848 @@
+/****************************************************************************
+ * arch/risc-v/src/bl808/bl808_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 <nuttx/arch.h>
+
+#include <sys/types.h>
+#include <stdint.h>
+#include <stdbool.h>
+#include <errno.h>
+#include <debug.h>
+
+#include <nuttx/irq.h>
+#include <nuttx/i2c/i2c_master.h>
+#include <nuttx/fs/ioctl.h>
+
+#include "hardware/bl808_i2c.h"
+#include "hardware/bl808_glb.h"
+#include "hardware/bl808_mm_glb.h"
+#include "riscv_internal.h"
+#include "chip.h"
+#include "bl808_gpio.h"
+#include "bl808_i2c.h"
+
+#if defined(CONFIG_BL808_I2C0) || defined(CONFIG_BL808_I2C1)    \
+  || defined(CONFIG_BL808_I2C2) || defined(CONFIG_BL808_I2C3)
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+
+#define XCLK 40000000 // 40 MHz crystal oscillator
+
+/****************************************************************************
+ * Private Types
+ ****************************************************************************/
+
+struct bl808_i2c_s
+{
+  struct i2c_ops_s *ops;
+
+  uint8_t irq;
+  uint8_t idx;
+  uint8_t scl_pin;
+  uint8_t sda_pin;
+  int refs;
+  mutex_t lock;
+  sem_t sem_isr;
+
+  struct i2c_msg_s *msgs;
+  int error;
+  uint8_t msgidx;
+  uint32_t bytes;
+};
+
+/****************************************************************************
+ * Private Function Prototypes
+ ****************************************************************************/
+
+int bl808_i2c_transfer(FAR struct i2c_master_s *dev,
+                       FAR struct i2c_msg_s *msgs, int count);
+#ifdef CONFIG_I2C_RESET
+int bl808_i2c_reset(FAR struct i2c_master_s *dev);
+#endif
+
+/****************************************************************************
+ * Private Data
+ ****************************************************************************/
+
+static struct i2c_ops_s bl808_i2c_ops =
+{
+  .transfer = bl808_i2c_transfer,
+#ifdef CONFIG_I2C_RESET
+  .reset = bl808_i2c_reset
+#endif
+};
+
+#ifdef CONFIG_BL808_I2C0
+static struct bl808_i2c_s bl808_i2c0 =
+{
+  .ops = &bl808_i2c_ops,
+  .irq = BL808_IRQ_I2C0,
+  .idx = 0,
+  .scl_pin = CONFIG_BL808_I2C0_SCL,
+  .sda_pin = CONFIG_BL808_I2C0_SDA,
+  .refs = 0,
+  .lock = NXMUTEX_INITIALIZER,
+  .sem_isr = SEM_INITIALIZER(0),
+  .msgs = NULL,
+  .error = OK,
+  .msgidx = 0,
+  .bytes = 0
+};
+#endif
+
+#ifdef CONFIG_BL808_I2C1
+static struct bl808_i2c_s bl808_i2c1 =
+{
+  .ops = &bl808_i2c_ops,
+  .irq = BL808_IRQ_I2C1,
+  .idx = 1,
+  .scl_pin = CONFIG_BL808_I2C1_SCL,
+  .sda_pin = CONFIG_BL808_I2C1_SDA,
+  .refs = 0,
+  .lock = NXMUTEX_INITIALIZER,
+  .sem_isr = SEM_INITIALIZER(0),
+  .msgs = NULL,
+  .error = OK,
+  .msgidx = 0,
+  .bytes = 0
+};
+#endif
+
+#ifdef CONFIG_BL808_I2C2
+static struct bl808_i2c_s bl808_i2c2 =
+{
+  .ops = &bl808_i2c_ops,
+  .irq = BL808_IRQ_I2C2,
+  .idx = 2,
+  .scl_pin = CONFIG_BL808_I2C2_SCL,
+  .sda_pin = CONFIG_BL808_I2C2_SDA,
+  .refs = 0,
+  .lock = NXMUTEX_INITIALIZER,
+  .sem_isr = SEM_INITIALIZER(0),
+  .msgs = NULL,
+  .error = OK,
+  .msgidx = 0,
+  .bytes = 0
+};
+#endif
+
+#ifdef CONFIG_BL808_I2C3
+static struct bl808_i2c_s bl808_i2c3 =
+{
+  .ops = &bl808_i2c_ops,
+  .irq = BL808_IRQ_I2C3,
+  .idx = 3,
+  .scl_pin = CONFIG_BL808_I2C3_SCL,
+  .sda_pin = CONFIG_BL808_I2C3_SDA,
+  .refs = 0,
+  .lock = NXMUTEX_INITIALIZER,
+  .sem_isr = SEM_INITIALIZER(0),
+  .msgs = NULL,
+  .error = OK,
+  .msgidx = 0,
+  .bytes = 0
+};
+#endif
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: bl808_i2c_find_clock_dividers
+ *
+ * Description:
+ *   Finds values for the clock divison registers to give the requested
+ *   frequency. Tries to keep period values small for better accuracy.
+ *   Warns when fails to match freq.
+ *
+ * Input Parameters:
+ *   module_div - Return location for I2C module clock divider
+ *   period_div - Return location for internal cycle count divider.
+ *   freq       - Requested frequency
+ *
+ ****************************************************************************/
+
+void bl808_i2c_find_clock_dividers(uint8_t *module_div,
+                                   uint8_t *period_div,
+                                   uint32_t freq)
+{
+  int best_module_div = 1;
+  int best_period_div = 2;
+  int min_error = abs(XCLK - freq);
+
+  /* This function will try to keep the value that goes into the prd fields
+   * as small as possible because I found through testing that this yields
+   * clocks that are more accurate to the target. Writing 0 to the period
+   * fields causes all transmissions to fail, so start with a divider of 2.
+   */
+
+  for (int period_iter = 2; period_iter < 256; period_iter++)
+    {
+      int calc_module = ((XCLK / (period_iter * 4)) / freq);
+      if (calc_module > 256) /* Discard non viable values */
+        {
+          continue;
+        }
+      else if (calc_module == 0) /* Avoid division by 0 */
+        {
+          /* All subsequent values of period_iter will result in 0 again */
+
+          break;
+        }
+
+      int freq_result = (XCLK / (4*period_iter)) / (calc_module);
+      if (freq_result == freq)
+        {
+          best_period_div = period_iter;
+          best_module_div = calc_module;
+          min_error = 0;
+          break;
+        }
+      else if (abs(freq_result - freq) < min_error)
+        {
+          best_module_div = period_iter;
+          best_period_div = calc_module;
+          min_error = abs(freq_result - freq);
+        }
+    }
+
+  if (min_error > 0)
+    {
+      i2cwarn("Couldn't match requested I2C frequency. Requested %d, got %d",
+              freq, XCLK / (4*best_module_div) / (best_period_div));
+    }
+
+  *module_div = best_module_div - 1;
+  *period_div = best_period_div - 1;
+}
+
+/****************************************************************************
+ * Name: bl808_i2c_set_freq
+ *
+ * Description:
+ *   Set I2C module frequency to freq.
+ *
+ * Input Parameters:
+ *   priv - Private I2C device structure
+ *   freq - Requested frequency
+ *
+ * Returned Value:
+ *   Error status. Always returns OK.
+ *
+ ****************************************************************************/
+
+int bl808_i2c_set_freq(struct bl808_i2c_s *priv, uint32_t freq)
+{
+  uint8_t idx = priv->idx;
+
+  uint8_t module_div;
+  uint8_t period_div;
+  bl808_i2c_find_clock_dividers(&module_div, &period_div, freq);
+
+  /* Now we have our dividers and we can write config registers */
+
+  if ((idx == 0) || (idx == 1))
+    {
+      /* Set clock source to xtal and set module clk division */
+
+      modifyreg32(BL808_GLB_I2C_CFG0, I2C_CFG_CLK_DIV_MASK,
+                  I2C_CFG_CLK_EN
+                  | I2C_CFG_CLK_XTAL
+                  | (module_div << I2C_CFG_CLK_DIV_SHIFT));
+    }
+  else if (idx == 2)
+    {
+      modifyreg32(BL808_MM_GLB_CLK_CTRL_CPU, 0,
+                  CLK_CTRL_CPU_I2C_CLK_XTAL);
+      modifyreg32(BL808_MM_GLB_CLK_CTRL_PERI,
+                  CLK_CTRL_PERI_I2C0_DIV_MASK,
+                  CLK_CTRL_PERI_I2C0_EN
+                  | CLK_CTRL_PERI_I2C0_DIV_EN
+                  | (module_div << CLK_CTRL_PERI_I2C0_DIV_SHIFT));
+    }
+  else if (idx == 3)
+    {
+      modifyreg32(BL808_MM_GLB_CLK_CTRL_CPU, 0,
+                  CLK_CTRL_CPU_I2C_CLK_XTAL);
+      modifyreg32(BL808_MM_GLB_CLK_CTRL_PERI3,
+                  CLK_CTRL_PERI_I2C1_DIV_MASK,
+                  CLK_CTRL_PERI_I2C1_EN
+                  | CLK_CTRL_PERI_I2C1_DIV_EN
+                  | (module_div << CLK_CTRL_PERI_I2C1_DIV_SHIFT));
+    }
+
+  modifyreg32(BL808_I2C_PRD_START(idx), 0xffffffff,
+              period_div << I2C_PRD_PH0_SHIFT
+              | period_div << I2C_PRD_PH1_SHIFT
+              | period_div << I2C_PRD_PH2_SHIFT
+              | period_div << I2C_PRD_PH3_SHIFT);
+  modifyreg32(BL808_I2C_PRD_STOP(idx), 0xffffffff,
+              period_div << I2C_PRD_PH0_SHIFT
+              | period_div << I2C_PRD_PH1_SHIFT
+              | period_div << I2C_PRD_PH2_SHIFT
+              | period_div << I2C_PRD_PH3_SHIFT);
+  modifyreg32(BL808_I2C_PRD_DATA(idx), 0xffffffff,
+              period_div << I2C_PRD_PH0_SHIFT
+              | period_div << I2C_PRD_PH1_SHIFT
+              | period_div << I2C_PRD_PH2_SHIFT
+              | period_div << I2C_PRD_PH3_SHIFT);
+
+  return OK;
+}
+
+/****************************************************************************
+ * Name: bl808_i2c_write
+ *
+ * Description:
+ *   Writes one message into the TX FIFO.
+ *
+ * Input Parameters:
+ *   priv - Private I2C device structure
+ *
+ ****************************************************************************/
+
+void bl808_i2c_write(struct bl808_i2c_s *priv)
+{
+  struct i2c_msg_s *msg = &priv->msgs[priv->msgidx];
+
+  uint8_t count = msg->length - priv->bytes;
+  if (count > 4)
+    {
+      count = 4; /* Only handle 4 bytes at a time */
+    }
+
+  uint32_t regval = 0;
+  for (int i = 0; i < count; i++)
+    {
+      uint8_t byte = msg->buffer[priv->bytes + i];
+      regval |= byte << (i * 8);
+    }
+
+  putreg32(regval, BL808_I2C_WDATA(priv->idx));
+  priv->bytes += count;
+}
+
+/****************************************************************************
+ * Name: bl808_i2c_read
+ *
+ * Description:
+ *   Reads one message from the RX FIFO.
+ *
+ * Input Parameters:
+ *   priv - Private I2C device structure
+ *
+ ****************************************************************************/
+
+void bl808_i2c_read(struct bl808_i2c_s *priv)
+{
+  struct i2c_msg_s *msg = &priv->msgs[priv->msgidx];
+  uint8_t count = msg->length - priv->bytes;
+  uint32_t regval = getreg32(BL808_I2C_RDATA(priv->idx));
+
+  if (count > 4)
+    {
+      count = 4; /* Only handle 4 bytes at a time */
+    }
+
+  for (int i = 0; i < count; i++)
+    {
+      msg->buffer[priv->bytes + i] = regval & 0xff;
+      regval = regval >> 8;
+    }
+
+  priv->bytes += count;
+}
+
+/****************************************************************************
+ * Name: bl808_i2c_transferbytes
+ *
+ * Description:
+ *   Determines a message should be written or read. Disables FIFO
+ *   interrupts all bytes are handled.
+ *
+ * Input Parameters:
+ *   priv - Private I2C device structure
+ *
+ ****************************************************************************/
+
+void bl808_i2c_transferbytes(struct bl808_i2c_s *priv)
+{
+  struct i2c_msg_s *msg = &priv->msgs[priv->msgidx];
+
+  if (msg->flags & I2C_M_READ)
+    {
+      if (priv->bytes < msg->length)
+        {
+          bl808_i2c_read(priv);
+        }
+      else
+        {
+          modifyreg32(BL808_I2C_INT_STS(priv->idx), 0,
+                      I2C_RX_FIFO_RDY_MASK);
+          modifyreg32(BL808_I2C_FIFO_CONFIG_0(priv->idx), 0,
+                      RX_FIFO_CLR);
+        }
+    }
+  else
+    {
+      if (priv->bytes < msg->length)
+        {
+          bl808_i2c_write(priv);
+        }
+      else
+        {
+          modifyreg32(BL808_I2C_INT_STS(priv->idx), 0,
+                      I2C_TX_FIFO_RDY_MASK);
+        }
+    }
+}
+
+/****************************************************************************
+ * Name: bl808_i2c_start_transfer
+ *
+ * Description:
+ *   Configures I2C module according to message, enables relevant
+ *   interrupts and intitiates the transaction.
+ *
+ * Input Parameters:
+ *   priv - Private I2C device structure
+ *
+ ****************************************************************************/
+
+void bl808_i2c_start_transfer(struct bl808_i2c_s *priv)
+{
+  struct i2c_msg_s *msg = &priv->msgs[priv->msgidx];
+
+  /* Set frequency and slave address */
+
+  bl808_i2c_set_freq(priv, msg->frequency);
+  modifyreg32(BL808_I2C_CONFIG(priv->idx), I2C_SLV_ADDR_MASK,
+              (msg->addr << I2C_SLV_ADDR_SHIFT));
+
+  modifyreg32(BL808_I2C_FIFO_CONFIG_0(priv->idx), 0,
+              TX_FIFO_CLR | RX_FIFO_CLR);
+  modifyreg32(BL808_I2C_CONFIG(priv->idx),
+              I2C_PKT_LEN_MASK,
+              ((msg->length - 1) << I2C_PKT_LEN_SHIFT));
+
+  modifyreg32(BL808_I2C_INT_STS(priv->idx),
+              I2C_END_MASK | I2C_NAK_MASK
+              | I2C_ARB_MASK
+              | I2C_FIFO_ERROR_MASK, 0);
+
+  /* Unmask relevant interrupts */
+
+  if (msg->flags & I2C_M_READ)
+    {
+      modifyreg32(BL808_I2C_INT_STS(priv->idx),
+                  I2C_RX_FIFO_RDY_MASK, 0);
+    }
+  else
+    {
+      modifyreg32(BL808_I2C_INT_STS(priv->idx),
+                  I2C_TX_FIFO_RDY_MASK, 0);
+    }
+
+  modifyreg32(BL808_I2C_CONFIG(priv->idx), 0,
+              I2C_M_EN);
+}
+
+/****************************************************************************
+ * Name: bl808_transfer_finished
+ *
+ * Description:
+ *   Disables I2C and clears FIFOs and interrupts.
+ *
+ * Input Parameters:
+ *   priv - Private I2C device structure
+ *
+ ****************************************************************************/
+
+void bl808_i2c_transfer_finished(struct bl808_i2c_s *priv)
+{
+  /* Disable i2c */
+
+  modifyreg32(BL808_I2C_CONFIG(priv->idx), I2C_M_EN, 0);
+
+  /* Clear FIFOs */
+
+  modifyreg32(BL808_I2C_FIFO_CONFIG_0(priv->idx), 0,
+              TX_FIFO_CLR | RX_FIFO_CLR);
+
+  /* Mask all interrupts */
+
+  modifyreg32(BL808_I2C_INT_STS(priv->idx), 0,
+              I2C_END_MASK | I2C_TX_FIFO_RDY_MASK
+              | I2C_RX_FIFO_RDY_MASK | I2C_NAK_MASK
+              | I2C_ARB_MASK | I2C_FIFO_ERROR_MASK);
+
+  /* Clear interrupts */
+
+  modifyreg32(BL808_I2C_INT_STS(priv->idx), 0,
+              I2C_END_CLR | I2C_NAK_CLR | I2C_ARB_CLR);
+
+  nxsem_post(&priv->sem_isr);
+}
+
+/****************************************************************************
+ * Name: i2c_interrupt
+ *
+ * Description:
+ *   I2C interrupt handler. Handles transfer-related errors, as well as
+ *   loading or reading from FIFOs when ready.
+ *
+ * Returned Value:
+ *   Error status. Returns -EIO for transfer errors, OK otherwise.
+ *
+ ****************************************************************************/
+
+int i2c_interrupt(int irq, void *context, void *arg)
+{
+  struct bl808_i2c_s *priv = (struct bl808_i2c_s *)arg;
+  uint32_t int_status = getreg32(BL808_I2C_INT_STS(priv->idx));
+
+  if (int_status & I2C_ARB_INT)
+    {
+      bl808_i2c_transfer_finished(priv);
+      i2cerr("Bus arbitration error\n");
+      priv->error = -EIO;
+      return -EIO;
+    }
+  else if (int_status & I2C_FIFO_ERROR_INT)
+    {
+      bl808_i2c_transfer_finished(priv);
+      i2cerr("FIFO error");
+      priv->error = -EIO;
+      return -EIO;
+    }
+  else if (int_status & I2C_NAK_INT)
+    {
+      bl808_i2c_transfer_finished(priv);
+      i2cwarn("Transfer not acknowledged\n");
+      priv->error = -EIO;
+      return -EIO;
+    }
+  else if (int_status & I2C_RX_FIFO_RDY_INT)
+    {
+      bl808_i2c_transferbytes(priv);
+      return OK;
+    }
+  else if (int_status & I2C_END_INT)
+    {
+      bl808_i2c_transfer_finished(priv);
+      return OK;
+    }
+  else if (int_status & I2C_TX_FIFO_RDY_INT)
+    {
+      bl808_i2c_transferbytes(priv);
+      return OK;
+    }
+
+  /* Should not get here */
+
+  i2cerr("Unidentified interrupt\n");
+  return -EIO;
+}
+
+/* Driver methods */
+
+/****************************************************************************
+ * Name: bl808_i2c_transfer
+ *
+ * Description:
+ *   Method accessed by the I2C master driver. Configures hardware for
+ *   each message to be transferred, then initiates each transfer.
+ *
+ * Input Parameters:
+ *   dev   - Public I2C device structure
+ *   msgs  - Message array to transfer
+ *   count - Number of messages to transfer
+ *
+ * Returned Value:
+ *   Error status. Returns errors that may propagate from nxmutex_lock()
+ *   or -EIO. Otherwise returns OK.
+ *
+ ****************************************************************************/
+
+int bl808_i2c_transfer(FAR struct i2c_master_s *dev,
+                       struct i2c_msg_s *msgs, int count)
+{
+  struct bl808_i2c_s *priv = (struct bl808_i2c_s *)dev;
+  int ret;
+  priv->msgs = msgs;
+  priv->error = OK;
+
+  ret = nxmutex_lock(&priv->lock);
+  if (ret < 0)
+    {
+      i2cerr("Lock error\n");
+      return ret;
+    }
+
+  modifyreg32(BL808_I2C_CONFIG(priv->idx),
+              I2C_SUB_ADDR_EN, 0);
+
+  for (int idx = 0; idx < count; idx++)
+    {
+      priv->bytes = 0;
+
+      /* Handle message flags */
+
+      if (msgs[idx].flags & I2C_M_READ)
+        {
+          modifyreg32(BL808_I2C_CONFIG(priv->idx),
+                      0, I2C_DIR_R);
+        }
+      else
+        {
+          modifyreg32(BL808_I2C_CONFIG(priv->idx),
+                      I2C_DIR_R, 0);
+        }
+
+      if (msgs[idx].flags & I2C_M_TEN)
+        {
+          modifyreg32(BL808_I2C_CONFIG(priv->idx),
+                      0, I2C_10B_ADDR_EN);
+        }
+      else
+        {
+          modifyreg32(BL808_I2C_CONFIG(priv->idx),
+                      I2C_10B_ADDR_EN, 0);
+        }
+
+      if (msgs[idx].flags & I2C_M_NOSTOP)
+        {
+          if (msgs[idx].length > 4)
+            {
+              return -EIO;
+            }
+
+          modifyreg32(BL808_I2C_CONFIG(priv->idx), I2C_SUB_ADDR_LEN_MASK,
+                      (I2C_SUB_ADDR_EN) | ((msgs[idx].length - 1)
+                                           << I2C_SUB_ADDR_LEN_SHIFT));
+
+          uint32_t subaddr_regval = 0;
+          for (uint8_t subaddr_idx = 0;
+               subaddr_idx < msgs[idx].length;
+               subaddr_idx++)
+            {
+              subaddr_regval |= msgs[idx].buffer[subaddr_idx]
+                << (subaddr_idx * 8);
+            }
+
+          putreg32(subaddr_regval, BL808_I2C_SUB_ADDR(priv->idx));
+
+          continue;
+        }
+
+      priv->msgidx = idx;
+
+      bl808_i2c_start_transfer(priv);
+
+      ret = nxsem_wait_uninterruptible(&priv->sem_isr);
+      if (ret < 0)
+        {
+          i2cerr("Transfer error\n");
+          return ret;
+        }
+    }
+
+  /* If something went wrong with the transfer (e.g. NAK),
+   * the interrupt will have set priv->error.
+   */
+
+  ret = priv->error;
+
+  nxmutex_unlock(&priv->lock);
+  return ret;
+}
+
+#ifdef CONFIG_I2C_RESET
+
+/****************************************************************************
+ * Name: bl808_i2c_reset
+ *
+ * Description:
+ *   Method accessed by the I2C master driver. Calls transfer_finished to
+ *   clear FIFOs and interrupts.
+ *
+ * Input Parameters:
+ *   dev - Public I2C device structure
+ *
+ * Returned Value:
+ *   Error status. Always returns OK.
+ *
+ ****************************************************************************/
+
+int bl808_i2c_reset(struct i2c_master_s *dev)
+{
+  struct bl808_i2c_s *priv = (struct bl808_i2c_s *)dev;
+  bl808_i2c_transfer_finished(priv);
+  return OK;
+}
+#endif
+
+/****************************************************************************
+ * Public Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: bl808_i2cbus_initialize
+ *
+ * Description:
+ *   Initialize the selected I2C port. And return a unique instance of struct
+ *   struct i2c_master_s.  This function may be called to obtain multiple
+ *   instances of the interface, each of which may be set up with a
+ *   different frequency and slave address.
+ *
+ * Input Parameter:
+ *   Port number (for hardware that has multiple I2C interfaces)
+ *
+ * Returned Value:
+ *   Valid I2C device structure reference on success; a NULL on failure
+ *
+ ****************************************************************************/
+
+struct i2c_master_s *bl808_i2cbus_initialize(int port)
+{
+  struct bl808_i2c_s *priv;
+  gpio_pinattr_t gpio_attr = GPIO_INPUT | GPIO_PULLUP;
+
+  switch (port)
+    {
+#ifdef CONFIG_BL808_I2C0
+    case 0:
+      priv = &bl808_i2c0;
+      gpio_attr |= GPIO_FUNC_I2C0;
+      break;
+#endif
+
+#ifdef CONFIG_BL808_I2C1
+    case 1:
+      priv = &bl808_i2c1;
+      gpio_attr |= GPIO_FUNC_I2C1;
+      break;
+#endif
+
+#ifdef CONFIG_BL808_I2C2
+    case 2:
+      priv = &bl808_i2c2;
+      gpio_attr |= GPIO_FUNC_I2C2;
+      break;
+#endif
+
+#ifdef CONFIG_BL808_I2C3
+    case 3:
+      priv = &bl808_i2c3;
+      gpio_attr |= GPIO_FUNC_I2C3;
+      break;
+#endif
+
+    default:
+      i2cerr("Invalid port number\n");
+      return NULL;
+    }
+
+  nxmutex_lock(&priv->lock);
+  priv->refs++;
+  if (priv->refs > 1)
+    {
+      nxmutex_unlock(&priv->lock);
+      return (struct i2c_master_s *)priv;
+    }
+
+  /* Initialize GPIO */
+
+  bl808_configgpio(priv->scl_pin, gpio_attr);
+  bl808_configgpio(priv->sda_pin, gpio_attr);
+
+  /* Enable interrupt */
+
+  int ret = irq_attach(priv->irq, i2c_interrupt, (void *)priv);
+  if (ret == OK)
+    {
+      up_enable_irq(priv->irq);
+    }
+  else
+    {
+      i2cerr("Error attaching ISR\n");
+    }
+
+  nxmutex_unlock(&priv->lock);
+
+  return (struct i2c_master_s *)priv;
+}
+
+/****************************************************************************
+ * Name: bl808_i2cbus_uninitialize
+ *
+ * Description:
+ *   De-initialize the selected I2C port.
+ *
+ * Input Parameter:
+ *   Device structure as returned by the i2c_i2cbus_initialize()
+ *
+ * Returned Value:
+ *   OK on success, ERROR when internal reference count mismatch or dev
+ *   points to invalid hardware device.
+ *
+ ****************************************************************************/
+
+int bl808_i2cbus_uninitialize(struct i2c_master_s *dev)
+{
+  struct bl808_i2c_s *priv = (struct bl808_i2c_s *)dev;
+
+  if (priv->refs == 0)
+    {
+      i2cerr("Attempt to deinit bus with no references\n");
+      return -EIO;
+    }
+
+  nxmutex_lock(&priv->lock);
+  priv->refs--;
+  if (priv->refs > 0)
+    {
+      nxmutex_unlock(&priv->lock);
+      return OK;
+    }
+
+  irq_detach(priv->irq);
+  up_disable_irq(priv->irq);
+  modifyreg32(BL808_I2C_INT_STS(priv->idx), 0,
+              I2C_END_MASK
+              | I2C_TX_FIFO_RDY_MASK
+              | I2C_RX_FIFO_RDY_MASK
+              | I2C_NAK_MASK
+              | I2C_ARB_MASK
+              | I2C_FIFO_ERROR_MASK);
+
+  nxmutex_unlock(&priv->lock);
+  return OK;
+}
+
+#endif
diff --git a/arch/risc-v/src/bl808/bl808_i2c.h 
b/arch/risc-v/src/bl808/bl808_i2c.h
new file mode 100644
index 0000000000..3315f80e93
--- /dev/null
+++ b/arch/risc-v/src/bl808/bl808_i2c.h
@@ -0,0 +1,70 @@
+/****************************************************************************
+ * arch/risc-v/src/bl808/bl808_i2c.h
+ *
+ * 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.
+ *
+ ****************************************************************************/
+
+#ifndef __ARCH_RISC_V_SRC_BL808_BL808_I2C_H
+#define __ARCH_RISC_V_SRC_BL808_BL808_I2C_H
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/i2c/i2c_master.h>
+
+/****************************************************************************
+ * Public Functions Prototypes
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: bl808_i2cbus_initialize
+ *
+ * Description:
+ *   Initialize the selected I2C port. And return a unique instance of struct
+ *   struct i2c_master_s.  This function may be called to obtain multiple
+ *   instances of the interface, each of which may be set up with a
+ *   different frequency and slave address.
+ *
+ * Input Parameter:
+ *   Port number (for hardware that has multiple I2C interfaces)
+ *
+ * Returned Value:
+ *   Valid I2C device structure reference on success; a NULL on failure
+ *
+ ****************************************************************************/
+
+struct i2c_master_s *bl808_i2cbus_initialize(int port);
+
+/****************************************************************************
+ * Name: bl808_i2cbus_uninitialize
+ *
+ * Description:
+ *   De-initialize the selected I2C port, and power down the device.
+ *
+ * Input Parameter:
+ *   Device structure as returned by the rp2040_i2cbus_initialize()
+ *
+ * Returned Value:
+ *   OK on success, ERROR when internal reference count mismatch or dev
+ *   points to invalid hardware device.
+ *
+ ****************************************************************************/
+
+int bl808_i2cbus_uninitialize(struct i2c_master_s *dev);
+
+#endif /* __ARCH_RISC_V_SRC_BL808_BL808_I2C_H */
diff --git a/arch/risc-v/src/bl808/bl808_irq.c 
b/arch/risc-v/src/bl808/bl808_irq.c
index af32ea6c18..2d9a775548 100644
--- a/arch/risc-v/src/bl808/bl808_irq.c
+++ b/arch/risc-v/src/bl808/bl808_irq.c
@@ -44,6 +44,35 @@
  * Private Functions
  ****************************************************************************/
 
+/****************************************************************************
+ * Name: m0ic_mask_irq
+ *
+ * Description:
+ *   Masks or unmasks an interrupt in the M0 Interrupt Controller.
+ *
+ * Input parameters:
+ *   irq  - IRQ number to mask or unmask
+ *   mask - 0 to unmask (enable), 1 to mask (disable)
+ *
+ ****************************************************************************/
+
+void m0ic_mask_irq(int irq, bool mask)
+{
+  int m0_extirq = irq - RISCV_IRQ_EXT
+    - BL808_M0_IRQ_OFFSET - BL808_IRQ_NUM_BASE;
+
+  if (mask)
+    {
+      modifyreg32(BL808_M0IC_MASK(m0_extirq / 32),
+                  0, 1 << (m0_extirq % 32));
+    }
+  else
+    {
+      modifyreg32(BL808_M0IC_MASK(m0_extirq / 32),
+                  1 << (m0_extirq % 32), 0);
+    }
+}
+
 /****************************************************************************
  * Name: m0ic_interrupt
  *
@@ -84,6 +113,15 @@ static int __m0ic_interrupt(int irq, void *context, void 
*arg)
   putreg32(status_0, BL808_M0IC_CLEAR(0));
   putreg32(status_1, BL808_M0IC_CLEAR(1));
 
+  /* M0IC interrupts respond to the rising edge of the
+   * source interrupts. If the source is held high but
+   * the M0IC interrupt is cleared, the interrupt
+   * never happens. So, use masks to refresh the interrupt.
+   */
+
+  m0ic_mask_irq(irqn, 1);
+  m0ic_mask_irq(irqn, 0);
+
   return OK;
 }
 
@@ -192,9 +230,7 @@ void up_disable_irq(int irq)
                && extirq <= (BL808_M0_MAX_EXTIRQ
                              + BL808_M0_IRQ_OFFSET))
         {
-          int m0_extirq = extirq - BL808_M0_IRQ_OFFSET - BL808_IRQ_NUM_BASE;
-          modifyreg32(BL808_M0IC_MASK(m0_extirq / 32),
-                      0, 1 << (m0_extirq % 32));
+          m0ic_mask_irq(irq, 1);
         }
       else
         {
@@ -242,9 +278,7 @@ void up_enable_irq(int irq)
                && extirq <= (BL808_M0_MAX_EXTIRQ
                              + BL808_M0_IRQ_OFFSET))
         {
-          int m0_extirq = extirq - BL808_M0_IRQ_OFFSET - BL808_IRQ_NUM_BASE;
-          modifyreg32(BL808_M0IC_MASK(m0_extirq / 32),
-                      1 << (m0_extirq % 32), 0);
+          m0ic_mask_irq(irq, 0);
         }
       else
         {
diff --git a/arch/risc-v/src/bl808/hardware/bl808_glb.h 
b/arch/risc-v/src/bl808/hardware/bl808_glb.h
index 2808bb8744..69eb7bfd96 100644
--- a/arch/risc-v/src/bl808/hardware/bl808_glb.h
+++ b/arch/risc-v/src/bl808/hardware/bl808_glb.h
@@ -38,6 +38,7 @@
 
 #define BL808_GLB_UART_CFG1_OFFSET    0x154
 #define BL808_GLB_UART_CFG2_OFFSET    0x158
+#define BL808_GLB_I2C_CFG0_OFFSET     0x180
 #define BL808_GLB_SPI_CFG0_OFFSET     0x1b0
 #define BL808_GLB_PARM_CFG0_OFFSET    0x510
 
@@ -47,6 +48,7 @@
 
 #define BL808_GLB_UART_CFG1 (BL808_GLB_BASE + BL808_GLB_UART_CFG1_OFFSET)
 #define BL808_GLB_UART_CFG2 (BL808_GLB_BASE + BL808_GLB_UART_CFG2_OFFSET)
+#define BL808_GLB_I2C_CFG0 (BL808_GLB_BASE + BL808_GLB_I2C_CFG0_OFFSET)
 #define BL808_GLB_SPI_CFG0 (BL808_GLB_BASE + BL808_GLB_SPI_CFG0_OFFSET)
 #define BL808_GLB_PARM_CFG0 (BL808_GLB_BASE + BL808_GLB_PARM_CFG0_OFFSET)
 
@@ -59,6 +61,13 @@
 #define UART_CFG_SIG_SEL_SHIFT(n)  ((n % 8) * 4)
 #define UART_CFG_SIG_SEL_MASK(n)   (0x0f << UART_CFG_SIG_SEL_SHIFT(n))
 
+/* I2C_CFG0 *****************************************************************/
+
+#define I2C_CFG_CLK_DIV_SHIFT 16
+#define I2C_CFG_CLK_DIV_MASK (0xff << I2C_CFG_CLK_DIV_SHIFT)
+#define I2C_CFG_CLK_EN (1 << 24)
+#define I2C_CFG_CLK_XTAL (1 << 25)
+
 /* SPI_CFG0 *****************************************************************/
 
 #define SPI_CFG_CLK_DIV_SHIFT 0
diff --git a/arch/risc-v/src/bl808/hardware/bl808_i2c.h 
b/arch/risc-v/src/bl808/hardware/bl808_i2c.h
new file mode 100644
index 0000000000..619f74d874
--- /dev/null
+++ b/arch/risc-v/src/bl808/hardware/bl808_i2c.h
@@ -0,0 +1,117 @@
+/****************************************************************************
+ * arch/risc-v/src/bl808/hardware/bl808_i2c.h
+ *
+ * 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.
+ *
+ ****************************************************************************/
+
+#ifndef __ARCH_RISCV_SRC_BL808_HARDWARE_BL808_I2C_H
+#define __ARCH_RISCV_SRC_BL808_HARDWARE_BL808_I2C_H
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+#include "bl808_memorymap.h"
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+
+#define BL808_I2C_BASE(n) (((n) == 0) ? BL808_I2C0_BASE \
+                           : ((n) == 1) ? BL808_I2C1_BASE \
+                           : ((n) == 2) ? BL808_I2C2_BASE \
+                           : BL808_I2C3_BASE)
+
+/* Register offsets *********************************************************/
+
+#define BL808_I2C_CONFIG_OFFSET        0x00
+#define BL808_I2C_INT_STS_OFFSET       0x04
+#define BL808_I2C_SUB_ADDR_OFFSET      0x08
+#define BL808_I2C_BUSY_OFFSET          0x0c
+#define BL808_I2C_PRD_START_OFFSET     0x10
+#define BL808_I2C_PRD_STOP_OFFSET      0x14
+#define BL808_I2C_PRD_DATA_OFFSET      0x18
+#define BL808_I2C_FIFO_CONFIG_0_OFFSET 0x80
+#define BL808_I2C_FIFO_CONFIG_1_OFFSET 0x84
+#define BL808_I2C_WDATA_OFFSET         0x88
+#define BL808_I2C_RDATA_OFFSET         0X8c
+
+/* Register definitions *****************************************************/
+
+#define BL808_I2C_CONFIG(n) (BL808_I2C_BASE(n) + BL808_I2C_CONFIG_OFFSET)
+#define BL808_I2C_INT_STS(n) (BL808_I2C_BASE(n) + BL808_I2C_INT_STS_OFFSET)
+#define BL808_I2C_SUB_ADDR(n) (BL808_I2C_BASE(n) + BL808_I2C_SUB_ADDR_OFFSET)
+#define BL808_I2C_BUSY(n) (BL808_I2C_BASE(n) + BL808_I2C_BUSY_OFFSET)
+#define BL808_I2C_PRD_START(n) (BL808_I2C_BASE(n) + BL808_I2C_PRD_START_OFFSET)
+#define BL808_I2C_PRD_STOP(n) (BL808_I2C_BASE(n) + BL808_I2C_PRD_STOP_OFFSET)
+#define BL808_I2C_PRD_DATA(n) (BL808_I2C_BASE(n) + BL808_I2C_PRD_DATA_OFFSET)
+#define BL808_I2C_FIFO_CONFIG_0(n) (BL808_I2C_BASE(n) + 
BL808_I2C_FIFO_CONFIG_0_OFFSET)
+#define BL808_I2C_FIFO_CONFIG_1(n) (BL808_I2C_BASE(n) + 
BL808_I2C_FIFO_CONFIG_1_OFFSET)
+#define BL808_I2C_WDATA(n) (BL808_I2C_BASE(n) + BL808_I2C_WDATA_OFFSET)
+#define BL808_I2C_RDATA(n) (BL808_I2C_BASE(n) + BL808_I2C_RDATA_OFFSET)
+
+/* Register bit definitions *************************************************/
+
+/* I2C_CONFIG */
+
+#define I2C_M_EN (1 << 0)
+#define I2C_DIR_R (1 << 1)
+#define I2C_SUB_ADDR_EN (1 << 4)
+#define I2C_SUB_ADDR_LEN_SHIFT (5)
+#define I2C_SUB_ADDR_LEN_MASK (0x3 << I2C_SUB_ADDR_LEN_SHIFT)
+#define I2C_10B_ADDR_EN (1 << 7)
+#define I2C_SLV_ADDR_SHIFT (8)
+#define I2C_SLV_ADDR_MASK (0x3ff << I2C_SLV_ADDR_SHIFT)
+#define I2C_PKT_LEN_SHIFT (20)
+#define I2C_PKT_LEN_MASK (0xff << I2C_PKT_LEN_SHIFT)
+
+/* I2C_INT_STS */
+
+#define I2C_END_INT (1 << 0)
+#define I2C_TX_FIFO_RDY_INT (1 << 1)
+#define I2C_RX_FIFO_RDY_INT (1 << 2)
+#define I2C_NAK_INT (1 << 3)
+#define I2C_ARB_INT (1 << 4)
+#define I2C_FIFO_ERROR_INT (1 << 5)
+#define I2C_END_MASK (1 << 8)
+#define I2C_TX_FIFO_RDY_MASK (1 << 9)
+#define I2C_RX_FIFO_RDY_MASK (1 << 10)
+#define I2C_NAK_MASK (1 << 11)
+#define I2C_ARB_MASK (1 << 12)
+#define I2C_FIFO_ERROR_MASK (1 << 13)
+#define I2C_END_CLR (1 << 16)
+#define I2C_NAK_CLR (1 << 19)
+#define I2C_ARB_CLR (1 << 20)
+
+/* I2C_PRD_x */
+
+#define I2C_PRD_PH0_SHIFT (0)
+#define I2C_PRD_PH0_MASK (0xff << I2C_PRD_PH0_SHIFT)
+#define I2C_PRD_PH1_SHIFT (8)
+#define I2C_PRD_PH1_MASK (0xff << I2C_PRD_PH1_SHIFT)
+#define I2C_PRD_PH2_SHIFT (16)
+#define I2C_PRD_PH2_MASK (0xff << I2C_PRD_PH2_SHIFT)
+#define I2C_PRD_PH3_SHIFT (24)
+#define I2C_PRD_PH3_MASK (0xff << I2C_PRD_PH3_SHIFT)
+
+/* I2C_FIFO_CONFIG_0 */
+
+#define TX_FIFO_CLR (1 << 2)
+#define RX_FIFO_CLR (1 << 3)
+
+#endif /* __ARCH_RISCV_SRC_BL808_HARDWARE_BL808_I2C_H */
diff --git a/arch/risc-v/src/bl808/hardware/bl808_memorymap.h 
b/arch/risc-v/src/bl808/hardware/bl808_memorymap.h
index c3bc204493..ed46e7931e 100644
--- a/arch/risc-v/src/bl808/hardware/bl808_memorymap.h
+++ b/arch/risc-v/src/bl808/hardware/bl808_memorymap.h
@@ -36,10 +36,14 @@
 #define BL808_UART0_BASE   0x2000a000ul
 #define BL808_UART1_BASE   0x2000a100ul
 #define BL808_SPI0_BASE    0x2000a200ul
+#define BL808_I2C0_BASE    0x2000a300ul
 #define BL808_TIMER0_BASE  0x2000a500ul
+#define BL808_I2C1_BASE    0x2000a900ul
 #define BL808_UART2_BASE   0x2000aa00ul
 #define BL808_AON_BASE     0x2000f000ul
 #define BL808_UART3_BASE   0x30002000ul
+#define BL808_I2C2_BASE    0x30003000ul
+#define BL808_I2C3_BASE    0x30004000ul
 #define BL808_MM_GLB_BASE  0x30007000ul
 #define BL808_SPI1_BASE    0x30008000ul
 #define BL808_TIMER1_BASE  0x30009000ul
diff --git a/arch/risc-v/src/bl808/hardware/bl808_mm_glb.h 
b/arch/risc-v/src/bl808/hardware/bl808_mm_glb.h
index 226b44cc7a..604d095129 100644
--- a/arch/risc-v/src/bl808/hardware/bl808_mm_glb.h
+++ b/arch/risc-v/src/bl808/hardware/bl808_mm_glb.h
@@ -36,20 +36,31 @@
 
 /* Register offsets *********************************************************/
 
+#define BL808_MM_GLB_CLK_CTRL_CPU_OFFSET 0x00
 #define BL808_MM_GLB_CLK_CTRL_PERI_OFFSET 0x10
+#define BL808_MM_GLB_CLK_CTRL_PERI3_OFFSET 0x18
 
 /* Register definitions *****************************************************/
 
+#define BL808_MM_GLB_CLK_CTRL_CPU (BL808_MM_GLB_BASE \
+                                   + BL808_MM_GLB_CLK_CTRL_CPU_OFFSET)
 #define BL808_MM_GLB_CLK_CTRL_PERI (BL808_MM_GLB_BASE \
                                    + BL808_MM_GLB_CLK_CTRL_PERI_OFFSET)
+#define BL808_MM_GLB_CLK_CTRL_PERI3 (BL808_MM_GLB_BASE \
+                                   + BL808_MM_GLB_CLK_CTRL_PERI3_OFFSET)
 
 /* Register bit definitions *************************************************/
 
+/* CLK_CTRL_CPU */
+
+#define CLK_CTRL_CPU_I2C_CLK_XTAL (1 << 6)
+
 /* CLK_CTRL_PERI ************************************************************/
 
 #define CLK_CTRL_PERI_I2C0_DIV_SHIFT 0
 #define CLK_CTRL_PERI_I2C0_DIV_MASK (0xff << CLK_CTRL_PERI_I2C0_DIV_SHIFT)
-#define CLK_CTRL_PERI_I2C0_EN_SHIFT 9
+#define CLK_CTRL_PERI_I2C0_DIV_EN (1 << 8)
+#define CLK_CTRL_PERI_I2C0_EN (1 << 9)
 #define CLK_CTRL_PERI_UART_DIV_EN_SHIFT 16
 #define CLK_CTRL_PERI_UART_DIV_SHIFT 17
 #define CLK_CTRL_PERI_UART_DIV_MASK (0x07 << CLK_CTRL_PERI_UART_DIV_SHIFT)
@@ -57,4 +68,11 @@
 #define CLK_CTRL_PERI_SPI_DIV_SHIFT 24
 #define CLK_CTRL_PERI_SPI_DIV_MASK (0xff << CLK_CTRL_PERI_SPI_DIV_SHIFT)
 
+/* CLK_CTRL_PERI3 ***********************************************************/
+
+#define CLK_CTRL_PERI_I2C1_DIV_SHIFT 0
+#define CLK_CTRL_PERI_I2C1_DIV_MASK (0xff << CLK_CTRL_PERI_I2C0_DIV_SHIFT)
+#define CLK_CTRL_PERI_I2C1_DIV_EN (1 << 8)
+#define CLK_CTRL_PERI_I2C1_EN (1 << 9)
+
 #endif /* __ARCH_RISCV_SRC_BL808_HARDWARE_BL808_MM_GLB_H */
diff --git a/boards/risc-v/bl808/ox64/src/bl808_appinit.c 
b/boards/risc-v/bl808/ox64/src/bl808_appinit.c
index 0f99a265cf..db444784d5 100644
--- a/boards/risc-v/bl808/ox64/src/bl808_appinit.c
+++ b/boards/risc-v/bl808/ox64/src/bl808_appinit.c
@@ -40,6 +40,10 @@
 #ifdef CONFIG_USERLED
 #include <nuttx/leds/userled.h>
 #endif
+#if defined(CONFIG_BL808_I2C0) || defined(CONFIG_BL808_I2C1) \
+  || defined(CONFIG_BL808_I2C2) || defined(CONFIG_BL808_I2C3)
+#include "bl808_i2c.h"
+#endif
 #if defined(CONFIG_BL808_SPI0) || defined(CONFIG_BL808_SPI1)
 #include "bl808_spi.h"
 #endif
@@ -179,6 +183,26 @@ void board_late_initialize(void)
   bl808_gpadc_init();
 #endif
 
+#ifdef CONFIG_BL808_I2C0
+  struct i2c_master_s *i2c0 = bl808_i2cbus_initialize(0);
+  i2c_register(i2c0, 0);
+#endif
+
+#ifdef CONFIG_BL808_I2C1
+  struct i2c_master_s *i2c1 = bl808_i2cbus_initialize(1);
+  i2c_register(i2c1, 1);
+#endif
+
+#ifdef CONFIG_BL808_I2C2
+  struct i2c_master_s *i2c2 = bl808_i2cbus_initialize(2);
+  i2c_register(i2c2, 2);
+#endif
+
+#ifdef CONFIG_BL808_I2C3
+  struct i2c_master_s *i2c3 = bl808_i2cbus_initialize(3);
+  i2c_register(i2c3, 3);
+#endif
+
 #ifdef CONFIG_BL808_SPI0
   struct spi_dev_s *spi0 = bl808_spibus_initialize(0);
   spi_register(spi0, 0);


Reply via email to