497 lines
14 KiB
C
497 lines
14 KiB
C
/*
|
|
* Copyright 2020-2022 NXP
|
|
* All rights reserved.
|
|
*
|
|
*
|
|
* SPDX-License-Identifier: BSD-3-Clause
|
|
*/
|
|
|
|
#include "fsl_flexspi.h"
|
|
#include "fsl_flexspi_edma.h"
|
|
#include "app.h"
|
|
|
|
/*******************************************************************************
|
|
* Definitions
|
|
******************************************************************************/
|
|
|
|
/*******************************************************************************
|
|
* Prototypes
|
|
******************************************************************************/
|
|
|
|
/*******************************************************************************
|
|
* Variables
|
|
*****************************************************************************/
|
|
extern flexspi_device_config_t deviceconfig;
|
|
extern const uint32_t customLUT[CUSTOM_LUT_LENGTH];
|
|
static volatile bool g_completionFlag = false;
|
|
extern edma_handle_t dmaTxHandle;
|
|
extern edma_handle_t dmaRxHandle;
|
|
static flexspi_edma_handle_t flexspiHandle;
|
|
/*******************************************************************************
|
|
* Code
|
|
******************************************************************************/
|
|
static void flexspi_callback(FLEXSPI_Type *base, flexspi_edma_handle_t *handle, status_t status, void *userData)
|
|
{
|
|
/* Signal transfer success when received success status. */
|
|
if (status == kStatus_Success)
|
|
{
|
|
g_completionFlag = true;
|
|
}
|
|
}
|
|
|
|
status_t flexspi_nor_write_enable(FLEXSPI_Type *base, uint32_t baseAddr)
|
|
{
|
|
flexspi_transfer_t flashXfer;
|
|
status_t status;
|
|
|
|
/* Write enable */
|
|
flashXfer.deviceAddress = baseAddr;
|
|
flashXfer.port = kFLEXSPI_PortA1;
|
|
flashXfer.cmdType = kFLEXSPI_Command;
|
|
flashXfer.SeqNumber = 1;
|
|
flashXfer.seqIndex = NOR_CMD_LUT_SEQ_IDX_WRITEENABLE;
|
|
|
|
status = FLEXSPI_TransferBlocking(base, &flashXfer);
|
|
|
|
return status;
|
|
}
|
|
|
|
status_t flexspi_nor_wait_bus_busy(FLEXSPI_Type *base)
|
|
{
|
|
/* Wait status ready. */
|
|
bool isBusy;
|
|
uint32_t readValue;
|
|
status_t status;
|
|
flexspi_transfer_t flashXfer;
|
|
|
|
flashXfer.deviceAddress = 0;
|
|
flashXfer.port = kFLEXSPI_PortA1;
|
|
flashXfer.cmdType = kFLEXSPI_Read;
|
|
flashXfer.SeqNumber = 1;
|
|
flashXfer.seqIndex = NOR_CMD_LUT_SEQ_IDX_READSTATUSREG;
|
|
flashXfer.data = &readValue;
|
|
flashXfer.dataSize = 1;
|
|
|
|
do
|
|
{
|
|
/* For flash targets, after doing erase/program, need to call flexspi_nor_wait_bus_busy to wait for the
|
|
operation finish, Use blocking way to read back the status instead of using DMA. The reason is that the called
|
|
DMA API calls memset which is placed in flash region, because the external flash is being erase/propgram, so
|
|
load instruction from external flash at this time may read back some invalid instructions. */
|
|
status = FLEXSPI_TransferBlocking(base, &flashXfer);
|
|
|
|
if (status != kStatus_Success)
|
|
{
|
|
return status;
|
|
}
|
|
if (FLASH_BUSY_STATUS_POL)
|
|
{
|
|
if (readValue & (1U << FLASH_BUSY_STATUS_OFFSET))
|
|
{
|
|
isBusy = true;
|
|
}
|
|
else
|
|
{
|
|
isBusy = false;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (readValue & (1U << FLASH_BUSY_STATUS_OFFSET))
|
|
{
|
|
isBusy = false;
|
|
}
|
|
else
|
|
{
|
|
isBusy = true;
|
|
}
|
|
}
|
|
|
|
} while (isBusy);
|
|
|
|
return status;
|
|
}
|
|
|
|
status_t flexspi_nor_enable_quad_mode(FLEXSPI_Type *base)
|
|
{
|
|
flexspi_transfer_t flashXfer;
|
|
status_t status;
|
|
uint32_t writeValue = FLASH_QUAD_ENABLE;
|
|
|
|
/* Make sure external flash is not in busy status. */
|
|
status = flexspi_nor_wait_bus_busy(base);
|
|
if (status != kStatus_Success)
|
|
{
|
|
return status;
|
|
}
|
|
|
|
/* Write enable */
|
|
status = flexspi_nor_write_enable(base, 0);
|
|
|
|
if (status != kStatus_Success)
|
|
{
|
|
return status;
|
|
}
|
|
|
|
/* Enable quad mode. */
|
|
flashXfer.deviceAddress = 0;
|
|
flashXfer.port = kFLEXSPI_PortA1;
|
|
flashXfer.cmdType = kFLEXSPI_Write;
|
|
flashXfer.SeqNumber = 1;
|
|
flashXfer.seqIndex = NOR_CMD_LUT_SEQ_IDX_WRITESTATUSREG;
|
|
flashXfer.data = &writeValue;
|
|
flashXfer.dataSize = 1;
|
|
|
|
status = FLEXSPI_TransferBlocking(base, &flashXfer);
|
|
if (status != kStatus_Success)
|
|
{
|
|
return status;
|
|
}
|
|
|
|
status = flexspi_nor_wait_bus_busy(base);
|
|
|
|
/* Do software reset. */
|
|
#if defined(FSL_FEATURE_SOC_OTFAD_COUNT) && defined(FLEXSPI_AHBCR_CLRAHBRXBUF_MASK) && \
|
|
defined(FLEXSPI_AHBCR_CLRAHBTXBUF_MASK)
|
|
base->AHBCR |= FLEXSPI_AHBCR_CLRAHBRXBUF_MASK | FLEXSPI_AHBCR_CLRAHBTXBUF_MASK;
|
|
base->AHBCR &= ~(FLEXSPI_AHBCR_CLRAHBRXBUF_MASK | FLEXSPI_AHBCR_CLRAHBTXBUF_MASK);
|
|
#else
|
|
FLEXSPI_SoftwareReset(base);
|
|
#endif
|
|
|
|
return status;
|
|
}
|
|
|
|
status_t flexspi_nor_flash_erase_sector(FLEXSPI_Type *base, uint32_t address)
|
|
{
|
|
status_t status;
|
|
flexspi_transfer_t flashXfer;
|
|
|
|
/* Make sure external flash is not in busy status. */
|
|
status = flexspi_nor_wait_bus_busy(base);
|
|
if (status != kStatus_Success)
|
|
{
|
|
return status;
|
|
}
|
|
|
|
/* Write enable */
|
|
flashXfer.deviceAddress = address;
|
|
flashXfer.port = kFLEXSPI_PortA1;
|
|
flashXfer.cmdType = kFLEXSPI_Command;
|
|
flashXfer.SeqNumber = 1;
|
|
flashXfer.seqIndex = NOR_CMD_LUT_SEQ_IDX_WRITEENABLE;
|
|
|
|
status = FLEXSPI_TransferBlocking(base, &flashXfer);
|
|
|
|
if (status != kStatus_Success)
|
|
{
|
|
return status;
|
|
}
|
|
|
|
flashXfer.deviceAddress = address;
|
|
flashXfer.port = kFLEXSPI_PortA1;
|
|
flashXfer.cmdType = kFLEXSPI_Command;
|
|
flashXfer.SeqNumber = 1;
|
|
flashXfer.seqIndex = NOR_CMD_LUT_SEQ_IDX_ERASESECTOR;
|
|
status = FLEXSPI_TransferBlocking(base, &flashXfer);
|
|
|
|
if (status != kStatus_Success)
|
|
{
|
|
return status;
|
|
}
|
|
|
|
status = flexspi_nor_wait_bus_busy(base);
|
|
|
|
/* Do software reset. */
|
|
#if defined(FSL_FEATURE_SOC_OTFAD_COUNT) && defined(FLEXSPI_AHBCR_CLRAHBRXBUF_MASK) && \
|
|
defined(FLEXSPI_AHBCR_CLRAHBTXBUF_MASK)
|
|
base->AHBCR |= FLEXSPI_AHBCR_CLRAHBRXBUF_MASK | FLEXSPI_AHBCR_CLRAHBTXBUF_MASK;
|
|
base->AHBCR &= ~(FLEXSPI_AHBCR_CLRAHBRXBUF_MASK | FLEXSPI_AHBCR_CLRAHBTXBUF_MASK);
|
|
#else
|
|
FLEXSPI_SoftwareReset(base);
|
|
#endif
|
|
|
|
return status;
|
|
}
|
|
|
|
status_t flexspi_nor_flash_program(FLEXSPI_Type *base, uint32_t dstAddr, const uint32_t *src, uint32_t length)
|
|
{
|
|
status_t status;
|
|
flexspi_transfer_t flashXfer;
|
|
|
|
/* Flash program limits program size smaller than flash page size for one program command sequence. */
|
|
assert(length <= FLASH_PAGE_SIZE);
|
|
|
|
/* Make sure external flash is not in busy status. */
|
|
status = flexspi_nor_wait_bus_busy(base);
|
|
if (status != kStatus_Success)
|
|
{
|
|
return status;
|
|
}
|
|
|
|
/* Write enable */
|
|
status = flexspi_nor_write_enable(base, dstAddr);
|
|
|
|
if (status != kStatus_Success)
|
|
{
|
|
return status;
|
|
}
|
|
|
|
/* Prepare page program command */
|
|
flashXfer.deviceAddress = dstAddr;
|
|
flashXfer.port = kFLEXSPI_PortA1;
|
|
flashXfer.cmdType = kFLEXSPI_Write;
|
|
flashXfer.SeqNumber = 1;
|
|
flashXfer.seqIndex = NOR_CMD_LUT_SEQ_IDX_PAGEPROGRAM_QUAD;
|
|
flashXfer.data = (uint32_t *)src;
|
|
flashXfer.dataSize = length;
|
|
|
|
g_completionFlag = false;
|
|
|
|
status = FLEXSPI_TransferEDMA(base, &flexspiHandle, &flashXfer);
|
|
|
|
if (status != kStatus_Success)
|
|
{
|
|
return status;
|
|
}
|
|
|
|
/* Wait for transfer completed. */
|
|
while (!g_completionFlag)
|
|
{
|
|
}
|
|
|
|
status = flexspi_nor_wait_bus_busy(base);
|
|
|
|
/* Do software reset. */
|
|
#if defined(FSL_FEATURE_SOC_OTFAD_COUNT) && defined(FLEXSPI_AHBCR_CLRAHBRXBUF_MASK) && \
|
|
defined(FLEXSPI_AHBCR_CLRAHBTXBUF_MASK)
|
|
base->AHBCR |= FLEXSPI_AHBCR_CLRAHBRXBUF_MASK | FLEXSPI_AHBCR_CLRAHBTXBUF_MASK;
|
|
base->AHBCR &= ~(FLEXSPI_AHBCR_CLRAHBRXBUF_MASK | FLEXSPI_AHBCR_CLRAHBTXBUF_MASK);
|
|
#else
|
|
FLEXSPI_SoftwareReset(base);
|
|
#endif
|
|
|
|
return status;
|
|
}
|
|
|
|
status_t flexspi_nor_flash_page_program(FLEXSPI_Type *base, uint32_t dstAddr, const uint32_t *src)
|
|
{
|
|
status_t status;
|
|
flexspi_transfer_t flashXfer;
|
|
|
|
/* Make sure external flash is not in busy status. */
|
|
status = flexspi_nor_wait_bus_busy(base);
|
|
if (status != kStatus_Success)
|
|
{
|
|
return status;
|
|
}
|
|
|
|
/* Write enable */
|
|
status = flexspi_nor_write_enable(base, dstAddr);
|
|
|
|
if (status != kStatus_Success)
|
|
{
|
|
return status;
|
|
}
|
|
|
|
/* Prepare page program command */
|
|
flashXfer.deviceAddress = dstAddr;
|
|
flashXfer.port = kFLEXSPI_PortA1;
|
|
flashXfer.cmdType = kFLEXSPI_Write;
|
|
flashXfer.SeqNumber = 1;
|
|
flashXfer.seqIndex = NOR_CMD_LUT_SEQ_IDX_PAGEPROGRAM_QUAD;
|
|
flashXfer.data = (uint32_t *)src;
|
|
flashXfer.dataSize = FLASH_PAGE_SIZE;
|
|
|
|
g_completionFlag = false;
|
|
|
|
status = FLEXSPI_TransferEDMA(base, &flexspiHandle, &flashXfer);
|
|
|
|
if (status != kStatus_Success)
|
|
{
|
|
return status;
|
|
}
|
|
|
|
/* Wait for transfer completed. */
|
|
while (!g_completionFlag)
|
|
{
|
|
}
|
|
|
|
status = flexspi_nor_wait_bus_busy(base);
|
|
|
|
/* Do software reset or clear AHB buffer directly. */
|
|
#if defined(FSL_FEATURE_SOC_OTFAD_COUNT) && defined(FLEXSPI_AHBCR_CLRAHBRXBUF_MASK) && \
|
|
defined(FLEXSPI_AHBCR_CLRAHBTXBUF_MASK)
|
|
base->AHBCR |= FLEXSPI_AHBCR_CLRAHBRXBUF_MASK | FLEXSPI_AHBCR_CLRAHBTXBUF_MASK;
|
|
base->AHBCR &= ~(FLEXSPI_AHBCR_CLRAHBRXBUF_MASK | FLEXSPI_AHBCR_CLRAHBTXBUF_MASK);
|
|
#else
|
|
FLEXSPI_SoftwareReset(base);
|
|
#endif
|
|
|
|
return status;
|
|
}
|
|
|
|
status_t flexspi_nor_read_data(FLEXSPI_Type *base, uint32_t startAddress, uint32_t *buffer, uint32_t length)
|
|
{
|
|
status_t status;
|
|
flexspi_transfer_t flashXfer;
|
|
uint32_t readAddress = startAddress;
|
|
|
|
/* Read page. */
|
|
flashXfer.deviceAddress = readAddress;
|
|
flashXfer.port = kFLEXSPI_PortA1;
|
|
flashXfer.cmdType = kFLEXSPI_Read;
|
|
flashXfer.SeqNumber = 1;
|
|
flashXfer.seqIndex = NOR_CMD_LUT_SEQ_IDX_READ_FAST_QUAD;
|
|
flashXfer.data = buffer;
|
|
flashXfer.dataSize = length;
|
|
|
|
g_completionFlag = false;
|
|
|
|
status = FLEXSPI_TransferEDMA(base, &flexspiHandle, &flashXfer);
|
|
|
|
if (status != kStatus_Success)
|
|
{
|
|
return status;
|
|
}
|
|
|
|
/* Wait for transfer completed. */
|
|
while (!g_completionFlag)
|
|
{
|
|
}
|
|
|
|
/* Do software reset or clear AHB buffer directly. */
|
|
#if defined(FSL_FEATURE_SOC_OTFAD_COUNT) && defined(FLEXSPI_AHBCR_CLRAHBRXBUF_MASK) && \
|
|
defined(FLEXSPI_AHBCR_CLRAHBTXBUF_MASK)
|
|
base->AHBCR |= FLEXSPI_AHBCR_CLRAHBRXBUF_MASK | FLEXSPI_AHBCR_CLRAHBTXBUF_MASK;
|
|
base->AHBCR &= ~(FLEXSPI_AHBCR_CLRAHBRXBUF_MASK | FLEXSPI_AHBCR_CLRAHBTXBUF_MASK);
|
|
#else
|
|
FLEXSPI_SoftwareReset(base);
|
|
#endif
|
|
|
|
return status;
|
|
}
|
|
|
|
status_t flexspi_nor_get_vendor_id(FLEXSPI_Type *base, uint8_t *vendorId)
|
|
{
|
|
uint32_t temp;
|
|
flexspi_transfer_t flashXfer;
|
|
flashXfer.deviceAddress = 0;
|
|
flashXfer.port = kFLEXSPI_PortA1;
|
|
flashXfer.cmdType = kFLEXSPI_Read;
|
|
flashXfer.SeqNumber = 1;
|
|
flashXfer.seqIndex = NOR_CMD_LUT_SEQ_IDX_READID;
|
|
flashXfer.data = &temp;
|
|
flashXfer.dataSize = 1;
|
|
|
|
status_t status = FLEXSPI_TransferBlocking(base, &flashXfer);
|
|
|
|
*vendorId = temp;
|
|
|
|
/* Do software reset or clear AHB buffer directly. */
|
|
#if defined(FSL_FEATURE_SOC_OTFAD_COUNT) && defined(FLEXSPI_AHBCR_CLRAHBRXBUF_MASK) && \
|
|
defined(FLEXSPI_AHBCR_CLRAHBTXBUF_MASK)
|
|
base->AHBCR |= FLEXSPI_AHBCR_CLRAHBRXBUF_MASK | FLEXSPI_AHBCR_CLRAHBTXBUF_MASK;
|
|
base->AHBCR &= ~(FLEXSPI_AHBCR_CLRAHBRXBUF_MASK | FLEXSPI_AHBCR_CLRAHBTXBUF_MASK);
|
|
#else
|
|
FLEXSPI_SoftwareReset(base);
|
|
#endif
|
|
|
|
return status;
|
|
}
|
|
|
|
status_t flexspi_nor_erase_chip(FLEXSPI_Type *base)
|
|
{
|
|
status_t status;
|
|
flexspi_transfer_t flashXfer;
|
|
|
|
/* Write enable */
|
|
status = flexspi_nor_write_enable(base, 0);
|
|
|
|
if (status != kStatus_Success)
|
|
{
|
|
return status;
|
|
}
|
|
|
|
flashXfer.deviceAddress = 0;
|
|
flashXfer.port = kFLEXSPI_PortA1;
|
|
flashXfer.cmdType = kFLEXSPI_Command;
|
|
flashXfer.SeqNumber = 1;
|
|
flashXfer.seqIndex = NOR_CMD_LUT_SEQ_IDX_ERASECHIP;
|
|
|
|
status = FLEXSPI_TransferBlocking(base, &flashXfer);
|
|
|
|
if (status != kStatus_Success)
|
|
{
|
|
return status;
|
|
}
|
|
|
|
status = flexspi_nor_wait_bus_busy(base);
|
|
|
|
return status;
|
|
}
|
|
|
|
void flexspi_nor_flash_init(FLEXSPI_Type *base)
|
|
{
|
|
flexspi_config_t config;
|
|
/* To store custom's LUT table in local. */
|
|
uint32_t tempLUT[CUSTOM_LUT_LENGTH] = {0x00U};
|
|
|
|
#if defined(__DCACHE_PRESENT) && (__DCACHE_PRESENT == 1U)
|
|
bool DCacheEnableFlag = false;
|
|
/* Disable D cache. */
|
|
if (SCB_CCR_DC_Msk == (SCB_CCR_DC_Msk & SCB->CCR))
|
|
{
|
|
SCB_DisableDCache();
|
|
DCacheEnableFlag = true;
|
|
}
|
|
#endif /* __DCACHE_PRESENT */
|
|
|
|
/* Waiting for bus idle only when FLEXSPI enabled. */
|
|
if ((base->MCR0 & FLEXSPI_MCR0_MDIS_MASK) != FLEXSPI_MCR0_MDIS_MASK)
|
|
{
|
|
/* Make sure flexspi bus idle before change its clock setting. */
|
|
while (!FLEXSPI_GetBusIdleStatus(base))
|
|
{
|
|
}
|
|
}
|
|
|
|
/* Copy LUT information from flash region into RAM region, because LUT update maybe corrupt read sequence(LUT[0])
|
|
* and load wrong LUT table from FLASH region. */
|
|
memcpy(tempLUT, customLUT, sizeof(tempLUT));
|
|
|
|
flexspi_clock_init();
|
|
|
|
/*Get FLEXSPI default settings and configure the flexspi. */
|
|
FLEXSPI_GetDefaultConfig(&config);
|
|
|
|
/*Set AHB buffer size for reading data through AHB bus. */
|
|
config.ahbConfig.enableAHBPrefetch = true;
|
|
config.ahbConfig.enableAHBBufferable = true;
|
|
config.ahbConfig.enableReadAddressOpt = true;
|
|
config.ahbConfig.enableAHBCachable = true;
|
|
config.rxSampleClock = kFLEXSPI_ReadSampleClkLoopbackFromDqsPad;
|
|
FLEXSPI_Init(base, &config);
|
|
|
|
/* Configure flash settings according to serial flash feature. */
|
|
FLEXSPI_SetFlashConfig(base, &deviceconfig, kFLEXSPI_PortA1);
|
|
|
|
/* Update LUT table. */
|
|
FLEXSPI_UpdateLUT(base, 0, tempLUT, CUSTOM_LUT_LENGTH);
|
|
|
|
/* Do software reset. */
|
|
FLEXSPI_SoftwareReset(base);
|
|
|
|
/* Create handle for flexspi. */
|
|
FLEXSPI_TransferCreateHandleEDMA(EXAMPLE_FLEXSPI, &flexspiHandle, flexspi_callback, NULL, &dmaTxHandle,
|
|
&dmaRxHandle);
|
|
|
|
#if defined(__DCACHE_PRESENT) && (__DCACHE_PRESENT == 1U)
|
|
if (DCacheEnableFlag)
|
|
{
|
|
/* Enable D cache. */
|
|
SCB_EnableDCache();
|
|
}
|
|
#endif /* __DCACHE_PRESENT */
|
|
}
|