drivers: memc: add NXP S32 QSPI controller
The NXP S32 QSPI controller acts as an interface to up to two serial flash memory devices, each with up to eight bidirectional data lines, depending on the platform. It is based on a LUT enginee to interface through commands with different memory types including flash NOR and Hyperram. This patch adds support for the QSPI in S32K344 which supports a single memory device (side A) with up to four bidirectional data lines and SDR only. Nevertheless, the memory controller is implemented flexible enough to be extended to support more feature-rich QSPI blocks. Signed-off-by: Manuel Argüelles <manuel.arguelles@nxp.com>
This commit is contained in:
parent
84b86d9b0c
commit
5dad944351
7 changed files with 449 additions and 1 deletions
|
@ -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)
|
||||
|
|
|
@ -29,4 +29,6 @@ source "drivers/memc/Kconfig.sam"
|
|||
|
||||
source "drivers/memc/Kconfig.sifive"
|
||||
|
||||
source "drivers/memc/Kconfig.nxp_s32"
|
||||
|
||||
endif
|
||||
|
|
10
drivers/memc/Kconfig.nxp_s32
Normal file
10
drivers/memc/Kconfig.nxp_s32
Normal file
|
@ -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.
|
197
drivers/memc/memc_nxp_s32_qspi.c
Normal file
197
drivers/memc/memc_nxp_s32_qspi.c
Normal file
|
@ -0,0 +1,197 @@
|
|||
/*
|
||||
* Copyright 2023 NXP
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#define DT_DRV_COMPAT nxp_s32_qspi
|
||||
|
||||
#include <zephyr/logging/log.h>
|
||||
LOG_MODULE_REGISTER(nxp_s32_qspi_memc, CONFIG_MEMC_LOG_LEVEL);
|
||||
|
||||
#include <zephyr/drivers/pinctrl.h>
|
||||
#include <zephyr/sys/util.h>
|
||||
|
||||
#include <soc.h>
|
||||
#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)
|
29
drivers/memc/memc_nxp_s32_qspi.h
Normal file
29
drivers/memc/memc_nxp_s32_qspi.h
Normal file
|
@ -0,0 +1,29 @@
|
|||
/*
|
||||
* Copyright 2023 NXP
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include <zephyr/device.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
#include <Qspi_Ip.h>
|
||||
|
||||
/**
|
||||
* @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);
|
208
dts/bindings/qspi/nxp,s32-qspi.yaml
Normal file
208
dts/bindings/qspi/nxp,s32-qspi.yaml
Normal file
|
@ -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
|
2
west.yml
2
west.yml
|
@ -183,7 +183,7 @@ manifest:
|
|||
groups:
|
||||
- hal
|
||||
- name: hal_nxp
|
||||
revision: e0116b8eae46c2d90f2c1b23baef73bb082d00bd
|
||||
revision: 35b0e5afc4f061cedc5a0411e024dad6d9e8101f
|
||||
path: modules/hal/nxp
|
||||
groups:
|
||||
- hal
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue