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

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

commit dfc55c9bbee7b3f677c30763702623fa72f34ea2
Author: dechao_gong <[email protected]>
AuthorDate: Fri Jul 17 10:10:18 2026 +0800

    arch/arm/rtl8721dx: add shared Ameba I2C driver
    
    Add a shared NuttX I2C master lower-half for the Realtek Ameba I2C
    controllers (I2C0/I2C1) in arch/arm/src/common/ameba, driven through
    the SDK fwlib in polling mode.  Per-chip wiring (controller count,
    register bases, clock masks, crossbar pad-mux codes and the fwlib
    I2C_InitTypeDef layout) lives in arch/arm/src/rtl8721dx/ameba_i2c_chip.h
    so a port to the other Ameba chips only supplies a same-named header.
    
    Each controller registers as /dev/i2cN from pke8721daf bring-up through
    the stock I2C character driver; a dedicated `i2c` defconfig drives the
    i2ctool for validation.
    
    Assisted-by: Claude <[email protected]>
    Signed-off-by: dechao_gong <[email protected]>
---
 arch/arm/src/common/ameba/Kconfig                  |  14 +
 arch/arm/src/common/ameba/ameba_i2c.c              | 600 +++++++++++++++++++++
 arch/arm/src/common/ameba/ameba_i2c.h              |  84 +++
 arch/arm/src/rtl8721dx/CMakeLists.txt              |   4 +
 arch/arm/src/rtl8721dx/Make.defs                   |   4 +
 arch/arm/src/rtl8721dx/ameba_board.mk              |   9 +
 arch/arm/src/rtl8721dx/ameba_i2c_chip.h            | 103 ++++
 .../arm/rtl8721dx/pke8721daf/configs/i2c/defconfig |  51 ++
 boards/arm/rtl8721dx/pke8721daf/src/CMakeLists.txt |   8 +-
 boards/arm/rtl8721dx/pke8721daf/src/Makefile       |   9 +
 .../rtl8721dx/pke8721daf/src/rtl8721dx_bringup.c   |  10 +
 .../arm/rtl8721dx/pke8721daf/src/rtl8721dx_i2c.c   | 102 ++++
 .../pke8721daf/src/rtl8721dx_pke8721daf.h          |  13 +
 tools/nxstyle.c                                    |   1 +
 14 files changed, 1011 insertions(+), 1 deletion(-)

diff --git a/arch/arm/src/common/ameba/Kconfig 
b/arch/arm/src/common/ameba/Kconfig
index b3391e049cf..ab27649ee54 100644
--- a/arch/arm/src/common/ameba/Kconfig
+++ b/arch/arm/src/common/ameba/Kconfig
@@ -55,4 +55,18 @@ config AMEBA_UART_TXBUFSIZE
 
 endif # AMEBA_UART
 
+config AMEBA_I2C
+       bool "I2C"
+       default n
+       select I2C
+       select I2C_DRIVER
+       ---help---
+               Expose the Ameba I2C controllers (I2C0/I2C1) as NuttX I2C master
+               buses at /dev/i2cN.  The board selects which controller is used 
and
+               its SCL/SDA pads in its bring-up code.
+
+               The driver (arch/arm/src/common/ameba/ameba_i2c.c) sits on the 
SDK
+               fwlib register layer and drives the DesignWare I2C block in 
polling
+               mode.
+
 endmenu # Ameba Peripheral Support
diff --git a/arch/arm/src/common/ameba/ameba_i2c.c 
b/arch/arm/src/common/ameba/ameba_i2c.c
new file mode 100644
index 00000000000..66ac13c3509
--- /dev/null
+++ b/arch/arm/src/common/ameba/ameba_i2c.c
@@ -0,0 +1,600 @@
+/****************************************************************************
+ * arch/arm/src/common/ameba/ameba_i2c.c
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * 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
+ ****************************************************************************/
+
+/* NuttX I2C master lower half for the Realtek Ameba I2C controllers (I2C0
+ * and I2C1).  Each controller is registered from board bring-up and appears
+ * as /dev/i2cN through the stock I2C character driver (i2c_register()).
+ *
+ * The controller is a Synopsys DesignWare I2C block programmed through the
+ * SDK fwlib I2C API in polling mode (no interrupts): I2C_Init() programs the
+ * speed/target, and I2C_MasterWrite()/Read()/RepeatRead() drive the FIFO and
+ * block until the transfer completes or the poll times out.
+ *
+ * Unlike the UART fwlib, which runs from on-chip ROM and resolves the secure
+ * register alias itself via TrustZone_IsSecure(), the I2C fwlib routines are
+ * compiled from ram_common/ameba_i2c.c (see AMEBA_FWLIB_SRCS) and use the
+ * register pointer they are handed without any secure conversion.  NuttX
+ * runs on the KM4 core in the SECURE state, yet the I2C block only responds
+ * on its NON-secure alias (see the note at AMEBA_I2C_BASES in
+ * ameba_i2c_chip.h), so this driver hands the fwlib those non-secure bases.
+ *
+ * The chip-specific wiring (controller count, register bases, clock masks,
+ * pad-mux codes and the fwlib I2C_InitTypeDef layout) lives in the per-chip
+ * ameba_i2c_chip.h.  To keep the vendor headers out of the NuttX include
+ * world, the few fwlib symbols and that struct layout used here are declared
+ * locally rather than pulled in from <ameba_i2c.h>.
+ */
+
+#include <nuttx/config.h>
+
+#include <stdint.h>
+#include <stdbool.h>
+#include <errno.h>
+#include <string.h>
+#include <debug.h>
+
+#include <nuttx/kmalloc.h>
+#include <nuttx/mutex.h>
+#include <nuttx/i2c/i2c_master.h>
+
+#include "ameba_i2c.h"
+#include "ameba_i2c_chip.h"
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+
+/* The register bases, peripheral-clock masks, crossbar pad-mux codes,
+ * controller count (AMEBA_NI2C) and I2C_InitTypeDef layout
+ * (AMEBA_I2C_HAS_DMA_FIELDS) all come from the per-chip ameba_i2c_chip.h.
+ * Everything below is common to every current Ameba chip.
+ */
+
+#define AMEBA_GPIO_PUPD_UP      0x2   /* GPIO_PuPd_UP                       */
+
+/* fwlib I2C_InitTypeDef field values (I2C_ADDR_*, I2C_*_MODE, ameba_i2c.h).
+ * These are identical on every Ameba chip audited, so they stay here rather
+ * than in the per-chip header.
+ */
+
+#define AMEBA_I2C_ADDR_7BIT     0x0   /* I2C_ADDR_7BIT                      */
+#define AMEBA_I2C_ADDR_10BIT    0x1   /* I2C_ADDR_10BIT                     */
+
+#define AMEBA_I2C_SS_MODE       0x1   /* I2C_SS_MODE  (<= 100 kHz)          */
+#define AMEBA_I2C_FS_MODE       0x2   /* I2C_FS_MODE  (<= 400 kHz)          */
+#define AMEBA_I2C_HS_MODE       0x3   /* I2C_HS_MODE  (I2C1 only)           */
+
+#define AMEBA_I2C_MASTER_MODE   0x1   /* I2C_MASTER_MODE                    */
+
+/* IC_RAW_INTR_STAT.TX_ABRT and the matching IC_CLR_TX_ABRT selector for
+ * I2C_ClearINT().  A transmit abort latches whenever an address or data
+ * byte goes unacknowledged (among other causes); the fwlib master helpers
+ * can miss it and still return the full byte count, so this driver checks
+ * it after each transfer to surface a NAK.  It sits at bit 6 of both
+ * registers on every Ameba chip audited (amebadplus/smart/lite/green2/
+ * RTL8720F), so it stays here rather than in the per-chip header.
+ */
+
+#define AMEBA_I2C_TX_ABRT       (1u << 6)  /* IC_RAW_INTR_STAT TX_ABRT     */
+#define AMEBA_I2C_R_TX_ABRT     (1u << 6)  /* I2C_ClearINT() TX_ABRT sel.  */
+
+/* IC_STATUS.TFNF (transmit FIFO not full) and the bounded spin used to wait
+ * for a free FIFO slot while pushing the leading bytes of a chained write
+ * (an I2C_M_NOSTOP segment).  TFNF sits at bit 1 on every Ameba chip.
+ */
+
+#define AMEBA_I2C_TFNF          (1u << 1)  /* IC_STATUS TFNF               */
+#define AMEBA_I2C_FIFO_TIMEOUT  100000     /* ~loop guard, ample for 16 FIFO */
+
+/* Second/third argument to fwlib "state" style APIs. */
+
+#define AMEBA_DISABLE           0x0
+#define AMEBA_ENABLE            0x1
+
+/* Default bus frequency used until the first transfer requests one. */
+
+#define AMEBA_I2C_DEFAULT_FREQ  I2C_SPEED_STANDARD
+
+/****************************************************************************
+ * Private Types
+ ****************************************************************************/
+
+/* Layout-compatible mirror of the fwlib I2C_InitTypeDef (all u32, same
+ * order); passed by address to I2C_StructInit()/I2C_Init().  The leading
+ * I2CIdx field is present on every Ameba chip audited (amebadplus/
+ * amebasmart/amebalite/amebagreen2/RTL8720F), so it stays unconditional.  A
+ * single per-chip layout switch keeps this struct byte-for-byte identical to
+ * the fwlib one: AMEBA_I2C_HAS_DMA_FIELDS gates the three DMA request-level
+ * fields between I2CFilter and I2CAckAddr1, which only some chips carry.
+ * See ameba_i2c_chip.h for the per-chip value.
+ */
+
+struct ameba_i2c_init_s
+{
+  uint32_t idx;                /* I2CIdx        */
+  uint32_t master;             /* I2CMaster     */
+  uint32_t addrmod;            /* I2CAddrMod    */
+  uint32_t spdmod;             /* I2CSpdMod     */
+  uint32_t rxtl;               /* I2CRXTL       */
+  uint32_t txtl;               /* I2CTXTL       */
+  uint32_t mstrestr;           /* I2CMstReSTR   */
+  uint32_t mstgc;              /* I2CMstGC      */
+  uint32_t mststartb;          /* I2CMstStartB  */
+  uint32_t slvnoack;           /* I2CSlvNoAck   */
+  uint32_t slvackgc;           /* I2CSlvAckGC   */
+  uint32_t ackaddr;            /* I2CAckAddr    */
+  uint32_t slvsetup;           /* I2CSlvSetup   */
+  uint32_t sdahd;              /* I2CSdaHd      */
+  uint32_t clk;                /* I2CClk (kHz)  */
+  uint32_t ipclk;              /* I2CIPClk (Hz) */
+  uint32_t filter;             /* I2CFilter     */
+#ifdef AMEBA_I2C_HAS_DMA_FIELDS
+  uint32_t txdmarqlv;          /* I2CTxDMARqLv  */
+  uint32_t rxdmarqlv;          /* I2CRxDMARqLv  */
+  uint32_t dmamod;             /* I2CDMAMod     */
+#endif
+  uint32_t ackaddr1;           /* I2CAckAddr1   */
+};
+
+struct ameba_i2c_dev_s
+{
+  struct i2c_master_s dev;     /* I2C master lower half (must be first) */
+  uintptr_t base;              /* Non-secure I2C register base address */
+  uint32_t  periph;            /* APBPeriph function mask (RCC arg 1) */
+  uint32_t  clk;               /* APBPeriph clock mask (RCC arg 2) */
+  uint32_t  frequency;         /* Currently programmed bus frequency (Hz) */
+  uint16_t  address;           /* Currently programmed target address */
+  uint8_t   addrmod;           /* Currently programmed AMEBA_I2C_ADDR_*BIT */
+  uint8_t   sclpin;            /* SCL pad (AMEBA_PA()/AMEBA_PB() encoding) */
+  uint8_t   sdapin;            /* SDA pad (AMEBA_PA()/AMEBA_PB() encoding) */
+  uint8_t   sclfid;            /* Pin mux function code for the SCL pad */
+  uint8_t   sdafid;            /* Pin mux function code for the SDA pad */
+  mutex_t   lock;              /* Serializes bus access */
+};
+
+/****************************************************************************
+ * Private Function Prototypes
+ ****************************************************************************/
+
+/* SDK fwlib I2C/pin/clock API.  The pin/clock helpers resolve to the on-chip
+ * ROM symbol table; the I2C helpers are compiled into libameba_fwlib.a from
+ * ram_common/ameba_i2c.c (see AMEBA_FWLIB_SRCS).
+ */
+
+extern void RCC_PeriphClockCmd(uint32_t periph, uint32_t clock,
+                               uint8_t newstate);
+extern void Pinmux_Config(uint8_t pin, uint32_t func);
+extern void PAD_PullCtrl(uint8_t pin, uint8_t pull);
+extern void I2C_StructInit(struct ameba_i2c_init_s *init);
+extern void I2C_Init(void *i2cx, struct ameba_i2c_init_s *init);
+extern void I2C_Cmd(void *i2cx, uint8_t newstate);
+extern uint32_t I2C_MasterWrite(void *i2cx, uint8_t *buf, uint32_t len);
+extern uint32_t I2C_MasterRead(void *i2cx, uint8_t *buf, uint32_t len);
+extern uint32_t I2C_MasterRepeatRead(void *i2cx, uint8_t *wbuf,
+                                     uint32_t wlen, uint8_t *rbuf,
+                                     uint32_t rlen);
+extern uint32_t I2C_GetRawINT(void *i2cx);
+extern uint32_t I2C_ClearINT(void *i2cx, uint32_t intrbit);
+extern uint8_t I2C_CheckFlagState(void *i2cx, uint32_t flag);
+extern void I2C_MasterSend(void *i2cx, uint8_t *buf, uint8_t cmd,
+                           uint8_t stop, uint8_t restart);
+
+/* I2C master lower-half operations. */
+
+static int ameba_i2c_transfer(struct i2c_master_s *dev,
+                              struct i2c_msg_s *msgs, int count);
+static int ameba_i2c_setup(struct i2c_master_s *dev);
+static int ameba_i2c_shutdown(struct i2c_master_s *dev);
+
+/****************************************************************************
+ * Private Data
+ ****************************************************************************/
+
+static const struct i2c_ops_s g_ameba_i2c_ops =
+{
+  .transfer = ameba_i2c_transfer,
+  .setup    = ameba_i2c_setup,
+  .shutdown = ameba_i2c_shutdown,
+};
+
+/* Per-controller register base, peripheral function/clock masks and crossbar
+ * pad-mux codes, indexed by controller number and supplied by the per-chip
+ * ameba_i2c_chip.h.  g_i2c_periph feeds the "function" arg and g_i2c_clk
+ * the "clock" argument of RCC_PeriphClockCmd() (equal on this chip, distinct
+ * on others).
+ */
+
+static const uintptr_t g_i2c_base[AMEBA_NI2C]  = AMEBA_I2C_BASES;
+static const uint32_t  g_i2c_periph[AMEBA_NI2C] = AMEBA_I2C_APBPERIPH;
+static const uint32_t  g_i2c_clk[AMEBA_NI2C]   = AMEBA_I2C_APBPERIPH_CLK;
+static const uint8_t   g_i2c_sclfid[AMEBA_NI2C] = AMEBA_I2C_SCLFID;
+static const uint8_t   g_i2c_sdafid[AMEBA_NI2C] = AMEBA_I2C_SDAFID;
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: ameba_i2c_reconfigure
+ *
+ * Description:
+ *   (Re)program the controller for a given target address, addressing mode
+ *   and bus frequency, and enable it.  This is a comparatively heavy DW-IP
+ *   reconfiguration (the block must be disabled to change IC_TAR/speed), so
+ *   it runs only when one of those parameters changes between transfers.
+ *
+ ****************************************************************************/
+
+static void ameba_i2c_reconfigure(struct ameba_i2c_dev_s *priv,
+                                  uint16_t address, uint8_t addrmod,
+                                  uint32_t frequency)
+{
+  struct ameba_i2c_init_s init;
+  void *i2cx = (void *)priv->base;
+
+  memset(&init, 0, sizeof(init));
+  I2C_StructInit(&init);
+
+  init.master   = AMEBA_I2C_MASTER_MODE;
+  init.addrmod  = addrmod;
+  init.ackaddr  = address;
+
+  /* Enable RESTART so a combined write-then-read (I2C_MasterRepeatRead())
+   * emits a true repeated START; the DW IP ignores the RESTART command bit
+   * unless IC_CON.IC_RESTART_EN is set.
+   */
+
+  init.mstrestr = AMEBA_ENABLE;
+
+  if (frequency <= I2C_SPEED_STANDARD)
+    {
+      init.spdmod = AMEBA_I2C_SS_MODE;
+    }
+  else if (frequency <= I2C_SPEED_FAST)
+    {
+      init.spdmod = AMEBA_I2C_FS_MODE;
+    }
+  else
+    {
+      init.spdmod = AMEBA_I2C_HS_MODE;
+    }
+
+  init.clk = frequency / 1000;    /* fwlib expects the bus clock in kHz */
+
+  I2C_Cmd(i2cx, AMEBA_DISABLE);
+  I2C_Init(i2cx, &init);
+  I2C_Cmd(i2cx, AMEBA_ENABLE);
+
+  priv->address   = address;
+  priv->addrmod   = addrmod;
+  priv->frequency = frequency;
+}
+
+/****************************************************************************
+ * Name: ameba_i2c_aborted
+ *
+ * Description:
+ *   Return true if the controller latched a transmit abort during the last
+ *   transfer.  The fwlib master helpers poll IC_STATUS.TFE, which the DW IP
+ *   also sets when it flushes the TX FIFO on an abort, so they can return
+ *   the full byte count even though the address (or a data byte) was never
+ *   acknowledged.  Checking IC_RAW_INTR_STAT.TX_ABRT directly is the only
+ *   reliable way to detect that NAK.  The abort is cleared here (reading
+ *   IC_CLR_TX_ABRT) so the block is left in a clean state.
+ *
+ ****************************************************************************/
+
+static bool ameba_i2c_aborted(struct ameba_i2c_dev_s *priv)
+{
+  void *i2cx = (void *)priv->base;
+
+  if ((I2C_GetRawINT(i2cx) & AMEBA_I2C_TX_ABRT) != 0)
+    {
+      I2C_ClearINT(i2cx, AMEBA_I2C_R_TX_ABRT);
+      return true;
+    }
+
+  return false;
+}
+
+/****************************************************************************
+ * Name: ameba_i2c_writeprefix
+ *
+ * Description:
+ *   Push the bytes of a non-terminating write segment (one flagged
+ *   I2C_M_NOSTOP) into the TX FIFO without asserting STOP.  Because the DW
+ *   IP only issues a STOP when a byte carries the STOP bit, leaving it clear
+ *   keeps the current transaction open so the following segment continues
+ *   without a repeated START -- this is how I2C_M_NOSTOP/I2C_M_NOSTART are
+ *   honoured for chained writes such as the "register address + data" pair
+ *   emitted by "i2c set".  The terminating segment is handled by the tested
+ *   I2C_MasterWrite(), which supplies the STOP and the final TFE wait.
+ *
+ ****************************************************************************/
+
+static int ameba_i2c_writeprefix(struct ameba_i2c_dev_s *priv,
+                                  const uint8_t *buf, uint32_t len)
+{
+  void *i2cx = (void *)priv->base;
+  uint32_t i;
+  uint32_t to;
+
+  for (i = 0; i < len; i++)
+    {
+      uint8_t byte = buf[i];
+
+      /* Wait for a free TX FIFO slot before pushing the next byte. */
+
+      for (to = AMEBA_I2C_FIFO_TIMEOUT;
+           to > 0 && I2C_CheckFlagState(i2cx, AMEBA_I2C_TFNF) == 0; to--);
+
+      if (to == 0)
+        {
+          return -ETIMEDOUT;
+        }
+
+      /* Write, no STOP, no RESTART: leave the transaction open. */
+
+      I2C_MasterSend(i2cx, &byte, 0, 0, 0);
+    }
+
+  return OK;
+}
+
+/****************************************************************************
+ * Name: ameba_i2c_transfer
+ *
+ * Description:
+ *   Run a sequence of I2C messages on the bus.  A write message immediately
+ *   followed by a read message to the same target is fused into a single
+ *   combined transaction with a repeated START.  A write flagged
+ *   I2C_M_NOSTOP is chained to the following write segment(s) without an
+ *   intervening STOP (e.g. register address + data from "i2c set").  Every
+ *   other message is a standalone START..STOP write or read.
+ *
+ ****************************************************************************/
+
+static int ameba_i2c_transfer(struct i2c_master_s *dev,
+                              struct i2c_msg_s *msgs, int count)
+{
+  struct ameba_i2c_dev_s *priv = (struct ameba_i2c_dev_s *)dev;
+  void *i2cx = (void *)priv->base;
+  int ret = OK;
+  int i;
+
+  if (msgs == NULL || count < 1)
+    {
+      return -EINVAL;
+    }
+
+  nxmutex_lock(&priv->lock);
+
+  for (i = 0; i < count; )
+    {
+      struct i2c_msg_s *msg = &msgs[i];
+      uint8_t addrmod = (msg->flags & I2C_M_TEN) ?
+                        AMEBA_I2C_ADDR_10BIT : AMEBA_I2C_ADDR_7BIT;
+      uint32_t freq = (msg->frequency != 0) ? msg->frequency :
+                      AMEBA_I2C_DEFAULT_FREQ;
+      uint32_t done;
+
+      /* Reprogram the controller only when the target, addressing mode or
+       * bus frequency differs from the running configuration.
+       */
+
+      if (msg->addr != priv->address || addrmod != priv->addrmod ||
+          freq != priv->frequency)
+        {
+          ameba_i2c_reconfigure(priv, msg->addr, addrmod, freq);
+        }
+
+      if ((msg->flags & I2C_M_READ) == 0 && (i + 1) < count &&
+          (msgs[i + 1].flags & I2C_M_READ) != 0 &&
+          msgs[i + 1].addr == msg->addr)
+        {
+          /* Write followed by read to the same target: one transaction with
+           * a repeated START between the two phases.
+           */
+
+          struct i2c_msg_s *rd = &msgs[i + 1];
+
+          done = I2C_MasterRepeatRead(i2cx, msg->buffer, msg->length,
+                                      rd->buffer, rd->length);
+          if (ameba_i2c_aborted(priv))
+            {
+              ret = -ENXIO;
+              break;
+            }
+
+          if (done != (uint32_t)rd->length)
+            {
+              ret = -EIO;
+              break;
+            }
+
+          i += 2;
+        }
+      else if ((msg->flags & I2C_M_READ) != 0)
+        {
+          done = I2C_MasterRead(i2cx, msg->buffer, msg->length);
+
+          if (ameba_i2c_aborted(priv))
+            {
+              ret = -ENXIO;
+              break;
+            }
+
+          if (done != (uint32_t)msg->length)
+            {
+              ret = -EIO;
+              break;
+            }
+
+          i += 1;
+        }
+      else if ((msg->flags & I2C_M_NOSTOP) != 0)
+        {
+          /* Leading segment of a chained write (e.g. the register address
+           * before the data in "i2c set"): push its bytes but keep the
+           * transaction open so the next segment continues without a STOP.
+           */
+
+          ret = ameba_i2c_writeprefix(priv, msg->buffer, msg->length);
+          if (ret < 0)
+            {
+              break;
+            }
+
+          i += 1;
+        }
+      else
+        {
+          done = I2C_MasterWrite(i2cx, msg->buffer, msg->length);
+          if (ameba_i2c_aborted(priv))
+            {
+              ret = -ENXIO;
+              break;
+            }
+
+          if (done != (uint32_t)msg->length)
+            {
+              ret = -EIO;
+              break;
+            }
+
+          i += 1;
+        }
+    }
+
+  /* A failed transfer can leave the DW IP with a latched TX abort; force a
+   * full reconfiguration before the next transfer to clear it.
+   */
+
+  if (ret < 0)
+    {
+      priv->frequency = 0;
+      priv->address   = 0xffff;
+    }
+
+  nxmutex_unlock(&priv->lock);
+  return ret;
+}
+
+/****************************************************************************
+ * Name: ameba_i2c_setup
+ *
+ * Description:
+ *   Called by the I2C character driver on the first open.  Gate the
+ *   peripheral clock and route the SCL/SDA pads to this controller; the
+ *   speed/target are programmed lazily on the first transfer.
+ *
+ ****************************************************************************/
+
+static int ameba_i2c_setup(struct i2c_master_s *dev)
+{
+  struct ameba_i2c_dev_s *priv = (struct ameba_i2c_dev_s *)dev;
+
+  RCC_PeriphClockCmd(priv->periph, priv->clk, AMEBA_ENABLE);
+
+  Pinmux_Config(priv->sclpin, priv->sclfid);
+  Pinmux_Config(priv->sdapin, priv->sdafid);
+  PAD_PullCtrl(priv->sclpin, AMEBA_GPIO_PUPD_UP);
+  PAD_PullCtrl(priv->sdapin, AMEBA_GPIO_PUPD_UP);
+
+  /* Force the first transfer to program the controller. */
+
+  priv->frequency = 0;
+  priv->address   = 0xffff;
+  return OK;
+}
+
+/****************************************************************************
+ * Name: ameba_i2c_shutdown
+ *
+ * Description:
+ *   Called by the I2C character driver on the last close.  Disable the
+ *   controller and gate its peripheral clock off.
+ *
+ ****************************************************************************/
+
+static int ameba_i2c_shutdown(struct i2c_master_s *dev)
+{
+  struct ameba_i2c_dev_s *priv = (struct ameba_i2c_dev_s *)dev;
+
+  I2C_Cmd((void *)priv->base, AMEBA_DISABLE);
+  RCC_PeriphClockCmd(priv->periph, priv->clk, AMEBA_DISABLE);
+  return OK;
+}
+
+/****************************************************************************
+ * Public Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: ameba_i2c_register
+ *
+ * Description:
+ *   See ameba_i2c.h.
+ *
+ ****************************************************************************/
+
+int ameba_i2c_register(int bus, uint8_t sclpin, uint8_t sdapin)
+{
+  struct ameba_i2c_dev_s *priv;
+  int ret;
+
+  if (bus < 0 || bus >= AMEBA_NI2C)
+    {
+      return -EINVAL;
+    }
+
+  priv = kmm_zalloc(sizeof(struct ameba_i2c_dev_s));
+  if (priv == NULL)
+    {
+      return -ENOMEM;
+    }
+
+  priv->dev.ops = &g_ameba_i2c_ops;
+  priv->base    = g_i2c_base[bus];
+  priv->periph  = g_i2c_periph[bus];
+  priv->clk     = g_i2c_clk[bus];
+  priv->sclpin  = sclpin;
+  priv->sdapin  = sdapin;
+  priv->sclfid  = g_i2c_sclfid[bus];
+  priv->sdafid  = g_i2c_sdafid[bus];
+  priv->address = 0xffff;
+  nxmutex_init(&priv->lock);
+
+  ret = i2c_register(&priv->dev, bus);
+  if (ret < 0)
+    {
+      _err("ERROR: i2c_register(/dev/i2c%d) failed: %d\n", bus, ret);
+      nxmutex_destroy(&priv->lock);
+      kmm_free(priv);
+    }
+
+  return ret;
+}
diff --git a/arch/arm/src/common/ameba/ameba_i2c.h 
b/arch/arm/src/common/ameba/ameba_i2c.h
new file mode 100644
index 00000000000..1c7435722f9
--- /dev/null
+++ b/arch/arm/src/common/ameba/ameba_i2c.h
@@ -0,0 +1,84 @@
+/****************************************************************************
+ * arch/arm/src/common/ameba/ameba_i2c.h
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * 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_ARM_SRC_COMMON_AMEBA_AMEBA_I2C_H
+#define __ARCH_ARM_SRC_COMMON_AMEBA_AMEBA_I2C_H
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+
+#include <stdint.h>
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+
+/* The Ameba I2C controllers exposed to NuttX as I2C master buses.  The SCL
+ * and SDA pads are given with the same AMEBA_PA()/AMEBA_PB() PinName code
+ * used by the GPIO driver (see ameba_gpio.h); any pad can be routed to an
+ * I2C bus through the pin mux.
+ */
+
+#define AMEBA_I2C0            0
+#define AMEBA_I2C1            1
+
+/****************************************************************************
+ * Public Function Prototypes
+ ****************************************************************************/
+
+#ifdef __cplusplus
+#define EXTERN extern "C"
+extern "C"
+{
+#else
+#define EXTERN extern
+#endif
+
+/****************************************************************************
+ * Name: ameba_i2c_register
+ *
+ * Description:
+ *   Configure one Ameba I2C controller as a master bus and register it with
+ *   the NuttX I2C character driver at /dev/i2cN, where N is the bus number.
+ *
+ * Input Parameters:
+ *   bus    - The controller index, AMEBA_I2C0 or AMEBA_I2C1.  Also used as
+ *            the /dev/i2cN minor number.
+ *   sclpin - The SCL pad, encoded with AMEBA_PA()/AMEBA_PB().
+ *   sdapin - The SDA pad, encoded with AMEBA_PA()/AMEBA_PB().
+ *
+ * Returned Value:
+ *   Zero (OK) on success; a negated errno value on failure.
+ *
+ ****************************************************************************/
+
+int ameba_i2c_register(int bus, uint8_t sclpin, uint8_t sdapin);
+
+#undef EXTERN
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __ARCH_ARM_SRC_COMMON_AMEBA_AMEBA_I2C_H */
diff --git a/arch/arm/src/rtl8721dx/CMakeLists.txt 
b/arch/arm/src/rtl8721dx/CMakeLists.txt
index a0127883991..bcf9450f9a3 100644
--- a/arch/arm/src/rtl8721dx/CMakeLists.txt
+++ b/arch/arm/src/rtl8721dx/CMakeLists.txt
@@ -48,6 +48,10 @@ if(CONFIG_AMEBA_UART)
   list(APPEND SRCS ${AMEBA_COMMON}/ameba_uart.c)
 endif()
 
+if(CONFIG_AMEBA_I2C)
+  list(APPEND SRCS ${AMEBA_COMMON}/ameba_i2c.c)
+endif()
+
 target_include_directories(arch PRIVATE ${AMEBA_COMMON})
 target_sources(arch PRIVATE ${SRCS})
 
diff --git a/arch/arm/src/rtl8721dx/Make.defs b/arch/arm/src/rtl8721dx/Make.defs
index 65420574bf6..fbe728e46f4 100644
--- a/arch/arm/src/rtl8721dx/Make.defs
+++ b/arch/arm/src/rtl8721dx/Make.defs
@@ -58,6 +58,10 @@ ifeq ($(CONFIG_AMEBA_UART),y)
 CHIP_CSRCS += ameba_uart.c
 endif
 
+ifeq ($(CONFIG_AMEBA_I2C),y)
+CHIP_CSRCS += ameba_i2c.c
+endif
+
 ############################################################################
 # Realtek RTL8721Dx SDK integration
 #
diff --git a/arch/arm/src/rtl8721dx/ameba_board.mk 
b/arch/arm/src/rtl8721dx/ameba_board.mk
index 2af16d519f8..1a59dce5832 100644
--- a/arch/arm/src/rtl8721dx/ameba_board.mk
+++ b/arch/arm/src/rtl8721dx/ameba_board.mk
@@ -137,6 +137,15 @@ ifeq ($(CONFIG_AMEBA_UART),y)
 AMEBA_FWLIB_SRCS += $(AMEBA_SOC)/fwlib/ram_common/ameba_uart.c
 endif
 
+# I2C register layer.  Unlike UART, the fwlib I2C API is NOT in ROM: the I2C
+# driver (arch/.../common/ameba/ameba_i2c.c) calls I2C_Init/StructInit/Cmd and
+# I2C_MasterWrite/Read/RepeatRead, all of which are compiled from this RAM
+# source and must be linked in (--gc-sections drops the unused DMA/interrupt
+# helpers).
+ifeq ($(CONFIG_AMEBA_I2C),y)
+AMEBA_FWLIB_SRCS += $(AMEBA_SOC)/fwlib/ram_common/ameba_i2c.c
+endif
+
 # -Wno-int-conversion: the vendored SDK passes NULL to irq_register()'s u32
 # "Data" (interrupt context) argument in many places -- an intentional
 # NULL-as-context idiom.  Silence -Wint-conversion for the SDK fwlib sources
diff --git a/arch/arm/src/rtl8721dx/ameba_i2c_chip.h 
b/arch/arm/src/rtl8721dx/ameba_i2c_chip.h
new file mode 100644
index 00000000000..7d5bf209a27
--- /dev/null
+++ b/arch/arm/src/rtl8721dx/ameba_i2c_chip.h
@@ -0,0 +1,103 @@
+/****************************************************************************
+ * arch/arm/src/rtl8721dx/ameba_i2c_chip.h
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * 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_ARM_SRC_RTL8721DX_AMEBA_I2C_CHIP_H
+#define __ARCH_ARM_SRC_RTL8721DX_AMEBA_I2C_CHIP_H
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+
+#include <stdint.h>
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+
+/* Per-chip I2C wiring for RTL8721DX (amebadplus).  The shared driver
+ * (arch/arm/src/common/ameba/ameba_i2c.c) includes this header to learn how
+ * many I2C controllers the chip exposes and, for each, its register base,
+ * peripheral-clock masks and crossbar pad-mux codes.  It also learns the
+ * chip's I2C_InitTypeDef layout through AMEBA_I2C_HAS_DMA_FIELDS.
+ *
+ * Contract for the other Ameba chips (amebalite / amebasmart / amebagreen2 /
+ * RTL8720F): supply a same-named header on the chip include path with the
+ * macros below.  What differs per chip, from the fwlib audit:
+ *
+ *   1. Controller count and bases: 2 controllers on amebadplus / amebalite /
+ *      amebagreen2 / RTL8720F, 3 on amebasmart (its I2C0 lives in the LP
+ *      domain).  The bases below are the NON-secure peripheral aliases; see
+ *      the note in ameba_i2c.c on why the secure alias must not be used.
+ *
+ *   2. APBPeriph "function" and "clock" masks are two separate lists because
+ *      RCC_PeriphClockCmd() takes them as distinct arguments.  They are
+ *      equal on every current chip, but amebasmart encodes the bits
+ *      differently (bit25/26/27, group bit30=0) versus (bit30|bit10/11)
+ *      here, so each chip must supply its own values.
+ *
+ *   3. Pad mux: amebadplus / amebalite / amebagreen2 / RTL8720F use a
+ *      crossbar with a distinct function code per SCL/SDA signal (supplied
+ *      as two lists).  amebasmart instead has a single generic
+ *      PINMUX_FUNCTION_I2C code (7) shared by every I2C pad; that chip fills
+ *      both the SCL and SDA lists with 7 and the driver needs no change.
+ *
+ *   4. I2C_InitTypeDef layout: amebadplus / amebagreen2 / RTL8720F carry
+ *      three DMA request-level fields (I2CTxDMARqLv / I2CRxDMARqLv /
+ *      I2CDMAMod) between I2CFilter and I2CAckAddr1 (21 u32 fields);
+ *      amebalite and amebasmart omit them (18 fields).  Define
+ *      AMEBA_I2C_HAS_DMA_FIELDS to 1 when present so the driver's mirror
+ *      struct stays byte-for-byte identical to the fwlib struct that
+ *      I2C_StructInit()/I2C_Init() use.
+ */
+
+#define AMEBA_NI2C                2
+
+/* NON-secure I2C register bases (I2C0_REG_BASE / I2C1_REG_BASE). */
+
+#define AMEBA_I2C_BASES           { 0x41108000ul, 0x4110a000ul }
+
+/* APBPeriph_I2Cx (function) and APBPeriph_I2Cx_CLOCK masks.  Equal on this
+ * chip; kept as two lists so chips where they differ can supply both.
+ */
+
+#define AMEBA_I2C_APBPERIPH       \
+        { (((uint32_t)1 << 30) | ((uint32_t)1 << 10)), \
+          (((uint32_t)1 << 30) | ((uint32_t)1 << 11)) }
+
+#define AMEBA_I2C_APBPERIPH_CLK   \
+        { (((uint32_t)1 << 30) | ((uint32_t)1 << 10)), \
+          (((uint32_t)1 << 30) | ((uint32_t)1 << 11)) }
+
+/* Crossbar pad-mux function codes (PINMUX_FUNCTION_I2Cx_SCL/SDA), indexed by
+ * controller.  On a chip with one generic I2C code, set both to that code.
+ */
+
+#define AMEBA_I2C_SCLFID          { 48, 50 }  /* I2C0_SCL, I2C1_SCL */
+#define AMEBA_I2C_SDAFID          { 49, 51 }  /* I2C0_SDA, I2C1_SDA */
+
+/* The amebadplus I2C_InitTypeDef carries the DMA request-level fields. */
+
+#define AMEBA_I2C_HAS_DMA_FIELDS  1
+
+#endif /* __ARCH_ARM_SRC_RTL8721DX_AMEBA_I2C_CHIP_H */
diff --git a/boards/arm/rtl8721dx/pke8721daf/configs/i2c/defconfig 
b/boards/arm/rtl8721dx/pke8721daf/configs/i2c/defconfig
new file mode 100644
index 00000000000..6dc0c975d42
--- /dev/null
+++ b/boards/arm/rtl8721dx/pke8721daf/configs/i2c/defconfig
@@ -0,0 +1,51 @@
+#
+# This file is autogenerated: PLEASE DO NOT EDIT IT.
+#
+# You can use "make menuconfig" to make any modifications to the installed 
.config file.
+# You can then do "make savedefconfig" to generate a new defconfig file that 
includes your
+# modifications.
+#
+# CONFIG_DEBUG_WARN is not set
+CONFIG_AMEBA_I2C=y
+CONFIG_ARCH="arm"
+CONFIG_ARCH_BOARD="pke8721daf"
+CONFIG_ARCH_BOARD_PKE8721DAF=y
+CONFIG_ARCH_CHIP="rtl8721dx"
+CONFIG_ARCH_CHIP_RTL8721DX=y
+CONFIG_ARCH_INTERRUPTSTACK=2048
+CONFIG_ARCH_STACKDUMP=y
+CONFIG_ARMV8M_SYSTICK=y
+CONFIG_BUILTIN=y
+CONFIG_DEBUG_ASSERTIONS=y
+CONFIG_DEBUG_FEATURES=y
+CONFIG_DEBUG_FULLOPT=y
+CONFIG_DEBUG_SYMBOLS=y
+CONFIG_DEFAULT_TASK_STACKSIZE=4096
+CONFIG_EXAMPLES_HELLO=y
+CONFIG_FS_PROCFS=y
+CONFIG_FS_TMPFS=y
+CONFIG_IDLETHREAD_STACKSIZE=4096
+CONFIG_INIT_ENTRYPOINT="nsh_main"
+CONFIG_LIBC_MEMFD_ERROR=y
+CONFIG_MM_DEFAULT_ALIGNMENT=32
+CONFIG_NSH_BUILTIN_APPS=y
+CONFIG_NSH_FILEIOSIZE=512
+CONFIG_NSH_READLINE=y
+CONFIG_PREALLOC_TIMERS=4
+CONFIG_RAM_SIZE=294912
+CONFIG_RAM_START=0x20020000
+CONFIG_RR_INTERVAL=200
+CONFIG_RTL8721DX_FLASH_FS=y
+CONFIG_SCHED_HPWORK=y
+CONFIG_SCHED_HPWORKPRIORITY=192
+CONFIG_SCHED_LPWORK=y
+CONFIG_STACK_COLORATION=y
+CONFIG_START_DAY=16
+CONFIG_START_MONTH=6
+CONFIG_START_YEAR=2026
+CONFIG_SYSTEM_I2CTOOL=y
+CONFIG_SYSTEM_NSH=y
+CONFIG_SYSTEM_NSH_STACKSIZE=2500
+CONFIG_TIMER=y
+CONFIG_TIMER_ARCH=y
+CONFIG_USEC_PER_TICK=1000
diff --git a/boards/arm/rtl8721dx/pke8721daf/src/CMakeLists.txt 
b/boards/arm/rtl8721dx/pke8721daf/src/CMakeLists.txt
index a513559f9c7..2ccfdc647a5 100644
--- a/boards/arm/rtl8721dx/pke8721daf/src/CMakeLists.txt
+++ b/boards/arm/rtl8721dx/pke8721daf/src/CMakeLists.txt
@@ -30,9 +30,15 @@ if(CONFIG_AMEBA_UART)
   list(APPEND SRCS rtl8721dx_uart.c)
 endif()
 
+if(CONFIG_AMEBA_I2C)
+  list(APPEND SRCS rtl8721dx_i2c.c)
+endif()
+
 target_sources(board PRIVATE ${SRCS})
 
-if(CONFIG_AMEBA_GPIO OR CONFIG_AMEBA_UART)
+if(CONFIG_AMEBA_GPIO
+   OR CONFIG_AMEBA_UART
+   OR CONFIG_AMEBA_I2C)
   # The board pin/UART tables pull in the shared driver's public headers from
   # arch/arm/src/common/ameba/, not on the default board include path.
   target_include_directories(board
diff --git a/boards/arm/rtl8721dx/pke8721daf/src/Makefile 
b/boards/arm/rtl8721dx/pke8721daf/src/Makefile
index 60e3a2ca1fe..ed80ab1a32e 100644
--- a/boards/arm/rtl8721dx/pke8721daf/src/Makefile
+++ b/boards/arm/rtl8721dx/pke8721daf/src/Makefile
@@ -42,4 +42,13 @@ CSRCS += rtl8721dx_uart.c
 CFLAGS += 
${INCDIR_PREFIX}$(TOPDIR)$(DELIM)arch$(DELIM)arm$(DELIM)src$(DELIM)common$(DELIM)ameba
 endif
 
+ifeq ($(CONFIG_AMEBA_I2C),y)
+CSRCS += rtl8721dx_i2c.c
+
+# The board I2C table pulls in the shared driver's public header from
+# arch/arm/src/common/ameba/, which is not on the default board include path.
+
+CFLAGS += 
${INCDIR_PREFIX}$(TOPDIR)$(DELIM)arch$(DELIM)arm$(DELIM)src$(DELIM)common$(DELIM)ameba
+endif
+
 include $(TOPDIR)/boards/Board.mk
diff --git a/boards/arm/rtl8721dx/pke8721daf/src/rtl8721dx_bringup.c 
b/boards/arm/rtl8721dx/pke8721daf/src/rtl8721dx_bringup.c
index 0bd11927d27..ab034d08ff7 100644
--- a/boards/arm/rtl8721dx/pke8721daf/src/rtl8721dx_bringup.c
+++ b/boards/arm/rtl8721dx/pke8721daf/src/rtl8721dx_bringup.c
@@ -128,6 +128,16 @@ int rtl8721dx_bringup(void)
     }
 #endif
 
+#ifdef CONFIG_AMEBA_I2C
+  /* Register the board's I2C master buses at /dev/i2cN. */
+
+  ret = rtl8721dx_i2c_initialize();
+  if (ret < 0)
+    {
+      syslog(LOG_ERR, "ERROR: rtl8721dx_i2c_initialize failed: %d\n", ret);
+    }
+#endif
+
   /* Install the inter-core HW IPC-semaphore RTOS hooks LAST -- after all the
    * flash / WHC bring-up above, and just before this (board_late_initialize)
    * path returns and nx_start() hands off to the init task.
diff --git a/boards/arm/rtl8721dx/pke8721daf/src/rtl8721dx_i2c.c 
b/boards/arm/rtl8721dx/pke8721daf/src/rtl8721dx_i2c.c
new file mode 100644
index 00000000000..a73c61ff2a2
--- /dev/null
+++ b/boards/arm/rtl8721dx/pke8721daf/src/rtl8721dx_i2c.c
@@ -0,0 +1,102 @@
+/****************************************************************************
+ * boards/arm/rtl8721dx/pke8721daf/src/rtl8721dx_i2c.c
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * 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/param.h>
+#include <syslog.h>
+
+#include "ameba_gpio.h"
+#include "ameba_i2c.h"
+#include "rtl8721dx_pke8721daf.h"
+
+#ifdef CONFIG_AMEBA_I2C
+
+/****************************************************************************
+ * Private Types
+ ****************************************************************************/
+
+/* One entry per I2C bus exposed to NuttX at /dev/i2cN.  The SCL/SDA pads are
+ * examples used by the `i2c` config (system/i2c i2ctool) -- any pad can be
+ * routed to an I2C controller through the pin mux, so adjust them to match
+ * your board's wiring.
+ */
+
+struct rtl8721dx_i2c_s
+{
+  int     bus;                  /* Controller index (AMEBA_I2C0/AMEBA_I2C1) */
+  uint8_t sclpin;               /* SCL pad (AMEBA_PA()/AMEBA_PB() encoding) */
+  uint8_t sdapin;               /* SDA pad (AMEBA_PA()/AMEBA_PB() encoding) */
+};
+
+/****************************************************************************
+ * Private Data
+ ****************************************************************************/
+
+static const struct rtl8721dx_i2c_s g_i2c_buses[] =
+{
+  {
+    AMEBA_I2C0, AMEBA_PB(20), AMEBA_PB(21)
+  },
+  {
+    AMEBA_I2C1, AMEBA_PB(18), AMEBA_PB(19)
+  },
+};
+
+/****************************************************************************
+ * Public Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: rtl8721dx_i2c_initialize
+ *
+ * Description:
+ *   Register the board's I2C master buses at /dev/i2cN.
+ *
+ ****************************************************************************/
+
+int rtl8721dx_i2c_initialize(void)
+{
+  int ret;
+  int i;
+
+  for (i = 0; i < (int)nitems(g_i2c_buses); i++)
+    {
+      ret = ameba_i2c_register(g_i2c_buses[i].bus, g_i2c_buses[i].sclpin,
+                               g_i2c_buses[i].sdapin);
+      if (ret < 0)
+        {
+          syslog(LOG_ERR,
+                 "ERROR: ameba_i2c_register(/dev/i2c%d) failed: %d\n",
+                 g_i2c_buses[i].bus, ret);
+          return ret;
+        }
+    }
+
+  return OK;
+}
+
+#endif /* CONFIG_AMEBA_I2C */
diff --git a/boards/arm/rtl8721dx/pke8721daf/src/rtl8721dx_pke8721daf.h 
b/boards/arm/rtl8721dx/pke8721daf/src/rtl8721dx_pke8721daf.h
index 8d57a1e09b3..52be521d3d9 100644
--- a/boards/arm/rtl8721dx/pke8721daf/src/rtl8721dx_pke8721daf.h
+++ b/boards/arm/rtl8721dx/pke8721daf/src/rtl8721dx_pke8721daf.h
@@ -95,6 +95,19 @@ int rtl8721dx_gpio_initialize(void);
 int rtl8721dx_uart_initialize(void);
 #endif
 
+#ifdef CONFIG_AMEBA_I2C
+/****************************************************************************
+ * Name: rtl8721dx_i2c_initialize
+ *
+ * Description:
+ *   Register the board's I2C master buses at /dev/i2cN
+ *   (boards/arm/rtl8721dx/pke8721daf/src/rtl8721dx_i2c.c).
+ *
+ ****************************************************************************/
+
+int rtl8721dx_i2c_initialize(void);
+#endif
+
 #ifdef CONFIG_RTL8721DX_FLASH_FS
 /****************************************************************************
  * Name: ameba_flash_fs_initialize
diff --git a/tools/nxstyle.c b/tools/nxstyle.c
index 2e9ae212af8..f5ddef85124 100644
--- a/tools/nxstyle.c
+++ b/tools/nxstyle.c
@@ -275,6 +275,7 @@ static const char *g_white_prefix[] =
   "FLASH_",
   "GPIO_",
   "Get_OSC131_",      /* Get_OSC131_STATE — Ameba SDK RTC accessor */
+  "I2C_",             /* I2C_Init, I2C_MasterWrite, I2C_InitTypeDef, etc. */
   "IPC_",
   "LOGUART_",
   "OSC2M_",

Reply via email to