arch/arm/rtl8721dx: add shared Ameba SPI driver

Add a shared NuttX SPI master lower-half for the Realtek Ameba SPI
controllers (SPI0/SPI1) in arch/arm/src/common/ameba, driven through the
SDK fwlib in polling mode with full-duplex exchange and a software chip
select.  Per-chip wiring (controller count, register bases, clock masks,
crossbar pad-mux codes and the fwlib SSI_InitTypeDef layout) lives in
arch/arm/src/rtl8721dx/ameba_spi_chip.h so a port to the other Ameba
chips only supplies a same-named header.

Each controller registers as /dev/spiN from pke8721daf bring-up through
the stock SPI character driver; a dedicated `spi` defconfig drives the
spitool for validation.

Assisted-by: Claude <noreply@anthropic.com>
Signed-off-by: dechao_gong <dechao_gong@realsil.com.cn>
This commit is contained in:
dechao_gong
2026-08-14 11:36:37 -03:00
committed by Alan C. Assis
parent e5a0c4ae88
commit 7dbf782e5b
15 changed files with 1100 additions and 1 deletions
@@ -39,6 +39,8 @@ Supported in this NuttX port:
directly on the SDK fwlib register layer
* I2C master buses exposed as ``/dev/i2cN`` character devices, driven directly
on the SDK fwlib register layer
* SPI master buses exposed as ``/dev/spiN`` character devices, driven directly
on the SDK fwlib register layer
Buttons and LEDs
================
@@ -116,6 +118,22 @@ external pull-ups on SCL/SDA. Probe a bus with the tool::
nsh> i2c dev -b 0 0x03 0x77 # scan /dev/i2c0 for devices
spi
---
Minimal NSH with the SPI master driver and the ``spi`` tool
(``system/spi``) enabled (no Wi-Fi). The board registers two buses from its
table (see ``boards/arm/rtl8721dx/pke8721daf/src/rtl8721dx_spi.c``): SPI0 at
``/dev/spi0`` with CLK/MOSI/MISO/CS on PA14/PA15/PA16/PA17 and SPI1 at
``/dev/spi1`` with CLK/MOSI/MISO on PB18/PB19/PB20 and a software chip-select
on PB21. Edit that table -- controller, CLK/MOSI/MISO pads and CS pad -- to
match a board's wiring; the pads use the same ``AMEBA_PA()`` / ``AMEBA_PB()``
encoding as the GPIO table and are muxed to the SPI function through the SDK
ROM, while the chip-select is driven as a plain GPIO. Exercise a bus with the
tool::
nsh> spi exch -b 1 -x 4 deadbeef # full-duplex transfer on /dev/spi1
Wi-Fi
=====
+16
View File
@@ -69,4 +69,20 @@ config AMEBA_I2C
fwlib register layer and drives the DesignWare I2C block in polling
mode.
config AMEBA_SPI
bool "SPI"
default n
select SPI
select SPI_DRIVER
select SPI_EXCHANGE
---help---
Expose the Ameba SPI controllers (SPI0/SPI1) as NuttX SPI master
buses at /dev/spiN. The board selects which controller is used and
its CLK/MOSI/MISO/CS pads in its bring-up code; the chip select is
driven as a plain GPIO (software CS).
The driver (arch/arm/src/common/ameba/ameba_spi.c) sits on the SDK
fwlib register layer and drives the DesignWare SSI block in polling
mode.
endmenu # Ameba Peripheral Support
File diff suppressed because it is too large Load Diff
+92
View File
@@ -0,0 +1,92 @@
/****************************************************************************
* arch/arm/src/common/ameba/ameba_spi.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_SPI_H
#define __ARCH_ARM_SRC_COMMON_AMEBA_AMEBA_SPI_H
/****************************************************************************
* Included Files
****************************************************************************/
#include <nuttx/config.h>
#include <stdint.h>
#include <nuttx/spi/spi.h>
/****************************************************************************
* Pre-processor Definitions
****************************************************************************/
/* The Ameba SPI (DesignWare SSI) controllers exposed to NuttX as SPI master
* buses. The CLK/MOSI/MISO/CS 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 SPI bus through the pin mux, and the chip select
* is driven as a plain GPIO (software CS) so any pad may serve as CS.
*/
#define AMEBA_SPI0 0
#define AMEBA_SPI1 1
/****************************************************************************
* Public Function Prototypes
****************************************************************************/
#ifdef __cplusplus
#define EXTERN extern "C"
extern "C"
{
#else
#define EXTERN extern
#endif
/****************************************************************************
* Name: ameba_spi_register
*
* Description:
* Configure one Ameba SPI (DesignWare SSI) controller as a master bus,
* registering it with the NuttX SPI character driver at /dev/spiN, where N
* is the bus number. The chip select is driven as a GPIO (software CS).
*
* Input Parameters:
* bus - The controller index, AMEBA_SPI0 or AMEBA_SPI1. Also used as
* the /dev/spiN minor number.
* clkpin - The SCLK pad, encoded with AMEBA_PA()/AMEBA_PB().
* mosipin - The MOSI pad, encoded with AMEBA_PA()/AMEBA_PB().
* misopin - The MISO pad, encoded with AMEBA_PA()/AMEBA_PB().
* cspin - The chip-select pad, driven as an active-low GPIO output.
*
* Returned Value:
* The SPI master lower half on success; NULL on failure.
*
****************************************************************************/
struct spi_dev_s *ameba_spi_register(int bus, uint8_t clkpin,
uint8_t mosipin, uint8_t misopin,
uint8_t cspin);
#undef EXTERN
#ifdef __cplusplus
}
#endif
#endif /* __ARCH_ARM_SRC_COMMON_AMEBA_AMEBA_SPI_H */
+4
View File
@@ -52,6 +52,10 @@ if(CONFIG_AMEBA_I2C)
list(APPEND SRCS ${AMEBA_COMMON}/ameba_i2c.c)
endif()
if(CONFIG_AMEBA_SPI)
list(APPEND SRCS ${AMEBA_COMMON}/ameba_spi.c)
endif()
target_include_directories(arch PRIVATE ${AMEBA_COMMON})
target_sources(arch PRIVATE ${SRCS})
+4
View File
@@ -62,6 +62,10 @@ ifeq ($(CONFIG_AMEBA_I2C),y)
CHIP_CSRCS += ameba_i2c.c
endif
ifeq ($(CONFIG_AMEBA_SPI),y)
CHIP_CSRCS += ameba_spi.c
endif
############################################################################
# Realtek RTL8721Dx SDK integration
#
+9
View File
@@ -146,6 +146,15 @@ ifeq ($(CONFIG_AMEBA_I2C),y)
AMEBA_FWLIB_SRCS += $(AMEBA_SOC)/fwlib/ram_common/ameba_i2c.c
endif
# SPI (DesignWare SSI) register layer. Like I2C, the fwlib SSI API is NOT in
# ROM: the SPI driver (arch/.../common/ameba/ameba_spi.c) calls SSI_Init/
# StructInit/Cmd and SSI_Writeable/Readable/WriteData/ReadData, all compiled
# from this RAM source and linked in (--gc-sections drops the unused DMA/
# interrupt helpers).
ifeq ($(CONFIG_AMEBA_SPI),y)
AMEBA_FWLIB_SRCS += $(AMEBA_SOC)/fwlib/ram_common/ameba_spi.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
+113
View File
@@ -0,0 +1,113 @@
/****************************************************************************
* arch/arm/src/rtl8721dx/ameba_spi_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_SPI_CHIP_H
#define __ARCH_ARM_SRC_RTL8721DX_AMEBA_SPI_CHIP_H
/****************************************************************************
* Included Files
****************************************************************************/
#include <nuttx/config.h>
#include <stdint.h>
/****************************************************************************
* Pre-processor Definitions
****************************************************************************/
/* Per-chip SPI wiring for RTL8721DX (amebadplus). The shared driver
* (arch/arm/src/common/ameba/ameba_spi.c) includes this header to learn how
* many SPI (DesignWare SSI) controllers the chip exposes and, for each, its
* register base, peripheral-clock masks and crossbar pad-mux codes.
*
* 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: amebadplus exposes two high-speed SSI
* masters, SPI0 and SPI1, in the PERI_HCLK domain. The bases below are
* the NON-secure peripheral aliases (0x4012xxxx); the secure aliases
* (0x5012xxxx) must not be used from the non-secure world.
*
* 2. APBPeriph "function" and "clock" masks are two separate lists because
* RCC_PeriphClockCmd() takes them as distinct arguments. They are
* equal on this chip. NOTE the group selector bit30 is 0 here (unlike
* the Ameba UART/I2C blocks where bit30 is 1), so each chip must supply
* its own values.
*
* 3. Pad mux: SPI0 (master) shares one generic PINMUX_FUNCTION_SPI code
* (8) on all four signals, whereas SPI1 uses per-signal codes
* (CLK/MOSI/MISO/CS). The codes are therefore supplied as one list per
* signal, indexed by controller. The chip-select entries are only used
* when hardware CS is selected; this driver drives CS as a plain GPIO
* (software CS), so the CS pad-mux code is informational.
*/
#define AMEBA_NSPI 2
/* NON-secure SPI register bases (SPI0_REG_BASE / SPI1_REG_BASE). */
#define AMEBA_SPI_BASES { 0x40124000ul, 0x40125000ul }
/* APBPeriph_SPIx (function) and APBPeriph_SPIx_CLOCK masks. Equal on this
* chip; kept as two lists so chips where they differ can supply both. The
* group selector (bit30) is 0 for the SPI block on amebadplus.
*/
#define AMEBA_SPI_APBPERIPH \
{ (((uint32_t)0 << 30) | ((uint32_t)1 << 14)), \
(((uint32_t)0 << 30) | ((uint32_t)1 << 15)) }
#define AMEBA_SPI_APBPERIPH_CLK \
{ (((uint32_t)0 << 30) | ((uint32_t)1 << 14)), \
(((uint32_t)0 << 30) | ((uint32_t)1 << 15)) }
/* Crossbar pad-mux function codes, one list per signal, indexed by
* controller. SPI0 uses the generic PINMUX_FUNCTION_SPI (8) on every
* signal; SPI1 uses its per-signal codes CLK=29 / MISO=30 / MOSI=31 / CS=32.
*/
#define AMEBA_SPI_CLKFID { 8, 29 } /* SPI0 generic, SPI1_CLK */
#define AMEBA_SPI_MOSIFID { 8, 31 } /* SPI0 generic, SPI1_MOSI */
#define AMEBA_SPI_MISOFID { 8, 30 } /* SPI0 generic, SPI1_MISO */
#define AMEBA_SPI_CSFID { 8, 32 } /* SPI0 generic, SPI1_CS */
/* SSI peripheral clock: ip_clk = PLL_ClkGet() / (HPERI divider + 1), the
* frequency the SPI baud divider divides down to make SCLK. On amebadplus
* the HPERI divider field is bits[10:8] of REG_LSYS_CKD_GRP0 in the system-
* control block (non-secure alias 0x41008000 + 0x0220). BOTH the register
* address and the field position are chip-specific -- e.g. on amebalite that
* same bit field is not HPERI -- so the whole computation lives here rather
* than in the shared driver. PLL_ClkGet() is the amebadplus fwlib PLL query;
* each chip header declares the clock-source query its AMEBA_SPI_IPCLK()
* uses (the shared driver includes this header before the macro expands).
*/
extern uint32_t PLL_ClkGet(void);
#define AMEBA_SPI_CKD_GRP0 0x41008220ul
#define AMEBA_SPI_IPCLK() \
(PLL_ClkGet() / \
((((*(volatile uint32_t *)AMEBA_SPI_CKD_GRP0) >> 8) & 0x7) + 1))
#endif /* __ARCH_ARM_SRC_RTL8721DX_AMEBA_SPI_CHIP_H */
@@ -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_SPI=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_NSH=y
CONFIG_SYSTEM_NSH_STACKSIZE=2500
CONFIG_SYSTEM_SPITOOL=y
CONFIG_TIMER=y
CONFIG_TIMER_ARCH=y
CONFIG_USEC_PER_TICK=1000
@@ -34,11 +34,16 @@ if(CONFIG_AMEBA_I2C)
list(APPEND SRCS rtl8721dx_i2c.c)
endif()
if(CONFIG_AMEBA_SPI)
list(APPEND SRCS rtl8721dx_spi.c)
endif()
target_sources(board PRIVATE ${SRCS})
if(CONFIG_AMEBA_GPIO
OR CONFIG_AMEBA_UART
OR CONFIG_AMEBA_I2C)
OR CONFIG_AMEBA_I2C
OR CONFIG_AMEBA_SPI)
# 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
@@ -51,4 +51,13 @@ CSRCS += rtl8721dx_i2c.c
CFLAGS += ${INCDIR_PREFIX}$(TOPDIR)$(DELIM)arch$(DELIM)arm$(DELIM)src$(DELIM)common$(DELIM)ameba
endif
ifeq ($(CONFIG_AMEBA_SPI),y)
CSRCS += rtl8721dx_spi.c
# The board SPI 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
@@ -138,6 +138,16 @@ int rtl8721dx_bringup(void)
}
#endif
#ifdef CONFIG_AMEBA_SPI
/* Register the board's SPI master buses at /dev/spiN. */
ret = rtl8721dx_spi_initialize();
if (ret < 0)
{
syslog(LOG_ERR, "ERROR: rtl8721dx_spi_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.
@@ -108,6 +108,19 @@ int rtl8721dx_uart_initialize(void);
int rtl8721dx_i2c_initialize(void);
#endif
#ifdef CONFIG_AMEBA_SPI
/****************************************************************************
* Name: rtl8721dx_spi_initialize
*
* Description:
* Register the board's SPI master buses at /dev/spiN
* (boards/arm/rtl8721dx/pke8721daf/src/rtl8721dx_spi.c).
*
****************************************************************************/
int rtl8721dx_spi_initialize(void);
#endif
#ifdef CONFIG_RTL8721DX_FLASH_FS
/****************************************************************************
* Name: ameba_flash_fs_initialize
@@ -0,0 +1,103 @@
/****************************************************************************
* boards/arm/rtl8721dx/pke8721daf/src/rtl8721dx_spi.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 <errno.h>
#include "ameba_gpio.h"
#include "ameba_spi.h"
#include "rtl8721dx_pke8721daf.h"
#ifdef CONFIG_AMEBA_SPI
/****************************************************************************
* Private Types
****************************************************************************/
/* One entry per SPI bus exposed to NuttX at /dev/spiN. The CLK/MOSI/MISO/CS
* pads below are examples used by the `spi` config (system/spi spitool);
* adjust them to match your board's wiring.
*/
struct rtl8721dx_spi_s
{
int bus; /* Controller index (AMEBA_SPI0/AMEBA_SPI1) */
uint8_t clkpin; /* SCLK pad (AMEBA_PA()/AMEBA_PB() encoding) */
uint8_t mosipin; /* MOSI pad */
uint8_t misopin; /* MISO pad */
uint8_t cspin; /* Chip-select pad (software CS GPIO) */
};
/****************************************************************************
* Private Data
****************************************************************************/
static const struct rtl8721dx_spi_s g_spi_buses[] =
{
{
AMEBA_SPI0, AMEBA_PA(14), AMEBA_PA(15), AMEBA_PA(16), AMEBA_PA(17)
},
{
AMEBA_SPI1, AMEBA_PB(18), AMEBA_PB(19), AMEBA_PB(20), AMEBA_PB(21)
},
};
/****************************************************************************
* Public Functions
****************************************************************************/
/****************************************************************************
* Name: rtl8721dx_spi_initialize
*
* Description:
* Register the board's SPI master buses at /dev/spiN.
*
****************************************************************************/
int rtl8721dx_spi_initialize(void)
{
int i;
for (i = 0; i < (int)nitems(g_spi_buses); i++)
{
if (ameba_spi_register(g_spi_buses[i].bus, g_spi_buses[i].clkpin,
g_spi_buses[i].mosipin, g_spi_buses[i].misopin,
g_spi_buses[i].cspin) == NULL)
{
syslog(LOG_ERR,
"ERROR: ameba_spi_register(/dev/spi%d) failed\n",
g_spi_buses[i].bus);
return -ENODEV;
}
}
return OK;
}
#endif /* CONFIG_AMEBA_SPI */
+4
View File
@@ -275,6 +275,7 @@ static const char *g_white_prefix[] =
"FLASH_",
"GPIO_",
"Get_OSC131_", /* Get_OSC131_STATE — Ameba SDK RTC accessor */
"HPERI_", /* HPERI_ClkGet — amebagreen2 SPI ip_clk query */
"I2C_", /* I2C_Init, I2C_MasterWrite, I2C_InitTypeDef, etc. */
"IPC_",
"LOGUART_",
@@ -282,14 +283,17 @@ static const char *g_white_prefix[] =
"OSC4M_",
"OSC131K_",
"PAD_",
"PLL_", /* PLL_ClkGet — amebadplus SPI ip_clk query */
"Pinmux_",
"RCC_",
"RTC_", /* RTC_InitTypeDef, RTC_Enable, RTC_SetTime, etc. */
"RTCIO_",
"SDM32K_", /* SDM32K_Enable */
"SSI_", /* SSI_Init, SSI_SetRole, SSI_WriteData, etc. */
"Set_OSC131_", /* Set_OSC131_STATE — Ameba SDK RTC accessor */
"SYSCFG_",
"SYSTIMER_",
"SYS_PLL_", /* SYS_PLL_ClkGet — RTL8720F SPI ip_clk query */
"UART_",
"SystemCoreClock", /* SystemCoreClock, SystemCoreClockUpdate */
"cmse_", /* ARM CMSE TrustZone intrinsics (arm_cmse.h) */