From 3253f461f708b8a4e095924288954a5a60f3d6c4 Mon Sep 17 00:00:00 2001 From: CoreBoxer <1289686261@qq.com> Date: Tue, 2 Jun 2026 14:10:38 +0800 Subject: [PATCH] [bsp][nxp] support qspi flash and filesystem for imxrt1180-evk board --- .../imxrt1180-nxp-evk/cm33/applications/mnt.c | 100 ++++ .../imxrt1180-nxp-evk/cm33/board/Kconfig | 9 + .../imxrt1180-nxp-evk/cm33/board/SConscript | 3 + .../cm33/board/linker_scripts/link.scf | 1 + .../cm33/board/ports/drv_flexspi_nor_flash.c | 521 ++++++++++++++++++ .../cm33/board/ports/drv_flexspi_nor_flash.h | 59 ++ .../cm33/board/ports/fal_cfg.h | 37 ++ .../cm33/board/ports/fal_flash_port.c | 57 ++ .../cm33/board/ports/flexspi_port.h | 62 +++ .../imx/imxrt/libraries/drivers/drv_flexspi.c | 132 ++++- .../imx/imxrt/libraries/drivers/drv_flexspi.h | 46 ++ 11 files changed, 1023 insertions(+), 4 deletions(-) create mode 100644 bsp/nxp/imx/imxrt/imxrt1180-nxp-evk/cm33/applications/mnt.c create mode 100644 bsp/nxp/imx/imxrt/imxrt1180-nxp-evk/cm33/board/ports/drv_flexspi_nor_flash.c create mode 100644 bsp/nxp/imx/imxrt/imxrt1180-nxp-evk/cm33/board/ports/drv_flexspi_nor_flash.h create mode 100644 bsp/nxp/imx/imxrt/imxrt1180-nxp-evk/cm33/board/ports/fal_cfg.h create mode 100644 bsp/nxp/imx/imxrt/imxrt1180-nxp-evk/cm33/board/ports/fal_flash_port.c create mode 100644 bsp/nxp/imx/imxrt/imxrt1180-nxp-evk/cm33/board/ports/flexspi_port.h create mode 100644 bsp/nxp/imx/imxrt/libraries/drivers/drv_flexspi.h diff --git a/bsp/nxp/imx/imxrt/imxrt1180-nxp-evk/cm33/applications/mnt.c b/bsp/nxp/imx/imxrt/imxrt1180-nxp-evk/cm33/applications/mnt.c new file mode 100644 index 00000000000..854f9aa21ff --- /dev/null +++ b/bsp/nxp/imx/imxrt/imxrt1180-nxp-evk/cm33/applications/mnt.c @@ -0,0 +1,100 @@ +/* + * Copyright (c) 2019-2024, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2026-06-02 CoreBoxer add LittleFS on QSPI NOR Flash (IMXRT1180-EVK) + */ + +#include + +/* ==================================================================== + * QSPI NOR Flash LittleFS (controlled by BSP_USING_QSPI_FLASH_FS) + * + * Dependencies: + * - drv_flash.c (Flash read/write/erase driver via ROM API) + * - FAL framework (partition management) + * - LittleFS component (RT-Thread package) + * + * FAL partition configuration (fal_cfg.h): + * "filesystem" partition → filesystem area of norflash0 + * + * Mount procedure: + * 1. Find FAL partition to confirm correct configuration + * 2. Create MTD NOR block device + * 3. Try direct mount (for already formatted filesystem) + * 4. If failed, perform mkfs then remount (first use or format scenario) + * ==================================================================== */ +#ifdef BSP_USING_QSPI_FLASH_FS +#include +#include + +#define FS_PARTITION_NAME "filesystem" +#define FS_TYPE_NAME "lfs" +#define FS_MOUNT_POINT "/" + +static int _qspi_flash_fs_mount(void) +{ + struct fal_mtd_nor_device *mtd_dev; + int ret; + + /* Find FAL partition to confirm configuration is correct */ + if (fal_partition_find(FS_PARTITION_NAME) == RT_NULL) + { + rt_kprintf("[qspi_fs] partition '%s' not found, check fal_cfg.h\n", + FS_PARTITION_NAME); + return -RT_ERROR; + } + + /* Create MTD NOR block device for filesystem partition */ + mtd_dev = (struct fal_mtd_nor_device *)fal_mtd_nor_device_create(FS_PARTITION_NAME); + if (mtd_dev == RT_NULL) + { + rt_kprintf("[qspi_fs] failed to create MTD NOR device for '%s'\n", + FS_PARTITION_NAME); + return -RT_ERROR; + } + + /* Try direct mount (for already formatted flash) */ + ret = dfs_mount(FS_PARTITION_NAME, FS_MOUNT_POINT, FS_TYPE_NAME, 0, 0); + if (ret == 0) + { + rt_kprintf("[qspi_fs] '%s' mounted at '%s'\n", + FS_PARTITION_NAME, FS_MOUNT_POINT); + return RT_EOK; + } + + /* Mount failed, indicating flash is not formatted (first use scenario), perform mkfs */ + rt_kprintf("[qspi_fs] mount failed (ret=%d), formatting '%s'...\n", + ret, FS_PARTITION_NAME); + + ret = dfs_mkfs(FS_TYPE_NAME, FS_PARTITION_NAME); + if (ret != 0) + { + rt_kprintf("[qspi_fs] mkfs failed (ret=%d)\n", ret); + return -RT_ERROR; + } + + /* Remount after successful mkfs */ + ret = dfs_mount(FS_PARTITION_NAME, FS_MOUNT_POINT, FS_TYPE_NAME, 0, 0); + if (ret != 0) + { + rt_kprintf("[qspi_fs] mount failed after mkfs (ret=%d)\n", ret); + return -RT_ERROR; + } + + rt_kprintf("[qspi_fs] '%s' formatted and mounted at '%s'\n", + FS_PARTITION_NAME, FS_MOUNT_POINT); + return RT_EOK; +} +INIT_APP_EXPORT(_qspi_flash_fs_mount); +/* + * FAL initialization registered as INIT_COMPONENT_EXPORT: + * after Flash driver (INIT_DEVICE_EXPORT) + * before filesystem mount (INIT_APP_EXPORT) + */ +INIT_COMPONENT_EXPORT(fal_init); + +#endif /* BSP_USING_QSPI_FLASH_FS */ \ No newline at end of file diff --git a/bsp/nxp/imx/imxrt/imxrt1180-nxp-evk/cm33/board/Kconfig b/bsp/nxp/imx/imxrt/imxrt1180-nxp-evk/cm33/board/Kconfig index 12d8078d8c2..33fed97f51b 100644 --- a/bsp/nxp/imx/imxrt/imxrt1180-nxp-evk/cm33/board/Kconfig +++ b/bsp/nxp/imx/imxrt/imxrt1180-nxp-evk/cm33/board/Kconfig @@ -229,6 +229,15 @@ menu "Onboard Peripheral Drivers" select BSP_USING_SDIO select RT_USING_DFS_ELMFAT default n + + config BSP_USING_QSPI_FLASH_FS + bool "Enable QSPI Flash (LittleFS)" + select BSP_USING_FLEXSPI1 + select RT_USING_FAL + select FAL_PART_HAS_TABLE_CFG + select RT_USING_MTD_NOR + select PKG_USING_LITTLEFS + default n endif endmenu diff --git a/bsp/nxp/imx/imxrt/imxrt1180-nxp-evk/cm33/board/SConscript b/bsp/nxp/imx/imxrt/imxrt1180-nxp-evk/cm33/board/SConscript index 0cfbe5a55b1..ad731bbaf21 100644 --- a/bsp/nxp/imx/imxrt/imxrt1180-nxp-evk/cm33/board/SConscript +++ b/bsp/nxp/imx/imxrt/imxrt1180-nxp-evk/cm33/board/SConscript @@ -12,6 +12,9 @@ MCUX_Config/clock_config.c MCUX_Config/pin_mux.c """) +if GetDepend(['BSP_USING_QSPI_FLASH_FS']): + src += ['ports/drv_flexspi_nor_flash.c', 'ports/fal_flash_port.c'] + CPPPATH = [cwd,cwd + '/MCUX_Config',cwd + '/ports'] CPPDEFINES = ['CPU_MIMXRT1189CVM8C_cm33', 'MCUXPRESSO_SDK', 'MCUX_META_BUILD', 'MIMXRT1189_cm33_SERIES', 'XIP_BOOT_HEADER_ENABLE=1', 'XIP_BOOT_HEADER_DCD_ENABLE=1', 'XIP_EXTERNAL_FLASH=1', 'ARM_MATH_CM33'] diff --git a/bsp/nxp/imx/imxrt/imxrt1180-nxp-evk/cm33/board/linker_scripts/link.scf b/bsp/nxp/imx/imxrt/imxrt1180-nxp-evk/cm33/board/linker_scripts/link.scf index 9ee6de8571d..08a0efca766 100644 --- a/bsp/nxp/imx/imxrt/imxrt1180-nxp-evk/cm33/board/linker_scripts/link.scf +++ b/bsp/nxp/imx/imxrt/imxrt1180-nxp-evk/cm33/board/linker_scripts/link.scf @@ -173,6 +173,7 @@ LR_m_text m_text_start m_text_size ER_m_QuickAccessCode m_qacode_start m_qacode_size { + fsl_flexspi.o (+RO-CODE) .ANY (CodeQuickAccess) } diff --git a/bsp/nxp/imx/imxrt/imxrt1180-nxp-evk/cm33/board/ports/drv_flexspi_nor_flash.c b/bsp/nxp/imx/imxrt/imxrt1180-nxp-evk/cm33/board/ports/drv_flexspi_nor_flash.c new file mode 100644 index 00000000000..2132bd4d26f --- /dev/null +++ b/bsp/nxp/imx/imxrt/imxrt1180-nxp-evk/cm33/board/ports/drv_flexspi_nor_flash.c @@ -0,0 +1,521 @@ +/* + * Copyright (c) 2006-2026, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2026-06-03 CoreBoxer support IMXRT1180-EVK + */ + +#include +#include + +#include "drv_flexspi_nor_flash.h" +#include "drv_flexspi.h" +#include "flexspi_port.h" +#include "fsl_flexspi.h" +#include "fsl_cache.h" + +/* + * Logging configuration + */ +#define LOG_TAG "drv.flash" +#define LOG_LVL LOG_LVL_INFO +#include + +/* + * FLASH_BUSY_STATUS_POL = 1 : SR[bit] = 1 means busy + * FLASH_BUSY_STATUS_OFFSET = 0 : WIP bit is SR[0] + * FLASH_QUAD_ENABLE = 0x40 : write to SR to enable Quad mode + */ +#define FLASH_QUAD_ENABLE_VAL 0x40U + +/* WIP polling upper limit to prevent infinite loop on Flash failure */ +#define WIP_TIMEOUT_COUNT 2000000UL + +/* + * Internal write buffer (must reside in SRAM) + * + * Reason: src data may come from const sections in XIP Flash. + * During FLEXSPI IP write commands, AHB access is suspended. + * If CPU tries to read src via AHB at that moment, bus hang or + * corrupted data may occur. Pre-copying to SRAM avoids this. + */ +static uint8_t SDK_ALIGN(s_write_buf[QSPI_PAGE_SIZE], 4); + +/* Internal state */ +static imxrt_flexspi_handle_t *s_handle = RT_NULL; +static struct rt_mutex s_mutex; +static int s_inited = 0; + +/* + * _range_valid - check whether [offset, offset+size) is within Flash + */ +rt_inline int _range_valid(rt_uint32_t offset, rt_size_t size) +{ + if (size == 0U) + { + return 1; + } + if ((offset + (rt_uint32_t)size) < offset) + { + return 0; /* overflow */ + } + if ((offset + (rt_uint32_t)size) > QSPI_FLASH_SIZE) + { + return 0; + } + return 1; +} + +/* + * _addr_safe - check whether [offset, offset+size) is within the FS partition + */ +rt_inline int _addr_safe(rt_uint32_t offset, rt_size_t size) +{ + if (size == 0U) + { + return 1; + } + if ((offset + (rt_uint32_t)size) < offset) + { + return 0; + } + if (offset < QSPI_FS_OFFSET) + { + return 0; + } + if ((offset + (rt_uint32_t)size) > (QSPI_FS_OFFSET + QSPI_FS_SIZE)) + { + return 0; + } + return 1; +} + +/* + * Low-level NOR Flash operations (mapped from SDK flexspi_nor_flash_ops.c) + * + * All functions are marked QSPI_RAM_CODE (placed in ITCM) because they are + * called while a FLEXSPI IP command is in progress, during which AHB access + * is suspended and the CPU must fetch instructions from ITCM. + */ + +/* + * _nor_write_enable - send WREN command (SDK: flexspi_nor_write_enable) + */ +QSPI_RAM_CODE static status_t _nor_write_enable(rt_uint32_t base_addr) +{ + flexspi_transfer_t xfer; + + xfer.deviceAddress = base_addr; + xfer.port = FLASH_PORT; + xfer.cmdType = kFLEXSPI_Command; + xfer.SeqNumber = 1; + xfer.seqIndex = NOR_CMD_LUT_SEQ_IDX_WRITEENABLE; + + return FLEXSPI_TransferBlocking(s_handle->base, &xfer); +} + +/* + * _nor_wait_busy - poll WIP bit until Flash is idle (SDK: flexspi_nor_wait_bus_busy) + * + * W25Q128JWSIQ SR1[0] = WIP (Write In Progress) + * FLASH_BUSY_STATUS_POL = 1: bit set means busy + */ +QSPI_RAM_CODE static status_t _nor_wait_busy(void) +{ + rt_uint32_t read_val = 0; + rt_uint32_t timeout = 0; + status_t status; + rt_bool_t is_busy; + flexspi_transfer_t xfer; + + xfer.deviceAddress = 0; + xfer.port = FLASH_PORT; + xfer.cmdType = kFLEXSPI_Read; + xfer.SeqNumber = 1; + xfer.seqIndex = NOR_CMD_LUT_SEQ_IDX_READSTATUSREG; + xfer.data = &read_val; + xfer.dataSize = 1; + + do + { + status = FLEXSPI_TransferBlocking(s_handle->base, &xfer); + if (status != kStatus_Success) + { + return status; + } + + /* Busy detection logic identical to SDK example */ + if (FLASH_BUSY_STATUS_POL) + { + is_busy = (read_val & (1U << FLASH_BUSY_STATUS_OFFSET)) != 0U; + } + else + { + is_busy = (read_val & (1U << FLASH_BUSY_STATUS_OFFSET)) == 0U; + } + + if (++timeout > WIP_TIMEOUT_COUNT) + { + return kStatus_Timeout; + } + } + while (is_busy); + + return kStatus_Success; +} + +/* + * _nor_erase_sector - erase one 4KB sector (SDK: flexspi_nor_flash_erase_sector) + * + * @addr: Flash internal offset (not AHB address) + */ +QSPI_RAM_CODE static status_t _nor_erase_sector(rt_uint32_t addr) +{ + rt_base_t level; + status_t status; + flexspi_transfer_t xfer; + + level = rt_hw_interrupt_disable(); + + xfer.deviceAddress = addr; + xfer.port = FLASH_PORT; + xfer.cmdType = kFLEXSPI_Command; + xfer.SeqNumber = 1; + xfer.seqIndex = NOR_CMD_LUT_SEQ_IDX_WRITEENABLE; + + status = FLEXSPI_TransferBlocking(s_handle->base, &xfer); + if (status != kStatus_Success) + { + rt_hw_interrupt_enable(level); + return status; + } + + xfer.seqIndex = NOR_CMD_LUT_SEQ_IDX_ERASESECTOR; + status = FLEXSPI_TransferBlocking(s_handle->base, &xfer); + if (status != kStatus_Success) + { + rt_hw_interrupt_enable(level); + return status; + } + + status = _nor_wait_busy(); + if (status == kStatus_Success) + { + FLEXSPI_SoftwareReset(s_handle->base); + } + + rt_hw_interrupt_enable(level); + + return status; +} + +/* + * _nor_page_program - program one page (SDK: flexspi_nor_flash_page_program) + * + * Uses PAGEPROGRAM_QUAD (0x32, 1-1-4 mode) for faster write speed, + * consistent with the SDK example. src must be a 4-byte aligned SRAM buffer. + */ +QSPI_RAM_CODE static status_t _nor_page_program(rt_uint32_t addr, const rt_uint32_t *src) +{ + rt_base_t level; + status_t status; + flexspi_transfer_t xfer; + + level = rt_hw_interrupt_disable(); + + status = _nor_wait_busy(); + if (status != kStatus_Success) + { + rt_hw_interrupt_enable(level); + return status; + } + + status = _nor_write_enable(addr); + if (status != kStatus_Success) + { + rt_hw_interrupt_enable(level); + return status; + } + + xfer.deviceAddress = addr; + xfer.port = FLASH_PORT; + xfer.cmdType = kFLEXSPI_Write; + xfer.SeqNumber = 1; + xfer.seqIndex = NOR_CMD_LUT_SEQ_IDX_PAGEPROGRAM_QUAD; + xfer.data = (rt_uint32_t *)src; + xfer.dataSize = QSPI_PAGE_SIZE; + + status = FLEXSPI_TransferBlocking(s_handle->base, &xfer); + if (status != kStatus_Success) + { + rt_hw_interrupt_enable(level); + return status; + } + + status = _nor_wait_busy(); + if (status == kStatus_Success) + { + FLEXSPI_SoftwareReset(s_handle->base); + } + + rt_hw_interrupt_enable(level); + + return status; +} + +/* + * rt_qspi_flash_init - initialize the QSPI NOR Flash driver + * + * Note: drv_flexspi.c has already performed the following via INIT_DEVICE_EXPORT: + * - flexspi_clock_init + * - FLEXSPI_Init + * - FLEXSPI_SetFlashConfig + * - FLEXSPI_UpdateLUT + * - FLEXSPI_SoftwareReset + * This function only acquires the handle and verifies Flash connectivity. + */ +int rt_qspi_flash_init(void) +{ + rt_uint8_t vendor_id = 0; + rt_uint32_t temp = 0; + status_t status; + flexspi_transfer_t xfer; + + if (s_inited) + { + return RT_EOK; + } + + /* Acquire the controller handle already initialized by drv_flexspi.c */ + s_handle = imxrt_flexspi_get_handle(); + if ((s_handle == RT_NULL) || (s_handle->base == RT_NULL)) + { + LOG_E("FlexSPI handle not ready, check INIT order"); + return -RT_ERROR; + } + + /* Read Vendor ID to verify Flash connectivity (SDK: flexspi_nor_get_vendor_id) */ + xfer.deviceAddress = 0; + xfer.port = FLASH_PORT; + xfer.cmdType = kFLEXSPI_Read; + xfer.SeqNumber = 1; + xfer.seqIndex = NOR_CMD_LUT_SEQ_IDX_READID; + xfer.data = &temp; + xfer.dataSize = 1; + + status = FLEXSPI_TransferBlocking(s_handle->base, &xfer); + if (status != kStatus_Success) + { + LOG_E("read vendor ID failed, status=0x%X", (unsigned)status); + return -RT_ERROR; + } + + vendor_id = (rt_uint8_t)(temp & 0xFFU); + if ((vendor_id == 0x00U) || (vendor_id == 0xFFU)) + { + LOG_E("Flash not detected (vendor=0x%02X)", vendor_id); + return -RT_ERROR; + } + + LOG_I("Vendor ID: 0x%02X", vendor_id); + + /* + * W25Q128JWSIQ: QE bit is set by default from factory, no Quad Enable + * sequence is needed. For other Flash models, refer to SDK example + * flexspi_nor_enable_quad_mode() to add this step. + */ + + if (rt_mutex_init(&s_mutex, "flashmtx", RT_IPC_FLAG_PRIO) != RT_EOK) + { + LOG_E("mutex init failed"); + return -RT_ERROR; + } + + s_inited = 1; + + LOG_I("init OK, base=0x%08X total=%uKB sector=%uB page=%uB", + (unsigned)QSPI_FLASH_BASE, + (unsigned)(QSPI_FLASH_SIZE / 1024U), + (unsigned)QSPI_SECTOR_SIZE, + (unsigned)QSPI_PAGE_SIZE); + LOG_I("fs partition: 0x%08X~0x%08X (%uKB)", + (unsigned)(QSPI_FLASH_BASE + QSPI_FS_OFFSET), + (unsigned)(QSPI_FLASH_BASE + QSPI_FS_OFFSET + QSPI_FS_SIZE - 1U), + (unsigned)(QSPI_FS_SIZE / 1024U)); + + return RT_EOK; +} +INIT_COMPONENT_EXPORT(rt_qspi_flash_init); + +/* + * rt_qspi_flash_read - read data via AHB direct memcpy (XIP mapping) + * + * Read operations do not interfere with FlexSPI IP commands, so this + * function does not need to be placed in ITCM. + */ +int rt_qspi_flash_read(rt_uint32_t offset, rt_uint8_t *buf, rt_size_t size) +{ + if (!s_inited) + { + return -RT_ERROR; + } + if (size == 0U) + { + return 0; + } + if (buf == RT_NULL) + { + return -RT_EINVAL; + } + if (!_range_valid(offset, size)) + { + return -RT_EINVAL; + } + + rt_memcpy(buf, (const void *)(QSPI_FLASH_BASE + offset), size); + + return (int)size; +} + +/* + * rt_qspi_flash_mmap - zero-copy read by returning AHB mapping pointer + */ +void *rt_qspi_flash_mmap(rt_uint32_t offset, rt_size_t size) +{ + if (!s_inited) + { + return RT_NULL; + } + if (!_range_valid(offset, size)) + { + return RT_NULL; + } + return (void *)(QSPI_FLASH_BASE + offset); +} + +/* + * rt_qspi_flash_write - write data to Flash (page-granular) + * + * This function is placed in ITCM (QSPI_RAM_CODE). + * + * Constraints (aligned with LittleFS prog_size = 256): + * - offset must be page-aligned (256B) + * - size must be a multiple of page size (256B) + * + * Data path: src -> s_write_buf (SRAM) -> Flash + */ +QSPI_RAM_CODE int rt_qspi_flash_write(rt_uint32_t offset, const rt_uint8_t *buf, rt_size_t size) +{ + rt_size_t written = 0; + status_t status; + + if (!s_inited) + { + return -RT_ERROR; + } + if (size == 0U) + { + return 0; + } + if (buf == RT_NULL) + { + return -RT_EINVAL; + } + if (!_addr_safe(offset, size)) + { + return -RT_EINVAL; + } + if ((offset % QSPI_PAGE_SIZE != 0U) || (size % QSPI_PAGE_SIZE != 0U)) + { + return -RT_EINVAL; + } + + rt_mutex_take(&s_mutex, RT_WAITING_FOREVER); + + while (written < size) + { + /* + * Data must be relayed through an SRAM buffer. + * SDK example declares s_nor_program_buffer as SDK_ALIGN(static uint8_t, 4). + * s_write_buf is equivalently 4-byte aligned and resides in SRAM. + */ + rt_memcpy(s_write_buf, buf + written, QSPI_PAGE_SIZE); + + status = _nor_page_program(offset + written, (const rt_uint32_t *)s_write_buf); + if (status != kStatus_Success) + { + rt_mutex_release(&s_mutex); + return -RT_ERROR; + } + + /* + * Cache maintenance (consistent with SDK DCACHE_InvalidateByRange): + * Invalidate the AHB-mapped cache region after write completes to + * ensure subsequent memcpy/mmap reads return newly written data. + */ + DCACHE_InvalidateByRange(QSPI_FLASH_BASE + offset + written, QSPI_PAGE_SIZE); + + written += QSPI_PAGE_SIZE; + } + + rt_mutex_release(&s_mutex); + return (int)size; +} + +/* + * rt_qspi_flash_erase - erase Flash sectors + * + * This function is placed in ITCM (QSPI_RAM_CODE). + * + * offset and size must be sector-aligned (4KB). + * Only the filesystem partition is allowed to be erased (_addr_safe guard). + */ +QSPI_RAM_CODE int rt_qspi_flash_erase(rt_uint32_t offset, rt_size_t size) +{ + rt_uint32_t addr; + rt_uint32_t addr_end; + status_t status; + + if (!s_inited) + { + return -RT_ERROR; + } + if (size == 0U) + { + return 0; + } + if (!_addr_safe(offset, size)) + { + return -RT_EINVAL; + } + if ((offset % QSPI_SECTOR_SIZE != 0U) || (size % QSPI_SECTOR_SIZE != 0U)) + { + return -RT_EINVAL; + } + + addr = offset; + addr_end = offset + (rt_uint32_t)size; + + rt_mutex_take(&s_mutex, RT_WAITING_FOREVER); + + while (addr < addr_end) + { + status = _nor_erase_sector(addr); + if (status != kStatus_Success) + { + rt_mutex_release(&s_mutex); + return -RT_ERROR; + } + + /* Invalidate D-Cache for the erased AHB region */ + DCACHE_InvalidateByRange(QSPI_FLASH_BASE + addr, QSPI_SECTOR_SIZE); + + addr += QSPI_SECTOR_SIZE; + } + + rt_mutex_release(&s_mutex); + return (int)size; +} diff --git a/bsp/nxp/imx/imxrt/imxrt1180-nxp-evk/cm33/board/ports/drv_flexspi_nor_flash.h b/bsp/nxp/imx/imxrt/imxrt1180-nxp-evk/cm33/board/ports/drv_flexspi_nor_flash.h new file mode 100644 index 00000000000..aceb7680a61 --- /dev/null +++ b/bsp/nxp/imx/imxrt/imxrt1180-nxp-evk/cm33/board/ports/drv_flexspi_nor_flash.h @@ -0,0 +1,59 @@ +/* + * Copyright (c) 2006-2026, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2024-06-03 CoreBoxer support IMXRT1180-EVK + */ + +#ifndef __DRV_FLEXSPI_NOR_FLASH_H__ +#define __DRV_FLEXSPI_NOR_FLASH_H__ + +#include + +/* ============================================================ + * Flash Physical Parameters + * Source: SDK example app.h + * FLASH_SIZE = 0x4000 (Unit: KB) = 16MB + * FlexSPI1_AMBA_BASE = 0x28000000 + * ============================================================ */ +#define QSPI_FLASH_BASE FlexSPI1_AMBA_BASE /* 0x28000000 */ +#define QSPI_FLASH_SIZE (0x4000U * 1024U) /* 16MB (0x4000 KB) */ +#define QSPI_SECTOR_SIZE 0x00001000U /* 4KB */ +#define QSPI_PAGE_SIZE 256U /* 256B */ + +/* + * Partition Plan (Total 16MB): + * 0x00000000 ~ 0x007FFFFF: Application Code Area (8MB, limited by Scatter) + * 0x00800000 ~ 0x00FFFFFF: File System Area (8MB, LittleFS) + */ +#define QSPI_FS_OFFSET 0x00800000U +#define QSPI_FS_SIZE 0x00800000U + +/* ============================================================ + * RAM Code Section Attributes + * Write/Erase functions placed in CodeQuickAccess (ITCM), + * CPU can safely fetch instructions during FlexSPI IP command execution. + * ============================================================ */ +#if defined(__CC_ARM) || defined(__ARMCC_VERSION) + #define QSPI_RAM_CODE __attribute__((section("CodeQuickAccess"), noinline)) +#elif defined(__GNUC__) + #define QSPI_RAM_CODE __attribute__((section(".itcm.text"), noinline)) +#elif defined(__ICCARM__) + #define QSPI_RAM_CODE __ramfunc +#else + #define QSPI_RAM_CODE +#endif + +/* ============================================================ + * External APIs + * ============================================================ */ +int rt_qspi_flash_init(void); +int rt_qspi_flash_read(uint32_t offset, uint8_t *buf, size_t size); +int rt_qspi_flash_write(uint32_t offset, const uint8_t *buf, size_t size); +int rt_qspi_flash_erase(uint32_t offset, size_t size); +void *rt_qspi_flash_mmap(uint32_t offset, size_t size); + +#endif /* __DRV_FLEXSPI_NOR_FLASH_H__ */ \ No newline at end of file diff --git a/bsp/nxp/imx/imxrt/imxrt1180-nxp-evk/cm33/board/ports/fal_cfg.h b/bsp/nxp/imx/imxrt/imxrt1180-nxp-evk/cm33/board/ports/fal_cfg.h new file mode 100644 index 00000000000..75725428304 --- /dev/null +++ b/bsp/nxp/imx/imxrt/imxrt1180-nxp-evk/cm33/board/ports/fal_cfg.h @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2006-2024, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2024-01-01 RT-Thread first version + */ + +#ifndef __FAL_CFG_H__ +#define __FAL_CFG_H__ + +#include +#include + +extern const struct fal_flash_dev imxrt1180_nor_flash; + +/* flash device table */ +#define FAL_FLASH_DEV_TABLE \ +{ \ + &imxrt1180_nor_flash, \ +} + +/* ====================== Partition Configuration ========================== */ +#ifdef FAL_PART_HAS_TABLE_CFG + +/* partition table */ +#define FAL_PART_TABLE \ +{ \ + {FAL_PART_MAGIC_WROD, "app", "norflash0", 0, 8*1024*1024, 0}, \ + {FAL_PART_MAGIC_WROD, "filesystem", "norflash0", 8*1024*1024, 8*1024*1024, 0}, \ +} + +#endif /* FAL_PART_HAS_TABLE_CFG */ + +#endif /* __FAL_CFG_H__ */ diff --git a/bsp/nxp/imx/imxrt/imxrt1180-nxp-evk/cm33/board/ports/fal_flash_port.c b/bsp/nxp/imx/imxrt/imxrt1180-nxp-evk/cm33/board/ports/fal_flash_port.c new file mode 100644 index 00000000000..6e31c51c918 --- /dev/null +++ b/bsp/nxp/imx/imxrt/imxrt1180-nxp-evk/cm33/board/ports/fal_flash_port.c @@ -0,0 +1,57 @@ +/* + * Copyright (c) 2006-2026, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2026-06-03 CoreBoxer support IMXRT1180-EVK + */ + +#include +#include +#include + +#include "drv_flexspi_nor_flash.h" + +/* + * Logging configuration + */ +#define LOG_TAG "fal.flash" +#define LOG_LVL LOG_LVL_INFO +#include + +/* FAL ops bridge */ +#ifdef RT_USING_FAL + +static int _fal_init(void) +{ + return rt_qspi_flash_init(); +} + +static int _fal_read(long offset, rt_uint8_t *buf, size_t size) +{ + return rt_qspi_flash_read((rt_uint32_t)offset, buf, size); +} + +QSPI_RAM_CODE static int _fal_write(long offset, const rt_uint8_t *buf, size_t size) +{ + return rt_qspi_flash_write((rt_uint32_t)offset, buf, size); +} + +QSPI_RAM_CODE static int _fal_erase(long offset, size_t size) +{ + return rt_qspi_flash_erase((rt_uint32_t)offset, size); +} + +const struct fal_flash_dev imxrt1180_nor_flash = +{ + .name = "norflash0", + .addr = QSPI_FLASH_BASE, + .len = QSPI_FLASH_SIZE, + .blk_size = QSPI_SECTOR_SIZE, + .ops = { _fal_init, _fal_read, _fal_write, _fal_erase }, + .write_gran = QSPI_PAGE_SIZE, +}; + +#endif /* RT_USING_FAL */ diff --git a/bsp/nxp/imx/imxrt/imxrt1180-nxp-evk/cm33/board/ports/flexspi_port.h b/bsp/nxp/imx/imxrt/imxrt1180-nxp-evk/cm33/board/ports/flexspi_port.h new file mode 100644 index 00000000000..dc7cb6c69e6 --- /dev/null +++ b/bsp/nxp/imx/imxrt/imxrt1180-nxp-evk/cm33/board/ports/flexspi_port.h @@ -0,0 +1,62 @@ +/* + * Copyright (c) 2006-2022, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2022-08-15 xjy198903 The first version for rt1170 + * 2026-06-03 CoreBoxer support IMXRT1180-EVK + */ + +#ifndef FLEXSPI_PORT_H__ +#define FLEXSPI_PORT_H__ + +/* parameters for flexpsi peripheral */ +#define FLEXSPI1_CONTROL_BASE FLEXSPI1 +#define FLEXSPI2_CONTROL_BASE FLEXSPI2 +#define FLEXSPI_ROOT_CLK (12000000U) /* serial root clk: 12MHz*/ +#define FLASH_SIZE (16 * 1024) /* device size 16*1024(KB) = 16MB */ +#define ARD_SEQ_NUMBER 1 /* Sequence number for AHB read command */ +#define ARD_SEQ_INDEX 0 /* Sequence ID for AHB read command */ +#define AWR_SEQ_NUMBER 0 /* Sequence number for AHB write command */ +#define AWR_SEQ_INDEX 0 /* Sequence ID for AHB write command */ +#define ARD_SEQ_CMD 0xBB /* cmd for read */ +#define AWR_SEQ_CMD 0xAA /* cmd for write */ +#define FLEXSPI_RX_SAMPLE_CLOCK kFLEXSPI_ReadSampleClkLoopbackFromDqsPad +#define FLASH_PORT kFLEXSPI_PortA1 +#define CLOCK_SRC kCLOCK_FLEXSPI1_ClockRoot_MuxOscRc24M +#define CLOCK_DIV 2U +#define CUSTOM_LUT_LENGTH 60U +#define FLEXSPI1_AHB_DATA_ADDRESS (0x28000000U) +#define FLEXSPI2_AHB_DATA_ADDRESS (0x60000000U) + +#define COMBINATION_MODE 0U +#define FREE_RUNNING_MODE 0U + +#define EXAMPLE_FLEXSPI_AMBA_BASE FlexSPI1_AMBA_BASE +#define FLASH_PAGE_SIZE 256 +#define SECTOR_SIZE 0x1000 /* 4K */ +#define EXAMPLE_FLEXSPI_CLOCK kCLOCK_Flexspi1 +#define FLASH_PORT kFLEXSPI_PortA1 + +#define NOR_CMD_LUT_SEQ_IDX_READ_NORMAL 7 +#define NOR_CMD_LUT_SEQ_IDX_READ_FAST 13 +#define NOR_CMD_LUT_SEQ_IDX_READ_FAST_QUAD 0 +#define NOR_CMD_LUT_SEQ_IDX_READSTATUS 1 +#define NOR_CMD_LUT_SEQ_IDX_WRITEENABLE 2 +#define NOR_CMD_LUT_SEQ_IDX_ERASESECTOR 3 +#define NOR_CMD_LUT_SEQ_IDX_PAGEPROGRAM_SINGLE 6 +#define NOR_CMD_LUT_SEQ_IDX_PAGEPROGRAM_QUAD 4 +#define NOR_CMD_LUT_SEQ_IDX_READID 8 +#define NOR_CMD_LUT_SEQ_IDX_WRITESTATUSREG 9 +#define NOR_CMD_LUT_SEQ_IDX_ENTERQPI 10 +#define NOR_CMD_LUT_SEQ_IDX_EXITQPI 11 +#define NOR_CMD_LUT_SEQ_IDX_READSTATUSREG 12 +#define NOR_CMD_LUT_SEQ_IDX_ERASECHIP 5 + +#define FLASH_QUAD_ENABLE 0x40 +#define FLASH_BUSY_STATUS_POL 1 +#define FLASH_BUSY_STATUS_OFFSET 0 + +#endif /* FLEXSPI_PORT_H__ */ diff --git a/bsp/nxp/imx/imxrt/libraries/drivers/drv_flexspi.c b/bsp/nxp/imx/imxrt/libraries/drivers/drv_flexspi.c index 0cacdc75f0e..2fe2f2dcedf 100644 --- a/bsp/nxp/imx/imxrt/libraries/drivers/drv_flexspi.c +++ b/bsp/nxp/imx/imxrt/libraries/drivers/drv_flexspi.c @@ -6,10 +6,12 @@ * Change Logs: * Date Author Notes * 2022-09-14 xjy198903 the first version for 1170 + * 2026-06-03 CoreBoxer support IMXRT1180-EVK */ #include -#ifdef BSP_USING_FLEXSPI +#if defined(BSP_USING_FLEXSPI) && \ + (defined(SOC_IMXRT1170_SERIES) || defined(SOC_IMXRT1180_SERIES)) #include "board.h" #include @@ -17,16 +19,23 @@ #include #endif +#include "drv_flexspi.h" #include "flexspi_port.h" #include "fsl_flexspi.h" +#ifndef COMBINATION_MODE #define COMBINATION_MODE 1U +#endif + +#ifndef FREE_RUNNING_MODE #define FREE_RUNNING_MODE 1U +#endif #define FLEXSPI_DEBUG #define LOG_TAG "drv.flexspi" #include +#if defined(SOC_IMXRT1170_SERIES) static flexspi_device_config_t deviceconfig = { .flexspiRootClk = 12000000, .flashSize = FLASH_SIZE, @@ -50,6 +59,114 @@ const uint32_t customLUT[CUSTOM_LUT_LENGTH] = { [4 * ARD_SEQ_INDEX] = FLEXSPI_LUT_SEQ(kFLEXSPI_Command_READ_DDR, kFLEXSPI_8PAD, 0x04, kFLEXSPI_Command_STOP, kFLEXSPI_8PAD, 0), }; +#elif defined(SOC_IMXRT1180_SERIES) +static flexspi_device_config_t deviceconfig = { + .flexspiRootClk = 12000000, + .flashSize = FLASH_SIZE, + .CSIntervalUnit = kFLEXSPI_CsIntervalUnit1SckCycle, + .CSInterval = 2, + .CSHoldTime = 3, + .CSSetupTime = 3, + .dataValidTime = 0, + .columnspace = 0, + .enableWordAddress = 0, + .AWRSeqIndex = AWR_SEQ_INDEX, + .AWRSeqNumber = AWR_SEQ_NUMBER, + .ARDSeqIndex = ARD_SEQ_INDEX, + .ARDSeqNumber = ARD_SEQ_NUMBER, + .AHBWriteWaitUnit = kFLEXSPI_AhbWriteWaitUnit2AhbCycle, + .AHBWriteWaitInterval = 0, +}; + +const uint32_t customLUT[CUSTOM_LUT_LENGTH] = { + /* Normal read mode - SDR */ + [4 * NOR_CMD_LUT_SEQ_IDX_READ_NORMAL] = + FLEXSPI_LUT_SEQ(kFLEXSPI_Command_SDR, kFLEXSPI_1PAD, 0x03, + kFLEXSPI_Command_RADDR_SDR, kFLEXSPI_1PAD, 0x18), + [4 * NOR_CMD_LUT_SEQ_IDX_READ_NORMAL + 1] = + FLEXSPI_LUT_SEQ(kFLEXSPI_Command_READ_SDR, kFLEXSPI_1PAD, 0x04, + kFLEXSPI_Command_STOP, kFLEXSPI_1PAD, 0), + + /* Fast read mode - SDR */ + [4 * NOR_CMD_LUT_SEQ_IDX_READ_FAST] = + FLEXSPI_LUT_SEQ(kFLEXSPI_Command_SDR, kFLEXSPI_1PAD, 0x0B, + kFLEXSPI_Command_RADDR_SDR, kFLEXSPI_1PAD, 0x18), + [4 * NOR_CMD_LUT_SEQ_IDX_READ_FAST + 1] = + FLEXSPI_LUT_SEQ(kFLEXSPI_Command_DUMMY_SDR, kFLEXSPI_1PAD, 0x08, + kFLEXSPI_Command_READ_SDR, kFLEXSPI_1PAD, 0x04), + + /* Fast read quad mode - SDR */ + [4 * NOR_CMD_LUT_SEQ_IDX_READ_FAST_QUAD] = + FLEXSPI_LUT_SEQ(kFLEXSPI_Command_SDR, kFLEXSPI_1PAD, 0xEB, + kFLEXSPI_Command_RADDR_SDR, kFLEXSPI_4PAD, 0x18), + [4 * NOR_CMD_LUT_SEQ_IDX_READ_FAST_QUAD + 1] = + FLEXSPI_LUT_SEQ(kFLEXSPI_Command_DUMMY_SDR, kFLEXSPI_4PAD, 0x06, + kFLEXSPI_Command_READ_SDR, kFLEXSPI_4PAD, 0x04), + + /* Write Enable */ + [4 * NOR_CMD_LUT_SEQ_IDX_WRITEENABLE] = + FLEXSPI_LUT_SEQ(kFLEXSPI_Command_SDR, kFLEXSPI_1PAD, 0x06, + kFLEXSPI_Command_STOP, kFLEXSPI_1PAD, 0), + + /* Erase Sector */ + [4 * NOR_CMD_LUT_SEQ_IDX_ERASESECTOR] = + FLEXSPI_LUT_SEQ(kFLEXSPI_Command_SDR, kFLEXSPI_1PAD, 0x20, + kFLEXSPI_Command_RADDR_SDR, kFLEXSPI_1PAD, 0x18), + + /* Page Program - single mode */ + [4 * NOR_CMD_LUT_SEQ_IDX_PAGEPROGRAM_SINGLE] = + FLEXSPI_LUT_SEQ(kFLEXSPI_Command_SDR, kFLEXSPI_1PAD, 0x02, + kFLEXSPI_Command_RADDR_SDR, kFLEXSPI_1PAD, 0x18), + [4 * NOR_CMD_LUT_SEQ_IDX_PAGEPROGRAM_SINGLE + 1] = + FLEXSPI_LUT_SEQ(kFLEXSPI_Command_WRITE_SDR, kFLEXSPI_1PAD, 0x04, + kFLEXSPI_Command_STOP, kFLEXSPI_1PAD, 0), + + /* Page Program - quad mode */ + [4 * NOR_CMD_LUT_SEQ_IDX_PAGEPROGRAM_QUAD] = + FLEXSPI_LUT_SEQ(kFLEXSPI_Command_SDR, kFLEXSPI_1PAD, 0x32, + kFLEXSPI_Command_RADDR_SDR, kFLEXSPI_1PAD, 0x18), + [4 * NOR_CMD_LUT_SEQ_IDX_PAGEPROGRAM_QUAD + 1] = + FLEXSPI_LUT_SEQ(kFLEXSPI_Command_WRITE_SDR, kFLEXSPI_4PAD, 0x04, + kFLEXSPI_Command_STOP, kFLEXSPI_1PAD, 0), + + /* Read ID */ + [4 * NOR_CMD_LUT_SEQ_IDX_READID] = + FLEXSPI_LUT_SEQ(kFLEXSPI_Command_SDR, kFLEXSPI_1PAD, 0x9F, + kFLEXSPI_Command_READ_SDR, kFLEXSPI_1PAD, 0x04), + + /* Write Status Register */ + [4 * NOR_CMD_LUT_SEQ_IDX_WRITESTATUSREG] = + FLEXSPI_LUT_SEQ(kFLEXSPI_Command_SDR, kFLEXSPI_1PAD, 0x01, + kFLEXSPI_Command_WRITE_SDR, kFLEXSPI_1PAD, 0x04), + + /* Read status register */ + [4 * NOR_CMD_LUT_SEQ_IDX_READSTATUSREG] = + FLEXSPI_LUT_SEQ(kFLEXSPI_Command_SDR, kFLEXSPI_1PAD, 0x05, + kFLEXSPI_Command_READ_SDR, kFLEXSPI_1PAD, 0x04), + + /* Erase whole chip */ + [4 * NOR_CMD_LUT_SEQ_IDX_ERASECHIP] = + FLEXSPI_LUT_SEQ(kFLEXSPI_Command_SDR, kFLEXSPI_1PAD, 0xC7, + kFLEXSPI_Command_STOP, kFLEXSPI_1PAD, 0), +}; +#else +#error "Unsupported SOC for drv_flexspi" +#endif + +/* -------------------------------------------------------------------------- + * Internal handle + * -------------------------------------------------------------------------- */ +static imxrt_flexspi_handle_t s_flexspi_handle = +{ + .base = FLEXSPI1_CONTROL_BASE, + .port = FLASH_PORT, + .ahb_base = FLEXSPI1_AHB_DATA_ADDRESS +}; + +imxrt_flexspi_handle_t *imxrt_flexspi_get_handle(void) +{ + return &s_flexspi_handle; +} static void flexspi_clock_init(clock_root_t root, uint8_t src, uint8_t div) { @@ -58,19 +175,26 @@ static void flexspi_clock_init(clock_root_t root, uint8_t src, uint8_t div) CLOCK_SetRootClockMux(root, src); } -static int rt_hw_imxrt_flexspi_init(void) +FLEXSPI_RAM_CODE static int rt_hw_imxrt_flexspi_init(void) { flexspi_config_t config; FLEXSPI_Type *base; #ifdef BSP_USING_FLEXSPI1 base = FLEXSPI1_CONTROL_BASE; + s_flexspi_handle.base = FLEXSPI1_CONTROL_BASE; + s_flexspi_handle.port = FLASH_PORT; + s_flexspi_handle.ahb_base = FLEXSPI1_AHB_DATA_ADDRESS; + //Set root clk 80MHz + flexspi_clock_init(kCLOCK_Root_Flexspi1, CLOCK_SRC, CLOCK_DIV); #else base = FLEXSPI2_CONTROL_BASE; + s_flexspi_handle.base = FLEXSPI2_CONTROL_BASE; + s_flexspi_handle.port = FLASH_PORT; + s_flexspi_handle.ahb_base = FLEXSPI2_AHB_DATA_ADDRESS; + flexspi_clock_init(kCLOCK_Root_Flexspi2, CLOCK_SRC, CLOCK_DIV); #endif - //Set root clk 80MHz - flexspi_clock_init(kCLOCK_Root_Flexspi1, CLOCK_SRC, CLOCK_DIV); /*Get FLEXSPI default settings and configure the flexspi. */ FLEXSPI_GetDefaultConfig(&config); diff --git a/bsp/nxp/imx/imxrt/libraries/drivers/drv_flexspi.h b/bsp/nxp/imx/imxrt/libraries/drivers/drv_flexspi.h new file mode 100644 index 00000000000..d72df3481a3 --- /dev/null +++ b/bsp/nxp/imx/imxrt/libraries/drivers/drv_flexspi.h @@ -0,0 +1,46 @@ +/* + * Copyright (c) 2006-2026, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2026-06-03 CoreBoxer support IMXRT1180-EVK + */ + +#ifndef __DRV_FLEXSPI_H__ +#define __DRV_FLEXSPI_H__ + +#include +#include "fsl_flexspi.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/* Put critical transfer code into ITCM (Code TCM) */ +#if defined(__CC_ARM) || defined(__ARMCC_VERSION) +#define FLEXSPI_RAM_CODE __attribute__((section("CodeQuickAccess"), noinline)) +#elif defined(__GNUC__) +#define FLEXSPI_RAM_CODE __attribute__((section("CodeQuickAccess"), noinline)) +#elif defined(__ICCARM__) +#define FLEXSPI_RAM_CODE __ramfunc +#else +#define FLEXSPI_RAM_CODE +#endif + +typedef struct +{ + FLEXSPI_Type *base; + flexspi_port_t port; + uint32_t ahb_base; +} imxrt_flexspi_handle_t; + +/* Get initialized FlexSPI handle (FlexSPI1 or FlexSPI2 decided by BSP config) */ +imxrt_flexspi_handle_t *imxrt_flexspi_get_handle(void); + +#ifdef __cplusplus +} +#endif + +#endif /* __DRV_FLEXSPI_H__ */