339 lines
8.9 KiB
C
339 lines
8.9 KiB
C
|
/*
|
||
|
* Copyright (c) 2016, Freescale Semiconductor, Inc.
|
||
|
* Copyright 2016-2019 NXP
|
||
|
* All rights reserved.
|
||
|
*
|
||
|
*
|
||
|
* SPDX-License-Identifier: BSD-3-Clause
|
||
|
*/
|
||
|
|
||
|
#include "fsl_flexspi.h"
|
||
|
#include "internal_flash.h"
|
||
|
|
||
|
#define FLASH_QUAD_ENABLE 0x02
|
||
|
#define FLASH_BUSY_STATUS_POL 1
|
||
|
#define FLASH_BUSY_STATUS_OFFSET 0
|
||
|
|
||
|
static inline void flexspi_clock_init(void)
|
||
|
{
|
||
|
/* Switch to PLL2 for XIP to avoid hardfault during re-initialize clock. */
|
||
|
CLOCK_InitSysPfd(kCLOCK_Pfd2, 24); /* Set PLL2 PFD2 clock 396MHZ. */
|
||
|
CLOCK_SetMux(kCLOCK_FlexspiMux, 0x2); /* Choose PLL2 PFD2 clock as flexspi source clock. */
|
||
|
CLOCK_SetDiv(kCLOCK_FlexspiDiv, 2); /* flexspi clock 133M. */
|
||
|
}
|
||
|
|
||
|
extern flexspi_device_config_t deviceconfig;
|
||
|
extern const uint32_t customLUT[CUSTOM_LUT_LENGTH];
|
||
|
|
||
|
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
|
||
|
{
|
||
|
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;
|
||
|
|
||
|
/* 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. */
|
||
|
FLEXSPI_SoftwareReset(base);
|
||
|
|
||
|
return status;
|
||
|
}
|
||
|
|
||
|
status_t flexspi_nor_flash_erase_sector(FLEXSPI_Type *base, uint32_t address)
|
||
|
{
|
||
|
status_t status;
|
||
|
flexspi_transfer_t flashXfer;
|
||
|
|
||
|
/* 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. */
|
||
|
FLEXSPI_SoftwareReset(base);
|
||
|
|
||
|
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;
|
||
|
|
||
|
/* 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;
|
||
|
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)
|
||
|
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;
|
||
|
|
||
|
/* 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;
|
||
|
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)
|
||
|
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. */
|
||
|
#if defined(FSL_FEATURE_SOC_OTFAD_COUNT)
|
||
|
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;
|
||
|
|
||
|
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, customLUT, CUSTOM_LUT_LENGTH);
|
||
|
|
||
|
/* Do software reset. */
|
||
|
FLEXSPI_SoftwareReset(base);
|
||
|
}
|