diff --git a/boards/nxp/mimxrt700_evk/board.c b/boards/nxp/mimxrt700_evk/board.c index a4b75c20d55de..7d07e73ad858e 100644 --- a/boards/nxp/mimxrt700_evk/board.c +++ b/boards/nxp/mimxrt700_evk/board.c @@ -544,6 +544,34 @@ void board_early_init_hook(void) CLOCK_EnableClock(kCLOCK_Acmp0); RESET_ClearPeripheralReset(kACMP0_RST_SHIFT_RSTn); #endif + +#if DT_NODE_HAS_STATUS(DT_NODELABEL(xspi0), okay) + POWER_DisablePD(kPDRUNCFG_APD_XSPI0); + POWER_DisablePD(kPDRUNCFG_PPD_XSPI0); + POWER_ApplyPD(); +#endif + +#if DT_NODE_HAS_STATUS(DT_NODELABEL(xspi1), okay) + xspi_setup_clock(XSPI1, 1U, 1U); /* Audio PLL PDF1 DIV1. */ + + POWER_DisablePD(kPDRUNCFG_APD_XSPI1); + POWER_DisablePD(kPDRUNCFG_PPD_XSPI1); + POWER_ApplyPD(); +#endif + +#if DT_NODE_HAS_STATUS(DT_NODELABEL(xspi2), okay) +#if CONFIG_SOC_MIMXRT798S_CM33_CPU0 + CLOCK_AttachClk(kMAIN_PLL_PFD3_to_XSPI2); +#elif CONFIG_SOC_MIMXRT798S_CM33_CPU1 + CLOCK_AttachClk(kFRO1_DIV1_to_COMMON_BASE); + CLOCK_AttachClk(kCOMMON_BASE_to_XSPI2); +#endif + CLOCK_SetClkDiv(kCLOCK_DivXspi2Clk, 1U); + + POWER_DisablePD(kPDRUNCFG_APD_XSPI2); + POWER_DisablePD(kPDRUNCFG_PPD_XSPI2); + POWER_ApplyPD(); +#endif } static void GlikeyWriteEnable(GLIKEY_Type *base, uint8_t idx) diff --git a/boards/nxp/mimxrt700_evk/mimxrt700_evk-pinctrl.dtsi b/boards/nxp/mimxrt700_evk/mimxrt700_evk-pinctrl.dtsi index 32afe95fe92ef..54927bd87c997 100644 --- a/boards/nxp/mimxrt700_evk/mimxrt700_evk-pinctrl.dtsi +++ b/boards/nxp/mimxrt700_evk/mimxrt700_evk-pinctrl.dtsi @@ -193,4 +193,83 @@ slew-rate = "normal"; }; }; + + pinmux_xspi0: pinmux_xspi0 { + group0 { + pinmux = , + , + , + , + , + , + , + , + , + , + , + , + ; + drive-strength = "normal"; + slew-rate = "normal"; + input-enable; + }; + }; + + pinmux_xspi1: pinmux_xspi1 { + group0 { + pinmux = , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + ; + drive-strength = "normal"; + slew-rate = "normal"; + input-enable; + }; + }; + + pinmux_xspi2: pinmux_xspi2 { + group0 { + pinmux = , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + ; + drive-strength = "normal"; + slew-rate = "normal"; + input-enable; + }; + }; }; diff --git a/boards/nxp/mimxrt700_evk/mimxrt700_evk_mimxrt798s_cm33_cpu0.dts b/boards/nxp/mimxrt700_evk/mimxrt700_evk_mimxrt798s_cm33_cpu0.dts index d35762b53e52f..de0a77e0db3b7 100644 --- a/boards/nxp/mimxrt700_evk/mimxrt700_evk_mimxrt798s_cm33_cpu0.dts +++ b/boards/nxp/mimxrt700_evk/mimxrt700_evk_mimxrt798s_cm33_cpu0.dts @@ -26,11 +26,12 @@ i2s-tx = &sai0; sdhc0 = &usdhc0; rtc = &rtc0; + sram-ext = &psram0; }; chosen { - zephyr,flash-controller = &mx25um51345g; - zephyr,flash = &mx25um51345g; + zephyr,flash-controller = &flash_controller0; + zephyr,flash = &ext_flash; zephyr,sram = &sram0; zephyr,console = &flexcomm0_lpuart0; zephyr,shell-uart = &flexcomm0_lpuart0; @@ -95,6 +96,19 @@ enable-gpios = <&gpio1 10 GPIO_ACTIVE_HIGH>; regulator-boot-on; }; + + memc: memc@8000000 { + status = "okay"; + reg = <0x08000000 DT_SIZE_M(32)>; + #address-cells = <1>; + #size-cells = <1>; + + psram: memory@8000000 { + compatible = "zephyr,memory-region"; + reg = <0x08000000 DT_SIZE_M(32)>; + zephyr,memory-region = "PSRAM"; + }; + }; }; &ctimer0 { @@ -325,47 +339,100 @@ zephyr_lcdif: &lcdif {}; &xspi0 { status = "okay"; + pinctrl-0 = <&pinmux_xspi0>; + pinctrl-names = "default"; + byte-order = <3>; + ahb-buffer-write-flush; + ahb-prefetch; - mx25um51345g: mx25um51345g@0 { - compatible = "nxp,xspi-mx25um51345g"; - /* MX25UM51245G is 64MB, 512MBit flash part */ - size = ; - reg = <0>; - spi-max-frequency = ; + flash_controller0: flash-controller@0 { status = "okay"; - jedec-id = [c2 81 3a]; - erase-block-size = ; - write-block-size = <2>; - - partitions { - compatible = "fixed-partitions"; - #address-cells = <1>; - #size-cells = <1>; - - /* - * Partition sizes must be aligned - * to the flash memory sector size of 4KB. - */ - boot_partition: partition@0 { - label = "mcuboot"; - reg = <0x00000000 DT_SIZE_K(128)>; - }; - slot0_partition: partition@20000 { - label = "image-0"; - reg = <0x00020000 DT_SIZE_M(7)>; - }; - slot1_partition: partition@720000 { - label = "image-1"; - reg = <0x00720000 DT_SIZE_M(7)>; - }; - storage_partition: partition@E20000 { - label = "storage"; - reg = <0x00E20000 (DT_SIZE_M(2) - DT_SIZE_K(128))>; + compatible = "nxp,xspi-nor"; + device-name = "mx25um51345g"; + /* MX25UM51245G is 64MB, 512Mbit flash part. */ + size = ; + reg = <0>; + sample-clk-source = <3>; + #address-cells = <1>; + #size-cells = <1>; + + ext_flash: flash@38000000 { + compatible = "soc-nv-flash"; + reg = <0x38000000 DT_SIZE_M(64)>; + erase-block-size = ; + write-block-size = <2>; + + partitions { + compatible = "fixed-partitions"; + #address-cells = <1>; + #size-cells = <1>; + + /* + * Partition sizes must be aligned + * to the flash memory sector size of 4KB. + */ + boot_partition: partition@0 { + label = "mcuboot"; + reg = <0x00000000 DT_SIZE_K(128)>; + }; + slot0_partition: partition@20000 { + label = "image-0"; + reg = <0x00020000 DT_SIZE_M(7)>; + }; + slot1_partition: partition@720000 { + label = "image-1"; + reg = <0x00720000 DT_SIZE_M(7)>; + }; + storage_partition: partition@E20000 { + label = "storage"; + reg = <0x00E20000 (DT_SIZE_M(2) - DT_SIZE_K(128))>; + }; }; }; }; }; +&xspi1 { + status = "okay"; + pinctrl-0 = <&pinmux_xspi1>; + pinctrl-names = "default"; + byte-order = <3>; + ahb-buffer-write-flush; + ahb-prefetch; + enable-ahb-write; + /* Connect JP45 1-2 to use XSPI1. */ + + psram0: memory-controller@0 { + status = "okay"; + compatible = "nxp,xspi-psram"; + device-name = "w958d6nbkx5l"; + size = ; + reg = <0>; + enable-differential-clk; + sample-clk-source = <3>; + }; +}; + +&xspi2 { + status = "okay"; + pinctrl-0 = <&pinmux_xspi2>; + pinctrl-names = "default"; + byte-order = <3>; + ahb-buffer-write-flush; + ahb-prefetch; + enable-ahb-write; + + psram1: memory-controller@0 { + status = "okay"; + compatible = "nxp,xspi-psram"; + device-name = "w958d6nbkx4l"; + size = ; + reg = <0>; + enable-differential-clk; + sample-clk-source = <3>; + }; +}; + zephyr_udc0: &usb0 { status = "okay"; phy-handle = <&usbphy>; diff --git a/boards/nxp/mimxrt700_evk/mimxrt700_evk_mimxrt798s_cm33_cpu0.yaml b/boards/nxp/mimxrt700_evk/mimxrt700_evk_mimxrt798s_cm33_cpu0.yaml index 05aba621c702a..eeade2809eaa6 100644 --- a/boards/nxp/mimxrt700_evk/mimxrt700_evk_mimxrt798s_cm33_cpu0.yaml +++ b/boards/nxp/mimxrt700_evk/mimxrt700_evk_mimxrt798s_cm33_cpu0.yaml @@ -24,4 +24,6 @@ supported: - usb_device - watchdog - hwinfo + - flash + - memc vendor: nxp diff --git a/boards/nxp/mimxrt700_evk/mimxrt700_evk_mimxrt798s_cm33_cpu1.dts b/boards/nxp/mimxrt700_evk/mimxrt700_evk_mimxrt798s_cm33_cpu1.dts index da52780a04fba..43ff70ffb76e0 100644 --- a/boards/nxp/mimxrt700_evk/mimxrt700_evk_mimxrt798s_cm33_cpu1.dts +++ b/boards/nxp/mimxrt700_evk/mimxrt700_evk_mimxrt798s_cm33_cpu1.dts @@ -20,6 +20,7 @@ sw0 = &user_button_1; ambient-temp0 = &p3t1755; rtc = &rtc1; + sram-ext = &psram; }; chosen { @@ -114,3 +115,23 @@ &mbox1_b { status = "okay"; }; + +&xspi2 { + status = "okay"; + pinctrl-0 = <&pinmux_xspi2>; + pinctrl-names = "default"; + byte-order = <3>; + ahb-buffer-write-flush; + ahb-prefetch; + enable-ahb-write; + + psram: memory-controller@0 { + status = "okay"; + compatible = "nxp,xspi-psram"; + device-name = "w958d6nbkx4l"; + size = ; + reg = <0>; + enable-differential-clk; + sample-clk-source = <3>; + }; +}; diff --git a/boards/nxp/mimxrt700_evk/mimxrt700_evk_mimxrt798s_cm33_cpu1.yaml b/boards/nxp/mimxrt700_evk/mimxrt700_evk_mimxrt798s_cm33_cpu1.yaml index e562a648b4cb6..13f0b3b766028 100644 --- a/boards/nxp/mimxrt700_evk/mimxrt700_evk_mimxrt798s_cm33_cpu1.yaml +++ b/boards/nxp/mimxrt700_evk/mimxrt700_evk_mimxrt798s_cm33_cpu1.yaml @@ -19,4 +19,6 @@ supported: - uart - adc - i3c + - flash + - memc vendor: nxp diff --git a/drivers/flash/CMakeLists.txt b/drivers/flash/CMakeLists.txt index 0aebf81894709..2b1f2bfcdbd66 100644 --- a/drivers/flash/CMakeLists.txt +++ b/drivers/flash/CMakeLists.txt @@ -34,6 +34,7 @@ zephyr_library_sources_ifdef(CONFIG_FLASH_INFINEON_CAT1 flash_ifx_cat1.c) zephyr_library_sources_ifdef(CONFIG_FLASH_MCUX_FLEXSPI_HYPERFLASH flash_mcux_flexspi_hyperflash.c) zephyr_library_sources_ifdef(CONFIG_FLASH_MCUX_FLEXSPI_MX25UM51345G flash_mcux_flexspi_mx25um51345g.c) zephyr_library_sources_ifdef(CONFIG_FLASH_MCUX_FLEXSPI_NOR flash_mcux_flexspi_nor.c) +zephyr_library_sources_ifdef(CONFIG_FLASH_MCUX_XSPI flash_mcux_xspi.c) zephyr_library_sources_ifdef(CONFIG_FLASH_MSPI_ATXP032 flash_mspi_atxp032.c) zephyr_library_sources_ifdef(CONFIG_FLASH_MSPI_EMUL_DEVICE flash_mspi_emul_device.c) zephyr_library_sources_ifdef(CONFIG_FLASH_MSPI_IS25XX0XX flash_mspi_is25xX0xx.c) @@ -101,6 +102,11 @@ if(CONFIG_FLASH_MCUX_FLEXSPI_XIP) endif() endif() +if(CONFIG_FLASH_MCUX_XSPI_XIP) + zephyr_code_relocate(FILES flash_mcux_xspi.c LOCATION ${CONFIG_FLASH_MCUX_XSPI_XIP_MEM}_TEXT) + zephyr_code_relocate(FILES flash_mcux_xspi.c LOCATION ${CONFIG_FLASH_MCUX_XSPI_XIP_MEM}_RODATA) +endif() + if(CONFIG_SOC_FLASH_STM32) zephyr_library_sources_ifdef(CONFIG_FLASH_EX_OP_ENABLED flash_stm32_ex_op.c) if(CONFIG_SOC_SERIES_STM32H7X) @@ -161,6 +167,11 @@ zephyr_library_include_directories_ifdef( ${ZEPHYR_BASE}/drivers/memc ) +zephyr_library_include_directories_ifdef( + CONFIG_FLASH_MCUX_XSPI + ${ZEPHYR_BASE}/drivers/memc +) + zephyr_library_sources_ifdef(CONFIG_FLASH_NXP_S32_QSPI_NOR flash_nxp_s32_qspi_nor.c) zephyr_library_sources_ifdef(CONFIG_FLASH_NXP_S32_QSPI_HYPERFLASH flash_nxp_s32_qspi_hyperflash.c) if(CONFIG_FLASH_NXP_S32_QSPI_NOR OR CONFIG_FLASH_NXP_S32_QSPI_HYPERFLASH) diff --git a/drivers/flash/Kconfig.mcux b/drivers/flash/Kconfig.mcux index dc15e01a394c5..17a2e7772cb44 100644 --- a/drivers/flash/Kconfig.mcux +++ b/drivers/flash/Kconfig.mcux @@ -118,3 +118,15 @@ choice FLASH_LOG_LEVEL_CHOICE endchoice endif # DT_HAS_NXP_IMX_FLEXSPI_ENABLED + +config FLASH_MCUX_XSPI + bool "MCUX XSPI flash driver" + default y + depends on DT_HAS_NXP_XSPI_NOR_ENABLED + select MEMC + select MEMC_MCUX_XSPI + select FLASH_HAS_DRIVER_ENABLED + select FLASH_HAS_PAGE_LAYOUT + select FLASH_HAS_EXPLICIT_ERASE + help + Enable the mcux xspi flash driver. diff --git a/drivers/flash/flash_mcux_xspi.c b/drivers/flash/flash_mcux_xspi.c new file mode 100644 index 0000000000000..ea2fb16fd7ef7 --- /dev/null +++ b/drivers/flash/flash_mcux_xspi.c @@ -0,0 +1,555 @@ +/* + * Copyright 2025 NXP + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#define DT_DRV_COMPAT nxp_xspi_nor + +#include +#include +#include +#include +#include +#include +#include +#include "memc_mcux_xspi.h" +#include "spi_nor.h" + +LOG_MODULE_REGISTER(flash_mcux_xspi); + +#define FLASH_MCUX_XSPI_LUT_ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0][0])) + +#define FLASH_BUSY_STATUS_OFFSET 0 +#define FLASH_WE_STATUS_OFFSET 7 + +#define FLASH_MX25_WRCR2_DTR_OPI_ENABLE_OFFSET (1U << 1) + +enum { + FLASH_CMD_MEM_READ, + FLASH_CMD_READ_STATUS, + FLASH_CMD_READ_STATUS_OPI, + FLASH_CMD_WRITE_ENABLE, + FLASH_CMD_WRITE_ENABLE_OPI, + FLASH_CMD_PAGEPROGRAM_OCTAL, + FLASH_CMD_ERASE_SECTOR, + FLASH_CMD_READ_ID_OPI, + FLASH_CMD_ENTER_OPI, +}; + +struct flash_mcux_xspi_config { + bool enable_differential_clk; + xspi_sample_clk_config_t sample_clk_config; +}; + +struct flash_mcux_xspi_data { + xspi_config_t xspi_config; + const struct device *xspi_dev; + const char *dev_name; + uint32_t amba_address; + struct flash_parameters flash_param; + uint64_t flash_size; +#if defined(CONFIG_FLASH_PAGE_LAYOUT) + struct flash_pages_layout layout; +#endif +}; + +/* + * Errata ERR052528: Limitation on LUT-Data Size < 8byte in xspi. + * Description: Read command including RDSR command can't work if LUT data size in read status is + * less than 8. Workaround: Use LUT data size of minimum 8 byte for read commands including RDSR. + */ +static const uint32_t flash_xspi_lut[][5] = { + /* Memory read. */ + [FLASH_CMD_MEM_READ] = { + XSPI_LUT_SEQ(kXSPI_Command_DDR, kXSPI_8PAD, 0xEE, kXSPI_Command_DDR, + kXSPI_8PAD, 0x11), + XSPI_LUT_SEQ(kXSPI_Command_RADDR_DDR, kXSPI_8PAD, 0x20, + kXSPI_Command_DUMMY_SDR, kXSPI_8PAD, 0x12), + XSPI_LUT_SEQ(kXSPI_Command_DUMMY_SDR, kXSPI_8PAD, 0x2, + kXSPI_Command_READ_DDR, kXSPI_8PAD, 0x8), + XSPI_LUT_SEQ(kXSPI_Command_STOP, kXSPI_8PAD, 0x0, 0, 0, 0), + }, + + /* Read status SPI. */ + [FLASH_CMD_READ_STATUS] = { + XSPI_LUT_SEQ(kXSPI_Command_SDR, kXSPI_1PAD, 0x05, kXSPI_Command_READ_SDR, + kXSPI_1PAD, 0x08), + }, + + /* Read Status OPI. */ + [FLASH_CMD_READ_STATUS_OPI] = { + XSPI_LUT_SEQ(kXSPI_Command_DDR, kXSPI_8PAD, 0x05, kXSPI_Command_DDR, + kXSPI_8PAD, 0xFA), + XSPI_LUT_SEQ(kXSPI_Command_RADDR_DDR, kXSPI_8PAD, 0x20, + kXSPI_Command_DUMMY_SDR, kXSPI_8PAD, 0x12), + XSPI_LUT_SEQ(kXSPI_Command_DUMMY_SDR, kXSPI_8PAD, 0x2, + kXSPI_Command_READ_DDR, kXSPI_8PAD, 0x8), + XSPI_LUT_SEQ(kXSPI_Command_STOP, kXSPI_8PAD, 0x0, 0, 0, 0), + }, + + /* Write enable. */ + [FLASH_CMD_WRITE_ENABLE] = { + XSPI_LUT_SEQ(kXSPI_Command_SDR, kXSPI_1PAD, 0x06, kXSPI_Command_STOP, + kXSPI_1PAD, 0x04), + }, + + /* Write Enable - OPI. */ + [FLASH_CMD_WRITE_ENABLE_OPI] = { + XSPI_LUT_SEQ(kXSPI_Command_DDR, kXSPI_8PAD, 0x06, kXSPI_Command_DDR, + kXSPI_8PAD, 0xF9), + }, + + /* Read ID. */ + [FLASH_CMD_READ_ID_OPI] = { + XSPI_LUT_SEQ(kXSPI_Command_DDR, kXSPI_8PAD, 0x9F, kXSPI_Command_DDR, + kXSPI_8PAD, 0x60), + XSPI_LUT_SEQ(kXSPI_Command_RADDR_DDR, kXSPI_8PAD, 0x20, + kXSPI_Command_DUMMY_SDR, kXSPI_8PAD, 0x04), + XSPI_LUT_SEQ(kXSPI_Command_READ_DDR, kXSPI_8PAD, 0x08, kXSPI_Command_STOP, + kXSPI_1PAD, 0x0), + }, + + /* Erase Sector. */ + [FLASH_CMD_ERASE_SECTOR] = { + XSPI_LUT_SEQ(kXSPI_Command_DDR, kXSPI_8PAD, 0x21, kXSPI_Command_DDR, + kXSPI_8PAD, 0xDE), + XSPI_LUT_SEQ(kXSPI_Command_RADDR_DDR, kXSPI_8PAD, 0x20, kXSPI_Command_STOP, + kXSPI_8PAD, 0x0), + }, + + /* Enable OPI DDR mode. */ + [FLASH_CMD_ENTER_OPI] = { + XSPI_LUT_SEQ(kXSPI_Command_SDR, kXSPI_1PAD, 0x72, kXSPI_Command_SDR, + kXSPI_1PAD, 0x00), + XSPI_LUT_SEQ(kXSPI_Command_SDR, kXSPI_1PAD, 0x00, kXSPI_Command_SDR, + kXSPI_1PAD, 0x00), + XSPI_LUT_SEQ(kXSPI_Command_SDR, kXSPI_1PAD, 0x00, kXSPI_Command_WRITE_SDR, + kXSPI_1PAD, 0x01), + }, + + /* Page program. */ + [FLASH_CMD_PAGEPROGRAM_OCTAL] = { + XSPI_LUT_SEQ(kXSPI_Command_DDR, kXSPI_8PAD, 0x12, kXSPI_Command_DDR, kXSPI_8PAD, + 0xED), + XSPI_LUT_SEQ(kXSPI_Command_RADDR_DDR, kXSPI_8PAD, 0x20, kXSPI_Command_WRITE_DDR, + kXSPI_8PAD, 0x8), + }}; + +/* Memory devices table. */ +static struct memc_xspi_dev_config device_configs[] = { + { + .name_prefix = "mx25um51345g", + .xspi_dev_config = { + .deviceInterface = kXSPI_StrandardExtendedSPI, + .interfaceSettings.strandardExtendedSPISettings.pageSize = 256, + .CSHoldTime = 2, + .CSSetupTime = 2, + .addrMode = kXSPI_DeviceByteAddressable, + .columnAddrWidth = 0, + .enableCASInterleaving = false, + .ptrDeviceDdrConfig = + &(xspi_device_ddr_config_t){ + .ddrDataAlignedClk = + kXSPI_DDRDataAlignedWith2xInternalRefClk, + .enableByteSwapInOctalMode = false, + .enableDdr = true, + }, + .deviceSize = {64 * 1024, 64 * 1024}, + }, + .lut_array = &flash_xspi_lut[0][0], + .lut_count = FLASH_MCUX_XSPI_LUT_ARRAY_SIZE(flash_xspi_lut), + }, +}; + +static int flash_xspi_nor_wait_bus_busy(const struct device *dev, bool enableOctal) +{ + struct flash_mcux_xspi_data *devData = dev->data; + const struct device *xspi_dev = devData->xspi_dev; + xspi_transfer_t flashXfer; + uint32_t readValue; + bool isBusy; + int ret; + + flashXfer.deviceAddress = devData->amba_address; + flashXfer.cmdType = kXSPI_Read; + flashXfer.data = &readValue; + flashXfer.targetGroup = kXSPI_TargetGroup0; + flashXfer.dataSize = enableOctal ? 2 : 1; + flashXfer.seqIndex = enableOctal ? FLASH_CMD_READ_STATUS_OPI : FLASH_CMD_READ_STATUS; + flashXfer.lockArbitration = false; + + do { + ret = memc_mcux_xspi_transfer(xspi_dev, &flashXfer); + if (ret < 0) { + break; + } + + isBusy = (readValue & (1U << FLASH_BUSY_STATUS_OFFSET)) ? true : false; + } while (isBusy); + + return ret; +} + +static int flash_mcux_xspi_read(const struct device *dev, off_t offset, void *data, size_t len) +{ + struct flash_mcux_xspi_data *devData = dev->data; + uint8_t *src = (uint8_t *)devData->amba_address + offset; + + if (len == 0) { + return 0; + } + + if (!data) { + return -EINVAL; + } + + if ((offset < 0) || (offset >= devData->flash_size) || + ((devData->flash_size - offset) < len)) { + return -EINVAL; + } + + XSPI_Cache64_InvalidateCacheByRange((uint32_t)src, len); + + (void)memcpy(data, src, len); + + return 0; +} + +static int flash_mcux_xspi_write_enable(const struct device *dev, uint32_t baseAddr, + bool enableOctal) +{ + struct flash_mcux_xspi_data *data = dev->data; + const struct device *xspi_dev = data->xspi_dev; + xspi_transfer_t flashXfer; + + flashXfer.deviceAddress = data->amba_address + baseAddr; + flashXfer.cmdType = kXSPI_Command; + flashXfer.targetGroup = kXSPI_TargetGroup0; + flashXfer.data = NULL; + flashXfer.dataSize = 0; + flashXfer.lockArbitration = false; + flashXfer.seqIndex = enableOctal ? FLASH_CMD_WRITE_ENABLE_OPI : FLASH_CMD_WRITE_ENABLE; + + return memc_mcux_xspi_transfer(xspi_dev, &flashXfer); +} + +static int flash_mcux_xspi_write(const struct device *dev, off_t offset, const void *data, + size_t len) +{ + struct flash_mcux_xspi_data *devData = dev->data; + const struct device *xspi_dev = devData->xspi_dev; + uint8_t *p_data = (uint8_t *)(uintptr_t)data; + xspi_transfer_t flashXfer; + size_t write_size; + uint32_t key = 0; + int ret = 0; + + if (memc_xspi_is_running_xip(xspi_dev)) { + key = irq_lock(); + memc_xspi_wait_bus_idle(xspi_dev); + } + + while (len > 0) { + write_size = MIN(len, SPI_NOR_PAGE_SIZE); + + ret = flash_mcux_xspi_write_enable(dev, 0, true); + if (ret < 0) { + break; + } + + flashXfer.deviceAddress = devData->amba_address + offset; + flashXfer.cmdType = kXSPI_Write; + flashXfer.seqIndex = FLASH_CMD_PAGEPROGRAM_OCTAL; + flashXfer.targetGroup = kXSPI_TargetGroup0; + flashXfer.data = (uint32_t *)p_data; + flashXfer.dataSize = write_size; + flashXfer.lockArbitration = false; + + ret = memc_mcux_xspi_transfer(xspi_dev, &flashXfer); + if (ret < 0) { + break; + } + + ret = flash_xspi_nor_wait_bus_busy(dev, true); + if (ret < 0) { + break; + } + + len -= write_size; + p_data = p_data + write_size; + offset += write_size; + } + + if (memc_xspi_is_running_xip(xspi_dev)) { + irq_unlock(key); + } + + return ret; +} + +static int flash_mcux_xspi_erase_sector(const struct device *dev, off_t offset) +{ + struct flash_mcux_xspi_data *data = dev->data; + const struct device *xspi_dev = data->xspi_dev; + xspi_transfer_t flashXfer; + + flashXfer.deviceAddress = data->amba_address + offset; + flashXfer.cmdType = kXSPI_Command; + flashXfer.seqIndex = FLASH_CMD_ERASE_SECTOR; + flashXfer.targetGroup = kXSPI_TargetGroup0; + flashXfer.lockArbitration = false; + flashXfer.dataSize = 0; + flashXfer.data = NULL; + + return memc_mcux_xspi_transfer(xspi_dev, &flashXfer); +} + +static int flash_mcux_xspi_erase(const struct device *dev, off_t offset, size_t size) +{ + struct flash_mcux_xspi_data *data = dev->data; + const struct device *xspi_dev = data->xspi_dev; + uint32_t key = 0; + int ret = 0; + + if (0 != (offset % SPI_NOR_SECTOR_SIZE)) { + LOG_ERR("Invalid offset"); + return -EINVAL; + } + + if (size % SPI_NOR_SECTOR_SIZE) { + LOG_ERR("Invalid size"); + return -EINVAL; + } + + if (memc_xspi_is_running_xip(xspi_dev)) { + key = irq_lock(); + memc_xspi_wait_bus_idle(xspi_dev); + } + + for (int i = 0; i < size / SPI_NOR_SECTOR_SIZE; i++) { + ret = flash_mcux_xspi_write_enable(dev, 0, true); + if (ret < 0) { + break; + } + + ret = flash_mcux_xspi_erase_sector(dev, offset + i * SPI_NOR_SECTOR_SIZE); + if (ret < 0) { + break; + } + + ret = flash_xspi_nor_wait_bus_busy(dev, true); + if (ret < 0) { + break; + } + } + + if (memc_xspi_is_running_xip(xspi_dev)) { + irq_unlock(key); + } + + return ret; +} + +static int flash_mcux_xspi_enable_opi(const struct device *dev) +{ + uint32_t value = FLASH_MX25_WRCR2_DTR_OPI_ENABLE_OFFSET; + struct flash_mcux_xspi_data *data = dev->data; + const struct device *xspi_dev = data->xspi_dev; + xspi_transfer_t flashXfer; + int ret; + + ret = flash_mcux_xspi_write_enable(dev, 0, true); + if (ret < 0) { + return ret; + } + + flashXfer.deviceAddress = data->amba_address; + flashXfer.cmdType = kXSPI_Write; + flashXfer.seqIndex = FLASH_CMD_ENTER_OPI; + flashXfer.targetGroup = kXSPI_TargetGroup0; + flashXfer.data = &value; + flashXfer.dataSize = 1; + flashXfer.lockArbitration = false; + + ret = memc_mcux_xspi_transfer(xspi_dev, &flashXfer); + if (ret < 0) { + return ret; + } + + return flash_xspi_nor_wait_bus_busy(dev, true); +} + +static const struct flash_parameters *flash_mcux_xspi_get_parameters(const struct device *dev) +{ + return &((const struct flash_mcux_xspi_data *)dev->data)->flash_param; +} + +static int flash_mcux_xspi_get_size(const struct device *dev, uint64_t *size) +{ + *size = ((const struct flash_mcux_xspi_data *)dev->data)->flash_size; + + return 0; +} + +#if defined(CONFIG_FLASH_PAGE_LAYOUT) +static void flash_mcux_xspi_pages_layout(const struct device *dev, + const struct flash_pages_layout **layout, + size_t *layout_size) +{ + struct flash_mcux_xspi_data *data = dev->data; + + *layout = &data->layout; + *layout_size = 1; +} +#endif /* CONFIG_FLASH_PAGE_LAYOUT */ + +#if defined(CONFIG_FLASH_JESD216_API) +static int flash_mcux_xspi_sfdp_read(const struct device *dev, off_t offset, void *data, size_t len) +{ + return -EOPNOTSUPP; +} + +static int flash_mcux_xspi_read_jedec_id(const struct device *dev, uint8_t *id) +{ + struct flash_mcux_xspi_data *data = dev->data; + const struct device *xspi_dev = data->xspi_dev; + xspi_transfer_t flashXfer; + + flashXfer.deviceAddress = data->amba_address; + flashXfer.cmdType = kXSPI_Read; + flashXfer.targetGroup = kXSPI_TargetGroup0; + flashXfer.seqIndex = FLASH_CMD_READ_ID_OPI; + flashXfer.data = id; + flashXfer.dataSize = 1; + flashXfer.lockArbitration = false; + + return memc_mcux_xspi_transfer(xspi_dev, &flashXfer); +} +#endif /* CONFIG_FLASH_JESD216_API */ + +static int flash_mcux_xspi_probe(const struct device *dev) +{ + const struct flash_mcux_xspi_config *flash_config = + (const struct flash_mcux_xspi_config *)dev->config; + struct flash_mcux_xspi_data *data = dev->data; + const struct device *xspi_dev = data->xspi_dev; + struct memc_xspi_dev_config *flash_dev_config = NULL; + xspi_device_config_t *dev_config = NULL; + uint32_t key = 0; + int ret; + + if (memc_xspi_is_running_xip(xspi_dev)) { + key = irq_lock(); + memc_xspi_wait_bus_idle(xspi_dev); + } + + /* Setup the specific flash parameters. */ + for (uint32_t i = 0; i < ARRAY_SIZE(device_configs); i++) { + if (strncmp(device_configs[i].name_prefix, data->dev_name, + strlen(device_configs[i].name_prefix)) == 0) { + flash_dev_config = &device_configs[i]; + break; + } + } + + do { + if (flash_dev_config == NULL) { + LOG_ERR("Unsupported device: %s", data->dev_name); + ret = -ENOTSUP; + break; + } + + /* Set special device configurations. */ + dev_config = &flash_dev_config->xspi_dev_config; + dev_config->enableCknPad = flash_config->enable_differential_clk; + dev_config->sampleClkConfig = flash_config->sample_clk_config; + + ret = memc_mcux_xspi_get_root_clock(xspi_dev, &dev_config->xspiRootClk); + if (ret < 0) { + break; + } + + ret = memc_xspi_set_device_config(xspi_dev, dev_config, flash_dev_config->lut_array, + flash_dev_config->lut_count); + } while (0); + + if (memc_xspi_is_running_xip(xspi_dev)) { + irq_unlock(key); + } + + return ret; +} + +static int flash_mcux_xspi_init(const struct device *dev) +{ + struct flash_mcux_xspi_data *data = dev->data; + const struct device *xspi_dev = data->xspi_dev; + int ret; + + if (!device_is_ready(xspi_dev)) { + LOG_ERR("XSPI device is not ready"); + return -ENODEV; + } + + ret = flash_mcux_xspi_probe(dev); + if (ret < 0) { + return ret; + } + + data->amba_address = memc_mcux_xspi_get_ahb_address(xspi_dev); + + return flash_mcux_xspi_enable_opi(dev); +} + +static DEVICE_API(flash, flash_mcux_xspi_api) = { + .read = flash_mcux_xspi_read, + .write = flash_mcux_xspi_write, + .erase = flash_mcux_xspi_erase, + .get_parameters = flash_mcux_xspi_get_parameters, + .get_size = flash_mcux_xspi_get_size, +#if defined(CONFIG_FLASH_PAGE_LAYOUT) + .page_layout = flash_mcux_xspi_pages_layout, +#endif +#if defined(CONFIG_FLASH_JESD216_API) + .sfdp_read = flash_mcux_xspi_sfdp_read, + .read_jedec_id = flash_mcux_xspi_read_jedec_id, +#endif +}; + +#if defined(CONFIG_FLASH_PAGE_LAYOUT) +#define FLASH_MCUX_XSPI_LAYOUT(n) \ + .layout.pages_size = SPI_NOR_SECTOR_SIZE, \ + .layout.pages_count = DT_INST_PROP(n, size) / SPI_NOR_SECTOR_SIZE, +#else +#define FLASH_MCUX_XSPI_LAYOUT(n) +#endif + +#define FLASH_MCUX_XSPI_INIT(n) \ + static const struct flash_mcux_xspi_config flash_mcux_xspi_config_##n = { \ + .sample_clk_config.sampleClkSource = DT_INST_PROP(n, sample_clk_source), \ + .sample_clk_config.enableDQSLatency = DT_INST_PROP(n, enable_dqs_latency), \ + .sample_clk_config.dllConfig = { \ + .dllMode = kXSPI_AutoUpdateMode, \ + .useRefValue = true, \ + .enableCdl8 = true, \ + }, \ + }; \ + static struct flash_mcux_xspi_data flash_mcux_xspi_data_##n = { \ + .xspi_dev = DEVICE_DT_GET(DT_INST_BUS(n)), \ + .dev_name = DT_INST_PROP(n, device_name), \ + .flash_param = { \ + .write_block_size = 1, \ + .erase_value = 0xFF, \ + .caps = { \ + .no_explicit_erase = false, \ + }, \ + }, \ + .flash_size = DT_INST_PROP(n, size), \ + FLASH_MCUX_XSPI_LAYOUT(n) \ + }; \ + DEVICE_DT_INST_DEFINE(n, &flash_mcux_xspi_init, NULL, &flash_mcux_xspi_data_##n, \ + &flash_mcux_xspi_config_##n, POST_KERNEL, \ + CONFIG_FLASH_INIT_PRIORITY, &flash_mcux_xspi_api); + +DT_INST_FOREACH_STATUS_OKAY(FLASH_MCUX_XSPI_INIT) diff --git a/drivers/memc/CMakeLists.txt b/drivers/memc/CMakeLists.txt index 2520652f9652e..df49de7b407b9 100644 --- a/drivers/memc/CMakeLists.txt +++ b/drivers/memc/CMakeLists.txt @@ -13,6 +13,12 @@ zephyr_library_sources_ifdef(CONFIG_MEMC_MCUX_FLEXSPI_W956A8MBYA memc_mcux_flexs if((DEFINED CONFIG_FLASH_MCUX_FLEXSPI_XIP) AND (DEFINED CONFIG_FLASH)) zephyr_code_relocate(FILES memc_mcux_flexspi.c LOCATION ${CONFIG_FLASH_MCUX_FLEXSPI_XIP_MEM}_TEXT) endif() +zephyr_library_sources_ifdef(CONFIG_MEMC_MCUX_XSPI memc_mcux_xspi.c) +if((DEFINED CONFIG_FLASH_MCUX_XSPI_XIP) AND (DEFINED CONFIG_FLASH)) + zephyr_code_relocate(FILES memc_mcux_xspi.c LOCATION ${CONFIG_FLASH_MCUX_XSPI_XIP_MEM}_TEXT) + zephyr_code_relocate(FILES memc_mcux_xspi.c LOCATION ${CONFIG_FLASH_MCUX_XSPI_XIP_MEM}_RODATA) +endif() +zephyr_library_sources_ifdef(CONFIG_MEMC_MCUX_XSPI_PSRAM memc_mcux_xspi_psram.c) zephyr_library_sources_ifdef(CONFIG_MEMC_MSPI_APS6404L memc_mspi_aps6404l.c) zephyr_library_sources_ifdef(CONFIG_MEMC_MSPI_APS_Z8 memc_mspi_aps_z8.c) diff --git a/drivers/memc/Kconfig.mcux b/drivers/memc/Kconfig.mcux index 54dc92144d927..1abd0726e6ba0 100644 --- a/drivers/memc/Kconfig.mcux +++ b/drivers/memc/Kconfig.mcux @@ -1,4 +1,4 @@ -# Copyright 2020-2023 NXP +# Copyright 2020-2023, 2025 NXP # Copyright (c) 2021 Basalte bv # Copyright (c) 2023, ithinx GmbH # Copyright (c) 2023, Tonies GmbH @@ -66,3 +66,25 @@ choice MEMC_LOG_LEVEL_CHOICE endchoice endif # DT_HAS_NXP_IMX_FLEXSPI_ENABLED + +config MEMC_MCUX_XSPI_INIT_XIP + bool "Initialize XSPI when using device for XIP" + help + Initialize the XSPI device even when using it for XIP. If this + Kconfig is enabled, the user must ensure that the pin control + state used does not reconfigure the pins used to interface with + the flash device used for XIP, and that the configuration settings + used for the XSPI are compatible with those needed for XIP from + the flash device. + +config MEMC_MCUX_XSPI_PSRAM + bool "MCUX XSPI psram driver" + default y + depends on DT_HAS_NXP_XSPI_PSRAM_ENABLED + select MEMC_MCUX_XSPI + help + Enable the mcux xspi psram driver. + +config MEMC_MCUX_XSPI + bool + select PINCTRL diff --git a/drivers/memc/memc_mcux_xspi.c b/drivers/memc/memc_mcux_xspi.c new file mode 100644 index 0000000000000..82d09943e96d0 --- /dev/null +++ b/drivers/memc/memc_mcux_xspi.c @@ -0,0 +1,257 @@ +/* + * Copyright 2025 NXP + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#define DT_DRV_COMPAT nxp_xspi + +#include +#include +#include +#include +#include +#include "memc_mcux_xspi.h" + +LOG_MODULE_REGISTER(memc_mcux_xspi, CONFIG_MEMC_LOG_LEVEL); + +#define MEMC_XSPI_TARGET_GROUP_COUNT 2 +#define MEMC_XSPI_SFP_FRAD_COUNT 8 + +struct memc_mcux_xspi_config { + const struct pinctrl_dev_config *pincfg; + xspi_config_t xspi_config; + xspi_sfp_mdad_config_t mdad_configs; + bool mdad_valid; + xspi_sfp_frad_config_t frad_configs; + bool frad_valid; +}; + +struct memc_mcux_xspi_data { + XSPI_Type *base; + bool xip; + uint32_t amba_address; + const struct device *clock_dev; + clock_control_subsys_t clock_subsys; +}; + +void memc_mcux_xspi_update_device_addr_mode(const struct device *dev, + xspi_device_addr_mode_t addr_mode) +{ + XSPI_Type *base = ((struct memc_mcux_xspi_data *)dev->data)->base; + + XSPI_UpdateDeviceAddrMode(base, addr_mode); +} + +int memc_mcux_xspi_get_root_clock(const struct device *dev, uint32_t *clock_rate) +{ + struct memc_mcux_xspi_data *data = dev->data; + + return clock_control_get_rate(data->clock_dev, data->clock_subsys, clock_rate); +} + +void memc_xspi_wait_bus_idle(const struct device *dev) +{ + struct memc_mcux_xspi_data *data = dev->data; + + while (!XSPI_GetBusIdleStatus(data->base)) { + } +} + +int memc_xspi_set_device_config(const struct device *dev, const xspi_device_config_t *device_config, + const uint32_t *lut_array, uint8_t lut_count) +{ + struct memc_mcux_xspi_data *data = dev->data; + XSPI_Type *base = data->base; + status_t status; + + /* Configure flash settings according to serial flash feature. */ + status = XSPI_SetDeviceConfig(base, (xspi_device_config_t *)device_config); + if (status != kStatus_Success) { + LOG_ERR("XSPI_SetDeviceConfig failed with status %u\n", status); + return -ENODEV; + } + + XSPI_UpdateLUT(base, 0, lut_array, lut_count); + + return 0; +} + +uint32_t memc_mcux_xspi_get_ahb_address(const struct device *dev) +{ + return ((const struct memc_mcux_xspi_data *)dev->data)->amba_address; +} + +int memc_mcux_xspi_transfer(const struct device *dev, xspi_transfer_t *xfer) +{ + XSPI_Type *base = ((struct memc_mcux_xspi_data *)dev->data)->base; + status_t status; + + status = XSPI_TransferBlocking(base, xfer); + + return (status == kStatus_Success) ? 0 : -EIO; +} + +bool memc_xspi_is_running_xip(const struct device *dev) +{ + return ((const struct memc_mcux_xspi_data *)dev->data)->xip; +} + +static int memc_mcux_xspi_init(const struct device *dev) +{ + const struct memc_mcux_xspi_config *memc_xspi_config = dev->config; + const struct pinctrl_dev_config *pincfg = memc_xspi_config->pincfg; + xspi_config_t config = memc_xspi_config->xspi_config; + XSPI_Type *base = ((struct memc_mcux_xspi_data *)dev->data)->base; + int ret; + + if ((memc_xspi_is_running_xip(dev)) && (!IS_ENABLED(CONFIG_MEMC_MCUX_XSPI_INIT_XIP))) { + LOG_DBG("XIP active on %s, skipping init\n", dev->name); + return 0; + } + + ret = pinctrl_apply_state(pincfg, PINCTRL_STATE_DEFAULT); + if (ret < 0) { + LOG_ERR("Failed to apply pinctrl state: %d", ret); + return ret; + } + + config.ptrAhbAccessConfig->ahbAlignment = kXSPI_AhbAlignmentNoLimit; + config.ptrAhbAccessConfig->ahbSplitSize = kXSPI_AhbSplitSizeDisabled; + + for (uint8_t i = 0U; i < XSPI_BUFCR_COUNT; i++) { + config.ptrAhbAccessConfig->buffer[i].masterId = i; + if (i == (XSPI_BUFCR_COUNT - 1U)) { + config.ptrAhbAccessConfig->buffer[i].enaPri.enableAllMaster = true; + } else { + config.ptrAhbAccessConfig->buffer[i].enaPri.enablePriority = false; + } + config.ptrAhbAccessConfig->buffer[i].bufferSize = 0x80U; + config.ptrAhbAccessConfig->buffer[i].ptrSubBuffer0Config = NULL; + config.ptrAhbAccessConfig->buffer[i].ptrSubBuffer1Config = NULL; + config.ptrAhbAccessConfig->buffer[i].ptrSubBuffer2Config = NULL; + config.ptrAhbAccessConfig->buffer[i].ptrSubBuffer3Config = NULL; + } + + if (memc_xspi_config->mdad_valid) { + config.ptrIpAccessConfig->ptrSfpMdadConfig = + (xspi_sfp_mdad_config_t *)&memc_xspi_config->mdad_configs; + } + if (memc_xspi_config->frad_valid) { + config.ptrIpAccessConfig->ptrSfpFradConfig = + (xspi_sfp_frad_config_t *)&memc_xspi_config->frad_configs; + } + + XSPI_Init(base, &config); + + return 0; +} + +#if defined(CONFIG_XIP) && defined(CONFIG_FLASH_MCUX_XSPI_XIP) +/* Checks if image flash base address is in the XSPI AHB base region */ +#define MEMC_XSPI_CFG_XIP(n) \ + ((CONFIG_FLASH_BASE_ADDRESS) >= DT_INST_REG_ADDR_BY_IDX(n, 1)) && \ + ((CONFIG_FLASH_BASE_ADDRESS) < \ + (DT_INST_REG_ADDR_BY_IDX(n, 1) + DT_INST_REG_SIZE_BY_IDX(n, 1))) + +#else +#define MEMC_XSPI_CFG_XIP(node_id) false +#endif + +#define MCUX_XSPI_GET_MDAD(n, idx, x) \ + DT_PROP(DT_CHILD(DT_DRV_INST(n), mdad_tg ## idx), x) +#define MCUX_XSPI_GET_FRAD(n, idx, x) \ + DT_PROP(DT_CHILD(DT_DRV_INST(n), frad_region ## idx), x) + +#define MCUX_XSPI_MDAD_INIT(idx, n) \ + COND_CODE_1(DT_NODE_EXISTS(DT_CHILD(DT_DRV_INST(n), mdad_tg ## idx)), \ + ({ \ + .enableDescriptorLock = \ + MCUX_XSPI_GET_MDAD(n, idx, enable_descriptor_lock), \ + .maskType = MCUX_XSPI_GET_MDAD(n, idx, mask_type), \ + .mask = MCUX_XSPI_GET_MDAD(n, idx, mask), \ + .masterIdReference = \ + MCUX_XSPI_GET_MDAD(n, idx, master_id_reference), \ + .secureAttribute = \ + MCUX_XSPI_GET_MDAD(n, idx, secure_attribute), \ + }), \ + ({ \ + 0 \ + })) + +#define MCUX_XSPI_FRAD_INIT(idx, n) \ + COND_CODE_1(DT_NODE_EXISTS(DT_CHILD(DT_DRV_INST(n), frad_region ## idx)), \ + ({ \ + .startAddress = MCUX_XSPI_GET_FRAD(n, idx, start_address), \ + .endAddress = MCUX_XSPI_GET_FRAD(n, idx, end_address), \ + .tg0MasterAccess = \ + MCUX_XSPI_GET_FRAD(n, idx, tg0_master_access), \ + .tg1MasterAccess = \ + MCUX_XSPI_GET_FRAD(n, idx, tg1_master_access), \ + .assignIsValid = true, \ + .descriptorLock = \ + MCUX_XSPI_GET_FRAD(n, idx, descriptor_lock), \ + .exclusiveAccessLock = \ + MCUX_XSPI_GET_FRAD(n, idx, exclusive_access_lock), \ + }), \ + ({ \ + 0 \ + })) + +#define MCUX_XSPI_INIT(n) \ + PINCTRL_DT_INST_DEFINE(n); \ + static const struct memc_mcux_xspi_config memc_mcux_xspi_config_##n = { \ + .pincfg = PINCTRL_DT_INST_DEV_CONFIG_GET(n), \ + .xspi_config = { \ + .byteOrder = DT_INST_PROP(n, byte_order), \ + .enableDoze = false, \ + .ptrAhbAccessConfig = &(xspi_ahb_access_config_t){ \ + .ahbErrorPayload = { \ + .highPayload = 0x5A5A5A5A, \ + .lowPayload = 0x5A5A5A5A, \ + }, \ + .ARDSeqIndex = 0, \ + .enableAHBBufferWriteFlush = \ + DT_INST_PROP(n, ahb_buffer_write_flush), \ + .enableAHBPrefetch = DT_INST_PROP(n, ahb_prefetch), \ + .ptrAhbWriteConfig = DT_INST_PROP(n, enable_ahb_write) ? \ + &(xspi_ahb_write_config_t){ \ + .AWRSeqIndex = 1, \ + .ARDSRSeqIndex = 0, \ + .blockRead = false, \ + .blockSequenceWrite = false, \ + } : NULL, \ + }, \ + .ptrIpAccessConfig = &(xspi_ip_access_config_t){ \ + .ipAccessTimeoutValue = 0xFFFFFFFF, \ + .ptrSfpFradConfig = NULL, \ + .ptrSfpMdadConfig = NULL, \ + .sfpArbitrationLockTimeoutValue = 0xFFFFFF, \ + }, \ + }, \ + .mdad_configs = { \ + .tgMdad = { \ + LISTIFY(MEMC_XSPI_TARGET_GROUP_COUNT, \ + MCUX_XSPI_MDAD_INIT, (,), n) \ + }, \ + }, \ + .mdad_valid = DT_NODE_EXISTS(DT_CHILD(DT_DRV_INST(n), mdad_tg0)), \ + .frad_configs = { \ + .fradConfig = { \ + LISTIFY(MEMC_XSPI_SFP_FRAD_COUNT, MCUX_XSPI_FRAD_INIT, (,), n) \ + }, \ + }, \ + .frad_valid = DT_NODE_EXISTS(DT_CHILD(DT_DRV_INST(n), frad_region0)), \ + }; \ + static struct memc_mcux_xspi_data memc_mcux_xspi_data_##n = { \ + .base = (XSPI_Type *)DT_INST_REG_ADDR(n), \ + .xip = MEMC_XSPI_CFG_XIP(n), \ + .amba_address = DT_INST_REG_ADDR_BY_IDX(n, 1), \ + .clock_dev = DEVICE_DT_GET(DT_INST_CLOCKS_CTLR(n)), \ + .clock_subsys = (clock_control_subsys_t)DT_INST_CLOCKS_CELL(n, name), \ + }; \ + DEVICE_DT_INST_DEFINE(n, &memc_mcux_xspi_init, NULL, \ + &memc_mcux_xspi_data_##n, &memc_mcux_xspi_config_##n, \ + POST_KERNEL, CONFIG_MEMC_INIT_PRIORITY, NULL); + +DT_INST_FOREACH_STATUS_OKAY(MCUX_XSPI_INIT) diff --git a/drivers/memc/memc_mcux_xspi.h b/drivers/memc/memc_mcux_xspi.h new file mode 100644 index 0000000000000..50f7e7961a8ab --- /dev/null +++ b/drivers/memc/memc_mcux_xspi.h @@ -0,0 +1,82 @@ +/* + * Copyright 2025 NXP + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef ZEPHYR_DRIVERS_MEMC_MCUX_XSPI_H_ +#define ZEPHYR_DRIVERS_MEMC_MCUX_XSPI_H_ + +#include +#include + +struct memc_xspi_dev_config { + const char *name_prefix; + xspi_device_config_t xspi_dev_config; + const uint32_t *lut_array; + size_t lut_count; +}; + +/** + * @brief Update device address mode. + * + * @param dev: XSPI device + * @param addr_mode: Address mode. + */ +void memc_mcux_xspi_update_device_addr_mode(const struct device *dev, + xspi_device_addr_mode_t addr_mode); + +/** + * @brief Get XSPI root clock frequency with Hz. + * + * @return 0 on success, negative value on failure + */ +int memc_mcux_xspi_get_root_clock(const struct device *dev, uint32_t *clock_rate); + +/** + * @brief Wait the XSPI bus idle status. + * + * @param dev: XSPI device + */ +void memc_xspi_wait_bus_idle(const struct device *dev); + +/** + * @brief Check whether XSPI is running in XIP mode. + * + * @return 0 - Not in XIP mode, 1 - In XIP mode. + */ +bool memc_xspi_is_running_xip(const struct device *dev); + +/** + * @brief XSPI transfer function. + * + * Configures new device on the XSPI bus. + * @param dev: XSPI device + * @param xfer: XSPI transfer structure. + * @return 0 on success, negative value on failure + */ +int memc_mcux_xspi_transfer(const struct device *dev, xspi_transfer_t *xfer); + +/** + * @brief Configure new XSPI device + * + * Configures new device on the XSPI bus. + * @param dev: XSPI device + * @param device_config: External device configuration. + * @param lut_array: Lookup table of XSPI flash commands for device + * @param lut_count: number of LUT entries (4 bytes each) in lut array + * @return 0 on success, negative value on failure + */ +int memc_xspi_set_device_config(const struct device *dev, const xspi_device_config_t *device_config, + const uint32_t *lut_array, uint8_t lut_count); + +/** + * @brief Get AHB address for XSPI + * + * This address is memory mapped, and can be read and written as though it were internal memory. + * @param dev: XSPI device + * @return AHB access address for XSPI device + */ +uint32_t memc_mcux_xspi_get_ahb_address(const struct device *dev); + +#endif /* ZEPHYR_DRIVERS_MEMC_MCUX_XSPI_H_ */ diff --git a/drivers/memc/memc_mcux_xspi_psram.c b/drivers/memc/memc_mcux_xspi_psram.c new file mode 100644 index 0000000000000..3a47d7b53dba9 --- /dev/null +++ b/drivers/memc/memc_mcux_xspi_psram.c @@ -0,0 +1,373 @@ +/* + * Copyright 2025 NXP + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#define DT_DRV_COMPAT nxp_xspi_psram + +#include +#include +#include +#include +#include +#include "memc_mcux_xspi.h" + +LOG_MODULE_REGISTER(memc_mcux_xspi_psram, CONFIG_MEMC_LOG_LEVEL); + +#define MEMC_MCUX_XSPI_LUT_ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0][0])) + +#define ID0_REG_ADDR (0U) +#define CR0_REG_ADDR (0x800U) +#define CR1_REG_ADDR (0x801U) + +#define ID0_REG_ID_MASK (0xFU) + +#define CR0_REG_DRIVE_STRENGTH_SHIFT 4U +#define CR0_REG_DRIVE_STRENGTH_MASK (0x07U << CR0_REG_DRIVE_STRENGTH_SHIFT) +#define CR0_REG_DRIVE_STRENGTH_46OHMS 3U +#define CR0_REG_VARIABLE_LATENCY_MASK (1U << 3) + +#define CR1_DIFFERENTIAL_CLOCK_SHIFT 6U +#define CR1_DIFFERENTIAL_CLOCK_MASK (1U << CR1_DIFFERENTIAL_CLOCK_SHIFT) + +enum { + PSRAM_MANUFACTURER_ID_WINBOND = 0x6U, +}; + +enum { + PSRAM_CMD_MEM_READ, + PSRAM_CMD_MEM_WRITE, + PSRAM_CMD_REG_READ, + PSRAM_CMD_REG_WRITE, +}; + +struct memc_mcux_xspi_psram_config { + bool enable_differential_clk; + xspi_sample_clk_config_t sample_clk_config; +}; + +struct memc_mcux_xspi_psram_data { + const struct device *xspi_dev; + const char *dev_name; + uint32_t amba_address; + uint32_t size; +}; + +const uint32_t memc_xspi_w958d6nbkx_lut[][5] = { + /* Memory Read */ + [PSRAM_CMD_MEM_READ] = { + XSPI_LUT_SEQ(kXSPI_Command_DDR, kXSPI_8PAD, 0xA0, kXSPI_Command_RADDR_DDR, + kXSPI_8PAD, 0x18), + XSPI_LUT_SEQ(kXSPI_Command_CADDR_DDR, kXSPI_8PAD, 0x10, + kXSPI_Command_DUMMY_SDR, kXSPI_8PAD, 6), + XSPI_LUT_SEQ(kXSPI_Command_READ_DDR, kXSPI_8PAD, 0x08, kXSPI_Command_STOP, + kXSPI_1PAD, 0x0), + }, + + /* Memory Write */ + [PSRAM_CMD_MEM_WRITE] = { + XSPI_LUT_SEQ(kXSPI_Command_DDR, kXSPI_8PAD, 0x20, kXSPI_Command_RADDR_DDR, + kXSPI_8PAD, 0x18), + XSPI_LUT_SEQ(kXSPI_Command_CADDR_DDR, kXSPI_8PAD, 0x10, + kXSPI_Command_DUMMY_SDR, kXSPI_8PAD, 6), + XSPI_LUT_SEQ(kXSPI_Command_WRITE_DDR, kXSPI_8PAD, 0x08, kXSPI_Command_STOP, + kXSPI_1PAD, 0X0), + }, + + /* Register Read */ + [PSRAM_CMD_REG_READ] = { + XSPI_LUT_SEQ(kXSPI_Command_DDR, kXSPI_8PAD, 0xE0, kXSPI_Command_RADDR_DDR, + kXSPI_8PAD, 0x18), + XSPI_LUT_SEQ(kXSPI_Command_CADDR_DDR, kXSPI_8PAD, 0x10, + kXSPI_Command_DUMMY_SDR, kXSPI_8PAD, + 6), /* Dummy cycle: 2 * 6 + 2 */ + XSPI_LUT_SEQ(kXSPI_Command_READ_DDR, kXSPI_8PAD, 0x08, kXSPI_Command_STOP, + kXSPI_1PAD, 0x0), + }, + + /* Register write */ + [PSRAM_CMD_REG_WRITE] = { + XSPI_LUT_SEQ(kXSPI_Command_DDR, kXSPI_8PAD, 0x60, kXSPI_Command_RADDR_DDR, + kXSPI_8PAD, 0x18), + XSPI_LUT_SEQ(kXSPI_Command_CADDR_DDR, kXSPI_8PAD, 0x10, + kXSPI_Command_WRITE_DDR, kXSPI_8PAD, 0x08), + XSPI_LUT_SEQ(kXSPI_Command_STOP, kXSPI_1PAD, 0x0, kXSPI_Command_STOP, + kXSPI_1PAD, 0x0), + }, +}; + +/* Memory devices table. */ +static struct memc_xspi_dev_config dev_configs[] = { + { + .name_prefix = "w958d6nbkx", + .xspi_dev_config = { + .deviceInterface = kXSPI_HyperBus, + .interfaceSettings.hyperBusSettings.x16Mode = kXSPI_x16ModeEnabledOnlyData, + .interfaceSettings.hyperBusSettings.enableVariableLatency = true, + .interfaceSettings.hyperBusSettings.forceBit10To1 = false, + .interfaceSettings.hyperBusSettings.pageSize = 1024, + .CSHoldTime = 2, + .CSSetupTime = 2, + .addrMode = kXSPI_Device4ByteAddressable, + .columnAddrWidth = 3, + .enableCASInterleaving = false, + .ptrDeviceDdrConfig = + &(xspi_device_ddr_config_t){ + .ddrDataAlignedClk = + kXSPI_DDRDataAlignedWith2xInternalRefClk, + .enableByteSwapInOctalMode = false, + .enableDdr = true, + }, + .deviceSize = {32 * 1024, 32 * 1024}, + }, + .lut_array = &memc_xspi_w958d6nbkx_lut[0][0], + .lut_count = MEMC_MCUX_XSPI_LUT_ARRAY_SIZE(memc_xspi_w958d6nbkx_lut), + }, +}; + +static int xspi_psram_write_reg(const struct device *dev, uint32_t regAddr, + uint8_t *data, uint32_t size); +static int xspi_psram_read_reg(const struct device *dev, uint32_t regAddr, + uint8_t *data, uint32_t size); + +static int memc_mcux_xspi_w958d6nbkx_enable_clk(const struct device *dev) +{ + uint16_t reg[2] = {0x0U, 0x0U}; + int ret = 0; + + do { + ret = xspi_psram_read_reg(dev, CR1_REG_ADDR, (uint8_t *)reg, 4); + if (ret < 0) { + break; + } + + reg[1] &= ~CR1_DIFFERENTIAL_CLOCK_MASK; + + ret = xspi_psram_write_reg(dev, CR1_REG_ADDR, (uint8_t *)reg, 4); + if (ret < 0) { + break; + } + + ret = xspi_psram_read_reg(dev, CR1_REG_ADDR, (uint8_t *)reg, 4); + if (ret < 0) { + break; + } + + if ((reg[1] & CR1_DIFFERENTIAL_CLOCK_MASK) != 0U) { + ret = -EIO; + break; + } + } while (0); + + return ret; +} + +static int memc_mcux_xspi_w958d6nbkx_enable_variable_latency(const struct device *dev) +{ + uint16_t reg[2] = {0x0U, 0x0U}; + int ret = 0; + + do { + ret = xspi_psram_read_reg(dev, CR0_REG_ADDR, (uint8_t *)reg, 4); + if (ret < 0) { + break; + } + + reg[1] &= ~CR0_REG_VARIABLE_LATENCY_MASK; + reg[0] &= ~CR0_REG_DRIVE_STRENGTH_MASK; + reg[0] |= (CR0_REG_DRIVE_STRENGTH_46OHMS << CR0_REG_DRIVE_STRENGTH_SHIFT); + + ret = xspi_psram_write_reg(dev, CR0_REG_ADDR, (uint8_t *)reg, 4); + if (ret < 0) { + break; + } + + ret = xspi_psram_read_reg(dev, CR0_REG_ADDR, (uint8_t *)reg, 4); + if (ret < 0) { + break; + } + + if ((reg[1] & CR0_REG_VARIABLE_LATENCY_MASK) != 0U) { + ret = -EIO; + break; + } + } while (0); + + return ret; +} + +static int memc_mcux_xspi_w958d6nbkx_setup(const struct device *dev, + xspi_device_config_t *config) +{ + struct memc_mcux_xspi_psram_data *data = dev->data; + const struct device *xspi_dev = data->xspi_dev; + uint16_t reg[2] = {0x0U, 0x0U}; + int ret = 0; + uint8_t id; + + memc_mcux_xspi_update_device_addr_mode(xspi_dev, kXSPI_DeviceByteAddressable); + + do { + ret = xspi_psram_read_reg(dev, ID0_REG_ADDR, (uint8_t *)reg, 4); + if (ret < 0) { + break; + } + + id = reg[1] & ID0_REG_ID_MASK; + if (id != PSRAM_MANUFACTURER_ID_WINBOND) { + LOG_ERR("Wrong manufacturer ID: 0x%X, expected: 0x%X", id, + PSRAM_MANUFACTURER_ID_WINBOND); + ret = -ENODEV; + break; + } + + if (config->enableCknPad) { + ret = memc_mcux_xspi_w958d6nbkx_enable_clk(dev); + if (ret < 0) { + break; + } + } + + if (config->interfaceSettings.hyperBusSettings.enableVariableLatency) { + ret = memc_mcux_xspi_w958d6nbkx_enable_variable_latency(dev); + if (ret < 0) { + break; + } + } + } while (0); + + memc_mcux_xspi_update_device_addr_mode(xspi_dev, kXSPI_Device4ByteAddressable); + + return ret; +} + +static int xspi_psram_write_reg(const struct device *dev, uint32_t regAddr, + uint8_t *data, uint32_t size) +{ + struct memc_mcux_xspi_psram_data *psram_data = dev->data; + xspi_transfer_t flashXfer; + + flashXfer.deviceAddress = psram_data->amba_address + regAddr; + flashXfer.cmdType = kXSPI_Write; + flashXfer.seqIndex = PSRAM_CMD_REG_WRITE; + flashXfer.targetGroup = kXSPI_TargetGroup0; + flashXfer.data = (uint32_t *)data; + flashXfer.dataSize = size; + flashXfer.lockArbitration = false; + + return memc_mcux_xspi_transfer(psram_data->xspi_dev, &flashXfer); +} + +static int xspi_psram_read_reg(const struct device *dev, uint32_t regAddr, + uint8_t *data, uint32_t size) +{ + struct memc_mcux_xspi_psram_data *psram_data = dev->data; + xspi_transfer_t flashXfer; + + flashXfer.deviceAddress = psram_data->amba_address + regAddr; + flashXfer.cmdType = kXSPI_Read; + flashXfer.seqIndex = PSRAM_CMD_REG_READ; + flashXfer.targetGroup = kXSPI_TargetGroup0; + flashXfer.data = (uint32_t *)data; + flashXfer.dataSize = size; + flashXfer.lockArbitration = false; + + return memc_mcux_xspi_transfer(psram_data->xspi_dev, &flashXfer); +} + +static int memc_mcux_xspi_psram_setup(const struct device *dev, + const char *dev_name_prefix, xspi_device_config_t *config) +{ + int ret = 0; + + if (strcmp(dev_name_prefix, "w958d6nbkx") == 0) { + ret = memc_mcux_xspi_w958d6nbkx_setup(dev, config); + } + + return ret; +} + +static int memc_mcux_xspi_psram_probe(const struct device *dev) +{ + const struct memc_mcux_xspi_psram_config *config = + (const struct memc_mcux_xspi_psram_config *)dev->config; + struct memc_mcux_xspi_psram_data *data = dev->data; + const struct device *xspi_dev = data->xspi_dev; + struct memc_xspi_dev_config *xspi_psram_config = NULL; + xspi_device_config_t *dev_config = NULL; + int ret; + + /* Get the specific memory parameters. */ + for (uint32_t i = 0; i < ARRAY_SIZE(dev_configs); i++) { + if (strncmp(dev_configs[i].name_prefix, data->dev_name, + strlen(dev_configs[i].name_prefix)) == 0) { + xspi_psram_config = (struct memc_xspi_dev_config *)&dev_configs[i]; + break; + } + } + + if (xspi_psram_config == NULL) { + LOG_ERR("Unsupported device: %s", data->dev_name); + return -ENOTSUP; + } + + /* Set special device configurations. */ + dev_config = &xspi_psram_config->xspi_dev_config; + dev_config->enableCknPad = config->enable_differential_clk; + dev_config->sampleClkConfig = config->sample_clk_config; + + ret = memc_mcux_xspi_get_root_clock(xspi_dev, &dev_config->xspiRootClk); + if (ret < 0) { + return ret; + } + + ret = memc_xspi_set_device_config(xspi_dev, dev_config, + xspi_psram_config->lut_array, xspi_psram_config->lut_count); + if (ret < 0) { + return ret; + } + + return memc_mcux_xspi_psram_setup(dev, xspi_psram_config->name_prefix, dev_config); +} + +static int memc_mcux_xspi_psram_init(const struct device *dev) +{ + struct memc_mcux_xspi_psram_data *psram_data = dev->data; + const struct device *xspi_dev = psram_data->xspi_dev; + + if (!device_is_ready(xspi_dev)) { + LOG_ERR("XSPI device is not ready"); + return -ENODEV; + } + + psram_data->amba_address = memc_mcux_xspi_get_ahb_address(xspi_dev); + + return memc_mcux_xspi_psram_probe(dev); +} + +#define MEMC_MCUX_XSPI_PSRAM_INIT(n) \ + static const struct memc_mcux_xspi_psram_config \ + memc_mcux_xspi_psram_config_##n = { \ + .enable_differential_clk = DT_INST_PROP(n, enable_differential_clk), \ + .sample_clk_config = { \ + .sampleClkSource = DT_INST_PROP(n, sample_clk_source), \ + .enableDQSLatency = DT_INST_PROP(n, enable_dqs_latency), \ + .dllConfig = { \ + .dllMode = kXSPI_AutoUpdateMode, \ + .useRefValue = true, \ + .enableCdl8 = true, \ + }, \ + }, \ + }; \ + static struct memc_mcux_xspi_psram_data memc_mcux_xspi_psram_data_##n = { \ + .xspi_dev = DEVICE_DT_GET(DT_INST_BUS(n)), \ + .dev_name = DT_INST_PROP(n, device_name), \ + .size = DT_INST_PROP(n, size), \ + }; \ + DEVICE_DT_INST_DEFINE(n, &memc_mcux_xspi_psram_init, NULL, \ + &memc_mcux_xspi_psram_data_##n, \ + &memc_mcux_xspi_psram_config_##n, POST_KERNEL, \ + CONFIG_MEMC_MCUX_XSPI_PSRAM, NULL); + +DT_INST_FOREACH_STATUS_OKAY(MEMC_MCUX_XSPI_PSRAM_INIT) diff --git a/dts/arm/nxp/nxp_rt7xx_cm33_cpu0.dtsi b/dts/arm/nxp/nxp_rt7xx_cm33_cpu0.dtsi index 451491ce28f24..bc94d4fbd656f 100644 --- a/dts/arm/nxp/nxp_rt7xx_cm33_cpu0.dtsi +++ b/dts/arm/nxp/nxp_rt7xx_cm33_cpu0.dtsi @@ -1110,8 +1110,26 @@ &xspi0 { compatible = "nxp,xspi"; status = "disabled"; - interrupts = <42 0>; + interrupts = <118 0>; #address-cells = <1>; #size-cells = <0>; - clocks = <&clkctl0 MCUX_XSPI_CLK>; + clocks = <&clkctl0 MCUX_XSPI0_CLK>; +}; + +&xspi1 { + compatible = "nxp,xspi"; + status = "disabled"; + interrupts = <119 0>; + #address-cells = <1>; + #size-cells = <0>; + clocks = <&clkctl0 MCUX_XSPI1_CLK>; +}; + +&xspi2 { + compatible = "nxp,xspi"; + status = "disabled"; + interrupts = <120 0>; + #address-cells = <1>; + #size-cells = <0>; + clocks = <&clkctl4 MCUX_XSPI2_CLK>; }; diff --git a/dts/arm/nxp/nxp_rt7xx_cm33_cpu1.dtsi b/dts/arm/nxp/nxp_rt7xx_cm33_cpu1.dtsi index 6d7a8aef3115e..e3814b1a4a546 100644 --- a/dts/arm/nxp/nxp_rt7xx_cm33_cpu1.dtsi +++ b/dts/arm/nxp/nxp_rt7xx_cm33_cpu1.dtsi @@ -370,3 +370,12 @@ status = "disabled"; }; }; + +&xspi2 { + compatible = "nxp,xspi"; + status = "disabled"; + interrupts = <74 0>; + #address-cells = <1>; + #size-cells = <0>; + clocks = <&clkctl4 MCUX_XSPI2_CLK>; +}; diff --git a/dts/arm/nxp/nxp_rt7xx_common.dtsi b/dts/arm/nxp/nxp_rt7xx_common.dtsi index e766fc5545114..414859695bbed 100644 --- a/dts/arm/nxp/nxp_rt7xx_common.dtsi +++ b/dts/arm/nxp/nxp_rt7xx_common.dtsi @@ -83,8 +83,9 @@ }; sram1: memory@20200000 { - compatible = "mmio-sram"; + compatible = "zephyr,memory-region", "mmio-sram"; reg = <0x20200000 DT_SIZE_K(2048)>; + zephyr,memory-region = "SRAM1"; }; sram3: memory@205C0000 { diff --git a/dts/bindings/flash_controller/nxp,xspi-nor.yaml b/dts/bindings/flash_controller/nxp,xspi-nor.yaml new file mode 100644 index 0000000000000..65eafa8920d13 --- /dev/null +++ b/dts/bindings/flash_controller/nxp,xspi-nor.yaml @@ -0,0 +1,8 @@ +# Copyright 2024-2025 NXP +# SPDX-License-Identifier: Apache-2.0 + +description: NXP XSPI Flash Controller + +compatible: "nxp,xspi-nor" + +include: ["nxp,xspi-device.yaml", "jedec,jesd216.yaml"] diff --git a/dts/bindings/memory-controllers/nxp,xspi-psram.yaml b/dts/bindings/memory-controllers/nxp,xspi-psram.yaml new file mode 100644 index 0000000000000..a4320329e85c3 --- /dev/null +++ b/dts/bindings/memory-controllers/nxp,xspi-psram.yaml @@ -0,0 +1,8 @@ +# Copyright 2025 NXP +# SPDX-License-Identifier: Apache-2.0 + +description: NXP XSPI PSRAM Controller + +compatible: "nxp,xspi-psram" + +include: ["nxp,xspi-device.yaml"] diff --git a/dts/bindings/mtd/nxp,xspi-device.yaml b/dts/bindings/mtd/nxp,xspi-device.yaml index d067b878c9306..2598d551f5013 100644 --- a/dts/bindings/mtd/nxp,xspi-device.yaml +++ b/dts/bindings/mtd/nxp,xspi-device.yaml @@ -1,6 +1,30 @@ -# Copyright 2024 NXP +# Copyright 2024-2025 NXP # SPDX-License-Identifier: Apache-2.0 description: NXP XSPI device -include: [spi-device.yaml, "jedec,jesd216.yaml"] +include: [base.yaml] + +properties: + device-name: + type: string + description: Memory device name. + + size: + type: int + required: true + description: Total memory size in bytes. + + enable-differential-clk: + type: boolean + description: Enable differential clock pad. + + sample-clk-source: + type: int + default: 3 + enum: [1, 2, 3, 5, 9] + description: Sample clock source. + + enable-dqs-latency: + type: boolean + description: Enable DQS latency. diff --git a/dts/bindings/mtd/nxp,xspi-mx25um51345g.yaml b/dts/bindings/mtd/nxp,xspi-mx25um51345g.yaml deleted file mode 100644 index 6554d5d61afd5..0000000000000 --- a/dts/bindings/mtd/nxp,xspi-mx25um51345g.yaml +++ /dev/null @@ -1,8 +0,0 @@ -# Copyright 2024 NXP -# SPDX-License-Identifier: Apache-2.0 - -description: NXP XSPI MX25UM51345G - -compatible: "nxp,xspi-mx25um51345g" - -include: ["nxp,xspi-device.yaml", soc-nv-flash.yaml] diff --git a/dts/bindings/spi/nxp,xspi.yaml b/dts/bindings/spi/nxp,xspi.yaml deleted file mode 100644 index 236ad62ba4880..0000000000000 --- a/dts/bindings/spi/nxp,xspi.yaml +++ /dev/null @@ -1,20 +0,0 @@ -# Copyright 2024 NXP -# SPDX-License-Identifier: Apache-2.0 - -description: NXP XSPI controller - -compatible: "nxp,xspi" - -include: [spi-controller.yaml, pinctrl-device.yaml] - -properties: - reg: - required: true - - interrupts: - required: true - -child-binding: - description: NXP XSPI port - - include: nxp,xspi-device.yaml diff --git a/dts/bindings/xspi/nxp,xspi.yaml b/dts/bindings/xspi/nxp,xspi.yaml new file mode 100644 index 0000000000000..25867dfbedd1d --- /dev/null +++ b/dts/bindings/xspi/nxp,xspi.yaml @@ -0,0 +1,88 @@ +# Copyright 2024-2025 NXP +# SPDX-License-Identifier: Apache-2.0 + +description: NXP XSPI controller + +compatible: "nxp,xspi" + +include: [spi-controller.yaml, pinctrl-device.yaml] + +properties: + reg: + required: true + + interrupts: + required: true + + byte-order: + type: int + default: 3 + enum: [0, 1, 2, 3] + description: | + Byte ordering endianness. + 0 = 64-bit BE, 1 = 32-bit LE, 2 = 32-bit BE, 3 = 64-bit LE + + enable-ahb-write: + type: boolean + description: Enable AHB write access. + + ahb-buffer-write-flush: + type: boolean + description: Enable flushing AHB buffer on write or IP access. + + ahb-prefetch: + type: boolean + description: Enable AHB read prefetch feature. + +child-binding: + description: NXP XSPI configurations + + properties: + # MDAD configuration + enable-descriptor-lock: + type: int + description: Enable descriptor lock + mask-type: + type: int + description: Mask type (0=AND, 1=OR) + mask: + type: int + description: 6-bit mask value + master-id-reference: + type: int + description: Master ID reference value + secure-attribute: + type: int + enum: [1, 2, 3] + description: Security attribute setting + + # FRAD configuration + start-address: + type: int + description: Start address of the region + end-address: + type: int + description: End address of the region + tg0-master-access: + type: int + description: Target group 0 access permissions + tg1-master-access: + type: int + description: Target group 1 access permissions + descriptor-lock: + type: int + enum: [0, 1, 2, 3] + description: | + Descriptor lock mode: + - Disable + - Enable till hard reset + - Enable except master ID + - Enable + exclusive-access-lock: + type: int + enum: [0, 2, 3] + description: | + Exclusive access lock mode: + - Disable + - Enable except master ID + - Enable diff --git a/modules/hal_nxp/mcux/mcux-sdk-ng/drivers/drivers.cmake b/modules/hal_nxp/mcux/mcux-sdk-ng/drivers/drivers.cmake index e1ecc7c2bcb4a..11aa68bfd3cf5 100644 --- a/modules/hal_nxp/mcux/mcux-sdk-ng/drivers/drivers.cmake +++ b/modules/hal_nxp/mcux/mcux-sdk-ng/drivers/drivers.cmake @@ -152,6 +152,7 @@ set_variable_ifdef(CONFIG_OPAMP_MCUX_OPAMP_FAST CONFIG_MCUX_COMPONENT_driver if(NOT CONFIG_SOC_MIMX9596) set_variable_ifdef(CONFIG_ETH_NXP_IMX_NETC CONFIG_MCUX_COMPONENT_driver.netc_switch) endif() +set_variable_ifdef(CONFIG_MEMC_MCUX_XSPI CONFIG_MCUX_COMPONENT_driver.xspi) set_variable_ifdef(CONFIG_SOC_SERIES_IMXRT10XX CONFIG_MCUX_COMPONENT_driver.ocotp) set_variable_ifdef(CONFIG_SOC_SERIES_IMXRT11XX CONFIG_MCUX_COMPONENT_driver.ocotp) @@ -317,5 +318,12 @@ if(CONFIG_SOC_MCXW236 OR CONFIG_SOC_MCXW235) set(CONFIG_MCUX_COMPONENT_driver.romapi ON) endif() +if((DEFINED CONFIG_FLASH_MCUX_XSPI_XIP) AND (DEFINED CONFIG_FLASH)) + zephyr_code_relocate(FILES ${MCUX_SDK_NG_DIR}/drivers/xspi/fsl_xspi.c + LOCATION ${CONFIG_FLASH_MCUX_XSPI_XIP_MEM}_TEXT) + zephyr_code_relocate(FILES ${MCUX_SDK_NG_DIR}/drivers/xspi/fsl_xspi.c + LOCATION ${CONFIG_FLASH_MCUX_XSPI_XIP_MEM}_RODATA) +endif() + # Load all drivers mcux_load_all_cmakelists_in_directory(${SdkRootDirPath}/drivers) diff --git a/samples/drivers/memc/src/main.c b/samples/drivers/memc/src/main.c index 85d82b5ed02da..d03ffd07caaab 100644 --- a/samples/drivers/memc/src/main.c +++ b/samples/drivers/memc/src/main.c @@ -32,6 +32,11 @@ #define MEMC_DEV DT_ALIAS(sram_ext) #define MEMC_BASE DT_REG_ADDR(MEMC_DEV) #define MEMC_SIZE DT_REG_SIZE(MEMC_DEV) +#elif DT_HAS_COMPAT_STATUS_OKAY(nxp_xspi_psram) +#define MEMC_DEV DT_ALIAS(sram_ext) +#define MSPI_BUS DT_BUS(MEMC_DEV) +#define MEMC_BASE DT_REG_ADDR_BY_IDX(MSPI_BUS, 1) +#define MEMC_SIZE (DT_PROP(MEMC_DEV, size) / 8) #else #error At least one driver should be selected! #endif diff --git a/samples/drivers/spi_flash/sample.yaml b/samples/drivers/spi_flash/sample.yaml index d17972e95899c..9b38ffe6a2dc0 100644 --- a/samples/drivers/spi_flash/sample.yaml +++ b/samples/drivers/spi_flash/sample.yaml @@ -10,6 +10,7 @@ tests: filter: dt_compat_enabled("jedec,spi-nor") or dt_compat_enabled("st,stm32-qspi-nor") or dt_compat_enabled("st,stm32-ospi-nor") or dt_compat_enabled("st,stm32-xspi-nor") or dt_compat_enabled("nordic,qspi-nor") or dt_compat_enabled("jedec,mspi-nor") + or dt_compat_enabled("nxp,xspi-nor") platform_exclude: - hifive_unmatched/fu740/s7 - hifive_unmatched/fu740/u74 diff --git a/samples/drivers/spi_flash/src/main.c b/samples/drivers/spi_flash/src/main.c index 07adf7739becc..f75aad5e1f71b 100644 --- a/samples/drivers/spi_flash/src/main.c +++ b/samples/drivers/spi_flash/src/main.c @@ -58,6 +58,8 @@ #define SPI_FLASH_COMPAT renesas_ra_ospi_b_nor #elif DT_HAS_COMPAT_STATUS_OKAY(renesas_ra_qspi_nor) #define SPI_FLASH_COMPAT renesas_ra_qspi_nor +#elif DT_HAS_COMPAT_STATUS_OKAY(nxp_xspi_nor) +#define SPI_FLASH_COMPAT nxp_xspi_nor #else #define SPI_FLASH_COMPAT invalid #endif diff --git a/soc/nxp/common/Kconfig.xspi_xip b/soc/nxp/common/Kconfig.xspi_xip index d087e8b2d2e3e..a2ad8724871d1 100644 --- a/soc/nxp/common/Kconfig.xspi_xip +++ b/soc/nxp/common/Kconfig.xspi_xip @@ -1,26 +1,29 @@ -# Copyright 2024 NXP +# Copyright 2024-2025 NXP # SPDX-License-Identifier: Apache-2.0 DT_CHOSEN_Z_FLASH := zephyr,flash DT_COMPAT_XSPI := nxp,xspi DT_CHOSEN_FLASH_NODE := $(dt_chosen_path,$(DT_CHOSEN_Z_FLASH)) -DT_CHOSEN_FLASH_PARENT := $(dt_node_parent,$(DT_CHOSEN_FLASH_NODE)) +DT_CHOSEN_FLASH_CTRL := $(dt_node_parent,$(DT_CHOSEN_FLASH_NODE)) +DT_CHOSEN_FLASH_CTRL_PARENT := $(dt_node_parent,$(DT_CHOSEN_FLASH_CTRL)) -DT_FLASH_PARENT_IS_XSPI := $(dt_node_has_compat,$(DT_CHOSEN_FLASH_PARENT),$(DT_COMPAT_XSPI)) -DT_FLASH_HAS_SIZE_PROP := $(dt_node_has_prop,$(DT_CHOSEN_FLASH_NODE),size) +DT_FLASH_CTRL_PARENT_IS_XSPI := $(dt_node_has_compat,$(DT_CHOSEN_FLASH_CTRL_PARENT),$(DT_COMPAT_XSPI)) +DT_FLASH_CTRL_HAS_SIZE_PROP := $(dt_node_has_prop,$(DT_CHOSEN_FLASH_CTRL),size) config FLASH_BASE_ADDRESS - default $(dt_node_reg_addr_hex,$(DT_CHOSEN_FLASH_PARENT),1) \ - if $(DT_FLASH_PARENT_IS_XSPI) + default $(dt_node_reg_addr_hex,$(DT_CHOSEN_FLASH_CTRL_PARENT),1) \ + if $(DT_FLASH_CTRL_PARENT_IS_XSPI) default $(dt_chosen_reg_addr_hex,$(DT_CHOSEN_Z_FLASH)) config FLASH_SIZE - default $(dt_node_int_prop_int,$(DT_CHOSEN_FLASH_NODE),size,Kb) + default $(dt_node_int_prop_int,$(DT_CHOSEN_FLASH_CTRL),size,K) \ + if $(DT_FLASH_CTRL_HAS_SIZE_PROP) + default $(dt_chosen_reg_size_int,$(DT_CHOSEN_Z_FLASH),0,K) config FLASH_MCUX_XSPI_XIP bool - default $(DT_FLASH_PARENT_IS_XSPI) + default $(DT_FLASH_CTRL_PARENT_IS_XSPI) select XIP help Allows the soc to safely initialize the clocks for the diff --git a/west.yml b/west.yml index a74e841f4990c..f7855c0c55547 100644 --- a/west.yml +++ b/west.yml @@ -210,7 +210,7 @@ manifest: groups: - hal - name: hal_nxp - revision: 41d5acc2cff410440d42e3c1b563aaa166aa034b + revision: pull/597/head path: modules/hal/nxp groups: - hal