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 7dbf782e5bb978f42e181416f9daf509c1bb5746 Author: dechao_gong <[email protected]> AuthorDate: Mon Jul 20 11:47:46 2026 +0800 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 <[email protected]> Signed-off-by: dechao_gong <[email protected]> --- .../arm/rtl8721dx/boards/pke8721daf/index.rst | 18 + arch/arm/src/common/ameba/Kconfig | 16 + arch/arm/src/common/ameba/ameba_spi.c | 648 +++++++++++++++++++++ arch/arm/src/common/ameba/ameba_spi.h | 92 +++ 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_spi_chip.h | 113 ++++ .../arm/rtl8721dx/pke8721daf/configs/spi/defconfig | 51 ++ boards/arm/rtl8721dx/pke8721daf/src/CMakeLists.txt | 7 +- boards/arm/rtl8721dx/pke8721daf/src/Makefile | 9 + .../rtl8721dx/pke8721daf/src/rtl8721dx_bringup.c | 10 + .../pke8721daf/src/rtl8721dx_pke8721daf.h | 13 + .../arm/rtl8721dx/pke8721daf/src/rtl8721dx_spi.c | 103 ++++ tools/nxstyle.c | 4 + 15 files changed, 1100 insertions(+), 1 deletion(-) diff --git a/Documentation/platforms/arm/rtl8721dx/boards/pke8721daf/index.rst b/Documentation/platforms/arm/rtl8721dx/boards/pke8721daf/index.rst index 1bbd3cf5c3d..d33d9fafc41 100644 --- a/Documentation/platforms/arm/rtl8721dx/boards/pke8721daf/index.rst +++ b/Documentation/platforms/arm/rtl8721dx/boards/pke8721daf/index.rst @@ -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 ===== diff --git a/arch/arm/src/common/ameba/Kconfig b/arch/arm/src/common/ameba/Kconfig index ab27649ee54..46285e3bc61 100644 --- a/arch/arm/src/common/ameba/Kconfig +++ b/arch/arm/src/common/ameba/Kconfig @@ -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 diff --git a/arch/arm/src/common/ameba/ameba_spi.c b/arch/arm/src/common/ameba/ameba_spi.c new file mode 100644 index 00000000000..755ee2bcd9b --- /dev/null +++ b/arch/arm/src/common/ameba/ameba_spi.c @@ -0,0 +1,648 @@ +/**************************************************************************** + * arch/arm/src/common/ameba/ameba_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 + ****************************************************************************/ + +/* NuttX SPI master lower half for the Realtek Ameba SPI controllers (SPI0 + * and SPI1). Each controller is registered from board bring-up and appears + * as /dev/spiN through the stock SPI character driver (spi_register()). + * + * The controller is a Synopsys DesignWare SSI block programmed through the + * SDK fwlib SSI API in polling mode (no interrupts): SSI_Init() programs the + * role/mode/clock, and each word is moved by spinning on SSI_Writeable() / + * SSI_Readable() around SSI_WriteData() / SSI_ReadData(). The block runs in + * transmit-and-receive mode so every word written yields one word read, + * which is exactly the full-duplex exchange NuttX expects. + * + * The SSI fwlib routines resolve to the on-chip ROM symbol table (they are + * _LONG_CALL_ entries), like the UART fwlib and unlike the I2C fwlib. The + * SSI baud divider is derived from the peripheral clock ip_clk = + * PLL_ClkGet() / (HPERI divider + 1), read once at registration. + * + * The chip select is driven as a plain GPIO (software CS) rather than the + * SSI hardware CS: select() toggles the CS pad through GPIO_WriteBit(), so + * any pad can serve as CS and the CS timing is not tied to the FIFO state. + * + * The chip-specific wiring (controller count, register bases, clock masks + * and pad-mux codes) lives in the per-chip ameba_spi_chip.h. To keep the + * vendor headers out of the NuttX include world, the few fwlib symbols and + * the SSI_InitTypeDef layout used here are declared locally rather than + * pulled in from <ameba_spi.h>. + */ + +#include <nuttx/config.h> + +#include <stdint.h> +#include <stdbool.h> +#include <errno.h> +#include <string.h> +#include <debug.h> +#include <inttypes.h> + +#include <nuttx/kmalloc.h> +#include <nuttx/mutex.h> +#include <nuttx/spi/spi.h> +#include <nuttx/spi/spi_transfer.h> + +#include "ameba_spi.h" +#include "ameba_spi_chip.h" +#include "ameba_gpio_chip.h" + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +/* The register bases, peripheral-clock masks, crossbar pad-mux codes and + * controller count (AMEBA_NSPI) come from the per-chip ameba_spi_chip.h. + * Everything below is common to every current Ameba chip. + */ + +/* fwlib SSI_InitTypeDef field values (SSI_MASTER, TMOD/FRF, SCPOL/SCPH). + * These are identical on every Ameba chip audited. + */ + +#define AMEBA_SSI_MASTER 0x1 /* SSI_MASTER (SPI_Role) */ +#define AMEBA_SSI_TMOD_TR 0x0 /* Transmit & receive (SPI_TransferMode) */ +#define AMEBA_SSI_FRF_SPI 0x0 /* FRF_MOTOROLA_SPI (SPI_DataFrameFormat) */ + +#define AMEBA_SSI_SCPOL_LOW 0x0 /* SCPOL_INACTIVE_IS_LOW (CPOL=0) */ +#define AMEBA_SSI_SCPOL_HIGH 0x1 /* SCPOL_INACTIVE_IS_HIGH (CPOL=1) */ +#define AMEBA_SSI_SCPH_MIDDLE 0x0 /* SCPH_TOGGLES_IN_MIDDLE (CPHA=0) */ +#define AMEBA_SSI_SCPH_START 0x1 /* SCPH_TOGGLES_AT_START (CPHA=1) */ + +/* GPIO field values for the software chip select (see ameba_gpio.c). */ + +#define AMEBA_GPIO_MODE_OUT 0x1 /* GPIO_Mode_OUT */ +#define AMEBA_GPIO_PUPD_NONE 0x0 /* GPIO_PuPd_NOPULL */ + +/* Second/third argument to fwlib "state" style APIs. */ + +#define AMEBA_DISABLE 0x0 +#define AMEBA_ENABLE 0x1 + +/* Bus defaults used until the upper half programs its own. */ + +#define AMEBA_SPI_DEFAULT_FREQ 1000000 +#define AMEBA_SPI_DEFAULT_BITS 8 + +/* Bounded spin guarding each FIFO wait so a wedged bus cannot hang. */ + +#define AMEBA_SPI_TIMEOUT 1000000 + +/**************************************************************************** + * Private Types + ****************************************************************************/ + +/* Layout-compatible mirror of the fwlib SSI_InitTypeDef (all u32, same + * order); passed by address to SSI_StructInit()/SSI_Init(). + */ + +struct ameba_ssi_init_s +{ + uint32_t dmarxlvl; /* SPI_DmaRxDataLevel */ + uint32_t dmatxlvl; /* SPI_DmaTxDataLevel */ + uint32_t rxthlvl; /* SPI_RxThresholdLevel */ + uint32_t txthlvl; /* SPI_TxThresholdLevel */ + uint32_t ssenable; /* SPI_SlaveSelectEnable */ + uint32_t divider; /* SPI_ClockDivider */ + uint32_t framenum; /* SPI_DataFrameNumber */ + uint32_t frameformat; /* SPI_DataFrameFormat */ + uint32_t framesize; /* SPI_DataFrameSize */ + uint32_t intmask; /* SPI_InterruptMask */ + uint32_t role; /* SPI_Role */ + uint32_t sclkphase; /* SPI_SclkPhase */ + uint32_t sclkpolarity; /* SPI_SclkPolarity */ + uint32_t transfermode; /* SPI_TransferMode */ +}; + +/* Layout-compatible mirror of the fwlib GPIO_InitTypeDef (see ameba_gpio.c), + * used to configure the software chip-select pad as an output. + */ + +struct ameba_gpio_init_s +{ + uint32_t mode; /* GPIO_Mode */ + uint32_t pupd; /* GPIO_PuPd */ + uint32_t ittrigger; /* GPIO_ITTrigger */ + uint32_t itpolarity; /* GPIO_ITPolarity */ + uint32_t itdebounce; /* GPIO_ITDebounce */ + uint32_t pin; /* GPIO_Pin */ +}; + +struct ameba_spi_dev_s +{ + struct spi_dev_s dev; /* SPI master lower half (must be first) */ + uintptr_t base; /* Non-secure SPI register base address */ + uint32_t periph; /* APBPeriph function mask (RCC arg 1) */ + uint32_t clk; /* APBPeriph clock mask (RCC arg 2) */ + uint32_t ipclk; /* SSI peripheral clock ip_clk (Hz) */ + uint32_t frequency; /* Requested bus frequency (Hz) */ + uint32_t actual; /* Achieved bus frequency (Hz) */ + uint32_t divider; /* SSI clock divider for that frequency */ + uint8_t mode; /* Currently programmed enum spi_mode_e */ + uint8_t nbits; /* Currently programmed bits per word */ + bool dirty; /* SSI needs an SSI_Init() before use */ + uint8_t clkpin; /* SCLK pad (AMEBA_PA()/AMEBA_PB()) */ + uint8_t mosipin; /* MOSI pad */ + uint8_t misopin; /* MISO pad */ + uint8_t cspin; /* Chip-select pad (GPIO output) */ + uint8_t clkfid; /* Pin mux code for the SCLK pad */ + uint8_t mosifid; /* Pin mux code for the MOSI pad */ + uint8_t misofid; /* Pin mux code for the MISO pad */ + mutex_t lock; /* Serializes bus access */ +}; + +/**************************************************************************** + * Private Function Prototypes + ****************************************************************************/ + +/* SDK fwlib SSI/GPIO/pin/clock API, all resolved from the on-chip ROM. */ + +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 GPIO_Init(struct ameba_gpio_init_s *init); +extern void GPIO_WriteBit(uint32_t pin, uint32_t state); +extern void SSI_SetRole(void *spidev, uint32_t role); +extern void SSI_StructInit(struct ameba_ssi_init_s *init); +extern void SSI_Init(void *spidev, struct ameba_ssi_init_s *init); +extern void SSI_Cmd(void *spidev, uint32_t newstate); +extern uint32_t SSI_Writeable(void *spidev); +extern uint32_t SSI_Readable(void *spidev); +extern void SSI_WriteData(void *spidev, uint32_t value); +extern uint32_t SSI_ReadData(void *spidev); + +/* SPI master lower-half operations. */ + +static int ameba_spi_lock(struct spi_dev_s *dev, bool lock); +static void ameba_spi_select(struct spi_dev_s *dev, uint32_t devid, + bool selected); +static uint32_t ameba_spi_setfrequency(struct spi_dev_s *dev, + uint32_t frequency); +static void ameba_spi_setmode(struct spi_dev_s *dev, enum spi_mode_e mode); +static void ameba_spi_setbits(struct spi_dev_s *dev, int nbits); +static uint8_t ameba_spi_status(struct spi_dev_s *dev, uint32_t devid); +static uint32_t ameba_spi_send(struct spi_dev_s *dev, uint32_t wd); +#ifdef CONFIG_SPI_EXCHANGE +static void ameba_spi_exchange(struct spi_dev_s *dev, + const void *txbuffer, void *rxbuffer, + size_t nwords); +#else +static void ameba_spi_sndblock(struct spi_dev_s *dev, const void *buffer, + size_t nwords); +static void ameba_spi_recvblock(struct spi_dev_s *dev, void *buffer, + size_t nwords); +#endif + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +static const struct spi_ops_s g_ameba_spi_ops = +{ + .lock = ameba_spi_lock, + .select = ameba_spi_select, + .setfrequency = ameba_spi_setfrequency, + .setmode = ameba_spi_setmode, + .setbits = ameba_spi_setbits, + .status = ameba_spi_status, + .send = ameba_spi_send, +#ifdef CONFIG_SPI_EXCHANGE + .exchange = ameba_spi_exchange, +#else + .sndblock = ameba_spi_sndblock, + .recvblock = ameba_spi_recvblock, +#endif +}; + +/* Per-controller register base, peripheral function/clock masks and crossbar + * pad-mux codes, indexed by controller number and supplied by the per-chip + * ameba_spi_chip.h. + */ + +static const uintptr_t g_spi_base[AMEBA_NSPI] = AMEBA_SPI_BASES; +static const uint32_t g_spi_periph[AMEBA_NSPI] = AMEBA_SPI_APBPERIPH; +static const uint32_t g_spi_clk[AMEBA_NSPI] = AMEBA_SPI_APBPERIPH_CLK; +static const uint8_t g_spi_clkfid[AMEBA_NSPI] = AMEBA_SPI_CLKFID; +static const uint8_t g_spi_mosifid[AMEBA_NSPI] = AMEBA_SPI_MOSIFID; +static const uint8_t g_spi_misofid[AMEBA_NSPI] = AMEBA_SPI_MISOFID; + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: ameba_spi_ipclk + * + * Description: + * Return the SSI peripheral clock ip_clk = PLL_ClkGet() / (HPERI + 1), + * the frequency the baud divider divides down to produce SCLK. + * + ****************************************************************************/ + +static uint32_t ameba_spi_ipclk(void) +{ + /* The register address and HPERI field position are chip-specific and are + * expressed by AMEBA_SPI_IPCLK() in the per-chip ameba_spi_chip.h. + */ + + return AMEBA_SPI_IPCLK(); +} + +/**************************************************************************** + * Name: ameba_spi_configure + * + * Description: + * (Re)program the SSI block for the currently requested frequency, mode + * and word length, and enable it. The DesignWare SSI can only be + * configured while disabled, so this runs lazily on the first word after + * any of those parameters changes (priv->dirty). + * + ****************************************************************************/ + +static void ameba_spi_configure(struct ameba_spi_dev_s *priv) +{ + struct ameba_ssi_init_s init; + void *spidev = (void *)priv->base; + + if (!priv->dirty) + { + return; + } + + memset(&init, 0, sizeof(init)); + SSI_StructInit(&init); + + init.role = AMEBA_SSI_MASTER; + init.transfermode = AMEBA_SSI_TMOD_TR; + init.frameformat = AMEBA_SSI_FRF_SPI; + init.ssenable = 0; /* Software CS: do not drive the SSI CS line */ + init.intmask = 0; /* Polling: no SSI interrupts */ + init.divider = priv->divider; + init.framesize = priv->nbits - 1; + + /* enum spi_mode_e encodes CPOL in bit1 and CPHA in bit0. */ + + init.sclkpolarity = (priv->mode & 0x2) ? AMEBA_SSI_SCPOL_HIGH : + AMEBA_SSI_SCPOL_LOW; + init.sclkphase = (priv->mode & 0x1) ? AMEBA_SSI_SCPH_START : + AMEBA_SSI_SCPH_MIDDLE; + + SSI_Cmd(spidev, AMEBA_DISABLE); + SSI_Init(spidev, &init); + SSI_Cmd(spidev, AMEBA_ENABLE); + + priv->dirty = false; +} + +/**************************************************************************** + * Name: ameba_spi_transfer + * + * Description: + * Move nwords words full-duplex in polling mode. Every word written to + * the TX FIFO produces one word in the RX FIFO (transmit-and-receive + * mode); a NULL txbuffer sends 0xffff and a NULL rxbuffer discards the + * received words. + * + ****************************************************************************/ + +static void ameba_spi_transfer(struct ameba_spi_dev_s *priv, + const void *txbuffer, void *rxbuffer, + size_t nwords) +{ + void *spidev = (void *)priv->base; + bool wide = priv->nbits > 8; + size_t i; + uint32_t to; + + ameba_spi_configure(priv); + + for (i = 0; i < nwords; i++) + { + uint32_t word = 0xffff; + uint32_t rxword; + + if (txbuffer != NULL) + { + word = wide ? ((const uint16_t *)txbuffer)[i] : + ((const uint8_t *)txbuffer)[i]; + } + + for (to = AMEBA_SPI_TIMEOUT; + to > 0 && SSI_Writeable(spidev) == 0; to--); + + SSI_WriteData(spidev, word); + + for (to = AMEBA_SPI_TIMEOUT; + to > 0 && SSI_Readable(spidev) == 0; to--); + + rxword = SSI_ReadData(spidev); + + if (rxbuffer != NULL) + { + if (wide) + { + ((uint16_t *)rxbuffer)[i] = (uint16_t)rxword; + } + else + { + ((uint8_t *)rxbuffer)[i] = (uint8_t)rxword; + } + } + } +} + +/**************************************************************************** + * Name: ameba_spi_lock + ****************************************************************************/ + +static int ameba_spi_lock(struct spi_dev_s *dev, bool lock) +{ + struct ameba_spi_dev_s *priv = (struct ameba_spi_dev_s *)dev; + + return lock ? nxmutex_lock(&priv->lock) : nxmutex_unlock(&priv->lock); +} + +/**************************************************************************** + * Name: ameba_spi_select + * + * Description: + * Assert (selected) or de-assert the active-low chip-select GPIO. A + * single CS pad is driven per bus, so the devid is not used. + * + ****************************************************************************/ + +static void ameba_spi_select(struct spi_dev_s *dev, uint32_t devid, + bool selected) +{ + struct ameba_spi_dev_s *priv = (struct ameba_spi_dev_s *)dev; + + GPIO_WriteBit(priv->cspin, selected ? AMEBA_DISABLE : AMEBA_ENABLE); +} + +/**************************************************************************** + * Name: ameba_spi_setfrequency + * + * Description: + * Choose the largest even SSI divider whose SCLK does not exceed the + * requested frequency (SCLK = ip_clk / divider, even, 2..65534), and + * return the frequency actually achieved. + * + ****************************************************************************/ + +static uint32_t ameba_spi_setfrequency(struct spi_dev_s *dev, + uint32_t frequency) +{ + struct ameba_spi_dev_s *priv = (struct ameba_spi_dev_s *)dev; + uint32_t divider; + + if (frequency == 0) + { + return priv->actual; + } + + if (frequency == priv->frequency) + { + return priv->actual; + } + + /* Round the divider down to an even value, then bump it up until SCLK no + * longer exceeds the request; clamp to the 16-bit even range. + */ + + divider = (priv->ipclk / frequency / 2) * 2; + if (divider < 2) + { + divider = 2; + } + + if ((priv->ipclk / divider) > frequency) + { + divider += 2; + } + + if (divider > 0xfffe) + { + divider = 0xfffe; + } + + priv->divider = divider; + priv->frequency = frequency; + priv->actual = priv->ipclk / divider; + priv->dirty = true; + + spiinfo("frequency=%" PRIu32 " actual=%" PRIu32 " div=%" PRIu32 "\n", + frequency, priv->actual, divider); + return priv->actual; +} + +/**************************************************************************** + * Name: ameba_spi_setmode + ****************************************************************************/ + +static void ameba_spi_setmode(struct spi_dev_s *dev, enum spi_mode_e mode) +{ + struct ameba_spi_dev_s *priv = (struct ameba_spi_dev_s *)dev; + + if ((uint8_t)mode != priv->mode) + { + priv->mode = (uint8_t)mode; + priv->dirty = true; + } +} + +/**************************************************************************** + * Name: ameba_spi_setbits + ****************************************************************************/ + +static void ameba_spi_setbits(struct spi_dev_s *dev, int nbits) +{ + struct ameba_spi_dev_s *priv = (struct ameba_spi_dev_s *)dev; + + if (nbits >= 4 && nbits <= 16 && (uint8_t)nbits != priv->nbits) + { + priv->nbits = (uint8_t)nbits; + priv->dirty = true; + } +} + +/**************************************************************************** + * Name: ameba_spi_status + ****************************************************************************/ + +static uint8_t ameba_spi_status(struct spi_dev_s *dev, uint32_t devid) +{ + UNUSED(dev); + UNUSED(devid); + return 0; +} + +/**************************************************************************** + * Name: ameba_spi_send + ****************************************************************************/ + +static uint32_t ameba_spi_send(struct spi_dev_s *dev, uint32_t wd) +{ + struct ameba_spi_dev_s *priv = (struct ameba_spi_dev_s *)dev; + uint16_t rxword = 0; + uint16_t txword = (uint16_t)wd; + + ameba_spi_transfer(priv, &txword, &rxword, 1); + return rxword; +} + +#ifdef CONFIG_SPI_EXCHANGE + +/**************************************************************************** + * Name: ameba_spi_exchange + ****************************************************************************/ + +static void ameba_spi_exchange(struct spi_dev_s *dev, + const void *txbuffer, void *rxbuffer, + size_t nwords) +{ + struct ameba_spi_dev_s *priv = (struct ameba_spi_dev_s *)dev; + + ameba_spi_transfer(priv, txbuffer, rxbuffer, nwords); +} + +#else + +/**************************************************************************** + * Name: ameba_spi_sndblock + ****************************************************************************/ + +static void ameba_spi_sndblock(struct spi_dev_s *dev, const void *buffer, + size_t nwords) +{ + struct ameba_spi_dev_s *priv = (struct ameba_spi_dev_s *)dev; + + ameba_spi_transfer(priv, buffer, NULL, nwords); +} + +/**************************************************************************** + * Name: ameba_spi_recvblock + ****************************************************************************/ + +static void ameba_spi_recvblock(struct spi_dev_s *dev, void *buffer, + size_t nwords) +{ + struct ameba_spi_dev_s *priv = (struct ameba_spi_dev_s *)dev; + + ameba_spi_transfer(priv, NULL, buffer, nwords); +} + +#endif /* CONFIG_SPI_EXCHANGE */ + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: ameba_spi_register + * + * Description: + * See ameba_spi.h. + * + ****************************************************************************/ + +struct spi_dev_s *ameba_spi_register(int bus, uint8_t clkpin, + uint8_t mosipin, uint8_t misopin, + uint8_t cspin) +{ + struct ameba_spi_dev_s *priv; + struct ameba_gpio_init_s gpio; + int ret; + + if (bus < 0 || bus >= AMEBA_NSPI) + { + return NULL; + } + + priv = kmm_zalloc(sizeof(struct ameba_spi_dev_s)); + if (priv == NULL) + { + return NULL; + } + + priv->dev.ops = &g_ameba_spi_ops; + priv->base = g_spi_base[bus]; + priv->periph = g_spi_periph[bus]; + priv->clk = g_spi_clk[bus]; + priv->clkpin = clkpin; + priv->mosipin = mosipin; + priv->misopin = misopin; + priv->cspin = cspin; + priv->clkfid = g_spi_clkfid[bus]; + priv->mosifid = g_spi_mosifid[bus]; + priv->misofid = g_spi_misofid[bus]; + priv->mode = SPIDEV_MODE0; + priv->nbits = AMEBA_SPI_DEFAULT_BITS; + priv->frequency = 0; + priv->dirty = true; + nxmutex_init(&priv->lock); + + /* Gate the SSI peripheral clock and route the CLK/MOSI/MISO pads. */ + + RCC_PeriphClockCmd(priv->periph, priv->clk, AMEBA_ENABLE); + + /* The controller powers up as an SPI slave (the LSYS SPIx_MST bit is clear + * out of reset), and in slave mode the master-only baud-rate and slave- + * enable registers are write-ignored so the block never drives SCLK. + * Switch it to master before any SSI_Init() so that configuration sticks. + */ + + SSI_SetRole((void *)priv->base, AMEBA_SSI_MASTER); + + Pinmux_Config(priv->clkpin, priv->clkfid); + Pinmux_Config(priv->mosipin, priv->mosifid); + Pinmux_Config(priv->misopin, priv->misofid); + + /* Configure the chip-select pad as a GPIO output, de-asserted (high). */ + + RCC_PeriphClockCmd(AMEBA_APBPERIPH_GPIO, AMEBA_APBPERIPH_GPIO, + AMEBA_ENABLE); + memset(&gpio, 0, sizeof(gpio)); + gpio.mode = AMEBA_GPIO_MODE_OUT; + gpio.pupd = AMEBA_GPIO_PUPD_NONE; + gpio.pin = priv->cspin; + GPIO_Init(&gpio); + GPIO_WriteBit(priv->cspin, AMEBA_ENABLE); + + /* Latch the peripheral clock and prime the default bus frequency. */ + + priv->ipclk = ameba_spi_ipclk(); + ameba_spi_setfrequency(&priv->dev, AMEBA_SPI_DEFAULT_FREQ); + + ret = spi_register(&priv->dev, bus); + if (ret < 0) + { + _err("ERROR: spi_register(/dev/spi%d) failed: %d\n", bus, ret); + nxmutex_destroy(&priv->lock); + kmm_free(priv); + return NULL; + } + + return &priv->dev; +} diff --git a/arch/arm/src/common/ameba/ameba_spi.h b/arch/arm/src/common/ameba/ameba_spi.h new file mode 100644 index 00000000000..92d7f275d50 --- /dev/null +++ b/arch/arm/src/common/ameba/ameba_spi.h @@ -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 */ diff --git a/arch/arm/src/rtl8721dx/CMakeLists.txt b/arch/arm/src/rtl8721dx/CMakeLists.txt index bcf9450f9a3..b43237a62a0 100644 --- a/arch/arm/src/rtl8721dx/CMakeLists.txt +++ b/arch/arm/src/rtl8721dx/CMakeLists.txt @@ -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}) diff --git a/arch/arm/src/rtl8721dx/Make.defs b/arch/arm/src/rtl8721dx/Make.defs index fbe728e46f4..333db513af8 100644 --- a/arch/arm/src/rtl8721dx/Make.defs +++ b/arch/arm/src/rtl8721dx/Make.defs @@ -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 # diff --git a/arch/arm/src/rtl8721dx/ameba_board.mk b/arch/arm/src/rtl8721dx/ameba_board.mk index 1a59dce5832..24b5d30e26f 100644 --- a/arch/arm/src/rtl8721dx/ameba_board.mk +++ b/arch/arm/src/rtl8721dx/ameba_board.mk @@ -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 diff --git a/arch/arm/src/rtl8721dx/ameba_spi_chip.h b/arch/arm/src/rtl8721dx/ameba_spi_chip.h new file mode 100644 index 00000000000..ee0f8dfbb40 --- /dev/null +++ b/arch/arm/src/rtl8721dx/ameba_spi_chip.h @@ -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 */ diff --git a/boards/arm/rtl8721dx/pke8721daf/configs/spi/defconfig b/boards/arm/rtl8721dx/pke8721daf/configs/spi/defconfig new file mode 100644 index 00000000000..3a4ec21ecae --- /dev/null +++ b/boards/arm/rtl8721dx/pke8721daf/configs/spi/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_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 diff --git a/boards/arm/rtl8721dx/pke8721daf/src/CMakeLists.txt b/boards/arm/rtl8721dx/pke8721daf/src/CMakeLists.txt index 2ccfdc647a5..34abcac3a23 100644 --- a/boards/arm/rtl8721dx/pke8721daf/src/CMakeLists.txt +++ b/boards/arm/rtl8721dx/pke8721daf/src/CMakeLists.txt @@ -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 diff --git a/boards/arm/rtl8721dx/pke8721daf/src/Makefile b/boards/arm/rtl8721dx/pke8721daf/src/Makefile index ed80ab1a32e..03eda20df62 100644 --- a/boards/arm/rtl8721dx/pke8721daf/src/Makefile +++ b/boards/arm/rtl8721dx/pke8721daf/src/Makefile @@ -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 diff --git a/boards/arm/rtl8721dx/pke8721daf/src/rtl8721dx_bringup.c b/boards/arm/rtl8721dx/pke8721daf/src/rtl8721dx_bringup.c index ab034d08ff7..5ff19f3177f 100644 --- a/boards/arm/rtl8721dx/pke8721daf/src/rtl8721dx_bringup.c +++ b/boards/arm/rtl8721dx/pke8721daf/src/rtl8721dx_bringup.c @@ -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. diff --git a/boards/arm/rtl8721dx/pke8721daf/src/rtl8721dx_pke8721daf.h b/boards/arm/rtl8721dx/pke8721daf/src/rtl8721dx_pke8721daf.h index 52be521d3d9..63a31287359 100644 --- a/boards/arm/rtl8721dx/pke8721daf/src/rtl8721dx_pke8721daf.h +++ b/boards/arm/rtl8721dx/pke8721daf/src/rtl8721dx_pke8721daf.h @@ -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 diff --git a/boards/arm/rtl8721dx/pke8721daf/src/rtl8721dx_spi.c b/boards/arm/rtl8721dx/pke8721daf/src/rtl8721dx_spi.c new file mode 100644 index 00000000000..83c3d42791d --- /dev/null +++ b/boards/arm/rtl8721dx/pke8721daf/src/rtl8721dx_spi.c @@ -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 */ diff --git a/tools/nxstyle.c b/tools/nxstyle.c index f5ddef85124..ddf8faa59ca 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 */ + "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) */
