diff --git a/bsp/README.md b/bsp/README.md index eae5c134efe..d78584f143d 100644 --- a/bsp/README.md +++ b/bsp/README.md @@ -760,9 +760,9 @@ This document is based on the RT-Thread mainline repository and categorizes the #### 🟢 K230 (RT-Smart) -| BSP Name | GPIO | UART | I2C | RTC | ADC | PWM | SDIO | HWTimer | WDT | -|----------|------|------|-----|-----|-----|-----|------|---------|-----| -| [k230](k230) | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | +| BSP Name | GPIO | UART | I2C | RTC | ADC | PWM | SDIO | HWTimer | WDT | SPI | +|----------|------|------|-----|-----|-----|-----|------|---------|-----|-----| +| [k230](k230) | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | #### 🟢 Xuantie (RT-Smart) diff --git a/bsp/k230/.ci/attachconfig/ci.attachconfig.yml b/bsp/k230/.ci/attachconfig/ci.attachconfig.yml index c649cfeaa81..e579d18af98 100644 --- a/bsp/k230/.ci/attachconfig/ci.attachconfig.yml +++ b/bsp/k230/.ci/attachconfig/ci.attachconfig.yml @@ -1,6 +1,12 @@ scons.args: &scons scons_arg: - '--strict' +devices.spi: + <<: *scons + kconfig: + - CONFIG_RT_USING_SPI=y + - CONFIG_BSP_USING_SPI=y + - CONFIG_BSP_USING_SPI0=y devices.i2c: <<: *scons kconfig: diff --git a/bsp/k230/.config b/bsp/k230/.config index f0f59f2e430..e4c479c1e6a 100644 --- a/bsp/k230/.config +++ b/bsp/k230/.config @@ -534,8 +534,6 @@ CONFIG_RT_USING_ADT_REF=y # CONFIG_RT_USING_RT_LINK is not set # end of Utilities -# CONFIG_RT_USING_VBUS is not set - # # Memory management # @@ -943,6 +941,7 @@ CONFIG_RT_USING_VDSO=y # CONFIG_PKG_USING_R_RHEALSTONE is not set # CONFIG_PKG_USING_HEARTBEAT is not set # CONFIG_PKG_USING_MICRO_ROS_RTTHREAD_PACKAGE is not set +# CONFIG_PKG_USING_CHERRYECAT is not set # end of system packages # @@ -1100,6 +1099,12 @@ CONFIG_RT_USING_VDSO=y # CONFIG_PKG_USING_GD32_ARM_CMSIS_DRIVER is not set # CONFIG_PKG_USING_GD32_ARM_SERIES_DRIVER is not set # end of GD32 Drivers + +# +# HPMicro SDK +# +# CONFIG_PKG_USING_HPM_SDK is not set +# end of HPMicro SDK # end of HAL & SDK Drivers # @@ -1619,6 +1624,7 @@ CONFIG_PKG_ZLIB_VER="latest" # # Drivers Configuration # +# CONFIG_BSP_USING_SPI is not set # CONFIG_BSP_USING_I2C is not set # CONFIG_BSP_USING_RTC is not set # CONFIG_BSP_USING_ADC is not set diff --git a/bsp/k230/board/Kconfig b/bsp/k230/board/Kconfig index d0324d2b325..9c34170d206 100644 --- a/bsp/k230/board/Kconfig +++ b/bsp/k230/board/Kconfig @@ -1,4 +1,31 @@ menu "Drivers Configuration" + + menuconfig BSP_USING_SPI + bool "Enable SPI" + select RT_USING_SPI + select RT_USING_QSPI + default n + + if BSP_USING_SPI + config BSP_USING_SPI0 + bool "Enable SPI0" + help + Support 1, 2, 4 and 8 lines, Max clock frequency is 200 Mhz. + default n + + config BSP_USING_SPI1 + bool "Enable SPI1" + help + Support 1, 2, and 4 lines, Max clock frequency is 100 Mhz. + default n + + config BSP_USING_SPI2 + bool "Enable SPI2" + help + Support 1, 2, and 4 lines, Max clock frequency is 100 Mhz. + default n + endif + menuconfig BSP_USING_I2C bool "Enable I2C" select RT_USING_I2C diff --git a/bsp/k230/drivers/interdrv/spi/SConscript b/bsp/k230/drivers/interdrv/spi/SConscript new file mode 100644 index 00000000000..2074d88fc97 --- /dev/null +++ b/bsp/k230/drivers/interdrv/spi/SConscript @@ -0,0 +1,11 @@ +# RT-Thread building script for SPI component + +from building import * + +cwd = GetCurrentDir() +src = Glob('*.c') +CPPPATH = [cwd] + +group = DefineGroup('SPI', src, depend = ['BSP_USING_SPI'], CPPPATH = CPPPATH) + +Return('group') diff --git a/bsp/k230/drivers/interdrv/spi/drv_spi.c b/bsp/k230/drivers/interdrv/spi/drv_spi.c new file mode 100644 index 00000000000..3bbb3e5a070 --- /dev/null +++ b/bsp/k230/drivers/interdrv/spi/drv_spi.c @@ -0,0 +1,721 @@ +/* Copyright (c) 2023, Canaan Bright Sight Co., Ltd + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND + * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* + * Copyright (c) 2006-2025 RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include + +#include "drv_spi.h" +#include +#include +#include "drv_gpio.h" +#include "riscv_io.h" +#include "board.h" +#include "ioremap.h" +#include "sysctl_rst.h" +#include "sysctl_clk.h" +#include "cache.h" + +#define DBG_TAG "spi" +#include + +struct k230_spi_dev +{ + struct rt_spi_bus dev; + void *base; + const char *name; + const char *event_name; + rt_uint32_t pa_base; + rt_uint32_t size; + rt_uint8_t idx; + rt_uint8_t rdse; + rt_uint8_t rdsd; + rt_uint8_t max_line; + rt_uint32_t max_hz; + struct rt_event event; + void *send_buf; + void *recv_buf; + rt_size_t send_length; + rt_size_t recv_length; + rt_uint8_t cell_size; + int vector; +}; + +static rt_err_t k230_spi_configure(struct rt_spi_device *device, struct rt_spi_configuration *configuration) +{ + RT_ASSERT(device != RT_NULL); + RT_ASSERT(configuration != RT_NULL); + struct k230_spi_dev *qspi_bus = (struct k230_spi_dev *)device->bus; + k230_spi_reg_t *qspi_reg = (k230_spi_reg_t *)qspi_bus->base; + struct rt_qspi_device *dev = (struct rt_qspi_device *)device; + struct rt_qspi_configuration *qspi_cfg = &dev->config; + struct rt_spi_configuration *qspi_cfg_parent = &dev->config.parent; + + rt_uint8_t dfs, mode, spi_ff; + rt_uint32_t max_hz, qspi_clk; + if (qspi_cfg->qspi_dl_width > qspi_bus->max_line || qspi_cfg->qspi_dl_width == 0) + { + return RT_EINVAL; + } + if (qspi_cfg_parent->data_width < 4 || qspi_cfg_parent->data_width > 32) + { + return RT_EINVAL; + } + + /* Check if the clock frequency exceeds the hardware limits */ + max_hz = qspi_cfg_parent->max_hz; + if (max_hz > qspi_bus->max_hz) + { + max_hz = qspi_bus->max_hz; + } + + /* Get QSPI Controller Clock Frequency */ + if (qspi_bus->idx == 0) + { + qspi_clk = sysctl_clk_get_leaf_freq(SYSCTL_CLK_SSI0); + } + else if (qspi_bus->idx == 1) + { + qspi_clk = sysctl_clk_get_leaf_freq(SYSCTL_CLK_SSI1); + } + else if (qspi_bus->idx == 2) + { + qspi_clk = sysctl_clk_get_leaf_freq(SYSCTL_CLK_SSI2); + } + else + { + return RT_EINVAL; + } + + /* Set SPI_FRF to config SPI mode*/ + if (qspi_cfg->qspi_dl_width == 1) + { + spi_ff = SPI_FRF_STD_SPI; + } + else if (qspi_cfg->qspi_dl_width == 2) + { + spi_ff = SPI_FRF_DUAL_SPI; + } + else if (qspi_cfg->qspi_dl_width == 4) + { + spi_ff = SPI_FRF_QUAD_SPI; + } + else if (qspi_cfg->qspi_dl_width == 8) + { + spi_ff = SPI_FRF_OCT_SPI; + } + else + { + return RT_EINVAL; + } + /* + * dfs: CPOL and CPHA, Write dfs into bits 8-9 of the register ctrlr0 + * mode: SPI mode ,Write mode into bits 0-4 of the register ctrlr0 + */ + mode = qspi_cfg_parent->mode & RT_SPI_MODE_3; + dfs = qspi_cfg_parent->data_width - 1; + + qspi_reg->ssienr = 0; + qspi_reg->ser = 0; + qspi_reg->baudr = qspi_clk / max_hz; + qspi_reg->rx_sample_delay = qspi_bus->rdse << 16 | qspi_bus->rdsd; + qspi_reg->axiawlen = SSIC_AXI_BLW << 8; + qspi_reg->axiarlen = SSIC_AXI_BLW << 8; + qspi_reg->ctrlr0 = (dfs) | (mode << 8) | (spi_ff << 22); + return RT_EOK; +} + +static rt_ssize_t k230_spi_xfer(struct rt_spi_device *device, struct rt_spi_message *message) +{ + struct k230_spi_dev *qspi_bus = (struct k230_spi_dev *)device->bus; + k230_spi_reg_t *qspi_reg = (k230_spi_reg_t *)qspi_bus->base; + struct rt_qspi_device *dev = (struct rt_qspi_device *)device; + struct rt_qspi_configuration *qspi_cfg = &dev->config; + struct rt_spi_configuration *qspi_cfg_parent = &dev->config.parent; + struct rt_qspi_message *msg = (struct rt_qspi_message *)message; + struct rt_spi_message *msg_parent = message; + /* Multi-line SPI transfers (2, 4, or 8 lines) DSPI QSPI OSPI */ + if (msg->qspi_data_lines > 1) + { + rt_uint8_t trans_type = 0; + if (msg->qspi_data_lines > qspi_cfg->qspi_dl_width) + { + LOG_E("data line is invalid"); + return 0; + } + /* Check other parameters */ + if (qspi_cfg_parent->data_width & (msg->qspi_data_lines - 1)) + { + LOG_E("data line and data width do not match"); + return 0; + } + if (msg->instruction.qspi_lines != 1 && msg->instruction.qspi_lines != msg->qspi_data_lines) + { + LOG_E("instruction line is invalid"); + return 0; + } + if (msg->address.size & 3 || msg->address.size > 32) + { + LOG_E("address size is invalid"); + return 0; + } + if (msg->address.size && msg->address.qspi_lines != 1 && msg->address.qspi_lines != msg->qspi_data_lines) + { + LOG_E("address line is invalid"); + return 0; + } + if (msg_parent->length > 0x10000) + { + LOG_E("data length is invalid, more than 0x10000"); + return 0; + } + if (msg->instruction.qspi_lines != 1) + { + trans_type = 2; + } + if (msg->address.size) + { + if (msg->address.qspi_lines != 1) + { + trans_type = trans_type ? trans_type : 1; + + } + else if (trans_type != 0) + { + LOG_E("instruction or address line is invalid"); + return 0; + } + } + if (msg->dummy_cycles > 31) + { + LOG_E("dummy cycle is invalid"); + return 0; + } + + rt_uint8_t tmod = msg_parent->recv_buf ? SPI_TMOD_RO : SPI_TMOD_TO; + rt_size_t length = msg_parent->length; + rt_size_t txfthr = length > (SSIC_TX_ABW / 2) ? (SSIC_TX_ABW / 2) : length - 1; + rt_uint8_t cell_size = (qspi_cfg_parent->data_width + 7) >> 3; + rt_uint8_t *buf = RT_NULL; + /* Allocate buffer for DMA transfer */ + if (length) + { + buf = rt_malloc_align(CACHE_ALIGN_TOP(length * cell_size), L1_CACHE_BYTES); + if (buf == RT_NULL) + { + LOG_E("alloc mem error"); + return 0; + } + } + + qspi_reg->spi_ctrlr0 = trans_type | (msg->address.size >> 2 << 2) | 512 | (msg->dummy_cycles << 11); + qspi_reg->ctrlr0 &= ~((3 << 22) | (3 << 10)); + + /* Config SPI frame format and transmission mode */ + if (length) + { + qspi_reg->ctrlr0 |= (tmod << 10); + qspi_reg->txftlr = (txfthr << 16) | (SSIC_TX_ABW / 2); + qspi_reg->rxftlr = (SSIC_RX_ABW - 1); + qspi_reg->imr = (1 << 11) | (1 << 8); + qspi_reg->dmacr = (1 << 6) | (3 << 3) | (1 << 2); + qspi_reg->ctrlr1 = length - 1; + qspi_reg->spidr = msg->instruction.content; + qspi_reg->spiar = msg->address.content; + if (tmod == SPI_TMOD_TO) + { + rt_memcpy(buf, msg_parent->send_buf, length * cell_size); + rt_hw_cpu_dcache_clean(buf, CACHE_ALIGN_TOP(length * cell_size)); + } + qspi_reg->axiar0 = (rt_uint32_t)((uint64_t)buf); + qspi_reg->axiar1 = (rt_uint32_t)((uint64_t)buf >> 32); + } + else + { + tmod = SPI_TMOD_TO; + qspi_reg->ctrlr0 |= (tmod << 10); + qspi_reg->txftlr = ((SSIC_TX_ABW - 1) << 16) | (SSIC_TX_ABW - 1); + qspi_reg->rxftlr = (SSIC_RX_ABW - 1); + qspi_reg->imr = 0; + qspi_reg->dmacr = 0; + } + rt_event_control(&qspi_bus->event, RT_IPC_CMD_RESET, 0); + + qspi_reg->ser = 1; + qspi_reg->ssienr = 1; + rt_uint32_t event; + rt_err_t err; + + /* + * Config QSPI address and instruction, + * if data is empty, unable dma and send address and instruction by write data register. + */ + if (length) + { + err = rt_event_recv(&qspi_bus->event, BIT(SSI_DONE) | BIT(SSI_AXIE), + RT_EVENT_FLAG_OR | RT_EVENT_FLAG_CLEAR, 1000, &event); + } + else + { + err = RT_EOK; + event = 0; + qspi_reg->dr[0] = msg->instruction.content; + length++; + if (msg->address.size) + { + qspi_reg->dr[0] = msg->address.content; + length++; + } + qspi_reg->txftlr = 0; + while ((qspi_reg->sr & 0x5) != 0x4); + } + qspi_reg->ser = 0; + qspi_reg->ssienr = 0; + if (err == -RT_ETIMEOUT) + { + LOG_E("qspi%d transfer data timeout", qspi_bus->idx); + if (buf) + { + rt_free_align(buf); + } + return 0; + } + if (event & BIT(SSI_AXIE)) + { + LOG_E("qspi%d dma error", qspi_bus->idx); + if (buf) + { + rt_free_align(buf); + } + return 0; + } + /* Read data from FIFO */ + if (tmod == SPI_TMOD_RO) + { + rt_hw_cpu_dcache_invalidate(buf, CACHE_ALIGN_TOP(length * cell_size)); + rt_memcpy(msg_parent->recv_buf, buf, length * cell_size); + } + if (buf) + { + rt_free_align(buf); + } + return length; + } + /* Standard SPI transfers */ + else + { + if (msg_parent->length == 0) + { + return 0; + } + rt_uint8_t cell_size = (qspi_cfg_parent->data_width + 7) >> 3; + rt_size_t length = msg_parent->length; + rt_size_t count = length > 0x10000 ? 0x10000 : length; + rt_size_t send_single = 0, send_length = 0, recv_single = 0, recv_length = 0; + void *send_buf = msg_parent->send_buf; + void *recv_buf = msg_parent->recv_buf; + rt_uint8_t tmod = send_buf ? SPI_TMOD_TO : SPI_TMOD_EPROMREAD; + tmod = recv_buf ? tmod & SPI_TMOD_RO : tmod; + /* Check Qspi Parameters */ + if (tmod == SPI_TMOD_EPROMREAD) + { + LOG_E("send_buf and recv_buf cannot both be empty"); + return 0; + } + if (tmod == SPI_TMOD_RO && qspi_cfg_parent->data_width == 8) + { + if ((msg->address.size & 7) || (msg->dummy_cycles & 7)) + { + LOG_E("instruction, address, dummy_cycles invalid"); + LOG_E("For read-only mode the instruction, address, dummy_cycles must be set to zero"); + LOG_E("For eeprom-read mode the instruction, address, dummy_cycles must be set to multiples of 8"); + return 0; + } + else if (msg->address.size) + { + if (length > 0x10000) + { + LOG_E("For eeprom-read mode, data length cannot exceed 0x10000"); + return 0; + } + tmod = SPI_TMOD_EPROMREAD; + } + } + /* Prepare the send buffer*/ + if (send_buf) + { + send_single = count; + send_buf = rt_malloc(count * cell_size); + if (send_buf == RT_NULL) + { + LOG_E("alloc mem error"); + return 0; + } + rt_memcpy(send_buf, msg_parent->send_buf, count * cell_size); + } + else if (tmod == SPI_TMOD_EPROMREAD) + { + send_single = 1 + msg->address.size / 8 + msg->dummy_cycles / 8; + send_buf = rt_malloc(send_single); + if (send_buf == RT_NULL) + { + LOG_E("alloc mem error"); + return 0; + } + rt_uint8_t *temp = send_buf; + *temp++ = msg->instruction.content; + for (int i = msg->address.size / 8; i; i--) + { + *temp++ = msg->address.content >> ((i - 1) * 8); + } + for (int i = msg->dummy_cycles / 8; i; i--) + { + *temp++ = 0xFF; + } + } + /* Prepare the receive buffer*/ + if (recv_buf) + { + recv_single = count; + recv_buf = rt_malloc(count * cell_size); + if (recv_buf == RT_NULL) + { + LOG_E("alloc mem error"); + if (send_buf) + { + rt_free(send_buf); + } + return 0; + } + } + send_length = 0; + recv_length = 0; + qspi_bus->cell_size = cell_size; + qspi_bus->send_buf = send_buf; + qspi_bus->recv_buf = recv_buf; + qspi_bus->send_length = send_single; + qspi_bus->recv_length = recv_single; + qspi_reg->ctrlr0 &= ~((3 << 22) | (3 << 10)); + qspi_reg->ctrlr0 |= (tmod << 10); + qspi_reg->ctrlr1 = count - 1; + qspi_reg->txftlr = ((SSIC_TX_ABW / 2) << 16) | (SSIC_TX_ABW / 2); + qspi_reg->rxftlr = count >= (SSIC_RX_ABW / 2) ? (SSIC_RX_ABW / 2 - 1) : count - 1; + qspi_reg->dmacr = 0; + /* Interrupt transmit or receive */ + qspi_reg->imr = (1 << 4) | (1 << 0); + rt_event_control(&qspi_bus->event, RT_IPC_CMD_RESET, 0); + qspi_reg->ser = 1; + qspi_reg->ssienr = 1; + if (tmod == SPI_TMOD_RO) + qspi_reg->dr[0] = 0; + rt_uint32_t event; + rt_err_t err; + while (RT_TRUE) + { + /* Waiting for transfer events */ + err = rt_event_recv(&qspi_bus->event, BIT(SSI_TXE) | BIT(SSI_RXF), + RT_EVENT_FLAG_OR | RT_EVENT_FLAG_CLEAR, 10000, &event); + if (err == -RT_ETIMEOUT) + { + LOG_E("qspi%d transfer data timeout", qspi_bus->idx); + length = 0; + if (send_buf) + { + rt_free(send_buf); + } + if (recv_buf) + { + rt_free(recv_buf); + } + return 0; + } + /* Handle Transmit buffer empty */ + if (event & BIT(SSI_TXE)) + { + send_length += send_single; + if (send_length < length && tmod <= SPI_TMOD_TO) + { + count = length - send_length; + count = count > 0x10000 ? 0x10000 : count; + rt_memcpy(send_buf, msg_parent->send_buf + send_length * cell_size, count * cell_size); + qspi_bus->send_buf = send_buf; + qspi_bus->send_length = count; + send_single = count; + qspi_reg->txftlr = ((SSIC_TX_ABW / 2) << 16) | (SSIC_TX_ABW / 2); + if (tmod == SPI_TMOD_TO) + qspi_reg->imr |= (1 << 0); + } + else if (tmod == SPI_TMOD_TO) + { + while ((qspi_reg->sr & 0x5) != 0x4); + break; + } + } + /* Handle receive buffer full */ + if (event & BIT(SSI_RXF)) + { + rt_memcpy(msg_parent->recv_buf + recv_length * cell_size, recv_buf, recv_single * cell_size); + recv_length += recv_single; + if (recv_length >= length) + { + break; + } + count = length - recv_length; + count = count > 0x10000 ? 0x10000 : count; + qspi_bus->recv_buf = recv_buf; + qspi_bus->recv_length = count; + recv_single = count; + qspi_reg->rxftlr = count >= (SSIC_RX_ABW / 2) ? (SSIC_RX_ABW / 2 - 1) : count - 1; + if (tmod == SPI_TMOD_TR) + { + qspi_reg->imr |= (1 << 0) | (1 << 4); + } + else if (tmod == SPI_TMOD_RO) + { + qspi_reg->imr |= (1 << 4); + qspi_reg->ssienr = 0; + qspi_reg->ctrlr1 = count - 1; + qspi_reg->ssienr = 1; + qspi_reg->dr[0] = 0; + qspi_reg->dr[0] = 0; + } + } + } + qspi_reg->ser = 0; + qspi_reg->ssienr = 0; + if (send_buf) + { + rt_free(send_buf); + } + if (recv_buf) + { + rt_free(recv_buf); + } + return length; + } + return 0; +} + +static const struct rt_spi_ops k230_qspi_ops = +{ + .configure = k230_spi_configure, + .xfer = k230_spi_xfer, +}; + +static void k230_spi_irq(int vector, void *param) +{ + struct k230_spi_dev *qspi_bus = param; + k230_spi_reg_t *qspi_reg = (k230_spi_reg_t *)qspi_bus->base; + vector -= IRQN_SPI0; + vector %= (IRQN_SPI1 - IRQN_SPI0); + + /* Handle transmit buffer empty interrupt */ + if (vector == SSI_TXE) + { + if (qspi_bus->send_buf == RT_NULL) + { + qspi_reg->imr &= ~1; + } + else if (qspi_bus->cell_size == 1) + { + while ((qspi_bus->send_length) && (qspi_reg->sr & 2)) + { + qspi_reg->dr[0] = *((rt_uint8_t *)qspi_bus->send_buf); + qspi_bus->send_buf++; + qspi_bus->send_length--; + } + } + else if (qspi_bus->cell_size == 2) + { + while ((qspi_bus->send_length) && (qspi_reg->sr & 2)) + { + qspi_reg->dr[0] = *((rt_uint16_t *)qspi_bus->send_buf); + qspi_bus->send_buf += 2; + qspi_bus->send_length--; + } + } + else if (qspi_bus->cell_size == 4) + { + while ((qspi_bus->send_length) && (qspi_reg->sr & 2)) + { + qspi_reg->dr[0] = *((rt_uint32_t *)qspi_bus->send_buf); + qspi_bus->send_buf += 4; + qspi_bus->send_length--; + } + } + else + { + LOG_E("qspi%d datawidth error", qspi_bus->idx); + } + if (qspi_bus->send_length == 0) + { + if (((qspi_reg->ctrlr0 >> 10) & SPI_TMOD_EPROMREAD) == SPI_TMOD_TO) + { + if (qspi_reg->txftlr) + return; + } + qspi_reg->txftlr = 0; + qspi_reg->imr &= ~1; + rt_event_send(&qspi_bus->event, BIT(SSI_TXE)); + } + } + /* Handle receive buffer full interrupt */ + else if (vector == SSI_RXF) + { + if (qspi_bus->recv_buf == RT_NULL) + { + qspi_reg->imr &= ~0x10; + } + else if (qspi_bus->cell_size == 1) + { + while ((qspi_bus->recv_length) && (qspi_reg->sr & 8)) + { + *((rt_uint8_t *)qspi_bus->recv_buf) = qspi_reg->dr[0]; + qspi_bus->recv_buf++; + qspi_bus->recv_length--; + } + } + else if (qspi_bus->cell_size == 2) + { + while ((qspi_bus->recv_length) && (qspi_reg->sr & 8)) + { + *((rt_uint16_t *)qspi_bus->recv_buf) = qspi_reg->dr[0]; + qspi_bus->recv_buf += 2; + qspi_bus->recv_length--; + } + } + else if (qspi_bus->cell_size == 4) + { + while ((qspi_bus->recv_length) && (qspi_reg->sr & 8)) + { + *((rt_uint32_t *)qspi_bus->recv_buf) = qspi_reg->dr[0]; + qspi_bus->recv_buf += 4; + qspi_bus->recv_length--; + } + } + else + { + LOG_E("qspi%d datawidth error", qspi_bus->idx); + } + if (qspi_bus->recv_length == 0) + { + qspi_reg->imr &= ~0x10; + rt_event_send(&qspi_bus->event, BIT(SSI_RXF)); + } + else if (qspi_bus->recv_length <= qspi_reg->rxftlr) + { + qspi_reg->rxftlr = qspi_bus->recv_length - 1; + } + } + /* Handle transfer complete interrupt */ + else if (vector == SSI_DONE) + { + qspi_reg->donecr; + rt_event_send(&qspi_bus->event, BIT(SSI_DONE)); + } + /* Handle DMA error interrupt */ + else if (vector == SSI_AXIE) + { + qspi_reg->axiecr; + rt_event_send(&qspi_bus->event, BIT(SSI_AXIE)); + } +} +static struct k230_spi_dev k230_spi_devs[] = +{ +#ifdef BSP_USING_SPI0 + { + .name = "spi0", + .event_name = "spi0_event", + .pa_base = SPI_OPI_BASE_ADDR, + .size = SPI_OPI_IO_SIZE, + .vector = IRQN_SPI0, + .idx = 0, + .rdse = 0, + .rdsd = 0, + .max_line = 8, + .max_hz = 200000000, + }, +#endif +#ifdef BSP_USING_SPI1 + { + .name = "spi1", + .event_name = "spi1_event", + .pa_base = SPI_QOPI_BASE_ADDR, + .size = SPI_QOPI_IO_SIZE / 2, + .vector = IRQN_SPI1, + .idx = 1, + .rdse = 0, + .rdsd = 0, + .max_line = 4, + .max_hz = 100000000, + }, +#endif +#ifdef BSP_USING_SPI2 + { + .name = "spi2", + .event_name = "spi2_event", + .pa_base = SPI_QOPI_BASE_ADDR + SPI_QOPI_IO_SIZE / 2, + .size = SPI_QOPI_IO_SIZE / 2, + .vector = IRQN_SPI2, + .idx = 2, + .rdse = 0, + .rdsd = 0, + .max_line = 4, + .max_hz = 100000000, + }, +#endif +}; + +int rt_hw_qspi_bus_init(void) +{ + rt_err_t ret; + int i; + for (i = 0; i < sizeof(k230_spi_devs) / sizeof(k230_spi_devs[0]); i++) + { + k230_spi_devs[i].base = (rt_ubase_t)rt_ioremap((void *)k230_spi_devs[i].pa_base, k230_spi_devs[i].size); + ret = rt_qspi_bus_register(&k230_spi_devs[i].dev, k230_spi_devs[i].name, &k230_qspi_ops); + if (ret) + { + LOG_E("%s register fail", k230_spi_devs[i].name); + return ret; + } + rt_event_init(&k230_spi_devs[i].event, k230_spi_devs[i].event_name, RT_IPC_FLAG_PRIO); + rt_hw_interrupt_install(k230_spi_devs[i].vector + SSI_TXE, k230_spi_irq, &k230_spi_devs[i], k230_spi_devs[i].name); + rt_hw_interrupt_umask(k230_spi_devs[i].vector + SSI_TXE); + rt_hw_interrupt_install(k230_spi_devs[i].vector + SSI_RXF, k230_spi_irq, &k230_spi_devs[i], k230_spi_devs[i].name); + rt_hw_interrupt_umask(k230_spi_devs[i].vector + SSI_RXF); + rt_hw_interrupt_install(k230_spi_devs[i].vector + SSI_DONE, k230_spi_irq, &k230_spi_devs[i], k230_spi_devs[i].name); + rt_hw_interrupt_umask(k230_spi_devs[i].vector + SSI_DONE); + rt_hw_interrupt_install(k230_spi_devs[i].vector + SSI_AXIE, k230_spi_irq, &k230_spi_devs[i], k230_spi_devs[i].name); + rt_hw_interrupt_umask(k230_spi_devs[i].vector + SSI_AXIE); + } + return RT_EOK; +} +INIT_DEVICE_EXPORT(rt_hw_qspi_bus_init); diff --git a/bsp/k230/drivers/interdrv/spi/drv_spi.h b/bsp/k230/drivers/interdrv/spi/drv_spi.h new file mode 100644 index 00000000000..282368d3832 --- /dev/null +++ b/bsp/k230/drivers/interdrv/spi/drv_spi.h @@ -0,0 +1,203 @@ +/* Copyright (c) 2023, Canaan Bright Sight Co., Ltd + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND + * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +/* + * Copyright (c) 2006-2025 RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + */ +#ifndef __DRV_SPI_H__ +#define __DRV_SPI_H__ + +#include +#include + +#define SSIC_HAS_DMA 2 +#define SSIC_AXI_BLW 8 +#define SSIC_TX_ABW 256 +#define SSIC_RX_ABW 256 + +#define IRQN_SPI0 146 +#define IRQN_SPI1 155 +#define IRQN_SPI2 164 + +#ifndef L1_CACHE_BYTES + #define L1_CACHE_BYTES 64 +#endif + +#define CACHE_ALIGN_TOP(x) (((x) + L1_CACHE_BYTES - 1) & ~(L1_CACHE_BYTES - 1)) +#define CACHE_ALIGN_BOTTOM(x) ((x) & ~(L1_CACHE_BYTES - 1)) +#define BIT(n) (1UL << (n)) + +enum +{ + SSI_TXE = 0, + SSI_TXO, + SSI_RXF, + SSI_RXO, + SSI_TXU, + SSI_RXU, + SSI_MST, + SSI_DONE, + SSI_AXIE, +}; +/* SPI mode */ +enum +{ + SPI_FRF_STD_SPI, + SPI_FRF_DUAL_SPI, + SPI_FRF_QUAD_SPI, + SPI_FRF_OCT_SPI, +}; + +/* SPI transmit mode */ +enum +{ + SPI_TMOD_TR, + SPI_TMOD_TO, + SPI_TMOD_RO, + SPI_TMOD_EPROMREAD, +}; + +/* Qspi register */ +typedef struct +{ + /* SPI Control Register 0 (0x00)*/ + volatile uint32_t ctrlr0; + /* SPI Control Register 1 (0x04)*/ + volatile uint32_t ctrlr1; + /* SPI Enable Register (0x08)*/ + volatile uint32_t ssienr; + /* SPI Microwire Control Register (0x0c)*/ + volatile uint32_t mwcr; + /* SPI Slave Enable Register (0x10)*/ + volatile uint32_t ser; + /* SPI Baud Rate Select (0x14)*/ + volatile uint32_t baudr; + /* SPI Transmit FIFO Threshold Level (0x18)*/ + volatile uint32_t txftlr; + /* SPI Receive FIFO Threshold Level (0x1c)*/ + volatile uint32_t rxftlr; + /* SPI Transmit FIFO Level Register (0x20)*/ + volatile uint32_t txflr; + /* SPI Receive FIFO Level Register (0x24)*/ + volatile uint32_t rxflr; + /* SPI Status Register (0x28)*/ + volatile uint32_t sr; + /* SPI Interrupt Mask Register (0x2c)*/ + volatile uint32_t imr; + /* SPI Interrupt Status Register (0x30)*/ + volatile uint32_t isr; + /* SPI Raw Interrupt Status Register (0x34)*/ + volatile uint32_t risr; + /* SPI Transmit FIFO Underflow Interrupt Clear Register (0x38)*/ + volatile uint32_t txeicr; + /* SPI Receive FIFO Overflow Interrupt Clear Register (0x3c)*/ + volatile uint32_t rxoicr; + /* SPI Receive FIFO Underflow Interrupt Clear Register (0x40)*/ + volatile uint32_t rxuicr; + /* SPI Multi-Master Interrupt Clear Register (0x44)*/ + volatile uint32_t msticr; + /* SPI Interrupt Clear Register (0x48)*/ + volatile uint32_t icr; + /* SPI DMA Control Register (0x4c)*/ + volatile uint32_t dmacr; +#if SSIC_HAS_DMA == 1 + /* SPI DMA Transmit Data Level (0x50)*/ + volatile uint32_t dmatdlr; + /* SPI DMA Receive Data Level (0x54)*/ + volatile uint32_t dmardlr; +#elif SSIC_HAS_DMA == 2 + /* SPI Destination Burst Length (0x50)*/ + volatile uint32_t axiawlen; + /* SPI Source Burst Length (0x54)*/ + volatile uint32_t axiarlen; +#else + uint32_t resv0[2]; +#endif + /* SPI Identification Register (0x58)*/ + volatile const uint32_t idr; + /* SPI DWC_ssi component version (0x5c)*/ + volatile uint32_t ssic_version_id; + /* SPI Data Register 0-36 (0x60 -- 0xec)*/ + volatile uint32_t dr[36]; + /* SPI RX Sample Delay Register (0xf0)*/ + volatile uint32_t rx_sample_delay; + /* SPI SPI Control Register (0xf4)*/ + volatile uint32_t spi_ctrlr0; + /* SPI Transmit Drive Edge Register (0xf8)*/ + volatile uint32_t ddr_drive_edge; + /* SPI XIP Mode bits (0xfc)*/ + volatile uint32_t xip_mode_bits; + /* SPI XIP INCR transfer opcode (0x100)*/ + volatile uint32_t xip_incr_inst; + /* SPI XIP WRAP transfer opcode (0x104)*/ + volatile uint32_t xip_wrap_inst; +#if SSIC_CONCURRENT_XIP_EN + /* SPI XIP Control Register (0x108)*/ + volatile uint32_t xip_ctrl; + /* SPI XIP Slave Enable Register (0x10c)*/ + volatile uint32_t xip_ser; + /* SPI XIP Receive FIFO Overflow Interrupt Clear Register (0x110)*/ + volatile uint32_t xrxoicr; + /* SPI XIP time out register for continuous transfers (0x114)*/ + volatile uint32_t xip_cnt_time_out; + /* not support dyn ws (0x118)*/ + uint32_t resv1[1]; + /* SPI Transmit Error Interrupt Clear Register (0x11c)*/ + volatile uint32_t spitecr; +#else + uint32_t resv1[6]; +#endif +#if SSIC_HAS_DMA == 2 + /* SPI Device Register (0x120)*/ + volatile uint32_t spidr; + /* SPI Device Address Register (0x124)*/ + volatile uint32_t spiar; + /* AXI Address Register 0 (0x128)*/ + volatile uint32_t axiar0; + /* AXI Address Register 1 (0x12c)*/ + volatile uint32_t axiar1; + /* AXI Master Error Interrupt Clear Register (0x130)*/ + volatile uint32_t axiecr; + /* Transfer Done Clear Interrupt Clear Register (0x134)*/ + volatile uint32_t donecr; +#endif + /* This register will not be used and is reserved. (0x138 ~ 0x13c)*/ + uint32_t resv3[2]; +#if SSIC_XIP_WRITE_REG_EN + /* XIP_WRITE_INCR_INST - XIP Write INCR transfer opcode (0x140)*/ + volatile uint32_t xip_write_incr_inst; + /* XIP_WRITE_WRAP_INST - XIP Write WRAP transfer opcode (0x144)*/ + volatile uint32_t xip_write_wrap_inst; + /* XIP_WRITE_CTRL - XIP Write Control Register (0x148)*/ + volatile uint32_t xip_write_ctrl; +#else + uint32_t resv4[3]; +#endif + // volatile uint32_t endian; +} __attribute__((packed, aligned(4))) k230_spi_reg_t; + + +#endif diff --git a/bsp/k230/drivers/utest/SConscript b/bsp/k230/drivers/utest/SConscript index a6fc1e57a8e..8fd31aa6f49 100644 --- a/bsp/k230/drivers/utest/SConscript +++ b/bsp/k230/drivers/utest/SConscript @@ -32,6 +32,9 @@ if GetDepend('BSP_UTEST_DRIVERS'): if GetDepend('BSP_USING_I2C'): src += ['test_i2c.c'] + + if GetDepend('BSP_USING_SPI'): + src += ['test_spi.c'] group = DefineGroup('utestcases', src, depend = ['']) diff --git a/bsp/k230/drivers/utest/test_spi.c b/bsp/k230/drivers/utest/test_spi.c new file mode 100644 index 00000000000..ae224233d72 --- /dev/null +++ b/bsp/k230/drivers/utest/test_spi.c @@ -0,0 +1,222 @@ +/* Copyright (c) 2023, Canaan Bright Sight Co., Ltd + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND + * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +/* + * Copyright (c) 2006-2025 RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + */ +#include +#include +#include +#include +#include "drv_spi.h" +#include +#include "drv_pinctrl.h" +#include "drv_gpio.h" +/* + * 测试 SPI0 在标准SPI模式下的数据发送功能 + * + * 功能说明: + * - 查找名为 "spi0" 的SPI总线设备; + * - 挂载SPI设备到总线; + * - 配置SPI设备参数: + * - 模式:标准SPI模式0 (RT_SPI_MODE_0) + * - 数据位:8位 + * - 最大频率:1MHz + * - 数据线宽度:1(标准SPI) + * - 准备测试数据(递增序列); + * - 创建SPI消息并发送16字节数据; + * - 发送完成接收从机的16字节数据; + * - 发送完成后卸载SPI设备。 + * + * 硬件说明: + * - 本测试基于 K230 平台; + * - 测试SPI0(OSPI)的标准SPI模式TX和RX功能,使用硬件CS; + * - 对应的引脚配置为: + * - CS: GPIO14 + * - CLK: GPIO15 + * - D0: GPIO16 + * - D1: GPIO17 + * - 需要连接SPI从设备(如SPI调试器等)来验证数据传输,本测试文件使用一块stm32制作的SPI调试器; + * - 如果没有实际从设备,可以使用逻辑分析仪或示波器观察SPI波形(但是只能验证TX功能,接收到会是16bit的0xff); + */ +#define SPI0_BUS_NAME "spi0" +#define SPI0_DEV_NAME0 "spi00" +#define TEST_DATA_LENGTH 16 +#define SPI0_CS_PIN 14 +#define SPI0_CLK_PIN 15 +#define SPI0_D0_PIN 16 +#define SPI0_D1_PIN 17 +#define SPI0_CS_PIN_AF IOMUX_FUNC2 +#define SPI0_CLK_PIN_AF IOMUX_FUNC2 +#define SPI0_D0_PIN_AF IOMUX_FUNC2 +#define SPI0_D1_PIN_AF IOMUX_FUNC2 + +static void spi_gpio_init(void) +{ + LOG_I("SPI demo: initializing SPI0 GPIO..."); + k230_pinctrl_set_function(SPI0_CS_PIN, SPI0_CS_PIN_AF); + k230_pinctrl_set_function(SPI0_CLK_PIN, SPI0_CLK_PIN_AF); + k230_pinctrl_set_function(SPI0_D0_PIN, SPI0_D0_PIN_AF); + k230_pinctrl_set_function(SPI0_D1_PIN, SPI0_D1_PIN_AF); + + k230_pinctrl_set_oe(SPI0_CS_PIN, 1); + k230_pinctrl_set_oe(SPI0_CLK_PIN, 1); + k230_pinctrl_set_oe(SPI0_D0_PIN, 1); + k230_pinctrl_set_oe(SPI0_D1_PIN, 1); + + k230_pinctrl_set_ie(SPI0_CS_PIN, 1); + k230_pinctrl_set_ie(SPI0_CLK_PIN, 1); + k230_pinctrl_set_ie(SPI0_D0_PIN, 1); + k230_pinctrl_set_ie(SPI0_D1_PIN, 1); +} + +static void spi_device_demo(void) +{ + struct rt_qspi_device *qspi_dev; + LOG_I("Using rt_qspi_device to transmit"); + rt_err_t ret; + uint8_t tx_data[TEST_DATA_LENGTH]; + uint8_t rx_data[TEST_DATA_LENGTH]; + for (int i = 0; i < TEST_DATA_LENGTH; i++) + { + tx_data[i] = i; + } + rt_memset(rx_data, 0, sizeof(rx_data)); + + /* Find QSPI Bus */ + struct rt_spi_bus *spi_bus = (struct rt_spi_bus *)rt_device_find(SPI0_BUS_NAME); + if (!spi_bus) + { + LOG_E("Failed to find SPI bus: %s", SPI0_BUS_NAME); + return; + } + LOG_I("Success to find SPI bus: %s", SPI0_BUS_NAME); + + qspi_dev = (struct rt_qspi_device *)rt_malloc(sizeof(struct rt_qspi_device)); + if (!qspi_dev) + { + LOG_E("Failed to allocate SPI device memory"); + return; + } + LOG_I("Success to allocate QSPI device memory"); + /* Attach SPI Device */ + ret = rt_spi_bus_attach_device(&(qspi_dev->parent), SPI0_DEV_NAME0, SPI0_BUS_NAME, RT_NULL); + if (ret != RT_EOK) + { + LOG_E("Failed to attach SPI device: %d", ret); + rt_free(qspi_dev); + return; + } + LOG_I("SPI device attached successfully"); + /* SPI Device Config*/ + struct rt_qspi_configuration qspi_cfg; + qspi_cfg.parent.mode = RT_SPI_MODE_0 | RT_SPI_MSB; + qspi_cfg.parent.data_width = 8; + qspi_cfg.parent.max_hz = 1000000; + qspi_cfg.parent.reserved = 0; + qspi_cfg.qspi_dl_width = 1; + qspi_cfg.medium_size = 0; + qspi_cfg.ddr_mode = 0; + + + ret = rt_qspi_configure(qspi_dev, &qspi_cfg); + if (ret != RT_EOK) + { + LOG_E("SPI configuration failed: %d", ret); + rt_free(qspi_dev); + return; + } + + LOG_I("SPI configuration: Standard SPI, mode=0, data_width=8, max_hz=%d, data_lines=%d", + qspi_cfg.parent.max_hz, qspi_cfg.qspi_dl_width); + LOG_I("Sending test data (length=%d):", TEST_DATA_LENGTH); + + for (int i = 0; i < TEST_DATA_LENGTH; i++) + { + rt_kprintf("%02X ", tx_data[i]); + } + rt_kprintf("\n"); + + /* Create SPI Message */ + struct rt_qspi_message msg; + rt_memset(&msg, 0, sizeof(msg)); + /*Using Standard SPI*/ + msg.instruction.content = 0; + msg.instruction.qspi_lines = 1; + msg.address.content = 0; + msg.address.size = 0; + msg.address.qspi_lines = 1; + msg.qspi_data_lines = 1; + msg.dummy_cycles = 0; + + /* SPI Message Config */ + msg.parent.send_buf = tx_data; + msg.parent.recv_buf = rx_data; + msg.parent.length = TEST_DATA_LENGTH; + msg.parent.cs_take = 1; + msg.parent.cs_release = 1; + msg.parent.next = RT_NULL; + + /* Transfer Data */ + ret = rt_qspi_transfer_message(qspi_dev, &msg); + if (ret != TEST_DATA_LENGTH) + { + LOG_E("SPI transfer failed, returned: %d", ret); + } + uassert_int_equal(ret, TEST_DATA_LENGTH); + + LOG_I("SPI TX demo: sent %d bytes successfully", ret); + LOG_I("Received data from slave (length=%d):", TEST_DATA_LENGTH); + for (int i = 0; i < TEST_DATA_LENGTH; i++) + { + rt_kprintf("%02X ", rx_data[i]); + } + rt_kprintf("\n"); + /* Detach SPI Device */ + ret = rt_spi_bus_detach_device(&(qspi_dev->parent)); + uassert_int_equal(ret, RT_EOK); + rt_free(qspi_dev); +} + +static void testcase(void) +{ + UTEST_UNIT_RUN(spi_gpio_init); + UTEST_UNIT_RUN(spi_device_demo); +} + +static rt_err_t utest_tc_init(void) +{ + LOG_I("SPI test case initialization"); + return RT_EOK; +} + +static rt_err_t utest_tc_cleanup(void) +{ + LOG_I("SPI test case cleanup"); + return RT_EOK; +} + +UTEST_TC_EXPORT(testcase, "bsp.k230.drivers.spi", utest_tc_init, utest_tc_cleanup, 10); diff --git a/bsp/k230/rtconfig.h b/bsp/k230/rtconfig.h index e0a4e114508..63d97d8f2cf 100644 --- a/bsp/k230/rtconfig.h +++ b/bsp/k230/rtconfig.h @@ -503,6 +503,10 @@ /* GD32 Drivers */ /* end of GD32 Drivers */ + +/* HPMicro SDK */ + +/* end of HPMicro SDK */ /* end of HAL & SDK Drivers */ /* sensors drivers */