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

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

commit f8d61600220c8e6e1eb521e6d6b359e39b98849e
Author: dechao_gong <[email protected]>
AuthorDate: Tue Jul 14 16:07:58 2026 +0800

    arch/arm/rtl8721dx: add shared Ameba UART driver.
    
    Add a shared Ameba high-speed UART driver on top of the GPIO driver's
    common/ameba/ infrastructure, exposing UART0/UART1 through the NuttX
    serial upper half.
    
    - arch/arm/src/common/ameba/ameba_uart.c/.h: serial lower-half driver
      built on the SDK fwlib UART register layer (ROM symbol table).  RX/TX
      dispatch through NuttX-native interrupts; TERMIOS get/set supported.
      The pins are muxed to the direction-specific UART crossbar function
      codes (TXD/RXD per controller) required by the amebadplus pinmux, and
      RX is pulled high through the SDK ROM.
    - arch/arm/src/common/ameba/Kconfig: AMEBA_UART option (selects SERIAL
      and ARCH_HAVE_SERIAL_TERMIOS) plus RX/TX buffer-size knobs.
    - arch/arm/src/rtl8721dx: wire ameba_uart.c into the Make/CMake builds
      and pull the fwlib ram_common UART table into the fwlib link set.
    - boards/arm/rtl8721dx/pke8721daf: board UART port table registering
      UART0 at /dev/ttyS1 (PB18/PB19, 115200), bring-up hook, and a uart
      NSH config with the serialrx/serialblaster examples.
    - Documentation: describe the driver and the uart board config.
    
    Verified on hardware (PKE8721DAF): pinmux routing, TX/RX and interrupt
    paths, and the TERMIOS ioctl path via loopback.
    
    Co-Authored-By: Claude Opus 4.8 <[email protected]>
    Signed-off-by: dechao_gong <[email protected]>
---
 .../arm/rtl8721dx/boards/pke8721daf/index.rst      |  22 +
 arch/arm/src/common/ameba/Kconfig                  |  33 +
 arch/arm/src/common/ameba/ameba_gpio.h             |   2 +
 arch/arm/src/common/ameba/ameba_uart.c             | 700 +++++++++++++++++++++
 .../common/ameba/{ameba_gpio.h => ameba_uart.h}    |  51 +-
 arch/arm/src/rtl8721dx/CMakeLists.txt              |   4 +
 arch/arm/src/rtl8721dx/Make.defs                   |   4 +
 arch/arm/src/rtl8721dx/ameba_board.mk              |   9 +
 .../rtl8721dx/pke8721daf/configs/uart/defconfig    |  55 ++
 boards/arm/rtl8721dx/pke8721daf/src/CMakeLists.txt |   8 +-
 boards/arm/rtl8721dx/pke8721daf/src/Makefile       |   9 +
 .../rtl8721dx/pke8721daf/src/rtl8721dx_bringup.c   |  10 +
 .../pke8721daf/src/rtl8721dx_pke8721daf.h          |  14 +
 .../arm/rtl8721dx/pke8721daf/src/rtl8721dx_uart.c  | 105 ++++
 tools/nxstyle.c                                    |   3 +
 15 files changed, 1000 insertions(+), 29 deletions(-)

diff --git a/Documentation/platforms/arm/rtl8721dx/boards/pke8721daf/index.rst 
b/Documentation/platforms/arm/rtl8721dx/boards/pke8721daf/index.rst
index 0570a3afd92..3d630cc6621 100644
--- a/Documentation/platforms/arm/rtl8721dx/boards/pke8721daf/index.rst
+++ b/Documentation/platforms/arm/rtl8721dx/boards/pke8721daf/index.rst
@@ -35,6 +35,8 @@ Supported in this NuttX port:
 * DHCP client (STA) and DHCP server (SoftAP)
 * GPIO pins exposed as ``/dev/gpioN`` character devices (input, output and
   interrupt), driven directly on the SDK fwlib register layer
+* General-purpose UARTs exposed as ``/dev/ttySN`` serial devices, driven
+  directly on the SDK fwlib register layer
 
 Buttons and LEDs
 ================
@@ -78,6 +80,26 @@ Pins are encoded with the ``AMEBA_PA()`` / ``AMEBA_PB()`` 
helpers from
 ``arch/arm/src/common/ameba/ameba_gpio.h`` (port A/B, pin 0-31), matching the
 Ameba SDK ``PinName`` layout.
 
+uart
+----
+
+Minimal NSH with the general-purpose UART driver and the ``serialrx`` /
+``serialblaster`` examples enabled (no Wi-Fi). The LOG-UART owns the console
+and ``/dev/ttyS0``, so the board registers UART0 from its table (see
+``boards/arm/rtl8721dx/pke8721daf/src/rtl8721dx_uart.c``) as ``/dev/ttyS1`` at
+115200 8N1. Edit that table -- controller, TX/RX pads and baud -- to match a
+board's wiring. The TX/RX pads use the same ``AMEBA_PA()`` / ``AMEBA_PB()``
+encoding as the GPIO table; the driver muxes them to the UART function and
+pulls RX high through the SDK ROM. Exercise the port with the examples (loop TX
+back to RX, or wire it to a host serial adapter)::
+
+    nsh> serialblaster    # stream a test pattern out /dev/ttyS1
+    nsh> serialrx         # receive and dump bytes from /dev/ttyS1
+
+The line format can be changed at runtime through ``tcsetattr()`` (the config
+enables ``CONFIG_SERIAL_TERMIOS``). UART2 is shared with Bluetooth and is not
+exposed by the driver.
+
 Wi-Fi
 =====
 
diff --git a/arch/arm/src/common/ameba/Kconfig 
b/arch/arm/src/common/ameba/Kconfig
index 5cf8da49e0b..b3391e049cf 100644
--- a/arch/arm/src/common/ameba/Kconfig
+++ b/arch/arm/src/common/ameba/Kconfig
@@ -22,4 +22,37 @@ config AMEBA_GPIO
                fwlib register layer and dispatches pin interrupts through the 
ROM
                GPIO interrupt handler.
 
+config AMEBA_UART
+       bool "UART"
+       default n
+       select SERIAL
+       select ARCH_HAVE_SERIAL_TERMIOS
+       ---help---
+               Expose the Ameba high-speed UARTs (UART0/UART1) through the 
NuttX
+               serial upper half.  The LOG-UART owns the console and 
/dev/ttyS0, so
+               the board registers these general-purpose ports at /dev/ttyS1 
and up
+               in its bring-up code, giving the TX/RX pads and baud for each.
+
+               The driver (arch/arm/src/common/ameba/ameba_uart.c) sits on the 
SDK
+               fwlib register layer and dispatches RX/TX through NuttX-native
+               interrupts.
+
+if AMEBA_UART
+
+config AMEBA_UART_RXBUFSIZE
+       int "UART receive buffer size"
+       default 256
+       ---help---
+               Size, in bytes, of the receive buffer allocated for each 
registered
+               Ameba UART port.
+
+config AMEBA_UART_TXBUFSIZE
+       int "UART transmit buffer size"
+       default 256
+       ---help---
+               Size, in bytes, of the transmit buffer allocated for each 
registered
+               Ameba UART port.
+
+endif # AMEBA_UART
+
 endmenu # Ameba Peripheral Support
diff --git a/arch/arm/src/common/ameba/ameba_gpio.h 
b/arch/arm/src/common/ameba/ameba_gpio.h
index 3dc0bd32147..bc5f04b6d76 100644
--- a/arch/arm/src/common/ameba/ameba_gpio.h
+++ b/arch/arm/src/common/ameba/ameba_gpio.h
@@ -83,7 +83,9 @@ extern "C"
  *
  ****************************************************************************/
 
+#ifdef CONFIG_DEV_GPIO
 int ameba_gpio_register(int minor, uint8_t pin, enum gpio_pintype_e pintype);
+#endif
 
 #undef EXTERN
 #ifdef __cplusplus
diff --git a/arch/arm/src/common/ameba/ameba_uart.c 
b/arch/arm/src/common/ameba/ameba_uart.c
new file mode 100644
index 00000000000..8eaf5f0e706
--- /dev/null
+++ b/arch/arm/src/common/ameba/ameba_uart.c
@@ -0,0 +1,700 @@
+/****************************************************************************
+ * arch/arm/src/common/ameba/ameba_uart.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 serial lower half for the Realtek Ameba high-speed UARTs (UART0 and
+ * UART1; UART2 is shared with Bluetooth and is not wired here).  The
+ * LOG-UART owns the console and /dev/ttyS0 (arch/.../ameba_loguart.c); this
+ * driver registers the general-purpose UARTs from board bring-up, so they
+ * appear as /dev/ttyS1 and up.
+ *
+ * The controller is programmed through the SDK fwlib UART API.  NuttX runs
+ * on the KM4 core in the SECURE state, so the fwlib routines resolve to the
+ * secure register aliases via TrustZone_IsSecure() and reach the UART, the
+ * PINMUX pad mux and the pull control without stall.  Every symbol used here
+ * (UART_Init, UART_SetBaud, UART_CharPut/Get, Pinmux_Config, PAD_PullCtrl,
+ * RCC_PeriphClockCmd, ...) resolves to the on-chip ROM symbol table; the
+ * fwlib data tables that ROM code indexes (UART_DEV_TABLE, APBPeriph_UARTx)
+ * are compiled into libameba_fwlib.a (see AMEBA_FWLIB_SRCS).  To keep the
+ * vendor headers out of the NuttX include world, the few fwlib symbols and
+ * the UART_InitTypeDef layout used here are declared locally below rather
+ * than pulled in from <ameba_uart.h>.
+ *
+ * Interrupt dispatch stays NuttX-native: each UART's NVIC vector is owned by
+ * NuttX (irq_attach), and the ISR drains RX / refills TX through the stock
+ * uart_recvchars()/uart_xmitchars() upper-half helpers.
+ */
+
+#include <nuttx/config.h>
+
+#include <stdint.h>
+#include <stdbool.h>
+#include <errno.h>
+#include <debug.h>
+
+#include <nuttx/irq.h>
+#include <nuttx/arch.h>
+#include <nuttx/kmalloc.h>
+#include <nuttx/spinlock.h>
+#include <nuttx/serial/serial.h>
+
+#ifdef CONFIG_SERIAL_TERMIOS
+#  include <termios.h>
+#endif
+
+#include "arm_internal.h"
+#include "ameba_uart.h"
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+
+/* Per-controller register base, peripheral clock mask and NuttX IRQ number.
+ * The driver source is shared by every Ameba ARM chip; each chip's
+ * <arch/irq.h> (pulled in by <nuttx/irq.h>) names its own vectors, so map
+ * them onto chip-agnostic aliases here.
+ */
+
+#if defined(CONFIG_ARCH_CHIP_RTL8721DX)
+#  define AMEBA_IRQ_UART0       RTL8721DX_IRQ_UART0
+#  define AMEBA_IRQ_UART1       RTL8721DX_IRQ_UART1
+#else
+#  error "Ameba UART: controller IRQ numbers not defined for this chip"
+#endif
+
+#define AMEBA_UART0_BASE        0x4100c000ul
+#define AMEBA_UART1_BASE        0x4100d000ul
+
+/* APBPeriph_UARTx / APBPeriph_UARTx_CLOCK (sysreg_lsys.h). */
+
+#define AMEBA_APBPERIPH_UART0   (((uint32_t)1 << 30) | ((uint32_t)1 << 6))
+#define AMEBA_APBPERIPH_UART1   (((uint32_t)1 << 30) | ((uint32_t)1 << 7))
+
+/* Pin mux function (ameba_pinmux.h) and pull control (ameba_gpio.h).  RX is
+ * held high while idle so a floating line is not read as a start bit.
+ *
+ * On this SoC (amebadplus) the pad mux is a crossbar: the generic
+ * PINMUX_FUNCTION_UART code is not enough -- each pad must be routed with a
+ * direction-specific function code so the crossbar knows which internal UART
+ * signal to drive onto it (see the SDK's per-chip branch in the raw UART
+ * example).  The codes are indexed by controller in g_uart_txfid/rxfid.
+ */
+
+#define AMEBA_PINMUX_UART0_TXD  19    /* PINMUX_FUNCTION_UART0_TXD          */
+#define AMEBA_PINMUX_UART0_RXD  20    /* PINMUX_FUNCTION_UART0_RXD          */
+#define AMEBA_PINMUX_UART1_TXD  23    /* PINMUX_FUNCTION_UART1_TXD          */
+#define AMEBA_PINMUX_UART1_RXD  24    /* PINMUX_FUNCTION_UART1_RXD          */
+#define AMEBA_GPIO_PUPD_UP      0x2   /* GPIO_PuPd_UP                       */
+
+/* Mirror of the fwlib UART_InitTypeDef field values (ameba_uart.h). */
+
+#define AMEBA_WLS_7BITS         0x0   /* RUART_WLS_7BITS                    */
+#define AMEBA_WLS_8BITS         0x1   /* RUART_WLS_8BITS                    */
+
+#define AMEBA_STOP_BIT_1        0x0   /* RUART_STOP_BIT_1                   */
+#define AMEBA_STOP_BIT_2        0x1   /* RUART_STOP_BIT_2                   */
+
+#define AMEBA_PARITY_DISABLE    0x0   /* RUART_PARITY_DISABLE               */
+#define AMEBA_PARITY_ENABLE     0x1   /* RUART_PARITY_ENABLE                */
+
+#define AMEBA_ODD_PARITY        0x0   /* RUART_ODD_PARITY                   */
+#define AMEBA_EVEN_PARITY       0x1   /* RUART_EVEN_PARITY                  */
+
+#define AMEBA_RX_FIFOTRIG_1     0x0   /* UART_RX_FIFOTRIG_LEVEL_1BYTES      */
+
+/* Interrupt enable register (IER) bits. */
+
+#define AMEBA_UART_INT_ERBI     ((uint32_t)1 << 0)  /* Rx data available    */
+#define AMEBA_UART_INT_ETBEI    ((uint32_t)1 << 1)  /* Tx FIFO empty        */
+#define AMEBA_UART_INT_ETOI     ((uint32_t)1 << 5)  /* Rx timeout           */
+
+/* Line status register (LSR) bits. */
+
+#define AMEBA_UART_LSR_TX_EMPTY ((uint32_t)1 << 5)  /* Tx FIFO empty        */
+
+/* Second/third argument to fwlib "state" style APIs. */
+
+#define AMEBA_DISABLE           0x0
+#define AMEBA_ENABLE            0x1
+
+/* Number of general-purpose controllers this driver can address. */
+
+#define AMEBA_NUART             2
+
+/* Local parity encoding (priv->parity). */
+
+#define AMEBA_PARITY_NONE       0
+#define AMEBA_PARITY_ODD        1
+#define AMEBA_PARITY_EVEN       2
+
+/****************************************************************************
+ * Private Types
+ ****************************************************************************/
+
+/* Layout-compatible mirror of the fwlib UART_InitTypeDef (all u32, same
+ * order); passed by address to UART_Init().
+ */
+
+struct ameba_uart_init_s
+{
+  uint32_t dmamodectrl;        /* DmaModeCtrl     */
+  uint32_t wordlen;            /* WordLen         */
+  uint32_t stopbit;            /* StopBit         */
+  uint32_t parity;             /* Parity          */
+  uint32_t paritytype;         /* ParityType      */
+  uint32_t stickparity;        /* StickParity     */
+  uint32_t flowcontrol;        /* FlowControl     */
+  uint32_t rxfifotriglevel;    /* RxFifoTrigLevel */
+  uint32_t rxerreportctrl;     /* RxErReportCtrl  */
+  uint32_t rxtimeoutcnt;       /* RxTimeOutCnt    */
+};
+
+struct ameba_uart_dev_s
+{
+  struct uart_dev_s dev;       /* Serial upper-half device (must be first) */
+  uintptr_t base;              /* UART register base address */
+  int       irq;               /* NuttX interrupt vector */
+  uint32_t  clk;               /* APBPeriph mask for RCC_PeriphClockCmd() */
+  uint32_t  baud;              /* Baud rate */
+  uint8_t   txpin;             /* TX pad (AMEBA_PA()/AMEBA_PB() encoding) */
+  uint8_t   rxpin;             /* RX pad (AMEBA_PA()/AMEBA_PB() encoding) */
+  uint8_t   txfid;             /* Pin mux function code for the TX pad */
+  uint8_t   rxfid;             /* Pin mux function code for the RX pad */
+  uint8_t   bits;              /* Data bits (7 or 8) */
+  uint8_t   parity;            /* AMEBA_PARITY_NONE / _ODD / _EVEN */
+  bool      stop2;             /* True: 2 stop bits, false: 1 */
+  bool      txint;             /* True while the TX interrupt is unmasked */
+  spinlock_t lock;             /* Serializes IER updates vs the ISR */
+};
+
+/****************************************************************************
+ * Private Function Prototypes
+ ****************************************************************************/
+
+/* SDK fwlib UART/pin/clock API (all resolve to the ROM symbol table). */
+
+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 UART_DeInit(void *uartx);
+extern void UART_StructInit(struct ameba_uart_init_s *init);
+extern void UART_Init(void *uartx, struct ameba_uart_init_s *init);
+extern void UART_SetBaud(void *uartx, uint32_t baud);
+extern void UART_RxCmd(void *uartx, uint32_t newstate);
+extern uint32_t UART_Writable(void *uartx);
+extern uint32_t UART_Readable(void *uartx);
+extern void UART_CharPut(void *uartx, uint8_t txdata);
+extern void UART_CharGet(void *uartx, uint8_t *rxbyte);
+extern void UART_INTConfig(void *uartx, uint32_t uart_it, uint32_t newstate);
+extern uint32_t UART_LineStatusGet(void *uartx);
+
+/* Serial lower-half operations. */
+
+static int  ameba_uart_setup(struct uart_dev_s *dev);
+static void ameba_uart_shutdown(struct uart_dev_s *dev);
+static int  ameba_uart_attach(struct uart_dev_s *dev);
+static void ameba_uart_detach(struct uart_dev_s *dev);
+static int  ameba_uart_ioctl(struct file *filep, int cmd,
+                             unsigned long arg);
+static int  ameba_uart_receive(struct uart_dev_s *dev, unsigned int *status);
+static void ameba_uart_rxint(struct uart_dev_s *dev, bool enable);
+static bool ameba_uart_rxavailable(struct uart_dev_s *dev);
+static void ameba_uart_send(struct uart_dev_s *dev, int ch);
+static void ameba_uart_txint(struct uart_dev_s *dev, bool enable);
+static bool ameba_uart_txready(struct uart_dev_s *dev);
+static bool ameba_uart_txempty(struct uart_dev_s *dev);
+
+/****************************************************************************
+ * Private Data
+ ****************************************************************************/
+
+static const struct uart_ops_s g_ameba_uart_ops =
+{
+  .setup       = ameba_uart_setup,
+  .shutdown    = ameba_uart_shutdown,
+  .attach      = ameba_uart_attach,
+  .detach      = ameba_uart_detach,
+  .ioctl       = ameba_uart_ioctl,
+  .receive     = ameba_uart_receive,
+  .rxint       = ameba_uart_rxint,
+  .rxavailable = ameba_uart_rxavailable,
+  .send        = ameba_uart_send,
+  .txint       = ameba_uart_txint,
+  .txready     = ameba_uart_txready,
+  .txempty     = ameba_uart_txempty,
+};
+
+/* Per-controller register base, peripheral clock and IRQ, indexed by the
+ * AMEBA_UART0/AMEBA_UART1 controller number.
+ */
+
+static const uintptr_t g_uart_base[AMEBA_NUART] =
+{
+  AMEBA_UART0_BASE,
+  AMEBA_UART1_BASE,
+};
+
+static const uint32_t g_uart_clk[AMEBA_NUART] =
+{
+  AMEBA_APBPERIPH_UART0,
+  AMEBA_APBPERIPH_UART1,
+};
+
+static const int g_uart_irq[AMEBA_NUART] =
+{
+  AMEBA_IRQ_UART0,
+  AMEBA_IRQ_UART1,
+};
+
+/* Direction-specific pad mux function codes, indexed by controller. */
+
+static const uint8_t g_uart_txfid[AMEBA_NUART] =
+{
+  AMEBA_PINMUX_UART0_TXD,
+  AMEBA_PINMUX_UART1_TXD,
+};
+
+static const uint8_t g_uart_rxfid[AMEBA_NUART] =
+{
+  AMEBA_PINMUX_UART0_RXD,
+  AMEBA_PINMUX_UART1_RXD,
+};
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: ameba_uart_configure
+ *
+ * Description:
+ *   Gate the peripheral clock, route the TX/RX pads to the UART function and
+ *   program the line format and baud through fwlib UART_Init()/SetBaud(),
+ *   then enable the receiver.  Called from setup() and on a termios change.
+ *
+ ****************************************************************************/
+
+static void ameba_uart_configure(struct ameba_uart_dev_s *priv)
+{
+  struct ameba_uart_init_s init;
+  void *uartx = (void *)priv->base;
+
+  /* Gate on the UART peripheral clock (idempotent). */
+
+  RCC_PeriphClockCmd(priv->clk, priv->clk, AMEBA_ENABLE);
+
+  /* Route the pads to this controller's TX/RX signals through the crossbar;
+   * hold RX high while idle.
+   */
+
+  Pinmux_Config(priv->txpin, priv->txfid);
+  Pinmux_Config(priv->rxpin, priv->rxfid);
+  PAD_PullCtrl(priv->rxpin, AMEBA_GPIO_PUPD_UP);
+
+  /* Start from the fwlib defaults, then apply the requested line format. */
+
+  UART_StructInit(&init);
+
+  init.wordlen         = (priv->bits == 7) ?
+                         AMEBA_WLS_7BITS : AMEBA_WLS_8BITS;
+  init.stopbit         = priv->stop2 ? AMEBA_STOP_BIT_2 : AMEBA_STOP_BIT_1;
+  init.flowcontrol     = AMEBA_DISABLE;
+  init.rxfifotriglevel = AMEBA_RX_FIFOTRIG_1;
+
+  if (priv->parity == AMEBA_PARITY_NONE)
+    {
+      init.parity     = AMEBA_PARITY_DISABLE;
+    }
+  else
+    {
+      init.parity     = AMEBA_PARITY_ENABLE;
+      init.paritytype = (priv->parity == AMEBA_PARITY_ODD) ?
+                        AMEBA_ODD_PARITY : AMEBA_EVEN_PARITY;
+    }
+
+  UART_Init(uartx, &init);
+  UART_SetBaud(uartx, priv->baud);
+  UART_RxCmd(uartx, AMEBA_ENABLE);
+}
+
+/****************************************************************************
+ * Name: ameba_uart_interrupt
+ *
+ * Description:
+ *   Common UART interrupt handler.  Drains the RX FIFO into the receive
+ *   buffer and, while the TX interrupt is unmasked, refills the TX FIFO from
+ *   the transmit buffer.
+ *
+ ****************************************************************************/
+
+static int ameba_uart_interrupt(int irq, void *context, void *arg)
+{
+  struct uart_dev_s *dev = (struct uart_dev_s *)arg;
+  struct ameba_uart_dev_s *priv = (struct ameba_uart_dev_s *)dev;
+  void *uartx = (void *)priv->base;
+
+  UNUSED(irq);
+  UNUSED(context);
+
+  /* Reading the line status register clears any latched RX error bits. */
+
+  UART_LineStatusGet(uartx);
+
+  if (UART_Readable(uartx))
+    {
+      uart_recvchars(dev);
+    }
+
+  if (priv->txint && UART_Writable(uartx))
+    {
+      uart_xmitchars(dev);
+    }
+
+  return OK;
+}
+
+/****************************************************************************
+ * Name: ameba_uart_setup
+ ****************************************************************************/
+
+static int ameba_uart_setup(struct uart_dev_s *dev)
+{
+  ameba_uart_configure((struct ameba_uart_dev_s *)dev);
+  return OK;
+}
+
+/****************************************************************************
+ * Name: ameba_uart_shutdown
+ ****************************************************************************/
+
+static void ameba_uart_shutdown(struct uart_dev_s *dev)
+{
+  struct ameba_uart_dev_s *priv = (struct ameba_uart_dev_s *)dev;
+
+  ameba_uart_rxint(dev, false);
+  ameba_uart_txint(dev, false);
+  UART_DeInit((void *)priv->base);
+}
+
+/****************************************************************************
+ * Name: ameba_uart_attach
+ ****************************************************************************/
+
+static int ameba_uart_attach(struct uart_dev_s *dev)
+{
+  struct ameba_uart_dev_s *priv = (struct ameba_uart_dev_s *)dev;
+  int ret;
+
+  ret = irq_attach(priv->irq, ameba_uart_interrupt, dev);
+  if (ret == OK)
+    {
+      up_enable_irq(priv->irq);
+    }
+
+  return ret;
+}
+
+/****************************************************************************
+ * Name: ameba_uart_detach
+ ****************************************************************************/
+
+static void ameba_uart_detach(struct uart_dev_s *dev)
+{
+  struct ameba_uart_dev_s *priv = (struct ameba_uart_dev_s *)dev;
+
+  up_disable_irq(priv->irq);
+  irq_detach(priv->irq);
+}
+
+/****************************************************************************
+ * Name: ameba_uart_ioctl
+ ****************************************************************************/
+
+static int ameba_uart_ioctl(struct file *filep, int cmd, unsigned long arg)
+{
+#ifdef CONFIG_SERIAL_TERMIOS
+  struct inode *inode = filep->f_inode;
+  struct uart_dev_s *dev = inode->i_private;
+  struct ameba_uart_dev_s *priv = (struct ameba_uart_dev_s *)dev;
+
+  switch (cmd)
+    {
+      case TCGETS:
+        {
+          struct termios *termiosp = (struct termios *)(uintptr_t)arg;
+          tcflag_t ccflag;
+
+          if (termiosp == NULL)
+            {
+              return -EINVAL;
+            }
+
+          ccflag = (priv->bits == 7) ? CS7 : CS8;
+          if (priv->parity != AMEBA_PARITY_NONE)
+            {
+              ccflag |= PARENB;
+              if (priv->parity == AMEBA_PARITY_ODD)
+                {
+                  ccflag |= PARODD;
+                }
+            }
+
+          if (priv->stop2)
+            {
+              ccflag |= CSTOPB;
+            }
+
+          termiosp->c_cflag = ccflag;
+          cfsetispeed(termiosp, priv->baud);
+          cfsetospeed(termiosp, priv->baud);
+          return OK;
+        }
+
+      case TCSETS:
+        {
+          struct termios *termiosp = (struct termios *)(uintptr_t)arg;
+          irqstate_t flags;
+
+          if (termiosp == NULL)
+            {
+              return -EINVAL;
+            }
+
+          flags = spin_lock_irqsave(&priv->lock);
+
+          priv->bits  = ((termiosp->c_cflag & CSIZE) == CS7) ? 7 : 8;
+          priv->stop2 = (termiosp->c_cflag & CSTOPB) != 0;
+
+          if ((termiosp->c_cflag & PARENB) == 0)
+            {
+              priv->parity = AMEBA_PARITY_NONE;
+            }
+          else if ((termiosp->c_cflag & PARODD) != 0)
+            {
+              priv->parity = AMEBA_PARITY_ODD;
+            }
+          else
+            {
+              priv->parity = AMEBA_PARITY_EVEN;
+            }
+
+          priv->baud = cfgetispeed(termiosp);
+
+          ameba_uart_configure(priv);
+
+          spin_unlock_irqrestore(&priv->lock, flags);
+          return OK;
+        }
+
+      default:
+        return -ENOTTY;
+    }
+#else
+  UNUSED(filep);
+  UNUSED(cmd);
+  UNUSED(arg);
+  return -ENOTTY;
+#endif
+}
+
+/****************************************************************************
+ * Name: ameba_uart_receive
+ ****************************************************************************/
+
+static int ameba_uart_receive(struct uart_dev_s *dev, unsigned int *status)
+{
+  struct ameba_uart_dev_s *priv = (struct ameba_uart_dev_s *)dev;
+  uint8_t rxbyte = 0;
+
+  *status = 0;
+  UART_CharGet((void *)priv->base, &rxbyte);
+  return rxbyte;
+}
+
+/****************************************************************************
+ * Name: ameba_uart_rxint
+ ****************************************************************************/
+
+static void ameba_uart_rxint(struct uart_dev_s *dev, bool enable)
+{
+  struct ameba_uart_dev_s *priv = (struct ameba_uart_dev_s *)dev;
+  irqstate_t flags;
+
+  flags = spin_lock_irqsave(&priv->lock);
+
+  /* Enable both the RX-trigger and RX-timeout sources so that bytes still
+   * held below the FIFO trigger level are delivered promptly.
+   */
+
+  UART_INTConfig((void *)priv->base,
+                 AMEBA_UART_INT_ERBI | AMEBA_UART_INT_ETOI,
+                 enable ? AMEBA_ENABLE : AMEBA_DISABLE);
+
+  spin_unlock_irqrestore(&priv->lock, flags);
+}
+
+/****************************************************************************
+ * Name: ameba_uart_rxavailable
+ ****************************************************************************/
+
+static bool ameba_uart_rxavailable(struct uart_dev_s *dev)
+{
+  struct ameba_uart_dev_s *priv = (struct ameba_uart_dev_s *)dev;
+
+  return UART_Readable((void *)priv->base) != 0;
+}
+
+/****************************************************************************
+ * Name: ameba_uart_send
+ ****************************************************************************/
+
+static void ameba_uart_send(struct uart_dev_s *dev, int ch)
+{
+  struct ameba_uart_dev_s *priv = (struct ameba_uart_dev_s *)dev;
+
+  UART_CharPut((void *)priv->base, (uint8_t)ch);
+}
+
+/****************************************************************************
+ * Name: ameba_uart_txint
+ *
+ * Description:
+ *   Unmask/mask the TX-FIFO-empty interrupt.  Enabling it while the FIFO is
+ *   already empty asserts the interrupt at once, so the ISR pumps the first
+ *   bytes; uart_xmitchars() re-disables it once the transmit buffer drains.
+ *
+ ****************************************************************************/
+
+static void ameba_uart_txint(struct uart_dev_s *dev, bool enable)
+{
+  struct ameba_uart_dev_s *priv = (struct ameba_uart_dev_s *)dev;
+  irqstate_t flags;
+
+  flags = spin_lock_irqsave(&priv->lock);
+
+  priv->txint = enable;
+  UART_INTConfig((void *)priv->base, AMEBA_UART_INT_ETBEI,
+                 enable ? AMEBA_ENABLE : AMEBA_DISABLE);
+
+  spin_unlock_irqrestore(&priv->lock, flags);
+}
+
+/****************************************************************************
+ * Name: ameba_uart_txready
+ ****************************************************************************/
+
+static bool ameba_uart_txready(struct uart_dev_s *dev)
+{
+  struct ameba_uart_dev_s *priv = (struct ameba_uart_dev_s *)dev;
+
+  return UART_Writable((void *)priv->base) != 0;
+}
+
+/****************************************************************************
+ * Name: ameba_uart_txempty
+ ****************************************************************************/
+
+static bool ameba_uart_txempty(struct uart_dev_s *dev)
+{
+  struct ameba_uart_dev_s *priv = (struct ameba_uart_dev_s *)dev;
+
+  return (UART_LineStatusGet((void *)priv->base) &
+          AMEBA_UART_LSR_TX_EMPTY) != 0;
+}
+
+/****************************************************************************
+ * Public Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: ameba_uart_register
+ *
+ * Description:
+ *   See ameba_uart.h.
+ *
+ ****************************************************************************/
+
+int ameba_uart_register(const char *path, int uart, uint8_t txpin,
+                        uint8_t rxpin, uint32_t baud)
+{
+  struct ameba_uart_dev_s *priv;
+  char *rxbuffer;
+  char *txbuffer;
+  int ret;
+
+  if (uart < 0 || uart >= AMEBA_NUART || path == NULL || baud == 0)
+    {
+      return -EINVAL;
+    }
+
+  priv = kmm_zalloc(sizeof(struct ameba_uart_dev_s));
+  if (priv == NULL)
+    {
+      return -ENOMEM;
+    }
+
+  rxbuffer = kmm_malloc(CONFIG_AMEBA_UART_RXBUFSIZE);
+  txbuffer = kmm_malloc(CONFIG_AMEBA_UART_TXBUFSIZE);
+  if (rxbuffer == NULL || txbuffer == NULL)
+    {
+      ret = -ENOMEM;
+      goto errout;
+    }
+
+  priv->base   = g_uart_base[uart];
+  priv->clk    = g_uart_clk[uart];
+  priv->irq    = g_uart_irq[uart];
+  priv->txpin  = txpin;
+  priv->rxpin  = rxpin;
+  priv->txfid  = g_uart_txfid[uart];
+  priv->rxfid  = g_uart_rxfid[uart];
+  priv->baud   = baud;
+  priv->bits   = 8;
+  priv->parity = AMEBA_PARITY_NONE;
+  priv->stop2  = false;
+  spin_lock_init(&priv->lock);
+
+  priv->dev.recv.size   = CONFIG_AMEBA_UART_RXBUFSIZE;
+  priv->dev.recv.buffer = rxbuffer;
+  priv->dev.xmit.size   = CONFIG_AMEBA_UART_TXBUFSIZE;
+  priv->dev.xmit.buffer = txbuffer;
+  priv->dev.ops         = &g_ameba_uart_ops;
+
+  ret = uart_register(path, &priv->dev);
+  if (ret < 0)
+    {
+      _err("ERROR: uart_register(%s) failed: %d\n", path, ret);
+      goto errout;
+    }
+
+  return OK;
+
+errout:
+  kmm_free(rxbuffer);
+  kmm_free(txbuffer);
+  kmm_free(priv);
+  return ret;
+}
diff --git a/arch/arm/src/common/ameba/ameba_gpio.h 
b/arch/arm/src/common/ameba/ameba_uart.h
similarity index 59%
copy from arch/arm/src/common/ameba/ameba_gpio.h
copy to arch/arm/src/common/ameba/ameba_uart.h
index 3dc0bd32147..c69691473dd 100644
--- a/arch/arm/src/common/ameba/ameba_gpio.h
+++ b/arch/arm/src/common/ameba/ameba_uart.h
@@ -1,5 +1,5 @@
 /****************************************************************************
- * arch/arm/src/common/ameba/ameba_gpio.h
+ * arch/arm/src/common/ameba/ameba_uart.h
  *
  * SPDX-License-Identifier: Apache-2.0
  *
@@ -20,8 +20,8 @@
  *
  ****************************************************************************/
 
-#ifndef __ARCH_ARM_SRC_COMMON_AMEBA_AMEBA_GPIO_H
-#define __ARCH_ARM_SRC_COMMON_AMEBA_AMEBA_GPIO_H
+#ifndef __ARCH_ARM_SRC_COMMON_AMEBA_AMEBA_UART_H
+#define __ARCH_ARM_SRC_COMMON_AMEBA_AMEBA_UART_H
 
 /****************************************************************************
  * Included Files
@@ -31,27 +31,19 @@
 
 #include <stdint.h>
 
-#include <nuttx/ioexpander/gpio.h>
-
 /****************************************************************************
  * Pre-processor Definitions
  ****************************************************************************/
 
-/* A pin is identified with the same encoding the Ameba SDK uses for its
- * "PinName": bits[7:5] select the GPIO port and bits[4:0] the pin within
- * the port.  Boards build their pin table from these helpers so the shared
- * driver never needs to know a board's wiring.
+/* The Ameba high-speed UART controllers exposed to NuttX.  UART2 is shared
+ * with Bluetooth on this family and is not wired for general serial use, so
+ * only UART0 and UART1 are registered by boards.  TX/RX pins are given with
+ * the same AMEBA_PA()/AMEBA_PB() PinName encoding used by the GPIO driver
+ * (see ameba_gpio.h); any pad can be routed to a UART through the pin mux.
  */
 
-#define AMEBA_PORT_A          0
-#define AMEBA_PORT_B          1
-
-#define AMEBA_PIN(port, num)  ((uint8_t)(((port) << 5) | ((num) & 0x1f)))
-
-/* Convenience: the RTL8721Dx / RTL8720F break out ports A and B. */
-
-#define AMEBA_PA(num)         AMEBA_PIN(AMEBA_PORT_A, (num))
-#define AMEBA_PB(num)         AMEBA_PIN(AMEBA_PORT_B, (num))
+#define AMEBA_UART0           0
+#define AMEBA_UART1           1
 
 /****************************************************************************
  * Public Function Prototypes
@@ -66,28 +58,33 @@ extern "C"
 #endif
 
 /****************************************************************************
- * Name: ameba_gpio_register
+ * Name: ameba_uart_register
  *
  * Description:
- *   Configure a single Ameba GPIO pin and register it with the NuttX GPIO
- *   (ioexpander) upper half at /dev/gpioN, where N is the given minor.
+ *   Configure one Ameba high-speed UART and register it with the NuttX
+ *   serial upper half at the given device path (e.g. "/dev/ttyS1").  The
+ *   LOG-UART console is owned separately (arch/.../ameba_loguart.c) and
+ *   already occupies /dev/ttyS0, so boards register the general-purpose
+ *   UARTs starting at /dev/ttyS1.
  *
  * Input Parameters:
- *   minor   - The /dev/gpioN minor number.
- *   pin     - The pin, encoded with AMEBA_PIN() / AMEBA_PA() / AMEBA_PB().
- *   pintype - One of enum gpio_pintype_e (input, output or one of the
- *             interrupt pin types).
+ *   path  - The serial device path to register (e.g. "/dev/ttyS1").
+ *   uart  - The controller index, AMEBA_UART0 or AMEBA_UART1.
+ *   txpin - The TX pad, encoded with AMEBA_PA()/AMEBA_PB().
+ *   rxpin - The RX pad, encoded with AMEBA_PA()/AMEBA_PB().
+ *   baud  - The initial baud rate.
  *
  * Returned Value:
  *   Zero (OK) on success; a negated errno value on failure.
  *
  ****************************************************************************/
 
-int ameba_gpio_register(int minor, uint8_t pin, enum gpio_pintype_e pintype);
+int ameba_uart_register(const char *path, int uart, uint8_t txpin,
+                        uint8_t rxpin, uint32_t baud);
 
 #undef EXTERN
 #ifdef __cplusplus
 }
 #endif
 
-#endif /* __ARCH_ARM_SRC_COMMON_AMEBA_AMEBA_GPIO_H */
+#endif /* __ARCH_ARM_SRC_COMMON_AMEBA_AMEBA_UART_H */
diff --git a/arch/arm/src/rtl8721dx/CMakeLists.txt 
b/arch/arm/src/rtl8721dx/CMakeLists.txt
index 7bd0ded54f2..a0127883991 100644
--- a/arch/arm/src/rtl8721dx/CMakeLists.txt
+++ b/arch/arm/src/rtl8721dx/CMakeLists.txt
@@ -44,6 +44,10 @@ if(CONFIG_AMEBA_GPIO)
   list(APPEND SRCS ${AMEBA_COMMON}/ameba_gpio.c)
 endif()
 
+if(CONFIG_AMEBA_UART)
+  list(APPEND SRCS ${AMEBA_COMMON}/ameba_uart.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 02c51ef4c12..65420574bf6 100644
--- a/arch/arm/src/rtl8721dx/Make.defs
+++ b/arch/arm/src/rtl8721dx/Make.defs
@@ -54,6 +54,10 @@ ifeq ($(CONFIG_AMEBA_GPIO),y)
 CHIP_CSRCS += ameba_gpio.c
 endif
 
+ifeq ($(CONFIG_AMEBA_UART),y)
+CHIP_CSRCS += ameba_uart.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 d7a61fa4a94..2af16d519f8 100644
--- a/arch/arm/src/rtl8721dx/ameba_board.mk
+++ b/arch/arm/src/rtl8721dx/ameba_board.mk
@@ -128,6 +128,15 @@ ifeq ($(CONFIG_AMEBA_GPIO),y)
 AMEBA_FWLIB_SRCS += $(AMEBA_SOC)/fwlib/ram_common/ameba_gpio.c
 endif
 
+# UART register layer.  The UART driver (arch/.../common/ameba/ameba_uart.c)
+# calls the fwlib UART API, all of which resolves to the ROM symbol table; but
+# the ROM routines index the fwlib data tables (UART_DEV_TABLE, 
APBPeriph_UARTx)
+# which live in this RAM source and must be compiled in (--gc-sections drops
+# the unused DMA/monitor helpers).
+ifeq ($(CONFIG_AMEBA_UART),y)
+AMEBA_FWLIB_SRCS += $(AMEBA_SOC)/fwlib/ram_common/ameba_uart.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/boards/arm/rtl8721dx/pke8721daf/configs/uart/defconfig 
b/boards/arm/rtl8721dx/pke8721daf/configs/uart/defconfig
new file mode 100644
index 00000000000..88f26e2cedb
--- /dev/null
+++ b/boards/arm/rtl8721dx/pke8721daf/configs/uart/defconfig
@@ -0,0 +1,55 @@
+#
+# 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_UART=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_EXAMPLES_SERIALBLASTER=y
+CONFIG_EXAMPLES_SERIALBLASTER_DEVPATH="/dev/ttyS1"
+CONFIG_EXAMPLES_SERIALRX=y
+CONFIG_EXAMPLES_SERIALRX_DEVPATH="/dev/ttyS1"
+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_SERIAL_TERMIOS=y
+CONFIG_STACK_COLORATION=y
+CONFIG_START_DAY=16
+CONFIG_START_MONTH=6
+CONFIG_START_YEAR=2026
+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 b1cc8e75471..a513559f9c7 100644
--- a/boards/arm/rtl8721dx/pke8721daf/src/CMakeLists.txt
+++ b/boards/arm/rtl8721dx/pke8721daf/src/CMakeLists.txt
@@ -26,10 +26,14 @@ if(CONFIG_AMEBA_GPIO)
   list(APPEND SRCS rtl8721dx_gpio.c)
 endif()
 
+if(CONFIG_AMEBA_UART)
+  list(APPEND SRCS rtl8721dx_uart.c)
+endif()
+
 target_sources(board PRIVATE ${SRCS})
 
-if(CONFIG_AMEBA_GPIO)
-  # The board pin table pulls in the shared driver's public header from
+if(CONFIG_AMEBA_GPIO OR CONFIG_AMEBA_UART)
+  # 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
                              PRIVATE ${NUTTX_DIR}/arch/arm/src/common/ameba)
diff --git a/boards/arm/rtl8721dx/pke8721daf/src/Makefile 
b/boards/arm/rtl8721dx/pke8721daf/src/Makefile
index be0c63a792d..60e3a2ca1fe 100644
--- a/boards/arm/rtl8721dx/pke8721daf/src/Makefile
+++ b/boards/arm/rtl8721dx/pke8721daf/src/Makefile
@@ -33,4 +33,13 @@ CSRCS += rtl8721dx_gpio.c
 CFLAGS += 
${INCDIR_PREFIX}$(TOPDIR)$(DELIM)arch$(DELIM)arm$(DELIM)src$(DELIM)common$(DELIM)ameba
 endif
 
+ifeq ($(CONFIG_AMEBA_UART),y)
+CSRCS += rtl8721dx_uart.c
+
+# The board UART 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 2532afcaf8f..0bd11927d27 100644
--- a/boards/arm/rtl8721dx/pke8721daf/src/rtl8721dx_bringup.c
+++ b/boards/arm/rtl8721dx/pke8721daf/src/rtl8721dx_bringup.c
@@ -118,6 +118,16 @@ int rtl8721dx_bringup(void)
     }
 #endif
 
+#ifdef CONFIG_AMEBA_UART
+  /* Register the board's general-purpose UART ports at /dev/ttySN. */
+
+  ret = rtl8721dx_uart_initialize();
+  if (ret < 0)
+    {
+      syslog(LOG_ERR, "ERROR: rtl8721dx_uart_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_pke8721daf.h 
b/boards/arm/rtl8721dx/pke8721daf/src/rtl8721dx_pke8721daf.h
index 85d13543d0e..8d57a1e09b3 100644
--- a/boards/arm/rtl8721dx/pke8721daf/src/rtl8721dx_pke8721daf.h
+++ b/boards/arm/rtl8721dx/pke8721daf/src/rtl8721dx_pke8721daf.h
@@ -81,6 +81,20 @@ int rtl8721dx_wifi_initialize(void);
 int rtl8721dx_gpio_initialize(void);
 #endif
 
+#ifdef CONFIG_AMEBA_UART
+/****************************************************************************
+ * Name: rtl8721dx_uart_initialize
+ *
+ * Description:
+ *   Register the board's general-purpose UART ports with the NuttX serial
+ *   upper half
+ *   (boards/arm/rtl8721dx/pke8721daf/src/rtl8721dx_uart.c).
+ *
+ ****************************************************************************/
+
+int rtl8721dx_uart_initialize(void);
+#endif
+
 #ifdef CONFIG_RTL8721DX_FLASH_FS
 /****************************************************************************
  * Name: ameba_flash_fs_initialize
diff --git a/boards/arm/rtl8721dx/pke8721daf/src/rtl8721dx_uart.c 
b/boards/arm/rtl8721dx/pke8721daf/src/rtl8721dx_uart.c
new file mode 100644
index 00000000000..a3a9fc39a89
--- /dev/null
+++ b/boards/arm/rtl8721dx/pke8721daf/src/rtl8721dx_uart.c
@@ -0,0 +1,105 @@
+/****************************************************************************
+ * boards/arm/rtl8721dx/pke8721daf/src/rtl8721dx_uart.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_uart.h"
+#include "rtl8721dx_pke8721daf.h"
+
+#ifdef CONFIG_AMEBA_UART
+
+/****************************************************************************
+ * Private Types
+ ****************************************************************************/
+
+/* One entry per general-purpose UART exposed to NuttX.  The LOG-UART owns
+ * the console and /dev/ttyS0, so these ports are registered starting at
+ * /dev/ttyS1 in the order listed below.  The TX/RX pads are examples used by
+ * the `uart` config (examples/serialrx / serialblaster) -- any pad can be
+ * routed to a UART through the pin mux, so adjust them to match your board's
+ * wiring.
+ */
+
+struct rtl8721dx_uart_s
+{
+  const char *path;             /* Device path (/dev/ttyS1, ...) */
+  int         uart;             /* Controller index (AMEBA_UART0/AMEBA_UART1) 
*/
+  uint8_t     txpin;            /* TX pad (AMEBA_PA()/AMEBA_PB() encoding) */
+  uint8_t     rxpin;            /* RX pad (AMEBA_PA()/AMEBA_PB() encoding) */
+  uint32_t    baud;             /* Initial baud rate */
+};
+
+/****************************************************************************
+ * Private Data
+ ****************************************************************************/
+
+static const struct rtl8721dx_uart_s g_uart_ports[] =
+{
+  {
+    "/dev/ttyS1", AMEBA_UART0, AMEBA_PB(18), AMEBA_PB(19), 115200
+  },
+};
+
+/****************************************************************************
+ * Public Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: rtl8721dx_uart_initialize
+ *
+ * Description:
+ *   Register the board's general-purpose UART ports with the NuttX serial
+ *   upper half.
+ *
+ ****************************************************************************/
+
+int rtl8721dx_uart_initialize(void)
+{
+  int ret;
+  size_t i;
+
+  for (i = 0; i < nitems(g_uart_ports); i++)
+    {
+      ret = ameba_uart_register(g_uart_ports[i].path, g_uart_ports[i].uart,
+                                g_uart_ports[i].txpin, g_uart_ports[i].rxpin,
+                                g_uart_ports[i].baud);
+      if (ret < 0)
+        {
+          syslog(LOG_ERR,
+                 "ERROR: ameba_uart_register(%s) failed: %d\n",
+                 g_uart_ports[i].path, ret);
+          return ret;
+        }
+    }
+
+  return OK;
+}
+
+#endif /* CONFIG_AMEBA_UART */
diff --git a/tools/nxstyle.c b/tools/nxstyle.c
index ae827641677..e471d064127 100644
--- a/tools/nxstyle.c
+++ b/tools/nxstyle.c
@@ -278,10 +278,13 @@ static const char *g_white_prefix[] =
   "OSC2M_",
   "OSC4M_",
   "OSC131K_",
+  "PAD_",
+  "Pinmux_",
   "RCC_",
   "RTCIO_",
   "SYSCFG_",
   "SYSTIMER_",
+  "UART_",
   "SystemCoreClock",  /* SystemCoreClock, SystemCoreClockUpdate */
   "cmse_",            /* ARM CMSE TrustZone intrinsics (arm_cmse.h) */
   "MQTTErrors",       /* apps/tools/netutils/mqttc/MQTT-C/include/mqtt.h */


Reply via email to