| /**************************************************************************** |
| * arch/arm/src/imxrt/imxrt_flexspi.c |
| * |
| * Licensed to the Apache Software Foundation (ASF) under one or more |
| * contributor license agreements. See the NOTICE file distributed with |
| * this work for additional information regarding copyright ownership. The |
| * ASF licenses this file to you under the Apache License, Version 2.0 (the |
| * "License"); you may not use this file except in compliance with the |
| * License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT |
| * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the |
| * License for the specific language governing permissions and limitations |
| * under the License. |
| * |
| ****************************************************************************/ |
| |
| /**************************************************************************** |
| * Included Files |
| ****************************************************************************/ |
| |
| #include <nuttx/config.h> |
| |
| #include <sys/types.h> |
| #include <stdint.h> |
| #include <stdbool.h> |
| #include <stdlib.h> |
| #include <string.h> |
| #include <errno.h> |
| #include <assert.h> |
| #include <debug.h> |
| |
| #include <arch/board/board.h> |
| |
| #include <nuttx/arch.h> |
| #include <nuttx/wdog.h> |
| #include <nuttx/clock.h> |
| #include <nuttx/kmalloc.h> |
| #include <nuttx/mutex.h> |
| |
| #include "arm_internal.h" |
| #include "barriers.h" |
| |
| #include "imxrt_gpio.h" |
| #include "imxrt_periphclks.h" |
| #include "imxrt_flexspi.h" |
| #include "hardware/imxrt_flexspi.h" |
| |
| #ifdef CONFIG_IMXRT_FLEXSPI |
| |
| /* There is AHBBUSERROREN bit in INTEN register */ |
| #define FLEXSPI_HAS_INTEN_AHBBUSERROREN (0) |
| |
| /**************************************************************************** |
| * Private Types |
| ****************************************************************************/ |
| |
| /* The state of the FlexSPI controller. |
| * |
| */ |
| |
| struct imxrt_flexspidev_s |
| { |
| struct flexspi_dev_s flexspi; /* Externally visible part of the FlexSPI |
| * interface */ |
| |
| struct flexspi_type_s *base; /* FlexSPI controller register base address */ |
| |
| bool initialized; /* TRUE: Controller has been initialized */ |
| |
| mutex_t lock; /* Assures mutually exclusive access to |
| * FlexSPI */ |
| }; |
| |
| /* FlexSPI methods */ |
| |
| static int imxrt_flexspi_lock(struct flexspi_dev_s *dev, bool lock); |
| static int imxrt_flexspi_transfer_blocking(struct flexspi_dev_s *dev, |
| struct flexspi_transfer_s *xfer); |
| static void imxrt_flexspi_software_reset(struct flexspi_dev_s *dev); |
| static void imxrt_flexspi_update_lut(struct flexspi_dev_s *dev, |
| uint32_t index, |
| const uint32_t *cmd, |
| uint32_t count); |
| static void imxrt_flexspi_set_device_config(struct flexspi_dev_s *dev, |
| struct flexspi_device_config_s *config, |
| enum flexspi_port_e port); |
| |
| /**************************************************************************** |
| * Private Data |
| ****************************************************************************/ |
| |
| /* FlexSPI0 driver operations */ |
| |
| static const struct flexspi_ops_s g_flexspi0ops = |
| { |
| .lock = imxrt_flexspi_lock, |
| .transfer_blocking = imxrt_flexspi_transfer_blocking, |
| .software_reset = imxrt_flexspi_software_reset, |
| .update_lut = imxrt_flexspi_update_lut, |
| .set_device_config = imxrt_flexspi_set_device_config, |
| }; |
| |
| /* This is the overall state of the FlexSPI0 controller */ |
| |
| #ifdef CONFIG_IMXRT_FLEXSPI1 |
| |
| static struct imxrt_flexspidev_s g_flexspi0dev = |
| { |
| .flexspi = |
| { |
| .ops = &g_flexspi0ops, |
| }, |
| .base = (struct flexspi_type_s *)IMXRT_FLEXSPIC_BASE, |
| .lock = NXMUTEX_INITIALIZER, |
| }; |
| |
| #endif |
| |
| #ifdef CONFIG_IMXRT_FLEXSPI2 |
| |
| static struct imxrt_flexspidev_s g_flexspi1dev = |
| { |
| .flexspi = |
| { |
| .ops = &g_flexspi0ops, |
| }, |
| .base = (struct flexspi_type_s *) IMXRT_FLEXSPI2C_BASE, |
| }; |
| |
| #endif |
| |
| #define FREQ_1MHz (1000000ul) |
| #define FLEXSPI_DLLCR_DEFAULT (0x100ul) |
| #define FLEXSPI_LUT_KEY_VAL (0x5af05af0ul) |
| |
| enum |
| { |
| FLEXSPI_DELAY_CELL_UNIT_MIN = 75, /* 75ps */ |
| |
| FLEXSPI_DELAY_CELL_UNIT_MAX = 225, /* 225ps */ |
| }; |
| |
| enum |
| { |
| FLEXSPI_FLASH_A_SAMPLE_CLOCK_SLAVE_DELAY_LOCKED = |
| FLEXSPI_STS2_ASLVLOCK_MASK, /* Flash A sample clock slave delay line locked */ |
| |
| FLEXSPI_FLASH_A_SAMPLE_CLOCK_REF_DELAY_LOCKED = |
| FLEXSPI_STS2_AREFLOCK_MASK, /* Flash A sample clock reference delay line locked */ |
| |
| FLEXSPI_FLASH_B_SAMPLE_CLOCK_SLAVE_DELAY_LOCKED = |
| FLEXSPI_STS2_BSLVLOCK_MASK, /* Flash B sample clock slave delay line locked */ |
| |
| FLEXSPI_FLASH_B_SAMPLE_CLOCK_REF_DELAY_LOCKED = |
| FLEXSPI_STS2_BREFLOCK_MASK, /* Flash B sample clock reference delay line locked */ |
| }; |
| |
| /* FLEXSPI interrupt status flags */ |
| |
| enum flexspi_flags_e |
| { |
| FLEXSPI_SEQUENCE_EXECUTION_TIMEOUT_FLAG = FLEXSPI_INTEN_SEQTIMEOUTEN_MASK, /* Sequence execution timeout */ |
| #if defined(FLEXSPI_HAS_INTEN_AHBBUSERROREN) && FLEXSPI_HAS_INTEN_AHBBUSERROREN |
| FLEXSPI_AHB_BUS_ERROR_FLAG = FLEXSPI_INTEN_AHBBUSERROREN_MASK, /* AHB Bus error flag */ |
| #else |
| FLEXSPI_AHB_BUS_TIMEOUT_FLAG = FLEXSPI_INTEN_AHBBUSTIMEOUTEN_MASK, /* AHB Bus timeout */ |
| #endif |
| FLEXSPI_SCK_STOPPED_BECAUSE_TX_EMPTY_FLAG = |
| FLEXSPI_INTEN_SCKSTOPBYWREN_MASK, /* SCK is stopped during command |
| * sequence because Async TX FIFO empty */ |
| |
| FLEXSPI_SCK_STOPPED_BECAUSE_RX_FULL_FLAG = |
| FLEXSPI_INTEN_SCKSTOPBYRDEN_MASK, /* SCK is stopped during command |
| * sequence because Async RX FIFO full */ |
| |
| FLEXSPI_IP_TX_FIFO_WATERMARK_EMPTY_FLAG = FLEXSPI_INTEN_IPTXWEEN_MASK, /* IP TX FIFO WaterMark empty */ |
| |
| FLEXSPI_IP_RX_FIFO_WATERMARK_AVAILABLE_FLAG = FLEXSPI_INTEN_IPRXWAEN_MASK, /* IP RX FIFO WaterMark available */ |
| |
| FLEXSPI_AHB_COMMAND_SEQUENCE_ERROR_FLAG = |
| FLEXSPI_INTEN_AHBCMDERREN_MASK, /* AHB triggered Command Sequences Error */ |
| |
| FLEXSPI_IP_COMMAND_SEQUENCE_ERROR_FLAG = FLEXSPI_INTEN_IPCMDERREN_MASK, /* IP triggered Command Sequences Error */ |
| |
| FLEXSPI_AHB_COMMAND_GRANT_TIMEOUT_FLAG = |
| FLEXSPI_INTEN_AHBCMDGEEN_MASK, /* AHB triggered Command Sequences Grant Timeout */ |
| |
| FLEXSPI_IP_COMMAND_GRANT_TIMEOUT_FLAG = |
| FLEXSPI_INTEN_IPCMDGEEN_MASK, /* IP triggered Command Sequences Grant Timeout */ |
| |
| FLEXSPI_IP_COMMAND_EXECUTION_DONE_FLAG = |
| FLEXSPI_INTEN_IPCMDDONEEN_MASK, /* IP triggered Command Sequences Execution finished */ |
| |
| FLEXSPI_ALL_INTERRUPT_FLAGS = 0xfffu, /* All flags */ |
| }; |
| |
| /* Common sets of flags used by the driver */ |
| |
| enum flexspi_flag_constants_e |
| { |
| /* Errors to check for */ |
| |
| ERROR_FLAGS = FLEXSPI_SEQUENCE_EXECUTION_TIMEOUT_FLAG | |
| FLEXSPI_IP_COMMAND_SEQUENCE_ERROR_FLAG | |
| FLEXSPI_IP_COMMAND_GRANT_TIMEOUT_FLAG, |
| }; |
| |
| #define FLEXSPI_AHB_BUFFER_COUNT (4) |
| |
| struct flexspi_ahb_buffer_config_s |
| { |
| uint8_t priority; /* This priority for AHB Master Read which this AHB RX Buffer is assigned */ |
| |
| uint8_t master_index; /* AHB Master ID the AHB RX Buffer is assigned */ |
| |
| uint16_t buffer_size; /* AHB buffer size in byte */ |
| |
| bool enable_prefetch; /* AHB Read Prefetch Enable for current AHB RX Buffer corresponding Master, allows |
| * prefetch disable/enable separately for each master */ |
| }; |
| |
| /* FLEXSPI configuration structure */ |
| |
| struct flexspi_config_s |
| { |
| enum flexspi_read_sample_clock_e rx_sample_clock; /* Sample Clock source selection for Flash Reading */ |
| |
| bool enable_sck_free_running; /* Enable/disable SCK output free-running */ |
| |
| bool enable_combination; /* Enable/disable combining PORT A and B Data Pins |
| * (SIOA[3:0] and SIOB[3:0]) to support Flash Octal mode */ |
| |
| bool enable_doze; /* Enable/disable doze mode support */ |
| |
| bool enable_half_speed_access; /* Enable/disable divide by 2 of the clock for half |
| * speed commands */ |
| |
| bool enable_sckb_diff_opt; /* Enable/disable SCKB pad use as SCKA differential clock |
| * output, when enable, Port B flash access is not available */ |
| |
| bool enable_same_config_for_all; /* Enable/disable same configuration for all connected devices |
| * when enabled, same configuration in FLASHA1CRx is applied to all */ |
| |
| uint16_t seq_timeout_cycle; /* Timeout wait cycle for command sequence execution, |
| * timeout after ahb_grant_timeout_cyle*1024 serial root clock cycles */ |
| |
| uint8_t ip_grant_timeout_cycle; /* Timeout wait cycle for IP command grant, timeout after |
| * ip_grant_timeout_cycle*1024 AHB clock cycles */ |
| |
| uint8_t tx_watermark; /* FLEXSPI IP transmit watermark value */ |
| |
| uint8_t rx_watermark; /* FLEXSPI receive watermark value */ |
| |
| struct |
| { |
| #if !(defined(FLEXSPI_HAS_NO_MCR0_ATDFEN) && FLEXSPI_HAS_NO_MCR0_ATDFEN) |
| bool enable_ahb_write_ip_tx_fifo; /* Enable AHB bus write access to IP TX FIFO */ |
| #endif |
| #if !(defined(FLEXSPI_HAS_NO_MCR0_ARDFEN) && FLEXSPI_HAS_NO_MCR0_ARDFEN) |
| bool enable_ahb_write_ip_rx_fifo; /* Enable AHB bus write access to IP RX FIFO */ |
| #endif |
| uint8_t ahb_grant_timeout_cycle; /* Timeout wait cycle for AHB command grant, |
| * timeout after ahb_grant_timeout_cyle*1024 AHB clock cycles */ |
| |
| uint16_t ahb_bus_timeout_cycle; /* Timeout wait cycle for AHB read/write access, |
| * timeout after ahb_bus_timeout_cycle*1024 AHB clock cycles */ |
| |
| uint8_t resume_wait_cycle; /* Wait cycle for idle state before suspended command sequence |
| * resume, timeout after ahb_bus_timeout_cycle AHB clock cycles */ |
| |
| struct flexspi_ahb_buffer_config_s buffer[FLEXSPI_AHB_BUFFER_COUNT]; /* AHB buffer size */ |
| |
| bool enable_clear_ahb_buffer_opt; /* Enable/disable automatically clean AHB RX Buffer and TX Buffer |
| * when FLEXSPI returns STOP mode ACK */ |
| |
| bool enable_read_address_opt; /* Enable/disable remove AHB read burst start address alignment limitation. |
| * when enable, there is no AHB read burst start address alignment limitation */ |
| |
| bool enable_ahb_prefetch; /* Enable/disable AHB read prefetch feature, when enabled, FLEXSPI |
| * will fetch more data than current AHB burst */ |
| |
| bool enable_ahb_bufferable; /* Enable/disable AHB bufferable write access support, when enabled, |
| * FLEXSPI return before waiting for command execution finished */ |
| |
| bool enable_ahb_cachable; /* Enable AHB bus cachable read access support */ |
| } ahb_config; |
| }; |
| |
| /**************************************************************************** |
| * Prototypes |
| ****************************************************************************/ |
| |
| /* Check and clear IP command execution errors. |
| * |
| * @param base FLEXSPI base pointer. |
| * @param status interrupt status. |
| */ |
| |
| static int imxrt_flexspi_check_and_clear_error(struct flexspi_type_s *base, |
| uint32_t status); |
| |
| /**************************************************************************** |
| * Variables |
| ****************************************************************************/ |
| |
| /**************************************************************************** |
| * Code |
| ****************************************************************************/ |
| |
| /* Software reset for the FLEXSPI logic. |
| * |
| * This function sets the software reset flags for both AHB and buffer domain |
| * and resets both AHB buffer and also IP FIFOs. |
| * |
| * @param base FLEXSPI peripheral base address. |
| */ |
| |
| static inline void imxrt_flexspi_software_reset_private( |
| struct flexspi_type_s *base) |
| { |
| base->MCR0 |= FLEXSPI_MCR0_SWRESET_MASK; |
| while (0u != (base->MCR0 & FLEXSPI_MCR0_SWRESET_MASK)) |
| { |
| } |
| } |
| |
| /* Returns whether the bus is idle. |
| * |
| * @param base FLEXSPI peripheral base address. |
| * @retval true Bus is idle. |
| * @retval false Bus is busy. |
| */ |
| |
| static inline bool imxrt_flexspi_get_bus_idle_status( |
| struct flexspi_type_s *base) |
| { |
| return (0u != (base->STS0 & FLEXSPI_STS0_ARBIDLE_MASK)) && |
| (0u != (base->STS0 & FLEXSPI_STS0_SEQIDLE_MASK)); |
| } |
| |
| static uint32_t imxrt_flexspi_configure_dll(struct flexspi_type_s *base, |
| struct flexspi_device_config_s *config) |
| { |
| bool is_unified_config = true; |
| uint32_t flexspi_dll_value; |
| uint32_t dll_value; |
| uint32_t temp; |
| |
| uint32_t rx_sample_clock = (base->MCR0 & FLEXSPI_MCR0_RXCLKSRC_MASK) >> |
| FLEXSPI_MCR0_RXCLKSRC_SHIFT; |
| switch (rx_sample_clock) |
| { |
| case (uint32_t)FLEXSPI_READ_SAMPLE_CLK_LOOPBACK_INTERNALLY: |
| case (uint32_t)FLEXSPI_READ_SAMPLE_CLK_LOOPBACK_FROM_DQS_PAD: |
| case (uint32_t)FLEXSPI_READ_SAMPLE_CLK_LOOPBACK_FROM_SCK_PAD: |
| is_unified_config = true; |
| break; |
| case (uint32_t)FLEXSPI_READ_SAMPLE_CLK_EXTERNAL_INPUT_FROM_DQS_PAD: |
| if (config->is_sck2_enabled) |
| { |
| is_unified_config = true; |
| } |
| else |
| { |
| is_unified_config = false; |
| } |
| break; |
| default: |
| ASSERT(false); |
| break; |
| } |
| |
| if (is_unified_config) |
| { |
| flexspi_dll_value = FLEXSPI_DLLCR_DEFAULT; /* 1 fixed delay cells in DLL delay chain */ |
| } |
| else |
| { |
| if (config->flexspi_root_clk >= 100u * FREQ_1MHz) |
| { |
| /* DLLEN = 1, SLVDLYTARGET = 0xF, */ |
| |
| flexspi_dll_value = FLEXSPI_DLLCR_DLLEN(1) | |
| FLEXSPI_DLLCR_SLVDLYTARGET(0x0f); |
| } |
| else |
| { |
| temp = (uint32_t)config->data_valid_time * 1000u; /* Convert data valid time in ns to ps */ |
| |
| dll_value = temp / (uint32_t)FLEXSPI_DELAY_CELL_UNIT_MIN; |
| if (dll_value * (uint32_t)FLEXSPI_DELAY_CELL_UNIT_MIN < temp) |
| { |
| dll_value++; |
| } |
| flexspi_dll_value = FLEXSPI_DLLCR_OVRDEN(1) | |
| FLEXSPI_DLLCR_OVRDVAL(dll_value); |
| } |
| } |
| return flexspi_dll_value; |
| } |
| |
| static int imxrt_flexspi_check_and_clear_error(struct flexspi_type_s *base, |
| uint32_t status) |
| { |
| int result = 0; |
| |
| /* Check for error */ |
| |
| status &= (uint32_t)ERROR_FLAGS; |
| if (0u != status) |
| { |
| /* Select the correct error code */ |
| |
| if (0u != (status & (uint32_t)FLEXSPI_SEQUENCE_EXECUTION_TIMEOUT_FLAG)) |
| { |
| result = STATUS_FLEXSPI_SEQUENCE_EXECUTION_TIMEOUT; |
| } |
| else if (0u != (status & |
| (uint32_t)FLEXSPI_IP_COMMAND_SEQUENCE_ERROR_FLAG)) |
| { |
| result = STATUS_FLEXSPI_IP_COMMAND_SEQUENCE_ERROR; |
| } |
| else if (0u != (status & |
| (uint32_t)FLEXSPI_IP_COMMAND_GRANT_TIMEOUT_FLAG)) |
| { |
| result = STATUS_FLEXSPI_IP_COMMAND_GRANT_TIMEOUT; |
| } |
| else |
| { |
| ASSERT(false); |
| } |
| |
| /* Clear the flags */ |
| |
| base->INTR |= status; |
| |
| /* Reset fifos. These flags clear automatically */ |
| |
| base->IPTXFCR |= FLEXSPI_IPTXFCR_CLRIPTXF_MASK; |
| base->IPRXFCR |= FLEXSPI_IPRXFCR_CLRIPRXF_MASK; |
| } |
| |
| return result; |
| } |
| |
| /* Initializes the FLEXSPI module and internal state. |
| * |
| * This function enables the clock for FLEXSPI and also configures the |
| * FLEXSPI with the input configure parameters. Users should call this |
| * function before any FLEXSPI operations. |
| * |
| * param base FLEXSPI peripheral base address. |
| * param config FLEXSPI configure structure. |
| */ |
| |
| void imxrt_flexspi_init(struct flexspi_type_s *base, |
| const struct flexspi_config_s *config) |
| { |
| uint32_t config_value = 0; |
| uint8_t i = 0; |
| |
| /* Reset peripheral before configuring it */ |
| |
| base->MCR0 &= ~FLEXSPI_MCR0_MDIS_MASK; |
| imxrt_flexspi_software_reset_private(base); |
| |
| /* Configure MCR0 configuration items */ |
| |
| config_value = FLEXSPI_MCR0_RXCLKSRC(config->rx_sample_clock) | |
| FLEXSPI_MCR0_DOZEEN(config->enable_doze) | |
| FLEXSPI_MCR0_IPGRANTWAIT(config->ip_grant_timeout_cycle) | |
| FLEXSPI_MCR0_AHBGRANTWAIT( |
| config->ahb_config.ahb_grant_timeout_cycle) | |
| FLEXSPI_MCR0_SCKFREERUNEN(config->enable_sck_free_running) | |
| FLEXSPI_MCR0_HSEN(config->enable_half_speed_access) | |
| FLEXSPI_MCR0_COMBINATIONEN(config->enable_combination) | |
| #if !(defined(FLEXSPI_HAS_NO_MCR0_ATDFEN) && FLEXSPI_HAS_NO_MCR0_ATDFEN) |
| FLEXSPI_MCR0_ATDFEN( |
| config->ahb_config.enable_ahb_write_ip_tx_fifo) | |
| #endif |
| #if !(defined(FLEXSPI_HAS_NO_MCR0_ARDFEN) && FLEXSPI_HAS_NO_MCR0_ARDFEN) |
| FLEXSPI_MCR0_ARDFEN( |
| config->ahb_config.enable_ahb_write_ip_rx_fifo) | |
| #endif |
| FLEXSPI_MCR0_MDIS_MASK; |
| base->MCR0 = config_value; |
| |
| /* Configure MCR1 configurations */ |
| |
| config_value = |
| FLEXSPI_MCR1_SEQWAIT(config->seq_timeout_cycle) | |
| FLEXSPI_MCR1_AHBBUSWAIT(config->ahb_config.ahb_bus_timeout_cycle); |
| |
| base->MCR1 = config_value; |
| |
| /* Configure MCR2 configurations */ |
| |
| config_value = base->MCR2; |
| config_value &= ~(FLEXSPI_MCR2_RESUMEWAIT_MASK | |
| FLEXSPI_MCR2_SCKBDIFFOPT_MASK | |
| FLEXSPI_MCR2_SAMEDEVICEEN_MASK | |
| FLEXSPI_MCR2_CLRAHBBUFOPT_MASK); |
| |
| config_value |= FLEXSPI_MCR2_RESUMEWAIT( |
| config->ahb_config.resume_wait_cycle) | |
| FLEXSPI_MCR2_SCKBDIFFOPT( |
| config->enable_sckb_diff_opt) | |
| FLEXSPI_MCR2_SAMEDEVICEEN( |
| config->enable_same_config_for_all) | |
| FLEXSPI_MCR2_CLRAHBBUFOPT( |
| config->ahb_config.enable_clear_ahb_buffer_opt); |
| |
| base->MCR2 = config_value; |
| |
| /* Configure AHB control items */ |
| |
| config_value = base->AHBCR; |
| config_value &= ~(FLEXSPI_AHBCR_READADDROPT_MASK | |
| FLEXSPI_AHBCR_PREFETCHEN_MASK | |
| FLEXSPI_AHBCR_BUFFERABLEEN_MASK | |
| FLEXSPI_AHBCR_CACHABLEEN_MASK); |
| |
| config_value |= FLEXSPI_AHBCR_READADDROPT( |
| config->ahb_config.enable_read_address_opt) | |
| FLEXSPI_AHBCR_PREFETCHEN( |
| config->ahb_config.enable_ahb_prefetch) | |
| FLEXSPI_AHBCR_BUFFERABLEEN( |
| config->ahb_config.enable_ahb_bufferable) | |
| FLEXSPI_AHBCR_CACHABLEEN( |
| config->ahb_config.enable_ahb_cachable); |
| |
| base->AHBCR = config_value; |
| |
| /* Configure AHB rx buffers */ |
| |
| for (i = 0; i < (uint32_t)FLEXSPI_AHB_BUFFER_COUNT; i++) |
| { |
| config_value = base->AHBRXBUFCR0[i]; |
| |
| config_value &= ~(FLEXSPI_AHBRXBUFCR0_PREFETCHEN_MASK | |
| FLEXSPI_AHBRXBUFCR0_PRIORITY_MASK | |
| FLEXSPI_AHBRXBUFCR0_MSTRID_MASK | |
| FLEXSPI_AHBRXBUFCR0_BUFSZ_MASK); |
| |
| config_value |= FLEXSPI_AHBRXBUFCR0_PREFETCHEN( |
| config->ahb_config.buffer[i].enable_prefetch) | |
| FLEXSPI_AHBRXBUFCR0_PRIORITY( |
| config->ahb_config.buffer[i].priority) | |
| FLEXSPI_AHBRXBUFCR0_MSTRID( |
| config->ahb_config.buffer[i].master_index) | |
| FLEXSPI_AHBRXBUFCR0_BUFSZ((uint32_t) |
| config->ahb_config.buffer[i].buffer_size / 8u); |
| |
| base->AHBRXBUFCR0[i] = config_value; |
| } |
| |
| /* Configure IP FIFO watermarks */ |
| |
| base->IPRXFCR &= ~FLEXSPI_IPRXFCR_RXWMRK_MASK; |
| base->IPRXFCR |= |
| FLEXSPI_IPRXFCR_RXWMRK((uint32_t)config->rx_watermark / 8u - 1u); |
| base->IPTXFCR &= ~FLEXSPI_IPTXFCR_TXWMRK_MASK; |
| base->IPTXFCR |= |
| FLEXSPI_IPTXFCR_TXWMRK((uint32_t)config->tx_watermark / 8u - 1u); |
| |
| /* Reset flash size on all ports */ |
| |
| for (i = 0; i < (uint32_t)FLEXSPI_PORT_COUNT; i++) |
| { |
| base->FLSHCR0[i] = 0; |
| } |
| } |
| |
| /* Gets default settings for FLEXSPI. |
| * |
| * param config FLEXSPI configuration structure. |
| */ |
| |
| void imxrt_flexspi_get_default_config(struct flexspi_config_s *config) |
| { |
| /* Initializes the configure structure to zero */ |
| |
| memset(config, 0, sizeof(*config)); |
| |
| config->rx_sample_clock = FLEXSPI_READ_SAMPLE_CLK_LOOPBACK_FROM_DQS_PAD; |
| config->enable_sck_free_running = false; |
| config->enable_combination = false; |
| config->enable_doze = true; |
| config->enable_half_speed_access = false; |
| config->enable_sckb_diff_opt = false; |
| config->enable_same_config_for_all = false; |
| config->seq_timeout_cycle = 0xffff; |
| config->ip_grant_timeout_cycle = 0xff; |
| config->tx_watermark = 8; |
| config->rx_watermark = 8; |
| |
| #if !(defined(FLEXSPI_HAS_NO_MCR0_ATDFEN) && FLEXSPI_HAS_NO_MCR0_ATDFEN) |
| config->ahb_config.enable_ahb_write_ip_tx_fifo = false; |
| #endif |
| |
| #if !(defined(FLEXSPI_HAS_NO_MCR0_ARDFEN) && FLEXSPI_HAS_NO_MCR0_ARDFEN) |
| config->ahb_config.enable_ahb_write_ip_rx_fifo = false; |
| #endif |
| |
| config->ahb_config.ahb_grant_timeout_cycle = 0xff; |
| config->ahb_config.ahb_bus_timeout_cycle = 0xffff; |
| config->ahb_config.resume_wait_cycle = 0x20; |
| memset(config->ahb_config.buffer, 0, |
| sizeof(config->ahb_config.buffer)); |
| |
| /* Use invalid master ID 0xF and buffer size 0 for the first several |
| * buffers. |
| */ |
| |
| for (uint8_t i = 0; i < ((uint8_t)FLEXSPI_AHB_BUFFER_COUNT - 2u); i++) |
| { |
| /* Default enable AHB prefetch */ |
| |
| config->ahb_config.buffer[i].enable_prefetch = true; |
| |
| /* Invalid master index which is no used, so will never hit */ |
| |
| config->ahb_config.buffer[i].master_index = 0xf; |
| |
| /* Default buffer size 0 for buffer0 to |
| * buffer(FLEXSPI_AHB_BUFFER_COUNT - 3) |
| */ |
| |
| config->ahb_config.buffer[i].buffer_size = 0; |
| } |
| |
| for (uint8_t i = ((uint8_t)FLEXSPI_AHB_BUFFER_COUNT - 2); |
| i < (uint8_t)FLEXSPI_AHB_BUFFER_COUNT; i++) |
| { |
| config->ahb_config.buffer[i].enable_prefetch = true; /* Default enable |
| * AHB prefetch. |
| */ |
| |
| config->ahb_config.buffer[i].buffer_size = 256u; /* Default buffer |
| * size 256 bytes. |
| */ |
| } |
| |
| config->ahb_config.enable_clear_ahb_buffer_opt = false; |
| config->ahb_config.enable_read_address_opt = false; |
| config->ahb_config.enable_ahb_prefetch = false; |
| config->ahb_config.enable_ahb_bufferable = false; |
| config->ahb_config.enable_ahb_cachable = false; |
| } |
| |
| /* Configures the connected device parameter. |
| * |
| * This function configures the connected device relevant parameters, such |
| * as the size, command, and so on. The flash configuration value cannot have |
| * a default value. The user needs to configure it according to the connected |
| * device. |
| * |
| * param base FLEXSPI peripheral base address. |
| * param config Device configuration parameters. |
| * param port FLEXSPI Operation port. |
| */ |
| |
| void imxrt_flexspi_set_device_config_private(struct flexspi_type_s *base, |
| struct flexspi_device_config_s *config, |
| enum flexspi_port_e port) |
| { |
| uint32_t config_value = 0; |
| uint32_t status_value = 0; |
| uint8_t index = (uint8_t)port >> 1u; /* PortA with index 0, PortB with index 1 */ |
| |
| /* Wait for bus idle before change flash configuration */ |
| |
| while (!imxrt_flexspi_get_bus_idle_status(base)) |
| { |
| } |
| |
| /* Configure flash size */ |
| |
| base->FLSHCR0[port] = config->flash_size; |
| |
| /* Configure flash parameters */ |
| |
| base->FLSHCR1[port] = |
| FLEXSPI_FLSHCR1_CSINTERVAL(config->cs_interval) | |
| FLEXSPI_FLSHCR1_CSINTERVALUNIT(config->cs_interval_unit) | |
| FLEXSPI_FLSHCR1_TCSH(config->cs_hold_time) | |
| FLEXSPI_FLSHCR1_TCSS(config->cs_setup_time) | |
| FLEXSPI_FLSHCR1_CAS(config->columnspace) | |
| FLEXSPI_FLSHCR1_WA(config->enable_word_address); |
| |
| /* Configure AHB operation items */ |
| |
| config_value = base->FLSHCR2[port]; |
| |
| config_value &= ~(FLEXSPI_FLSHCR2_AWRWAITUNIT_MASK | |
| FLEXSPI_FLSHCR2_AWRWAIT_MASK | |
| FLEXSPI_FLSHCR2_AWRSEQNUM_MASK | |
| FLEXSPI_FLSHCR2_AWRSEQID_MASK | |
| FLEXSPI_FLSHCR2_ARDSEQNUM_MASK | |
| FLEXSPI_FLSHCR2_ARDSEQID_MASK); |
| |
| config_value |= |
| FLEXSPI_FLSHCR2_AWRWAITUNIT(config->ahb_write_wait_unit) | |
| FLEXSPI_FLSHCR2_AWRWAIT(config->ahb_write_wait_interval); |
| |
| if (config->awr_seq_number > 0u) |
| { |
| config_value |= FLEXSPI_FLSHCR2_AWRSEQID( |
| (uint32_t)config->awr_seq_index) | |
| FLEXSPI_FLSHCR2_AWRSEQNUM( |
| (uint32_t)config->awr_seq_number - 1u); |
| } |
| |
| if (config->ard_seq_number > 0u) |
| { |
| config_value |= FLEXSPI_FLSHCR2_ARDSEQID( |
| (uint32_t)config->ard_seq_index) | |
| FLEXSPI_FLSHCR2_ARDSEQNUM( |
| (uint32_t)config->ard_seq_number - 1u); |
| } |
| |
| base->FLSHCR2[port] = config_value; |
| |
| /* Configure DLL */ |
| |
| config_value = imxrt_flexspi_configure_dll(base, config); |
| base->DLLCR[index] = config_value; |
| |
| /* Configure write mask */ |
| |
| if (config->enable_write_mask) |
| { |
| base->FLSHCR4 &= ~FLEXSPI_FLSHCR4_WMOPT1_MASK; |
| } |
| else |
| { |
| base->FLSHCR4 |= FLEXSPI_FLSHCR4_WMOPT1_MASK; |
| } |
| |
| if (index == 0u) /* Port A */ |
| { |
| base->FLSHCR4 &= ~FLEXSPI_FLSHCR4_WMENA_MASK; |
| base->FLSHCR4 |= FLEXSPI_FLSHCR4_WMENA(config->enable_write_mask); |
| } |
| else |
| { |
| base->FLSHCR4 &= ~FLEXSPI_FLSHCR4_WMENB_MASK; |
| base->FLSHCR4 |= FLEXSPI_FLSHCR4_WMENB(config->enable_write_mask); |
| } |
| |
| /* Set RX Sample Clock */ |
| |
| config_value = base->MCR0; |
| config_value &= ~FLEXSPI_MCR0_RXCLKSRC_MASK; |
| config_value |= FLEXSPI_MCR0_RXCLKSRC(config->rx_sample_clock); |
| |
| /* Exit stop mode */ |
| |
| config_value &= ~FLEXSPI_MCR0_MDIS_MASK; |
| |
| base->MCR0 = config_value; |
| |
| /* According to ERR011377, need to delay at least 100 NOPs to ensure the |
| * DLL is locked. |
| */ |
| |
| status_value = |
| (index == 0u) ? |
| ((uint32_t)FLEXSPI_FLASH_A_SAMPLE_CLOCK_SLAVE_DELAY_LOCKED | |
| (uint32_t)FLEXSPI_FLASH_A_SAMPLE_CLOCK_REF_DELAY_LOCKED) : |
| ((uint32_t)FLEXSPI_FLASH_B_SAMPLE_CLOCK_SLAVE_DELAY_LOCKED | |
| (uint32_t)FLEXSPI_FLASH_B_SAMPLE_CLOCK_REF_DELAY_LOCKED); |
| |
| if (0u != (config_value & FLEXSPI_DLLCR_DLLEN_MASK)) |
| { |
| /* Wait slave delay line locked and slave reference delay line locked */ |
| |
| while ((base->STS2 & status_value) != status_value) |
| { |
| } |
| |
| /* Wait at least 100 NOPs */ |
| |
| for (uint8_t delay = 100u; delay > 0u; delay--) |
| { |
| asm("NOP"); |
| } |
| } |
| } |
| |
| /* Updates the LUT table. |
| * |
| * param base FLEXSPI peripheral base address. |
| * param index From which index start to update. |
| * It could be any index of the LUT table, which also allows |
| * user to update command content inside a command. Each command |
| * consists of up to 8 instructions and occupy 4*32-bit memory. |
| * param cmd Command sequence array. |
| * param count Number of sequences. |
| */ |
| |
| void imxrt_flexspi_update_lut_private(struct flexspi_type_s *base, |
| uint32_t index, |
| const uint32_t *cmd, |
| uint32_t count) |
| { |
| ASSERT(index < 64u); |
| |
| uint32_t i = 0; |
| volatile uint32_t *lut_base; |
| |
| /* Wait for bus idle before change flash configuration */ |
| |
| while (!imxrt_flexspi_get_bus_idle_status(base)) |
| { |
| } |
| |
| /* Unlock LUT for update */ |
| |
| base->LUTKEY = FLEXSPI_LUT_KEY_VAL; |
| base->LUTCR = 0x02; |
| |
| lut_base = &base->LUT[index]; |
| for (i = 0; i < count; i++) |
| { |
| *lut_base++ = *cmd++; |
| } |
| |
| /* Lock LUT */ |
| |
| base->LUTKEY = FLEXSPI_LUT_KEY_VAL; |
| base->LUTCR = 0x01; |
| } |
| |
| /* Sends a buffer of data bytes using blocking method. |
| * note This function blocks via polling until all bytes have been sent. |
| * param base FLEXSPI peripheral base address |
| * param buffer The data bytes to send |
| * param size The number of data bytes to send |
| * retval 0 write success without error |
| * STATUS_FLEXSPI_SEQUENCE_EXECUTION_TIMEOUT sequence execution timeout |
| * STATUS_FLEXSPI_IP_COMMAND_SEQUENCE_ERROR IP command sequence error |
| * detected |
| * STATUS_FLEXSPI_IP_COMMAND_GRANT_TIMEOUT IP command grant |
| * timeout detected. |
| */ |
| |
| static int imxrt_flexspi_write_blocking(struct flexspi_type_s *base, |
| uint32_t *buffer, size_t size) |
| { |
| uint32_t tx_watermark = ((base->IPTXFCR & FLEXSPI_IPTXFCR_TXWMRK_MASK) >> |
| FLEXSPI_IPTXFCR_TXWMRK_SHIFT) + 1u; |
| uint32_t status; |
| int result = 0; |
| uint32_t i = 0; |
| |
| /* Send data buffer */ |
| |
| while (0u != size) |
| { |
| /* Wait until there is room in the fifo. This also checks for errors */ |
| |
| while (0u == ((status = base->INTR) & |
| (uint32_t)FLEXSPI_IP_TX_FIFO_WATERMARK_EMPTY_FLAG)) |
| { |
| } |
| |
| result = imxrt_flexspi_check_and_clear_error(base, status); |
| |
| if (0 != result) |
| { |
| return result; |
| } |
| |
| /* Write watermark level data into tx fifo */ |
| |
| if (size >= 8u * tx_watermark) |
| { |
| for (i = 0u; i < 2u * tx_watermark; i++) |
| { |
| base->TFDR[i] = *buffer++; |
| } |
| |
| size = size - 8u * tx_watermark; |
| } |
| else |
| { |
| for (i = 0u; i < (size / 4u + 1u); i++) |
| { |
| base->TFDR[i] = *buffer++; |
| } |
| size = 0u; |
| } |
| |
| /* Push a watermark level data into IP TX FIFO */ |
| |
| base->INTR |= (uint32_t)FLEXSPI_IP_TX_FIFO_WATERMARK_EMPTY_FLAG; |
| } |
| |
| return result; |
| } |
| |
| /* Receives a buffer of data bytes using a blocking method. |
| * note This function blocks via polling until all bytes have been sent. |
| * param base FLEXSPI peripheral base address |
| * param buffer The data bytes to send |
| * param size The number of data bytes to receive |
| * retval 0 read success without error |
| * retval STATUS_FLEXSPI_SEQUENCE_EXECUTION_TIMEOUT sequence execution |
| * timeout retval STATUS_FLEXSPI_IP_COMMAND_SEQUENCE_ERROR IP command |
| * sequence error detected retval STATUS_FLEXSPI_IP_COMMAND_GRANT_TIMEOUT |
| * IP command grant timeout detected. |
| */ |
| |
| static int imxrt_flexspi_read_blocking(struct flexspi_type_s *base, |
| uint32_t *buffer, size_t size) |
| { |
| uint32_t rx_watermark = ((base->IPRXFCR & FLEXSPI_IPRXFCR_RXWMRK_MASK) >> |
| FLEXSPI_IPRXFCR_RXWMRK_SHIFT) + 1u; |
| uint32_t status; |
| int result = 0; |
| uint32_t i = 0; |
| bool is_return = false; |
| |
| /* Send data buffer */ |
| |
| while (0u != size) |
| { |
| if (size >= 8u * rx_watermark) |
| { |
| /* Wait until there is room in the fifo. This also checks for |
| * errors. |
| */ |
| |
| while (0u == ((status = base->INTR) & |
| (uint32_t)FLEXSPI_IP_RX_FIFO_WATERMARK_AVAILABLE_FLAG)) |
| { |
| result = imxrt_flexspi_check_and_clear_error(base, status); |
| |
| if (0 != result) |
| { |
| is_return = true; |
| break; |
| } |
| } |
| } |
| else |
| { |
| /* Wait fill level. This also checks for errors */ |
| |
| while (size > ((((base->IPRXFSTS) & FLEXSPI_IPRXFSTS_FILL_MASK) >> |
| FLEXSPI_IPRXFSTS_FILL_SHIFT) * 8u)) |
| { |
| result = imxrt_flexspi_check_and_clear_error(base, base->INTR); |
| |
| if (0 != result) |
| { |
| is_return = true; |
| break; |
| } |
| } |
| } |
| |
| if (is_return) |
| { |
| break; |
| } |
| |
| result = imxrt_flexspi_check_and_clear_error(base, base->INTR); |
| |
| if (0 != result) |
| { |
| break; |
| } |
| |
| /* Read watermark level data from rx fifo */ |
| |
| if (size >= 8u * rx_watermark) |
| { |
| for (i = 0u; i < 2u * rx_watermark; i++) |
| { |
| *buffer++ = base->RFDR[i]; |
| } |
| |
| size = size - 8u * rx_watermark; |
| } |
| else |
| { |
| for (i = 0u; i < ((size + 3u) / 4u); i++) |
| { |
| *buffer++ = base->RFDR[i]; |
| } |
| size = 0; |
| } |
| |
| /* Pop out a watermark level datas from IP RX FIFO */ |
| |
| base->INTR |= (uint32_t)FLEXSPI_IP_RX_FIFO_WATERMARK_AVAILABLE_FLAG; |
| } |
| |
| return result; |
| } |
| |
| /* Brief Execute command to transfer a buffer data bytes using a blocking |
| * method. param base FLEXSPI peripheral base address param xfer pointer to |
| * the transfer structure. retval 0 command transfer success without error |
| * retval STATUS_FLEXSPI_SEQUENCE_EXECUTION_TIMEOUT sequence execution |
| * timeout retval STATUS_FLEXSPI_IP_COMMAND_SEQUENCE_ERROR IP command |
| * sequence error detected retval STATUS_FLEXSPI_IP_COMMAND_GRANT_TIMEOUT |
| * IP command grant timeout detected. |
| */ |
| |
| int imxrt_flexspi_transfer_blocking_private(struct flexspi_type_s *base, |
| struct flexspi_transfer_s *xfer) |
| { |
| uint32_t config_value = 0; |
| int result = 0; |
| |
| /* Clear sequence pointer before sending data to external devices */ |
| |
| base->FLSHCR2[xfer->port] |= FLEXSPI_FLSHCR2_CLRINSTRPTR_MASK; |
| |
| /* Clear former pending status before start this transfer */ |
| |
| base->INTR |= FLEXSPI_INTR_AHBCMDERR_MASK | |
| FLEXSPI_INTR_IPCMDERR_MASK | |
| FLEXSPI_INTR_AHBCMDGE_MASK | |
| FLEXSPI_INTR_IPCMDGE_MASK; |
| |
| /* Configure base address */ |
| |
| base->IPCR0 = xfer->device_address; |
| |
| /* Reset fifos */ |
| |
| base->IPTXFCR |= FLEXSPI_IPTXFCR_CLRIPTXF_MASK; |
| base->IPRXFCR |= FLEXSPI_IPRXFCR_CLRIPRXF_MASK; |
| |
| /* Configure data size */ |
| |
| if ((xfer->cmd_type == FLEXSPI_READ) || |
| (xfer->cmd_type == FLEXSPI_WRITE) || |
| (xfer->cmd_type == FLEXSPI_CONFIG)) |
| { |
| config_value = FLEXSPI_IPCR1_IDATSZ(xfer->data_size); |
| } |
| |
| /* Configure sequence ID */ |
| |
| config_value |= |
| FLEXSPI_IPCR1_ISEQID((uint32_t)xfer->seq_index) | \ |
| FLEXSPI_IPCR1_ISEQNUM((uint32_t)xfer->seq_number - 1u); |
| base->IPCR1 = config_value; |
| |
| /* Start Transfer */ |
| |
| base->IPCMD |= FLEXSPI_IPCMD_TRG_MASK; |
| |
| if ((xfer->cmd_type == FLEXSPI_WRITE) || |
| (xfer->cmd_type == FLEXSPI_CONFIG)) |
| { |
| result = imxrt_flexspi_write_blocking(base, xfer->data, |
| xfer->data_size); |
| } |
| else if (xfer->cmd_type == FLEXSPI_READ) |
| { |
| result = imxrt_flexspi_read_blocking(base, xfer->data, |
| xfer->data_size); |
| } |
| else |
| { |
| /* Empty else */ |
| } |
| |
| /* Wait for bus idle */ |
| |
| while (!imxrt_flexspi_get_bus_idle_status(base)) |
| { |
| } |
| |
| if (xfer->cmd_type == FLEXSPI_COMMAND) |
| { |
| result = imxrt_flexspi_check_and_clear_error(base, base->INTR); |
| } |
| |
| return result; |
| } |
| |
| /**************************************************************************** |
| * Name: imxrt_flexspi_lock |
| * |
| * Description: |
| * On FlexSPI buses where there are multiple devices, it will be necessary |
| * to lock FlexSPI to have exclusive access to the buses for a sequence of |
| * transfers. The bus should be locked before the chip is selected. |
| * |
| * Input Parameters: |
| * dev - Device-specific state data |
| * lock - true: Lock FlexSPI bus, false: unlock FlexSPI bus |
| * |
| * Returned Value: |
| * Semaphore status |
| * |
| ****************************************************************************/ |
| |
| static int imxrt_flexspi_lock(struct flexspi_dev_s *dev, bool lock) |
| { |
| struct imxrt_flexspidev_s *priv = (struct imxrt_flexspidev_s *)dev; |
| int ret; |
| |
| spiinfo("lock=%d\n", lock); |
| if (lock) |
| { |
| ret = nxmutex_lock(&priv->lock); |
| } |
| else |
| { |
| ret = nxmutex_unlock(&priv->lock); |
| } |
| |
| return ret; |
| } |
| |
| /**************************************************************************** |
| * Name: imxrt_flexspi_transfer_blocking |
| * |
| * Description: |
| * Perform one FlexSPI transfer |
| * |
| * Input Parameters: |
| * dev - Device-specific state data |
| * xfer - Describes the transfer to be performed. |
| * |
| * Returned Value: |
| * 0 on SUCCESS, STATUS_FLEXSPI_SEQUENCE_EXECUTION_TIMEOUT, |
| * STATUS_FLEXSPI_IP_COMMAND_SEQUENCE_ERROR or |
| * STATUS_FLEXSPI_IP_COMMAND_GRANT_TIMEOUT otherwise |
| * |
| ****************************************************************************/ |
| |
| static int imxrt_flexspi_transfer_blocking(struct flexspi_dev_s *dev, |
| struct flexspi_transfer_s *xfer) |
| { |
| struct imxrt_flexspidev_s *priv = (struct imxrt_flexspidev_s *)dev; |
| |
| return (int)imxrt_flexspi_transfer_blocking_private(priv->base, xfer); |
| } |
| |
| /**************************************************************************** |
| * Name: imxrt_flexspi_software_reset |
| * |
| * Description: |
| * Performs a software reset of FlexSPI |
| * |
| * Input Parameters: |
| * dev - Device-specific state data |
| * |
| * Returned Value: |
| * None |
| * |
| ****************************************************************************/ |
| |
| static void imxrt_flexspi_software_reset(struct flexspi_dev_s *dev) |
| { |
| struct imxrt_flexspidev_s *priv = (struct imxrt_flexspidev_s *)dev; |
| |
| imxrt_flexspi_software_reset_private(priv->base); |
| } |
| |
| /**************************************************************************** |
| * Name: imxrt_flexspi_update_lut |
| * |
| * Description: |
| * Perform FlexSPI LUT table update |
| * |
| * Input Parameters: |
| * dev - Device-specific state data |
| * index - Index start to update |
| * cmd - Command array |
| * count - Size of the array |
| * |
| * Returned Value: |
| * none |
| * |
| ****************************************************************************/ |
| |
| static void imxrt_flexspi_update_lut(struct flexspi_dev_s *dev, |
| uint32_t index, |
| const uint32_t *cmd, |
| uint32_t count) |
| { |
| struct imxrt_flexspidev_s *priv = (struct imxrt_flexspidev_s *)dev; |
| |
| imxrt_flexspi_update_lut_private(priv->base, index, cmd, count); |
| } |
| |
| /**************************************************************************** |
| * Name: imxrt_flexspi_set_device_config |
| * |
| * Description: |
| * Perform FlexSPI device config |
| * |
| * Input Parameters: |
| * dev - Device-specific state data |
| * config - Config data for external device |
| * port - Port |
| * |
| * Returned Value: |
| * none |
| * |
| ****************************************************************************/ |
| |
| static void imxrt_flexspi_set_device_config(struct flexspi_dev_s *dev, |
| struct flexspi_device_config_s *config, |
| enum flexspi_port_e port) |
| { |
| struct imxrt_flexspidev_s *priv = (struct imxrt_flexspidev_s *)dev; |
| |
| imxrt_flexspi_set_device_config_private(priv->base, config, port); |
| } |
| |
| /**************************************************************************** |
| * Public Functions |
| ****************************************************************************/ |
| |
| /**************************************************************************** |
| * Name: imxrt_flexspi_initialize |
| * |
| * Description: |
| * Initialize the selected FlexSPI port in master mode |
| * |
| * Input Parameters: |
| * intf - Interface number(must be zero) |
| * |
| * Returned Value: |
| * Valid FlexSPI device structure reference on success; a NULL on failure |
| * |
| ****************************************************************************/ |
| |
| struct flexspi_dev_s *imxrt_flexspi_initialize(int intf) |
| { |
| struct imxrt_flexspidev_s *priv; |
| struct flexspi_config_s flexspi_config; |
| |
| /* The supported i.MXRT parts have only a single FlexSPI port. Other |
| * is reserved for code XIP |
| */ |
| |
| DEBUGASSERT(intf >= 0 && intf < 2); |
| |
| /* Select the FlexSPI interface */ |
| |
| #ifdef CONFIG_IMXRT_FLEXSPI1 |
| if (intf == 0) |
| { |
| /* If this function is called multiple times, the following operations |
| * will be performed multiple times. |
| */ |
| |
| /* Select FlexSPI */ |
| |
| priv = &g_flexspi0dev; |
| |
| /* Enable clocking to the FlexSPI peripheral */ |
| |
| imxrt_clockrun_flexspi(); |
| } |
| |
| #endif |
| #ifdef CONFIG_IMXRT_FLEXSPI2 |
| if (intf == 1) |
| { |
| /* If this function is called multiple times, the following operations |
| * will be performed multiple times. |
| */ |
| |
| /* Select FlexSPI */ |
| |
| priv = &g_flexspi1dev; |
| |
| /* Enable clocking to the FlexSPI peripheral */ |
| |
| imxrt_clockrun_flexspi2(); |
| } |
| |
| #endif |
| else |
| { |
| return NULL; |
| } |
| |
| /* Has the FlexSPI hardware been initialized? */ |
| |
| if (!priv->initialized) |
| { |
| /* No perform one time initialization */ |
| |
| /* Perform hardware initialization. Puts the FlexSPI into an active |
| * state. |
| */ |
| |
| imxrt_flexspi_get_default_config(&flexspi_config); |
| imxrt_flexspi_init(priv->base, &flexspi_config); |
| |
| /* Enable interrupts at the NVIC */ |
| |
| priv->initialized = true; |
| } |
| |
| return &priv->flexspi; |
| } |
| |
| #endif /* CONFIG_IMXRT_FLEXSPI */ |