diff --git a/boards/nxp/frdm_rw612/frdm_rw612.dts b/boards/nxp/frdm_rw612/frdm_rw612.dts index 03c8adc32875c4..e3868f95f77e4d 100644 --- a/boards/nxp/frdm_rw612/frdm_rw612.dts +++ b/boards/nxp/frdm_rw612/frdm_rw612.dts @@ -56,6 +56,13 @@ &flexspi { status = "okay"; + ahb-bufferable; + ahb-prefetch; + ahb-cacheable; + ahb-read-addr-opt; + ahb-boundary = "1024"; + rx-clock-source = <1>; + rx-clock-source-b = <1>; /* Winbond external flash */ w25q512jvfiq: w25q512jvfiq@0 { compatible = "nxp,imx-flexspi-nor"; @@ -66,6 +73,25 @@ write-block-size = <1>; spi-max-frequency = <133000000>; }; + aps6404l: aps6404l@2 { + compatible = "nxp,imx-flexspi-aps6404l"; + /* APS6404L is 8MB, 64MBit pSRAM */ + size = ; + reg = <2>; + spi-max-frequency = <109000000>; + /* PSRAM cannot be enabled while board is in default XIP + * configuration, as it will conflict with flash chip. + */ + status = "disabled"; + cs-interval-unit = <1>; + cs-interval = <2>; + cs-hold-time = <3>; + cs-setup-time = <3>; + data-valid-time = <6>; + column-space = <0>; + ahb-write-wait-unit = <2>; + ahb-write-wait-interval = <0>; + }; }; &hci { diff --git a/drivers/memc/CMakeLists.txt b/drivers/memc/CMakeLists.txt index c45825d68cafc6..5cfb571c34233f 100644 --- a/drivers/memc/CMakeLists.txt +++ b/drivers/memc/CMakeLists.txt @@ -11,6 +11,7 @@ zephyr_library_sources_ifdef(CONFIG_MEMC_MCUX_FLEXSPI memc_mcux_flexspi.c) zephyr_library_sources_ifdef(CONFIG_MEMC_MCUX_FLEXSPI_W956A8MBYA memc_mcux_flexspi_w956a8mbya.c) zephyr_library_sources_ifdef(CONFIG_MEMC_MCUX_FLEXSPI_S27KS0641 memc_mcux_flexspi_s27ks0641.c) zephyr_library_sources_ifdef(CONFIG_MEMC_MCUX_FLEXSPI_APS6408L memc_mcux_flexspi_aps6408l.c) +zephyr_library_sources_ifdef(CONFIG_MEMC_MCUX_FLEXSPI_APS6404L memc_mcux_flexspi_aps6404l.c) zephyr_library_sources_ifdef(CONFIG_MEMC_MCUX_FLEXSPI_IS66WVQ8M4 memc_mcux_flexspi_is66wvq8m4.c) zephyr_library_sources_ifdef(CONFIG_MEMC_NXP_FLEXRAM memc_nxp_flexram.c) diff --git a/drivers/memc/Kconfig.mcux b/drivers/memc/Kconfig.mcux index 7d231808ea3752..23aac7e3fa72a1 100644 --- a/drivers/memc/Kconfig.mcux +++ b/drivers/memc/Kconfig.mcux @@ -30,6 +30,12 @@ config MEMC_MCUX_FLEXSPI_IS66WVQ8M4 depends on DT_HAS_NXP_IMX_FLEXSPI_IS66WVQ8M4_ENABLED select MEMC_MCUX_FLEXSPI +config MEMC_MCUX_FLEXSPI_APS6404L + bool "MCUX FlexSPI AP Memory APS6404L pSRAM driver" + default y + depends on DT_HAS_NXP_IMX_FLEXSPI_APS6404L_ENABLED + select MEMC_MCUX_FLEXSPI + config MEMC_MCUX_FLEXSPI_INIT_PRIORITY int "MCUX FLEXSPI MEMC driver initialization priority" default MEMC_INIT_PRIORITY diff --git a/drivers/memc/memc_mcux_flexspi.c b/drivers/memc/memc_mcux_flexspi.c index c3ad20aab4b2d1..f62b418f7ad8e2 100644 --- a/drivers/memc/memc_mcux_flexspi.c +++ b/drivers/memc/memc_mcux_flexspi.c @@ -53,6 +53,7 @@ struct memc_flexspi_data { bool ahb_cacheable; bool ahb_prefetch; bool ahb_read_addr_opt; + uint8_t ahb_boundary; bool combination_mode; bool sck_differential_clock; flexspi_read_sample_clock_t rx_sample_clock; @@ -265,14 +266,6 @@ void *memc_flexspi_get_ahb_address(const struct device *dev, offset += data->size[i]; } -#if defined(FSL_FEATURE_FLEXSPI_SUPPORT_ADDRESS_SHIFT) && \ - (FSL_FEATURE_FLEXSPI_SUPPORT_ADDRESS_SHIFT) - if (data->base->FLSHCR0[port] & FLEXSPI_FLSHCR0_ADDRSHIFT_MASK) { - /* Address shift is set, add 0x1000_0000 to AHB address */ - offset += 0x10000000; - } -#endif - return data->ahb_base + offset; } @@ -350,6 +343,12 @@ FSL_FEATURE_FLEXSPI_SUPPORT_SEPERATE_RXCLKSRC_PORTB FLEXSPI_Init(data->base, &flexspi_config); +#if defined(FLEXSPI_AHBCR_ALIGNMENT_MASK) + /* Configure AHB alignment boundary */ + data->base->AHBCR = (data->base->AHBCR & ~FLEXSPI_AHBCR_ALIGNMENT_MASK) | + FLEXSPI_AHBCR_ALIGNMENT(data->ahb_boundary); +#endif + if (memc_flexspi_is_running_xip(dev)) { /* Restore flash sizes */ for (i = 0; i < kFLEXSPI_PortCount; i++) { @@ -422,6 +421,7 @@ static int memc_flexspi_pm_action(const struct device *dev, enum pm_device_actio .ahb_cacheable = DT_INST_PROP(n, ahb_cacheable), \ .ahb_prefetch = DT_INST_PROP(n, ahb_prefetch), \ .ahb_read_addr_opt = DT_INST_PROP(n, ahb_read_addr_opt),\ + .ahb_boundary = DT_INST_ENUM_IDX(n, ahb_boundary), \ .combination_mode = DT_INST_PROP(n, combination_mode), \ .sck_differential_clock = DT_INST_PROP(n, sck_differential_clock), \ .rx_sample_clock = DT_INST_PROP(n, rx_clock_source), \ diff --git a/drivers/memc/memc_mcux_flexspi_aps6404l.c b/drivers/memc/memc_mcux_flexspi_aps6404l.c new file mode 100644 index 00000000000000..cbf7182112df91 --- /dev/null +++ b/drivers/memc/memc_mcux_flexspi_aps6404l.c @@ -0,0 +1,268 @@ +/* + * Copyright 2024 NXP + * + * SPDX-License-Identifier: Apache-2.0 + */ + + /* + * Based on memc_mcux_flexspi_s27ks0641, which is: Copyright 2021 Basalte bv + */ + + #define DT_DRV_COMPAT nxp_imx_flexspi_aps6404l + + #include + #include + #include + + #include "memc_mcux_flexspi.h" + + +/* + * NOTE: If CONFIG_FLASH_MCUX_FLEXSPI_XIP is selected, Any external functions + * called while interacting with the flexspi MUST be relocated to SRAM or ITCM + * at runtime, so that the chip does not access the flexspi to read program + * instructions while it is being written to + */ +#if defined(CONFIG_FLASH_MCUX_FLEXSPI_XIP) && (CONFIG_MEMC_LOG_LEVEL > 0) +#warning "Enabling memc driver logging and XIP mode simultaneously can cause \ + read-while-write hazards. This configuration is not recommended." +#endif + +LOG_MODULE_REGISTER(memc_flexspi_aps6404l, CONFIG_MEMC_LOG_LEVEL); + +#define APM_VENDOR_ID 0xD + +enum { + READ_DATA = 0, + WRITE_DATA, + RESET_EN, + RESET, + READ_ID +}; + +struct memc_flexspi_aps6404l_config { + flexspi_port_t port; + flexspi_device_config_t config; +}; + +/* Device variables used in critical sections should be in this structure */ +struct memc_flexspi_aps6404l_data { + const struct device *controller; +}; + + +static const uint32_t memc_flexspi_aps6404l_lut[][4] = { + /* Read Data (Sync read, linear burst) */ + [READ_DATA] = { + FLEXSPI_LUT_SEQ(kFLEXSPI_Command_SDR, kFLEXSPI_1PAD, 0xEB, + kFLEXSPI_Command_RADDR_SDR, kFLEXSPI_4PAD, 0x18), + FLEXSPI_LUT_SEQ(kFLEXSPI_Command_DUMMY_SDR, kFLEXSPI_4PAD, + 0x06, kFLEXSPI_Command_READ_SDR, kFLEXSPI_4PAD, 0x04), + }, + /* Write Data (Sync write, linear burst) */ + [WRITE_DATA] = { + FLEXSPI_LUT_SEQ(kFLEXSPI_Command_SDR, kFLEXSPI_1PAD, 0x38, + kFLEXSPI_Command_RADDR_SDR, kFLEXSPI_4PAD, 0x18), + FLEXSPI_LUT_SEQ(kFLEXSPI_Command_WRITE_SDR, kFLEXSPI_4PAD, + 0x00, kFLEXSPI_Command_STOP, kFLEXSPI_1PAD, 0x00), + }, + + [RESET_EN] = { + FLEXSPI_LUT_SEQ(kFLEXSPI_Command_SDR, kFLEXSPI_1PAD, 0x66, + kFLEXSPI_Command_STOP, kFLEXSPI_1PAD, 0x00), + }, + /* Reset (Global reset) */ + [RESET] = { + FLEXSPI_LUT_SEQ(kFLEXSPI_Command_SDR, kFLEXSPI_1PAD, 0x99, + kFLEXSPI_Command_STOP, kFLEXSPI_1PAD, 0x00), + }, + + [READ_ID] = { + FLEXSPI_LUT_SEQ(kFLEXSPI_Command_SDR, kFLEXSPI_1PAD, 0x9F, + kFLEXSPI_Command_RADDR_SDR, kFLEXSPI_1PAD, 0x18), + FLEXSPI_LUT_SEQ(kFLEXSPI_Command_READ_SDR, kFLEXSPI_1PAD, 0x08, + kFLEXSPI_Command_STOP, kFLEXSPI_1PAD, 0), + }, +}; + + +static int memc_flexspi_aps6404l_get_vendor_id(const struct device *dev, + uint8_t *vendor_id) +{ + const struct memc_flexspi_aps6404l_config *config = dev->config; + struct memc_flexspi_aps6404l_data *data = dev->data; + uint32_t buffer = 0; + int ret; + + flexspi_transfer_t transfer = { + .deviceAddress = 0x0, + .port = config->port, + .cmdType = kFLEXSPI_Read, + .SeqNumber = 1, + .seqIndex = READ_ID, + .data = &buffer, + .dataSize = 1, + }; + + ret = memc_flexspi_transfer(data->controller, &transfer); + *vendor_id = buffer & 0x1f; + + return ret; +} + +static int memc_flexspi_aps6404l_reset_enable(const struct device *dev) +{ + const struct memc_flexspi_aps6404l_config *config = dev->config; + struct memc_flexspi_aps6404l_data *data = dev->data; + int ret; + + flexspi_transfer_t transfer = { + .deviceAddress = 0x0, + .port = config->port, + .cmdType = kFLEXSPI_Command, + .SeqNumber = 1, + .seqIndex = RESET_EN, + .data = NULL, + .dataSize = 0, + }; + + LOG_DBG("Enabling reset ram"); + ret = memc_flexspi_transfer(data->controller, &transfer); + if (ret < 0) { + return ret; + } + /* We need to delay 5 ms to allow APS6404L pSRAM to reinitialize */ + k_msleep(5); + + return ret; +} + + +static int memc_flexspi_aps6404l_reset(const struct device *dev) +{ + const struct memc_flexspi_aps6404l_config *config = dev->config; + struct memc_flexspi_aps6404l_data *data = dev->data; + int ret; + + flexspi_transfer_t transfer = { + .deviceAddress = 0x0, + .port = config->port, + .cmdType = kFLEXSPI_Command, + .SeqNumber = 1, + .seqIndex = RESET, + .data = NULL, + .dataSize = 0, + }; + + LOG_DBG("Resetting ram"); + ret = memc_flexspi_transfer(data->controller, &transfer); + if (ret < 0) { + return ret; + } + /* We need to delay 5 ms to allow APS6404L pSRAM to reinitialize */ + k_msleep(5); + + return ret; +} + +static int memc_flexspi_aps6404l_init(const struct device *dev) +{ + const struct memc_flexspi_aps6404l_config *config = dev->config; + struct memc_flexspi_aps6404l_data *data = dev->data; + uint8_t vendor_id; + + if (!device_is_ready(data->controller)) { + LOG_ERR("Controller device not ready"); + return -ENODEV; + } + + if (memc_flexspi_set_device_config(data->controller, &config->config, + (const uint32_t *) memc_flexspi_aps6404l_lut, + sizeof(memc_flexspi_aps6404l_lut) / MEMC_FLEXSPI_CMD_SIZE, + config->port)) { + LOG_ERR("Could not set device configuration"); + return -EINVAL; + } + + memc_flexspi_reset(data->controller); + + if (memc_flexspi_aps6404l_reset_enable(dev)) { + LOG_ERR("Could not enable reset pSRAM"); + return -EIO; + } + + if (memc_flexspi_aps6404l_reset(dev)) { + LOG_ERR("Could not reset pSRAM"); + return -EIO; + } + + if (memc_flexspi_aps6404l_get_vendor_id(dev, &vendor_id)) { + LOG_ERR("Could not read vendor id"); + return -EIO; + } + LOG_DBG("Vendor id: 0x%0x", vendor_id); + if (vendor_id != APM_VENDOR_ID) { + LOG_WRN("Vendor ID does not match expected value of 0x%0x", + APM_VENDOR_ID); + } + + return 0; +} + +#define CONCAT3(x, y, z) x ## y ## z + +#define CS_INTERVAL_UNIT(unit) \ + CONCAT3(kFLEXSPI_CsIntervalUnit, unit, SckCycle) + +#define AHB_WRITE_WAIT_UNIT(unit) \ + CONCAT3(kFLEXSPI_AhbWriteWaitUnit, unit, AhbCycle) + +#define MEMC_FLEXSPI_DEVICE_CONFIG(n) \ + { \ + .flexspiRootClk = DT_INST_PROP(n, spi_max_frequency), \ + .isSck2Enabled = false, \ + .flashSize = DT_INST_PROP(n, size) / 8 / KB(1), \ + .addressShift = false, \ + .CSIntervalUnit = \ + CS_INTERVAL_UNIT( \ + DT_INST_PROP(n, cs_interval_unit)), \ + .CSInterval = DT_INST_PROP(n, cs_interval), \ + .CSHoldTime = DT_INST_PROP(n, cs_hold_time), \ + .CSSetupTime = DT_INST_PROP(n, cs_setup_time), \ + .dataValidTime = DT_INST_PROP(n, data_valid_time), \ + .columnspace = DT_INST_PROP(n, column_space), \ + .enableWordAddress = DT_INST_PROP(n, word_addressable), \ + .AWRSeqIndex = WRITE_DATA, \ + .AWRSeqNumber = 1, \ + .ARDSeqIndex = READ_DATA, \ + .ARDSeqNumber = 1, \ + .AHBWriteWaitUnit = \ + AHB_WRITE_WAIT_UNIT( \ + DT_INST_PROP(n, ahb_write_wait_unit)), \ + .AHBWriteWaitInterval = \ + DT_INST_PROP(n, ahb_write_wait_interval), \ + .enableWriteMask = false, \ + } \ + +#define MEMC_FLEXSPI_APS6404L(n) \ + static const struct memc_flexspi_aps6404l_config \ + memc_flexspi_aps6404l_config_##n = { \ + .port = DT_INST_REG_ADDR(n), \ + .config = MEMC_FLEXSPI_DEVICE_CONFIG(n), \ + }; \ + \ + static struct memc_flexspi_aps6404l_data \ + memc_flexspi_aps6404l_data_##n = { \ + .controller = DEVICE_DT_GET(DT_INST_BUS(n)), \ + }; \ + \ + DEVICE_DT_INST_DEFINE(n, \ + memc_flexspi_aps6404l_init, \ + NULL, \ + &memc_flexspi_aps6404l_data_##n, \ + &memc_flexspi_aps6404l_config_##n, \ + POST_KERNEL, \ + CONFIG_MEMC_INIT_PRIORITY, \ + NULL); + +DT_INST_FOREACH_STATUS_OKAY(MEMC_FLEXSPI_APS6404L) diff --git a/drivers/memc/memc_mcux_flexspi_is66wvq8m4.c b/drivers/memc/memc_mcux_flexspi_is66wvq8m4.c index 7ce698b3dd6506..1a964b1454f1ec 100644 --- a/drivers/memc/memc_mcux_flexspi_is66wvq8m4.c +++ b/drivers/memc/memc_mcux_flexspi_is66wvq8m4.c @@ -201,7 +201,7 @@ static int memc_flexspi_is66wvq8m4_init(const struct device *dev) .flexspiRootClk = DT_INST_PROP(n, spi_max_frequency), \ .isSck2Enabled = false, \ .flashSize = DT_INST_PROP(n, size) / 8 / KB(1), \ - .addressShift = DT_INST_REG_ADDR(n) != 0, \ + .addressShift = true, \ .CSIntervalUnit = \ CS_INTERVAL_UNIT( \ DT_INST_PROP(n, cs_interval_unit)), \ diff --git a/dts/bindings/mtd/nxp,imx-flexspi-aps6404l.yaml b/dts/bindings/mtd/nxp,imx-flexspi-aps6404l.yaml new file mode 100644 index 00000000000000..1f38a7288be82b --- /dev/null +++ b/dts/bindings/mtd/nxp,imx-flexspi-aps6404l.yaml @@ -0,0 +1,8 @@ +# Copyright 2022 NXP +# SPDX-License-Identifier: Apache-2.0 + +description: AP Memory APS6404L pSRAM on NXP FlexSPI bus + +compatible: "nxp,imx-flexspi-aps6404l" + +include: nxp,imx-flexspi-device.yaml diff --git a/dts/bindings/spi/nxp,imx-flexspi.yaml b/dts/bindings/spi/nxp,imx-flexspi.yaml index 8a266a8a91dd32..cedf484ba7a5a0 100644 --- a/dts/bindings/spi/nxp,imx-flexspi.yaml +++ b/dts/bindings/spi/nxp,imx-flexspi.yaml @@ -86,6 +86,18 @@ properties: master_id: AHBRXBUFxCRx[MSTRID] buf_size: AHBRXBUFxCRx[BUFSZ] + ahb-boundary: + type: string + default: "no-boundary" + enum: + - "no-boundary" + - "1024" + - "512" + - "256" + description: | + Sets the AHB read/write boundary. Only supported by some versions of + the FLEXSPI IP. When set, all memory accesses that cross an address + boundary of the specified size will be divided into smaller sub accesses. child-binding: description: NXP FlexSPI port diff --git a/samples/drivers/memc/boards/frdm_rw612.conf b/samples/drivers/memc/boards/frdm_rw612.conf new file mode 100644 index 00000000000000..c2db7600626083 --- /dev/null +++ b/samples/drivers/memc/boards/frdm_rw612.conf @@ -0,0 +1,22 @@ +# Copyright 2024 NXP +# SPDX-License-Identifier: Apache-2.0 + +# In order to safely access the PSRAM on port B of the RW FlexSPI peripheral, +# the QSPI flash on port A must be configured by the application. Otherwise, +# the PSRAM configuration will overwrite the LUT entries for the QSPI flash, +# and the application will no longer be able to XIP from the flash. +# To make sure the QSPI flash is configured, enable flash drivers. +CONFIG_FLASH=y + +# Initialization priorities are critical here. The FlexSPI MEMC driver must +# initialize first. Then, the QSPI flash driver must initialize to program +# the LUT table for port A. Finally, the PSRAM driver can initialize and +# program the LUT table for port B +CONFIG_MEMC_MCUX_FLEXSPI_INIT_PRIORITY=0 +CONFIG_FLASH_INIT_PRIORITY=50 +CONFIG_MEMC_INIT_PRIORITY=60 + +# This board has the PSRAM attached to the same FLEXSPI device as the flash +# chip used for XIP, so we must explicitly enable the FLEXSPI MEMC driver +# to reconfigure the flash device it is executing from +CONFIG_MEMC_MCUX_FLEXSPI_INIT_XIP=y diff --git a/samples/drivers/memc/boards/frdm_rw612.overlay b/samples/drivers/memc/boards/frdm_rw612.overlay new file mode 100644 index 00000000000000..88b2498e592fab --- /dev/null +++ b/samples/drivers/memc/boards/frdm_rw612.overlay @@ -0,0 +1,51 @@ +/* + * Copyright 2024 NXP + * + * SPDX-License-Identifier: Apache-2.0 + */ + +/ { + aliases { + sram-ext = &aps6404l; + }; +}; + +&w25q512jvfiq { + /* + * Lower max FlexSPI frequency to 109MHz, as the PSRAM does not support + * higher frequencies at 3.3V + */ + spi-max-frequency = <109000000>; +}; + +&aps6404l { + status = "okay"; +}; + +&pinctrl { + pinmux_flexspi_safe: pinmux-flexspi-safe { + group0 { + pinmux = ; + slew-rate = "normal"; + }; + + group1 { + pinmux = ; + slew-rate = "normal"; + bias-pull-down; + }; + }; +}; + +/* Override pin control state to use one that only changes the PSRAM pin + * configuration + */ +&flexspi { + pinctrl-0 = <&pinmux_flexspi_safe>; + pinctrl-names = "default"; +};