diff --git a/drivers/memc/CMakeLists.txt b/drivers/memc/CMakeLists.txt index 114c6495d9b..a991e4e4716 100644 --- a/drivers/memc/CMakeLists.txt +++ b/drivers/memc/CMakeLists.txt @@ -18,3 +18,5 @@ zephyr_library_sources_ifdef(CONFIG_MEMC_HIFIVE_UNMATCHED_DRAM sifive_ddr.c) 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_NXP_S32_QSPI memc_nxp_s32_qspi.c) diff --git a/drivers/memc/Kconfig b/drivers/memc/Kconfig index 28cabd2bda3..f6416557355 100644 --- a/drivers/memc/Kconfig +++ b/drivers/memc/Kconfig @@ -29,4 +29,6 @@ source "drivers/memc/Kconfig.sam" source "drivers/memc/Kconfig.sifive" +source "drivers/memc/Kconfig.nxp_s32" + endif diff --git a/drivers/memc/Kconfig.nxp_s32 b/drivers/memc/Kconfig.nxp_s32 new file mode 100644 index 00000000000..a1ec0d0d0b6 --- /dev/null +++ b/drivers/memc/Kconfig.nxp_s32 @@ -0,0 +1,10 @@ +# Copyright 2023 NXP +# SPDX-License-Identifier: Apache-2.0 + +config MEMC_NXP_S32_QSPI + bool "NXP S32 Quad Serial Peripheral Interface (QSPI) controller" + default y + depends on DT_HAS_NXP_S32_QSPI_ENABLED + select PINCTRL + help + Enable NXP S32 Quad Serial Peripheral Interface (QSPI) controller. diff --git a/drivers/memc/memc_nxp_s32_qspi.c b/drivers/memc/memc_nxp_s32_qspi.c new file mode 100644 index 00000000000..b2dc1466d6f --- /dev/null +++ b/drivers/memc/memc_nxp_s32_qspi.c @@ -0,0 +1,197 @@ +/* + * Copyright 2023 NXP + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#define DT_DRV_COMPAT nxp_s32_qspi + +#include +LOG_MODULE_REGISTER(nxp_s32_qspi_memc, CONFIG_MEMC_LOG_LEVEL); + +#include +#include + +#include +#include "memc_nxp_s32_qspi.h" + +/* Mapping between QSPI chip select signals and devicetree chip select identifiers */ +#define QSPI_PCSFA1 0 +#define QSPI_PCSFA2 1 +#define QSPI_PCSFB1 2 +#define QSPI_PCSFB2 3 + +struct memc_nxp_s32_qspi_data { + uint8_t instance; +}; + +struct memc_nxp_s32_qspi_config { + QuadSPI_Type *base; + const struct pinctrl_dev_config *pincfg; + + const Qspi_Ip_ControllerConfigType *controller_cfg; +}; + +static inline uint8_t get_instance(QuadSPI_Type *base) +{ + QuadSPI_Type *const base_ptrs[QuadSPI_INSTANCE_COUNT] = IP_QuadSPI_BASE_PTRS; + uint8_t i; + + for (i = 0; i < QuadSPI_INSTANCE_COUNT; i++) { + if (base_ptrs[i] == base) { + break; + } + } + __ASSERT_NO_MSG(i < QuadSPI_INSTANCE_COUNT); + + return i; +} + +static int memc_nxp_s32_qspi_init(const struct device *dev) +{ + const struct memc_nxp_s32_qspi_config *config = dev->config; + struct memc_nxp_s32_qspi_data *data = dev->data; + Qspi_Ip_StatusType status; + + data->instance = get_instance(config->base); + + if (pinctrl_apply_state(config->pincfg, PINCTRL_STATE_DEFAULT)) { + return -EIO; + } + + status = Qspi_Ip_ControllerInit(data->instance, config->controller_cfg); + if (status != STATUS_QSPI_IP_SUCCESS) { + LOG_ERR("Fail to initialize QSPI controller %d (%d)", + data->instance, status); + return -EIO; + } + + return 0; +} + +uint8_t memc_nxp_s32_qspi_get_instance(const struct device *dev) +{ + struct memc_nxp_s32_qspi_data *data = dev->data; + + return data->instance; +} + +#define QSPI_DATA_CFG(n) \ + IF_ENABLED(FEATURE_QSPI_DDR, ( \ + .dataRate = CONCAT(QSPI_IP_DATA_RATE_, \ + DT_INST_STRING_UPPER_TOKEN(n, data_rate)), \ + .dataAlign = COND_CODE_1(DT_INST_PROP(n, hold_time_2x), \ + (QSPI_IP_FLASH_DATA_ALIGN_2X_REFCLK), \ + (QSPI_IP_FLASH_DATA_ALIGN_REFCLK)), \ + )) + +#define QSPI_ADDR_CFG(n) \ + IF_ENABLED(FEATURE_QSPI_ADDR_CFG, ( \ + .columnAddr = DT_INST_PROP_OR(n, column_space, 0), \ + .wordAddresable = DT_INST_PROP(n, word_addressable), \ + )) + +#define QSPI_BYTES_SWAP_ADDR(n) \ + IF_ENABLED(FEATURE_QSPI_BYTES_SWAP_ADDR, \ + (.byteSwap = DT_INST_PROP(n, byte_swapping),)) + +#define QSPI_SAMPLE_DELAY(n) \ + COND_CODE_1(DT_INST_PROP(n, sample_delay_half_cycle), \ + (QSPI_IP_SAMPLE_DELAY_HALFCYCLE_EARLY_DQS), \ + (QSPI_IP_SAMPLE_DELAY_SAME_DQS)) + +#define QSPI_SAMPLE_PHASE(n) \ + COND_CODE_1(DT_INST_PROP(n, sample_phase_inverted), \ + (QSPI_IP_SAMPLE_PHASE_INVERTED), \ + (QSPI_IP_SAMPLE_PHASE_NON_INVERTED)) + +#define QSPI_AHB_BUFFERS(n) \ + { \ + .masters = DT_INST_PROP(n, ahb_buffers_masters), \ + .sizes = DT_INST_PROP(n, ahb_buffers_sizes), \ + .allMasters = (bool)DT_INST_PROP(n, ahb_buffers_all_masters), \ + } + +#define QSPI_DLL_CFG(n, side, side_upper) \ + IF_ENABLED(FEATURE_QSPI_HAS_DLL, ( \ + .dllSettings##side_upper = { \ + .dllMode = CONCAT(QSPI_IP_DLL_, \ + DT_INST_STRING_UPPER_TOKEN(n, side##_dll_mode)), \ + .freqEnable = DT_INST_PROP(n, side##_dll_freq_enable), \ + .coarseDelay = DT_INST_PROP(n, side##_dll_coarse_delay), \ + .fineDelay = DT_INST_PROP(n, side##_dll_fine_delay), \ + .tapSelect = DT_INST_PROP(n, side##_dll_tap_select), \ + IF_ENABLED(FEATURE_QSPI_DLL_LOOPCONTROL, ( \ + .referenceCounter = DT_INST_PROP(n, side##_dll_ref_counter), \ + .resolution = DT_INST_PROP(n, side##_dll_resolution), \ + )) \ + }, \ + )) + +#define QSPI_READ_MODE(n, side, side_upper) \ + CONCAT(QSPI_IP_READ_MODE_, DT_INST_STRING_UPPER_TOKEN(n, side##_rx_clock_source)) + +#define QSPI_IDLE_SIGNAL_DRIVE(n, side, side_upper) \ + IF_ENABLED(FEATURE_QSPI_CONFIGURABLE_ISD, ( \ + .io2IdleValue##side_upper = (uint8_t)DT_INST_PROP(n, side##_io2_idle_high),\ + .io3IdleValue##side_upper = (uint8_t)DT_INST_PROP(n, side##_io3_idle_high),\ + )) + +#define QSPI_PORT_SIZE_FN(node_id, side_upper, port) \ + COND_CODE_1(IS_EQ(DT_REG_ADDR(node_id), QSPI_PCSF##side_upper##port), \ + (COND_CODE_1(DT_NODE_HAS_STATUS(node_id, okay), \ + (.memSize##side_upper##port = DT_PROP(node_id, size) / 8,), \ + (.memSize##side_upper##port = 0,))), \ + (EMPTY)) + +#define QSPI_PORT_SIZE(n, side_upper) \ + DT_INST_FOREACH_CHILD_VARGS(n, QSPI_PORT_SIZE_FN, side_upper, 1) \ + DT_INST_FOREACH_CHILD_VARGS(n, QSPI_PORT_SIZE_FN, side_upper, 2) + +#define QSPI_SIDE_CFG(n, side, side_upper) \ + QSPI_IDLE_SIGNAL_DRIVE(n, side, side_upper) \ + QSPI_DLL_CFG(n, side, side_upper) \ + QSPI_PORT_SIZE(n, side_upper) \ + .readMode##side_upper = QSPI_READ_MODE(n, side, side_upper), + +#define MEMC_NXP_S32_QSPI_CONTROLLER_CONFIG(n) \ + BUILD_ASSERT(DT_INST_PROP_LEN(n, ahb_buffers_masters) == QSPI_IP_AHB_BUFFERS, \ + "ahb-buffers-masters must be of size QSPI_IP_AHB_BUFFERS"); \ + BUILD_ASSERT(DT_INST_PROP_LEN(n, ahb_buffers_sizes) == QSPI_IP_AHB_BUFFERS, \ + "ahb-buffers-sizes must be of size QSPI_IP_AHB_BUFFERS"); \ + BUILD_ASSERT( \ + CONCAT(FEATURE_QSPI_, DT_INST_STRING_UPPER_TOKEN(n, a_rx_clock_source)) == 1,\ + "a-rx-clock-source source mode selected is not supported"); \ + \ + static const Qspi_Ip_ControllerConfigType \ + memc_nxp_s32_qspi_controller_cfg_##n = { \ + .csHoldTime = DT_INST_PROP(n, cs_hold_time), \ + .csSetupTime = DT_INST_PROP(n, cs_setup_time), \ + .sampleDelay = QSPI_SAMPLE_DELAY(n), \ + .samplePhase = QSPI_SAMPLE_PHASE(n), \ + .ahbConfig = QSPI_AHB_BUFFERS(n), \ + QSPI_SIDE_CFG(n, a, A) \ + QSPI_DATA_CFG(n) \ + QSPI_ADDR_CFG(n) \ + QSPI_BYTES_SWAP_ADDR(n) \ + } + +#define MEMC_NXP_S32_QSPI_INIT_DEVICE(n) \ + PINCTRL_DT_INST_DEFINE(n); \ + MEMC_NXP_S32_QSPI_CONTROLLER_CONFIG(n); \ + static struct memc_nxp_s32_qspi_data memc_nxp_s32_qspi_data_##n; \ + static const struct memc_nxp_s32_qspi_config memc_nxp_s32_qspi_config_##n = { \ + .base = (QuadSPI_Type *)DT_INST_REG_ADDR(n), \ + .controller_cfg = &memc_nxp_s32_qspi_controller_cfg_##n, \ + .pincfg = PINCTRL_DT_INST_DEV_CONFIG_GET(n), \ + }; \ + DEVICE_DT_INST_DEFINE(n, \ + memc_nxp_s32_qspi_init, \ + NULL, \ + &memc_nxp_s32_qspi_data_##n, \ + &memc_nxp_s32_qspi_config_##n, \ + POST_KERNEL, \ + CONFIG_MEMC_INIT_PRIORITY, \ + NULL); + +DT_INST_FOREACH_STATUS_OKAY(MEMC_NXP_S32_QSPI_INIT_DEVICE) diff --git a/drivers/memc/memc_nxp_s32_qspi.h b/drivers/memc/memc_nxp_s32_qspi.h new file mode 100644 index 00000000000..c4f30e97dae --- /dev/null +++ b/drivers/memc/memc_nxp_s32_qspi.h @@ -0,0 +1,29 @@ +/* + * Copyright 2023 NXP + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include + +#include + +/** + * @brief Build a QSPI Look-up Table (LUT) sequence entry. + * + * @param inst instruction + * @param pads pad information + * @param op operand + */ +#define QSPI_LUT_OP(inst, pads, op) \ + ((Qspi_Ip_InstrOpType)((Qspi_Ip_InstrOpType)(inst) \ + | (Qspi_Ip_InstrOpType)(pads) \ + | (Qspi_Ip_InstrOpType)(op))) + +/** + * @brief Get the QSPI peripheral hardware instance number. + * + * @param dev device pointer + */ +uint8_t memc_nxp_s32_qspi_get_instance(const struct device *dev); diff --git a/dts/bindings/qspi/nxp,s32-qspi.yaml b/dts/bindings/qspi/nxp,s32-qspi.yaml new file mode 100644 index 00000000000..7d61e5d8104 --- /dev/null +++ b/dts/bindings/qspi/nxp,s32-qspi.yaml @@ -0,0 +1,208 @@ +# Copyright 2023 NXP +# SPDX-License-Identifier: Apache-2.0 + +description: | + NXP S32 Quad Serial Peripheral Interface (QSPI) Controller. + + QSPI acts as an interface to up to two serial flash memory devices, each with + up to eight bidirectional bidirectional data lines, depending on the platform. + +compatible: "nxp,s32-qspi" + +include: [base.yaml, pinctrl-device.yaml] + +bus: qspi + +properties: + reg: + required: true + + "#address-cells": + const: 1 + + "#size-cells": + const: 0 + + data-rate: + type: string + enum: + - SDR + - DDR + description: | + Selects the read mode: + - Single Data Rate (SDR): sampling of incoming data occurs on single edges. + - Double Data Rate (DDR): sampling of incoming data occurs on both edges. + + hold-time-2x: + type: boolean + description: | + Set to align incoming data with 2x serial flash half clock, when in DDR + mode. Otherwise, data will be aligned to the posedge of the controller's + internal reference clock. + + sample-delay-half-cycle: + type: boolean + description: | + Set to use half-cycle early DQS delay when sampling received data. + + sample-phase-inverted: + type: boolean + description: | + Set to sample received data at inverted clock. + + cs-setup-time: + type: int + default: 3 + description: | + Chip select setup time, in serial clock cycles. A bigger value will pull + the CS signal earlier before the transaction starts. + The default corresponds to the reset value of the register field. + + cs-hold-time: + type: int + default: 3 + description: | + Chip select hold time, in serial clock cycles. A bigger value will release + the CS signal later after the transaction ends. + The default corresponds to the reset value of the register field. + + column-space: + type: int + default: 0 + description: | + Column Address Space bit width. For example, if the coulmn address is + [2:0] of QSPI_SFAR/AHB address, then the column address space bit width + must be 3. If there is no column address separation in any serial flash + device connected to this controller, this value must be programmed to 0. + The default corresponds to the reset value of the register field. + + word-addressable: + type: boolean + description: | + Set if the serial flash device connected to this controller is word + (2 bytes) addressable. + + byte-swapping: + type: boolean + description: | + In case of Octal DDR mode, specifies whether a word unit composed of two + bytes from posedge and negedge of a single DQS cycle needs to be swapped. + + ahb-buffers-masters: + type: array + description: | + Masters ID's for the AHB receive buffers. The master ID of every incoming + request is checked and the data is returned or fetched into the + corresponding associated buffer. The maximum number of buffers is SoC + specific. + + ahb-buffers-sizes: + type: array + description: | + Sizes (in bytes) of the AHB receive buffers. The maximum buffer size and + maximum number of buffers is SoC specific. + + ahb-buffers-all-masters: + type: boolean + description: | + Any access from a master not associated with any other buffer is routed to + the last buffer. + + a-rx-clock-source: + type: string + enum: + - LOOPBACK + - LOOPBACK DQS + - INTERNAL DQS + - EXTERNAL DQS + description: | + Selects DQS clock source for sampling read data at side A: + - LOOPBACK: use loopback clock from dummy internal PAD as strobe signal. + - LOOPBACK DQS: use loopback clock from PAD as strobe signal. + - INTERNAL DQS: use internally generated strobe signal. + - EXTERNAL DQS: use external strobe signal. + + a-io2-idle-high: + type: boolean + description: | + Set if the logic level of IO2 signal output of this controller must be + driven high in the inactive state. + This property applies to side A of the controller. + + a-io3-idle-high: + type: boolean + description: | + Set if the logic level of IO3 signal output of this controller must be + driven high in the inactive state. + This property applies to side A of the controller. + + a-dll-mode: + type: string + enum: + - BYPASSED + - MANUAL UPDATE + - AUTO UPDATE + default: BYPASSED + description: | + DLL mode. The supported modes depends on the SoC. + This property applies to side A of the controller. + + a-dll-freq-enable: + type: boolean + description: | + Selects delay-chain for high frequency of operation. + This property applies to side A of the controller. + + a-dll-ref-counter: + type: int + enum: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15] + default: 1 + description: | + Select the "n+1" interval of DLL phase detection and reference delay + updating interval. + Minimum recommended value is 1 (reset value). + This property applies to side A of the controller. + + a-dll-resolution: + type: int + enum: [2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15] + default: 2 + description: | + Minimum resolution for DLL phase detector to remain locked/unlocked based + on flash memory clock jitter. + The minimum value is 2 (reset value). + This property applies to side A of the controller. + + a-dll-coarse-delay: + type: int + enum: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15] + default: 0 + description: | + This field sets the number of delay elements in each delay tap. The field + is used to overwrite DLL-generated delay values. + Default to 0 (reset value). + This property applies to side A of the controller. + + a-dll-fine-delay: + type: int + enum: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15] + default: 0 + description: | + This field sets the number of fine offset delay elements up to 16 in + incoming DQS. + Default to 0 (reset value). + This property applies to side A of the controller. + + a-dll-tap-select: + type: int + enum: [0, 1, 2, 3, 4, 5, 6, 7] + default: 0 + description: | + Selects the Nth tap provided by the slave delay-chain. + Default to 0 (reset value). + This property applies to side A of the controller. + +child-binding: + description: NXP S32 QuadSPI port + + include: nxp,s32-qspi-device.yaml diff --git a/west.yml b/west.yml index 3def5691788..f3fa27117d4 100644 --- a/west.yml +++ b/west.yml @@ -183,7 +183,7 @@ manifest: groups: - hal - name: hal_nxp - revision: e0116b8eae46c2d90f2c1b23baef73bb082d00bd + revision: 35b0e5afc4f061cedc5a0411e024dad6d9e8101f path: modules/hal/nxp groups: - hal