CS hold time parameter is not correct which may cause bus fault randomly. System hang during status register reading after flash progromming which is caused by parameter accessing in XIP mode. Add dummy delay for READ command according the flash datasheet which is required for SDR mode. Use FlexSPI internal divider for clock updating instead of register in CCM to avoid potential risk caused by flash access during clock updating procedure. Signed-off-by: Raymond Lei <raymond.lei@nxp.com>
735 lines
22 KiB
C
735 lines
22 KiB
C
/*
|
|
* Copyright (c) 2021 Volvo Construction Equipment
|
|
* Copyright 2023 NXP
|
|
*
|
|
* SPDX-License-Identifier: Apache-2.0
|
|
*/
|
|
|
|
#define DT_DRV_COMPAT nxp_imx_flexspi_hyperflash
|
|
|
|
#include <zephyr/kernel.h>
|
|
#include <errno.h>
|
|
#include <zephyr/drivers/flash.h>
|
|
|
|
#include <zephyr/logging/log.h>
|
|
|
|
/*
|
|
* NOTE: If CONFIG_FLASH_MCUX_FLEXSPI_XIP is selected, Any external functions
|
|
* called while interacting with the flexspi MUST be relocated to SRAM or ITCM
|
|
* at runtime, so that the chip does not access the flexspi to read program
|
|
* instructions while it is being written to
|
|
*
|
|
* Additionally, no data used by this driver should be stored in flash.
|
|
*/
|
|
#if defined(CONFIG_FLASH_MCUX_FLEXSPI_XIP) && (CONFIG_FLASH_LOG_LEVEL > 0)
|
|
#warning "Enabling flash driver logging and XIP mode simultaneously can cause \
|
|
read-while-write hazards. This configuration is not recommended."
|
|
#endif
|
|
|
|
LOG_MODULE_REGISTER(flexspi_hyperflash, CONFIG_FLASH_LOG_LEVEL);
|
|
|
|
#ifdef CONFIG_HAS_MCUX_CACHE
|
|
#include <fsl_cache.h>
|
|
#endif
|
|
|
|
#include <zephyr/sys/util.h>
|
|
#include "memc_mcux_flexspi.h"
|
|
|
|
#define SPI_HYPERFLASH_SECTOR_SIZE (0x40000U)
|
|
#define SPI_HYPERFLASH_PAGE_SIZE (512U)
|
|
|
|
#define HYPERFLASH_ERASE_VALUE (0xFF)
|
|
|
|
/* Hyper flash support SDR and DDR, from the FlexSPI controller point of view,
|
|
* if DDR enabled, commands in LUT need to be DDR command and root clock is
|
|
* double of Serial clock (clock output to flash).
|
|
* if DDR mode enabled, set it to 2
|
|
* if SDR mode enabled, set it to 1
|
|
*/
|
|
#define MCUX_FLEXSPI_HYPERFLASH_DDR_SDR_MODE 2
|
|
|
|
/* Some hyper flashs require a lower frequency when doing writing operation. */
|
|
#define FREQ_FOR_HYPERFLASH_WRITE (MHZ(42) * MCUX_FLEXSPI_HYPERFLASH_DDR_SDR_MODE)
|
|
|
|
#ifdef CONFIG_FLASH_MCUX_FLEXSPI_HYPERFLASH_WRITE_BUFFER
|
|
static uint8_t hyperflash_write_buf[SPI_HYPERFLASH_PAGE_SIZE];
|
|
#endif
|
|
|
|
enum {
|
|
/* Instructions matching with XIP layout */
|
|
READ_DATA = 0,
|
|
WRITE_DATA = 1,
|
|
READ_STATUS = 2,
|
|
WRITE_ENABLE = 4,
|
|
ERASE_SECTOR = 6,
|
|
PAGE_PROGRAM = 10,
|
|
ERASE_CHIP = 12,
|
|
};
|
|
|
|
#define CUSTOM_LUT_LENGTH 64
|
|
|
|
static const uint32_t flash_flexspi_hyperflash_lut[CUSTOM_LUT_LENGTH] = {
|
|
/* Read Data */
|
|
[4 * READ_DATA] =
|
|
FLEXSPI_LUT_SEQ(kFLEXSPI_Command_DDR, kFLEXSPI_8PAD, 0xA0,
|
|
kFLEXSPI_Command_RADDR_DDR, kFLEXSPI_8PAD, 0x18),
|
|
[4 * READ_DATA + 1] =
|
|
FLEXSPI_LUT_SEQ(kFLEXSPI_Command_CADDR_DDR, kFLEXSPI_8PAD, 0x10,
|
|
kFLEXSPI_Command_DUMMY_RWDS_DDR, kFLEXSPI_8PAD, 0xC),
|
|
[4 * READ_DATA + 2] =
|
|
FLEXSPI_LUT_SEQ(kFLEXSPI_Command_READ_DDR, kFLEXSPI_8PAD, 0x04,
|
|
kFLEXSPI_Command_STOP, kFLEXSPI_1PAD, 0x0),
|
|
/* Write Data */
|
|
[4 * WRITE_DATA] =
|
|
FLEXSPI_LUT_SEQ(kFLEXSPI_Command_DDR, kFLEXSPI_8PAD, 0x20,
|
|
kFLEXSPI_Command_RADDR_DDR, kFLEXSPI_8PAD, 0x18),
|
|
[4 * WRITE_DATA + 1] =
|
|
FLEXSPI_LUT_SEQ(kFLEXSPI_Command_CADDR_DDR, kFLEXSPI_8PAD, 0x10,
|
|
kFLEXSPI_Command_WRITE_DDR, kFLEXSPI_8PAD, 0x02),
|
|
/* Read Status */
|
|
[4 * READ_STATUS] =
|
|
FLEXSPI_LUT_SEQ(kFLEXSPI_Command_DDR, kFLEXSPI_8PAD, 0x00,
|
|
kFLEXSPI_Command_DDR, kFLEXSPI_8PAD, 0x00),
|
|
[4 * READ_STATUS + 1] =
|
|
FLEXSPI_LUT_SEQ(kFLEXSPI_Command_DDR, kFLEXSPI_8PAD, 0x00,
|
|
kFLEXSPI_Command_DDR, kFLEXSPI_8PAD, 0xAA),
|
|
[4 * READ_STATUS + 2] =
|
|
FLEXSPI_LUT_SEQ(kFLEXSPI_Command_DDR, kFLEXSPI_8PAD, 0x00,
|
|
kFLEXSPI_Command_DDR, kFLEXSPI_8PAD, 0x05),
|
|
[4 * READ_STATUS + 3] =
|
|
FLEXSPI_LUT_SEQ(kFLEXSPI_Command_DDR, kFLEXSPI_8PAD, 0x00,
|
|
kFLEXSPI_Command_DDR, kFLEXSPI_8PAD, 0x70),
|
|
[4 * READ_STATUS + 4] =
|
|
FLEXSPI_LUT_SEQ(kFLEXSPI_Command_DDR, kFLEXSPI_8PAD, 0xA0,
|
|
kFLEXSPI_Command_RADDR_DDR, kFLEXSPI_8PAD, 0x18),
|
|
[4 * READ_STATUS + 5] =
|
|
FLEXSPI_LUT_SEQ(kFLEXSPI_Command_CADDR_DDR, kFLEXSPI_8PAD, 0x10,
|
|
kFLEXSPI_Command_DUMMY_RWDS_DDR, kFLEXSPI_8PAD, 0x0B),
|
|
[4 * READ_STATUS + 6] =
|
|
FLEXSPI_LUT_SEQ(kFLEXSPI_Command_READ_DDR, kFLEXSPI_8PAD, 0x04,
|
|
kFLEXSPI_Command_STOP, kFLEXSPI_1PAD, 0x0),
|
|
/* Write Enable */
|
|
[4 * WRITE_ENABLE] =
|
|
FLEXSPI_LUT_SEQ(kFLEXSPI_Command_DDR, kFLEXSPI_8PAD, 0x20,
|
|
kFLEXSPI_Command_DDR, kFLEXSPI_8PAD, 0x00),
|
|
[4 * WRITE_ENABLE + 1] =
|
|
FLEXSPI_LUT_SEQ(kFLEXSPI_Command_DDR, kFLEXSPI_8PAD, 0x00,
|
|
kFLEXSPI_Command_DDR, kFLEXSPI_8PAD, 0xAA),
|
|
[4 * WRITE_ENABLE + 2] =
|
|
FLEXSPI_LUT_SEQ(kFLEXSPI_Command_DDR, kFLEXSPI_8PAD, 0x00,
|
|
kFLEXSPI_Command_DDR, kFLEXSPI_8PAD, 0x05),
|
|
[4 * WRITE_ENABLE + 3] =
|
|
FLEXSPI_LUT_SEQ(kFLEXSPI_Command_DDR, kFLEXSPI_8PAD, 0x00,
|
|
kFLEXSPI_Command_DDR, kFLEXSPI_8PAD, 0xAA),
|
|
[4 * WRITE_ENABLE + 4] =
|
|
FLEXSPI_LUT_SEQ(kFLEXSPI_Command_DDR, kFLEXSPI_8PAD, 0x20,
|
|
kFLEXSPI_Command_DDR, kFLEXSPI_8PAD, 0x00),
|
|
[4 * WRITE_ENABLE + 5] =
|
|
FLEXSPI_LUT_SEQ(kFLEXSPI_Command_DDR, kFLEXSPI_8PAD, 0x00,
|
|
kFLEXSPI_Command_DDR, kFLEXSPI_8PAD, 0x55),
|
|
[4 * WRITE_ENABLE + 6] =
|
|
FLEXSPI_LUT_SEQ(kFLEXSPI_Command_DDR, kFLEXSPI_8PAD, 0x00,
|
|
kFLEXSPI_Command_DDR, kFLEXSPI_8PAD, 0x02),
|
|
[4 * WRITE_ENABLE + 7] =
|
|
FLEXSPI_LUT_SEQ(kFLEXSPI_Command_DDR, kFLEXSPI_8PAD, 0x00,
|
|
kFLEXSPI_Command_DDR, kFLEXSPI_8PAD, 0x55),
|
|
|
|
/* Erase Sector */
|
|
[4 * ERASE_SECTOR] =
|
|
FLEXSPI_LUT_SEQ(kFLEXSPI_Command_DDR, kFLEXSPI_8PAD, 0x00,
|
|
kFLEXSPI_Command_DDR, kFLEXSPI_8PAD, 0x00),
|
|
[4 * ERASE_SECTOR + 1] =
|
|
FLEXSPI_LUT_SEQ(kFLEXSPI_Command_DDR, kFLEXSPI_8PAD, 0x00,
|
|
kFLEXSPI_Command_DDR, kFLEXSPI_8PAD, 0xAA),
|
|
[4 * ERASE_SECTOR + 2] =
|
|
FLEXSPI_LUT_SEQ(kFLEXSPI_Command_DDR, kFLEXSPI_8PAD, 0x00,
|
|
kFLEXSPI_Command_DDR, kFLEXSPI_8PAD, 0x05),
|
|
[4 * ERASE_SECTOR + 3] =
|
|
FLEXSPI_LUT_SEQ(kFLEXSPI_Command_DDR, kFLEXSPI_8PAD, 0x00,
|
|
kFLEXSPI_Command_DDR, kFLEXSPI_8PAD, 0x80),
|
|
[4 * ERASE_SECTOR + 4] =
|
|
FLEXSPI_LUT_SEQ(kFLEXSPI_Command_DDR, kFLEXSPI_8PAD, 0x00,
|
|
kFLEXSPI_Command_DDR, kFLEXSPI_8PAD, 0x00),
|
|
[4 * ERASE_SECTOR + 5] =
|
|
FLEXSPI_LUT_SEQ(kFLEXSPI_Command_DDR, kFLEXSPI_8PAD, 0x00,
|
|
kFLEXSPI_Command_DDR, kFLEXSPI_8PAD, 0xAA),
|
|
[4 * ERASE_SECTOR + 6] =
|
|
FLEXSPI_LUT_SEQ(kFLEXSPI_Command_DDR, kFLEXSPI_8PAD, 0x00,
|
|
kFLEXSPI_Command_DDR, kFLEXSPI_8PAD, 0x05),
|
|
[4 * ERASE_SECTOR + 7] =
|
|
FLEXSPI_LUT_SEQ(kFLEXSPI_Command_DDR, kFLEXSPI_8PAD, 0x00,
|
|
kFLEXSPI_Command_DDR, kFLEXSPI_8PAD, 0xAA),
|
|
[4 * ERASE_SECTOR + 8] =
|
|
FLEXSPI_LUT_SEQ(kFLEXSPI_Command_DDR, kFLEXSPI_8PAD, 0x00,
|
|
kFLEXSPI_Command_DDR, kFLEXSPI_8PAD, 0x00),
|
|
[4 * ERASE_SECTOR + 9] =
|
|
FLEXSPI_LUT_SEQ(kFLEXSPI_Command_DDR, kFLEXSPI_8PAD, 0x00,
|
|
kFLEXSPI_Command_DDR, kFLEXSPI_8PAD, 0x55),
|
|
[4 * ERASE_SECTOR + 10] =
|
|
FLEXSPI_LUT_SEQ(kFLEXSPI_Command_DDR, kFLEXSPI_8PAD, 0x00,
|
|
kFLEXSPI_Command_DDR, kFLEXSPI_8PAD, 0x02),
|
|
[4 * ERASE_SECTOR + 11] =
|
|
FLEXSPI_LUT_SEQ(kFLEXSPI_Command_DDR, kFLEXSPI_8PAD, 0x00,
|
|
kFLEXSPI_Command_DDR, kFLEXSPI_8PAD, 0x55),
|
|
[4 * ERASE_SECTOR + 12] =
|
|
FLEXSPI_LUT_SEQ(kFLEXSPI_Command_DDR, kFLEXSPI_8PAD, 0x00,
|
|
kFLEXSPI_Command_RADDR_DDR, kFLEXSPI_8PAD, 0x18),
|
|
[4 * ERASE_SECTOR + 13] =
|
|
FLEXSPI_LUT_SEQ(kFLEXSPI_Command_CADDR_DDR, kFLEXSPI_8PAD, 0x10,
|
|
kFLEXSPI_Command_DDR, kFLEXSPI_8PAD, 0x00),
|
|
[4 * ERASE_SECTOR + 14] =
|
|
FLEXSPI_LUT_SEQ(kFLEXSPI_Command_DDR, kFLEXSPI_8PAD, 0x30,
|
|
kFLEXSPI_Command_STOP, kFLEXSPI_1PAD, 0x00),
|
|
|
|
/* program page with word program command sequence */
|
|
[4 * PAGE_PROGRAM] =
|
|
FLEXSPI_LUT_SEQ(kFLEXSPI_Command_DDR, kFLEXSPI_8PAD, 0x20,
|
|
kFLEXSPI_Command_DDR, kFLEXSPI_8PAD, 0x00),
|
|
[4 * PAGE_PROGRAM + 1] =
|
|
FLEXSPI_LUT_SEQ(kFLEXSPI_Command_DDR, kFLEXSPI_8PAD, 0x00,
|
|
kFLEXSPI_Command_DDR, kFLEXSPI_8PAD, 0xAA),
|
|
[4 * PAGE_PROGRAM + 2] =
|
|
FLEXSPI_LUT_SEQ(kFLEXSPI_Command_DDR, kFLEXSPI_8PAD, 0x00,
|
|
kFLEXSPI_Command_DDR, kFLEXSPI_8PAD, 0x05),
|
|
[4 * PAGE_PROGRAM + 3] =
|
|
FLEXSPI_LUT_SEQ(kFLEXSPI_Command_DDR, kFLEXSPI_8PAD, 0x00,
|
|
kFLEXSPI_Command_DDR, kFLEXSPI_8PAD, 0xA0),
|
|
[4 * PAGE_PROGRAM + 4] =
|
|
FLEXSPI_LUT_SEQ(kFLEXSPI_Command_DDR, kFLEXSPI_8PAD, 0x20,
|
|
kFLEXSPI_Command_RADDR_DDR, kFLEXSPI_8PAD, 0x18),
|
|
[4 * PAGE_PROGRAM + 5] =
|
|
FLEXSPI_LUT_SEQ(kFLEXSPI_Command_CADDR_DDR, kFLEXSPI_8PAD, 0x10,
|
|
kFLEXSPI_Command_WRITE_DDR, kFLEXSPI_8PAD, 0x80),
|
|
|
|
/* Erase chip */
|
|
[4 * ERASE_CHIP] =
|
|
FLEXSPI_LUT_SEQ(kFLEXSPI_Command_DDR, kFLEXSPI_8PAD, 0x00,
|
|
kFLEXSPI_Command_DDR, kFLEXSPI_8PAD, 0x00),
|
|
[4 * ERASE_CHIP + 1] =
|
|
FLEXSPI_LUT_SEQ(kFLEXSPI_Command_DDR, kFLEXSPI_8PAD, 0x00,
|
|
kFLEXSPI_Command_DDR, kFLEXSPI_8PAD, 0xAA),
|
|
[4 * ERASE_CHIP + 2] =
|
|
FLEXSPI_LUT_SEQ(kFLEXSPI_Command_DDR, kFLEXSPI_8PAD, 0x00,
|
|
kFLEXSPI_Command_DDR, kFLEXSPI_8PAD, 0x05),
|
|
[4 * ERASE_CHIP + 3] =
|
|
FLEXSPI_LUT_SEQ(kFLEXSPI_Command_DDR, kFLEXSPI_8PAD, 0x00,
|
|
kFLEXSPI_Command_DDR, kFLEXSPI_8PAD, 0x80),
|
|
/* 1 */
|
|
[4 * ERASE_CHIP + 4] =
|
|
FLEXSPI_LUT_SEQ(kFLEXSPI_Command_DDR, kFLEXSPI_8PAD, 0x00,
|
|
kFLEXSPI_Command_DDR, kFLEXSPI_8PAD, 0x00),
|
|
[4 * ERASE_CHIP + 5] =
|
|
FLEXSPI_LUT_SEQ(kFLEXSPI_Command_DDR, kFLEXSPI_8PAD, 0x00,
|
|
kFLEXSPI_Command_DDR, kFLEXSPI_8PAD, 0xAA),
|
|
[4 * ERASE_CHIP + 6] =
|
|
FLEXSPI_LUT_SEQ(kFLEXSPI_Command_DDR, kFLEXSPI_8PAD, 0x00,
|
|
kFLEXSPI_Command_DDR, kFLEXSPI_8PAD, 0x05),
|
|
[4 * ERASE_CHIP + 7] =
|
|
FLEXSPI_LUT_SEQ(kFLEXSPI_Command_DDR, kFLEXSPI_8PAD, 0x00,
|
|
kFLEXSPI_Command_DDR, kFLEXSPI_8PAD, 0xAA),
|
|
/* 2 */
|
|
[4 * ERASE_CHIP + 8] =
|
|
FLEXSPI_LUT_SEQ(kFLEXSPI_Command_DDR, kFLEXSPI_8PAD, 0x00,
|
|
kFLEXSPI_Command_DDR, kFLEXSPI_8PAD, 0x00),
|
|
[4 * ERASE_CHIP + 9] =
|
|
FLEXSPI_LUT_SEQ(kFLEXSPI_Command_DDR, kFLEXSPI_8PAD, 0x00,
|
|
kFLEXSPI_Command_DDR, kFLEXSPI_8PAD, 0x55),
|
|
[4 * ERASE_CHIP + 10] =
|
|
FLEXSPI_LUT_SEQ(kFLEXSPI_Command_DDR, kFLEXSPI_8PAD, 0x00,
|
|
kFLEXSPI_Command_DDR, kFLEXSPI_8PAD, 0x02),
|
|
[4 * ERASE_CHIP + 11] =
|
|
FLEXSPI_LUT_SEQ(kFLEXSPI_Command_DDR, kFLEXSPI_8PAD, 0x00,
|
|
kFLEXSPI_Command_DDR, kFLEXSPI_8PAD, 0x55),
|
|
/* 3 */
|
|
[4 * ERASE_CHIP + 12] =
|
|
FLEXSPI_LUT_SEQ(kFLEXSPI_Command_DDR, kFLEXSPI_8PAD, 0x00,
|
|
kFLEXSPI_Command_DDR, kFLEXSPI_8PAD, 0x00),
|
|
[4 * ERASE_CHIP + 13] =
|
|
FLEXSPI_LUT_SEQ(kFLEXSPI_Command_DDR, kFLEXSPI_8PAD, 0x00,
|
|
kFLEXSPI_Command_DDR, kFLEXSPI_8PAD, 0xAA),
|
|
[4 * ERASE_CHIP + 14] =
|
|
FLEXSPI_LUT_SEQ(kFLEXSPI_Command_DDR, kFLEXSPI_8PAD, 0x00,
|
|
kFLEXSPI_Command_DDR, kFLEXSPI_8PAD, 0x05),
|
|
[4 * ERASE_CHIP + 15] =
|
|
FLEXSPI_LUT_SEQ(kFLEXSPI_Command_DDR, kFLEXSPI_8PAD, 0x00,
|
|
kFLEXSPI_Command_DDR, kFLEXSPI_8PAD, 0x10),
|
|
};
|
|
|
|
|
|
struct flash_flexspi_hyperflash_config {
|
|
const struct device *controller;
|
|
};
|
|
|
|
/* Device variables used in critical sections should be in this structure */
|
|
struct flash_flexspi_hyperflash_data {
|
|
struct device controller;
|
|
flexspi_device_config_t config;
|
|
flexspi_port_t port;
|
|
struct flash_pages_layout layout;
|
|
struct flash_parameters flash_parameters;
|
|
};
|
|
|
|
/* Make sure all parameters accessed by this function are in RAM when XIP is enabled. */
|
|
static int flash_flexspi_hyperflash_wait_bus_busy(const struct flash_flexspi_hyperflash_data *data)
|
|
{
|
|
flexspi_transfer_t transfer;
|
|
int ret;
|
|
bool is_busy;
|
|
uint32_t read_value;
|
|
|
|
transfer.deviceAddress = 0;
|
|
transfer.port = data->port;
|
|
transfer.cmdType = kFLEXSPI_Read;
|
|
transfer.SeqNumber = 2;
|
|
transfer.seqIndex = READ_STATUS;
|
|
transfer.data = &read_value;
|
|
transfer.dataSize = 2;
|
|
|
|
do {
|
|
ret = memc_flexspi_transfer(&data->controller, &transfer);
|
|
if (ret != 0) {
|
|
return ret;
|
|
}
|
|
|
|
is_busy = !(read_value & 0x8000);
|
|
|
|
if (read_value & 0x3200) {
|
|
ret = -EINVAL;
|
|
break;
|
|
}
|
|
} while (is_busy);
|
|
|
|
return ret;
|
|
}
|
|
|
|
/* Make sure all parameters accessed by this function are in RAM when XIP is enabled. */
|
|
static int flash_flexspi_hyperflash_write_enable(const struct flash_flexspi_hyperflash_data *data,
|
|
uint32_t address)
|
|
{
|
|
flexspi_transfer_t transfer;
|
|
int ret;
|
|
|
|
transfer.deviceAddress = address;
|
|
transfer.port = data->port;
|
|
transfer.cmdType = kFLEXSPI_Command;
|
|
transfer.SeqNumber = 2;
|
|
transfer.seqIndex = WRITE_ENABLE;
|
|
|
|
ret = memc_flexspi_transfer(&data->controller, &transfer);
|
|
|
|
return ret;
|
|
}
|
|
|
|
static int flash_flexspi_hyperflash_check_vendor_id(const struct device *dev)
|
|
{
|
|
struct flash_flexspi_hyperflash_data *data = dev->data;
|
|
uint8_t writebuf[4] = {0x00, 0x98};
|
|
uint32_t buffer[2];
|
|
int ret;
|
|
flexspi_transfer_t transfer;
|
|
|
|
transfer.deviceAddress = (0x555 * 2);
|
|
transfer.port = data->port;
|
|
transfer.cmdType = kFLEXSPI_Write;
|
|
transfer.SeqNumber = 1;
|
|
transfer.seqIndex = WRITE_DATA;
|
|
transfer.data = (uint32_t *)writebuf;
|
|
transfer.dataSize = 2;
|
|
|
|
LOG_DBG("Reading id");
|
|
|
|
ret = memc_flexspi_transfer(&data->controller, &transfer);
|
|
if (ret != 0) {
|
|
LOG_ERR("failed to CFI");
|
|
return ret;
|
|
}
|
|
|
|
transfer.deviceAddress = (0x10 * 2);
|
|
transfer.port = data->port;
|
|
transfer.cmdType = kFLEXSPI_Read;
|
|
transfer.SeqNumber = 1;
|
|
transfer.seqIndex = READ_DATA;
|
|
transfer.data = buffer;
|
|
transfer.dataSize = 8;
|
|
|
|
ret = memc_flexspi_transfer(&data->controller, &transfer);
|
|
if (ret != 0) {
|
|
LOG_ERR("failed to read id");
|
|
return ret;
|
|
}
|
|
buffer[1] &= 0xFFFF;
|
|
/* Check that the data read out is unicode "QRY" in big-endian order */
|
|
if ((buffer[0] != 0x52005100) || (buffer[1] != 0x5900)) {
|
|
LOG_ERR("data read out is wrong!");
|
|
return -EINVAL;
|
|
}
|
|
|
|
writebuf[1] = 0xF0;
|
|
transfer.deviceAddress = 0;
|
|
transfer.port = data->port;
|
|
transfer.cmdType = kFLEXSPI_Write;
|
|
transfer.SeqNumber = 1;
|
|
transfer.seqIndex = WRITE_DATA;
|
|
transfer.data = (uint32_t *)writebuf;
|
|
transfer.dataSize = 2;
|
|
|
|
ret = memc_flexspi_transfer(&data->controller, &transfer);
|
|
if (ret != 0) {
|
|
LOG_ERR("failed to exit");
|
|
return ret;
|
|
}
|
|
|
|
memc_flexspi_reset(&data->controller);
|
|
|
|
return ret;
|
|
}
|
|
|
|
/* Make sure all parameters accessed by this function are in RAM when XIP is enabled. */
|
|
static int flash_flexspi_hyperflash_page_program(const struct flash_flexspi_hyperflash_data *data,
|
|
off_t offset, const void *buffer, size_t len)
|
|
{
|
|
flexspi_transfer_t transfer = {
|
|
.deviceAddress = offset,
|
|
.port = data->port,
|
|
.cmdType = kFLEXSPI_Write,
|
|
.SeqNumber = 2,
|
|
.seqIndex = PAGE_PROGRAM,
|
|
.data = (uint32_t *)buffer,
|
|
.dataSize = len,
|
|
};
|
|
|
|
LOG_DBG("Page programming %d bytes to 0x%08lx", len, offset);
|
|
|
|
return memc_flexspi_transfer(&data->controller, &transfer);
|
|
}
|
|
|
|
static int flash_flexspi_hyperflash_read(const struct device *dev, off_t offset,
|
|
void *buffer, size_t len)
|
|
{
|
|
struct flash_flexspi_hyperflash_data *data = dev->data;
|
|
|
|
if (len == 0) {
|
|
return 0;
|
|
}
|
|
|
|
if (!buffer) {
|
|
return -EINVAL;
|
|
}
|
|
|
|
uint8_t *src = memc_flexspi_get_ahb_address(&data->controller,
|
|
data->port,
|
|
offset);
|
|
if (!src) {
|
|
return -EINVAL;
|
|
}
|
|
|
|
memcpy(buffer, src, len);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int flash_flexspi_hyperflash_write(const struct device *dev, off_t offset,
|
|
const void *buffer, size_t len)
|
|
{
|
|
struct flash_flexspi_hyperflash_data *data = dev->data;
|
|
size_t size = len;
|
|
uint8_t *src = (uint8_t *)buffer;
|
|
unsigned int key = 0;
|
|
int i, j;
|
|
int ret = -1;
|
|
|
|
uint8_t *dst = memc_flexspi_get_ahb_address(&data->controller,
|
|
data->port,
|
|
offset);
|
|
if (!dst) {
|
|
return -EINVAL;
|
|
}
|
|
|
|
if (memc_flexspi_is_running_xip(&data->controller)) {
|
|
/*
|
|
* ==== ENTER CRITICAL SECTION ====
|
|
* No flash access should be performed in critical section. All
|
|
* code and data accessed must reside in ram.
|
|
*/
|
|
key = irq_lock();
|
|
memc_flexspi_wait_bus_idle(&data->controller);
|
|
}
|
|
|
|
/* Update clock to a freq which usually lower than normal working freq. */
|
|
if (memc_flexspi_update_clock(&data->controller, &data->config,
|
|
data->port, FREQ_FOR_HYPERFLASH_WRITE)) {
|
|
ret = -ENOTSUP;
|
|
goto __exit;
|
|
}
|
|
|
|
while (len) {
|
|
/* Writing between two page sizes crashes the platform so we
|
|
* have to write the part that fits in the first page and then
|
|
* update the offset.
|
|
*/
|
|
i = MIN(SPI_HYPERFLASH_PAGE_SIZE - (offset %
|
|
SPI_HYPERFLASH_PAGE_SIZE), len);
|
|
#ifdef CONFIG_FLASH_MCUX_FLEXSPI_HYPERFLASH_WRITE_BUFFER
|
|
for (j = 0; j < i; j++) {
|
|
hyperflash_write_buf[j] = src[j];
|
|
}
|
|
#endif
|
|
ret = flash_flexspi_hyperflash_write_enable(data, offset);
|
|
if (ret != 0) {
|
|
LOG_ERR("failed to enable write");
|
|
break;
|
|
}
|
|
#ifdef CONFIG_FLASH_MCUX_FLEXSPI_HYPERFLASH_WRITE_BUFFER
|
|
ret = flash_flexspi_hyperflash_page_program(data, offset,
|
|
hyperflash_write_buf, i);
|
|
#else
|
|
ret = flash_flexspi_hyperflash_page_program(data, offset, src, i);
|
|
#endif
|
|
if (ret != 0) {
|
|
LOG_ERR("failed to write");
|
|
break;
|
|
}
|
|
|
|
ret = flash_flexspi_hyperflash_wait_bus_busy(data);
|
|
if (ret != 0) {
|
|
LOG_ERR("failed to wait bus busy");
|
|
break;
|
|
}
|
|
|
|
/* Do software reset. */
|
|
memc_flexspi_reset(&data->controller);
|
|
src += i;
|
|
offset += i;
|
|
len -= i;
|
|
}
|
|
|
|
/* Clock FlexSPI at max freq flash support. */
|
|
if (memc_flexspi_update_clock(&data->controller, &data->config,
|
|
data->port, data->config.flexspiRootClk)) {
|
|
ret = -ENOTSUP;
|
|
goto __exit;
|
|
}
|
|
|
|
#ifdef CONFIG_HAS_MCUX_CACHE
|
|
DCACHE_InvalidateByRange((uint32_t) dst, size);
|
|
#endif
|
|
|
|
__exit:
|
|
if (memc_flexspi_is_running_xip(&data->controller)) {
|
|
/* ==== EXIT CRITICAL SECTION ==== */
|
|
irq_unlock(key);
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
static int flash_flexspi_hyperflash_erase(const struct device *dev, off_t offset, size_t size)
|
|
{
|
|
struct flash_flexspi_hyperflash_data *data = dev->data;
|
|
flexspi_transfer_t transfer;
|
|
int ret = -1;
|
|
int i;
|
|
unsigned int key = 0;
|
|
int num_sectors = size / SPI_HYPERFLASH_SECTOR_SIZE;
|
|
uint8_t *dst = memc_flexspi_get_ahb_address(&data->controller,
|
|
data->port,
|
|
offset);
|
|
|
|
if (!dst) {
|
|
return -EINVAL;
|
|
}
|
|
|
|
if (offset % SPI_HYPERFLASH_SECTOR_SIZE) {
|
|
LOG_ERR("Invalid offset");
|
|
return -EINVAL;
|
|
}
|
|
|
|
if (size % SPI_HYPERFLASH_SECTOR_SIZE) {
|
|
LOG_ERR("Invalid offset");
|
|
return -EINVAL;
|
|
}
|
|
|
|
if (memc_flexspi_is_running_xip(&data->controller)) {
|
|
/*
|
|
* ==== ENTER CRITICAL SECTION ====
|
|
* No flash access should be performed in critical section. All
|
|
* code and data accessed must reside in ram.
|
|
*/
|
|
key = irq_lock();
|
|
memc_flexspi_wait_bus_idle(&data->controller);
|
|
}
|
|
|
|
for (i = 0; i < num_sectors; i++) {
|
|
ret = flash_flexspi_hyperflash_write_enable(data, offset);
|
|
if (ret != 0) {
|
|
LOG_ERR("failed to write_enable");
|
|
break;
|
|
}
|
|
|
|
LOG_DBG("Erasing sector at 0x%08lx", offset);
|
|
|
|
transfer.deviceAddress = offset;
|
|
transfer.port = data->port;
|
|
transfer.cmdType = kFLEXSPI_Command;
|
|
transfer.SeqNumber = 4;
|
|
transfer.seqIndex = ERASE_SECTOR;
|
|
|
|
ret = memc_flexspi_transfer(&data->controller, &transfer);
|
|
if (ret != 0) {
|
|
LOG_ERR("failed to erase");
|
|
break;
|
|
}
|
|
|
|
/* wait bus busy */
|
|
ret = flash_flexspi_hyperflash_wait_bus_busy(data);
|
|
if (ret != 0) {
|
|
LOG_ERR("failed to wait bus busy");
|
|
break;
|
|
}
|
|
|
|
/* Do software reset. */
|
|
memc_flexspi_reset(&data->controller);
|
|
|
|
offset += SPI_HYPERFLASH_SECTOR_SIZE;
|
|
}
|
|
|
|
#ifdef CONFIG_HAS_MCUX_CACHE
|
|
DCACHE_InvalidateByRange((uint32_t) dst, size);
|
|
#endif
|
|
|
|
if (memc_flexspi_is_running_xip(&data->controller)) {
|
|
/* ==== EXIT CRITICAL SECTION ==== */
|
|
irq_unlock(key);
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
static const struct flash_parameters *flash_flexspi_hyperflash_get_parameters(
|
|
const struct device *dev)
|
|
{
|
|
struct flash_flexspi_hyperflash_data *data = dev->data;
|
|
|
|
return &data->flash_parameters;
|
|
}
|
|
|
|
|
|
static void flash_flexspi_hyperflash_pages_layout(const struct device *dev,
|
|
const struct flash_pages_layout **layout,
|
|
size_t *layout_size)
|
|
{
|
|
struct flash_flexspi_hyperflash_data *data = dev->data;
|
|
|
|
*layout = &data->layout;
|
|
*layout_size = 1;
|
|
}
|
|
|
|
static int flash_flexspi_hyperflash_init(const struct device *dev)
|
|
{
|
|
const struct flash_flexspi_hyperflash_config *config = dev->config;
|
|
struct flash_flexspi_hyperflash_data *data = dev->data;
|
|
|
|
/* Since the controller variable may be used in critical sections,
|
|
* copy the device pointer into a variable stored in RAM
|
|
*/
|
|
memcpy(&data->controller, config->controller, sizeof(struct device));
|
|
|
|
if (!device_is_ready(&data->controller)) {
|
|
LOG_ERR("Controller device not ready");
|
|
return -ENODEV;
|
|
}
|
|
|
|
memc_flexspi_wait_bus_idle(&data->controller);
|
|
|
|
if (memc_flexspi_set_device_config(&data->controller, &data->config,
|
|
(const uint32_t *)flash_flexspi_hyperflash_lut,
|
|
sizeof(flash_flexspi_hyperflash_lut) / MEMC_FLEXSPI_CMD_SIZE,
|
|
data->port)) {
|
|
LOG_ERR("Could not set device configuration");
|
|
return -EINVAL;
|
|
}
|
|
|
|
memc_flexspi_reset(&data->controller);
|
|
|
|
if (flash_flexspi_hyperflash_check_vendor_id(dev)) {
|
|
LOG_ERR("Could not read vendor id");
|
|
return -EIO;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static DEVICE_API(flash, flash_flexspi_hyperflash_api) = {
|
|
.read = flash_flexspi_hyperflash_read,
|
|
.write = flash_flexspi_hyperflash_write,
|
|
.erase = flash_flexspi_hyperflash_erase,
|
|
.get_parameters = flash_flexspi_hyperflash_get_parameters,
|
|
#if defined(CONFIG_FLASH_PAGE_LAYOUT)
|
|
.page_layout = flash_flexspi_hyperflash_pages_layout,
|
|
#endif
|
|
};
|
|
|
|
#define CONCAT3(x, y, z) x ## y ## z
|
|
|
|
#define CS_INTERVAL_UNIT(unit) \
|
|
CONCAT3(kFLEXSPI_CsIntervalUnit, unit, SckCycle)
|
|
|
|
#define AHB_WRITE_WAIT_UNIT(unit) \
|
|
CONCAT3(kFLEXSPI_AhbWriteWaitUnit, unit, AhbCycle)
|
|
|
|
#define FLASH_FLEXSPI_DEVICE_CONFIG(n) \
|
|
{ \
|
|
.flexspiRootClk = DT_INST_PROP(n, spi_max_frequency) * \
|
|
MCUX_FLEXSPI_HYPERFLASH_DDR_SDR_MODE, \
|
|
.flashSize = DT_INST_PROP(n, size) / 8 / KB(1), \
|
|
.CSIntervalUnit = \
|
|
CS_INTERVAL_UNIT( \
|
|
DT_INST_PROP(n, cs_interval_unit)), \
|
|
.CSInterval = DT_INST_PROP(n, cs_interval), \
|
|
.CSHoldTime = DT_INST_PROP(n, cs_hold_time), \
|
|
.CSSetupTime = DT_INST_PROP(n, cs_setup_time), \
|
|
.dataValidTime = DT_INST_PROP(n, data_valid_time), \
|
|
.columnspace = DT_INST_PROP(n, column_space), \
|
|
.enableWordAddress = DT_INST_PROP(n, word_addressable), \
|
|
.AWRSeqIndex = WRITE_DATA, \
|
|
.AWRSeqNumber = 1, \
|
|
.ARDSeqIndex = READ_DATA, \
|
|
.ARDSeqNumber = 1, \
|
|
.AHBWriteWaitUnit = \
|
|
AHB_WRITE_WAIT_UNIT( \
|
|
DT_INST_PROP(n, ahb_write_wait_unit)), \
|
|
.AHBWriteWaitInterval = \
|
|
DT_INST_PROP(n, ahb_write_wait_interval), \
|
|
} \
|
|
|
|
#define FLASH_FLEXSPI_HYPERFLASH(n) \
|
|
static struct flash_flexspi_hyperflash_config \
|
|
flash_flexspi_hyperflash_config_##n = { \
|
|
.controller = DEVICE_DT_GET(DT_INST_BUS(n)), \
|
|
}; \
|
|
static struct flash_flexspi_hyperflash_data \
|
|
flash_flexspi_hyperflash_data_##n = { \
|
|
.config = FLASH_FLEXSPI_DEVICE_CONFIG(n), \
|
|
.port = DT_INST_REG_ADDR(n), \
|
|
.layout = { \
|
|
.pages_count = DT_INST_PROP(n, size) / 8 \
|
|
/ SPI_HYPERFLASH_SECTOR_SIZE, \
|
|
.pages_size = SPI_HYPERFLASH_SECTOR_SIZE, \
|
|
}, \
|
|
.flash_parameters = { \
|
|
.write_block_size = DT_INST_PROP(n, write_block_size), \
|
|
.erase_value = HYPERFLASH_ERASE_VALUE, \
|
|
}, \
|
|
}; \
|
|
\
|
|
DEVICE_DT_INST_DEFINE(n, \
|
|
flash_flexspi_hyperflash_init, \
|
|
NULL, \
|
|
&flash_flexspi_hyperflash_data_##n, \
|
|
&flash_flexspi_hyperflash_config_##n, \
|
|
POST_KERNEL, \
|
|
CONFIG_FLASH_INIT_PRIORITY, \
|
|
&flash_flexspi_hyperflash_api);
|
|
|
|
DT_INST_FOREACH_STATUS_OKAY(FLASH_FLEXSPI_HYPERFLASH)
|