#include <string.h>
#include "general.h"
#include "target.h"
#include "target_internal.h"
#include "cortexm.h"
#include "spi.h"
#include "sfdp.h"
#define IMXRT_SRC_BASE UINT32_C(0x400f8000)
#define IMXRT_SRC_BOOT_MODE1 (IMXRT_SRC_BASE + 0x004U)
#define IMXRT_SRC_BOOT_MODE2 (IMXRT_SRC_BASE + 0x01cU)
#define IMXRT_OCRAM1_BASE UINT32_C(0x20280000)
#define IMXRT_OCRAM1_SIZE 0x00080000U
#define IMXRT_OCRAM2_BASE UINT32_C(0x20200000)
#define IMXRT_OCRAM2_SIZE 0x00080000U
#define IMXRT_FLEXSPI_BASE UINT32_C(0x60000000)
#define IMXRT_MPU_BASE UINT32_C(0xe000ed90)
#define IMXRT_MPU_CTRL (IMXRT_MPU_BASE + 0x04U)
#define IMXRT_CCM_ANALOG_BASE UINT32_C(0x400d8000)
#define IMXRT_CCM_ANALOG_PLL3_PFD (IMXRT_CCM_ANALOG_BASE + 0x0f0U)
#define IMXRT_CCM_ANALOG_PLL_PFD0_FRAC_MASK 0xffffffc0
#define IMXRT_CCM_BASE UINT32_C(0x400fc000)
#define IMXRT_CCM_CSCM1 (IMXRT_CCM_BASE + 0x01cU)
#define IMXRT_CCM_CCG6 (IMXRT_CCM_BASE + 0x080U)
#define IMXRT_CCM_CSCM1_FLEXSPI_CLK_SEL_MASK 0xfc7fffffU
#define IMXRT_CCM_CSCM1_FLEXSPI_CLK_SEL_PLL3_PFD0 0x03800000U
#define IMXRT_CCM_CCG6_FLEXSPI_CLK_MASK 0xfffff3ffU
#define IMXRT_CCM_CCG6_FLEXSPI_CLK_ENABLE 0x00000c00U
#define IMXRT_FLEXSPI1_BASE UINT32_C(0x402a8000)
#define IMXRT_FLEXSPI1_MOD_CTRL0 (IMXRT_FLEXSPI1_BASE + 0x000U)
#define IMXRT_FLEXSPI1_INT (IMXRT_FLEXSPI1_BASE + 0x014U)
#define IMXRT_FLEXSPI1_LUT_KEY (IMXRT_FLEXSPI1_BASE + 0x018U)
#define IMXRT_FLEXSPI1_LUT_CTRL (IMXRT_FLEXSPI1_BASE + 0x01cU)
#define IMXRT_FLEXSPI1_CTRL0 (IMXRT_FLEXSPI1_BASE + 0x060U)
#define IMXRT_FLEXSPI1_CTRL1 (IMXRT_FLEXSPI1_BASE + 0x070U)
#define IMXRT_FLEXSPI1_CTRL2 (IMXRT_FLEXSPI1_BASE + 0x080U)
#define IMXRT_FLEXSPI1_PRG_CTRL0 (IMXRT_FLEXSPI1_BASE + 0x0a0U)
#define IMXRT_FLEXSPI1_PRG_CTRL1 (IMXRT_FLEXSPI1_BASE + 0x0a4U)
#define IMXRT_FLEXSPI1_PRG_CMD (IMXRT_FLEXSPI1_BASE + 0x0b0U)
#define IMXRT_FLEXSPI1_PRG_READ_FIFO_CTRL (IMXRT_FLEXSPI1_BASE + 0x0b8U)
#define IMXRT_FLEXSPI1_PRG_WRITE_FIFO_CTRL (IMXRT_FLEXSPI1_BASE + 0x0bcU)
#define IMXRT_FLEXSPI1_STAT1 (IMXRT_FLEXSPI1_BASE + 0x0e4U)
#define IMXRT_FLEXSPI1_PRG_WRITE_FIFO_STATUS (IMXRT_FLEXSPI1_BASE + 0x0f4U)
#define IMXRT_FLEXSPI1_PRG_READ_FIFO (IMXRT_FLEXSPI1_BASE + 0x100U)
#define IMXRT_FLEXSPI1_PRG_WRITE_FIFO (IMXRT_FLEXSPI1_BASE + 0x180U)
#define IMXRT_FLEXSPI1_LUT_BASE (IMXRT_FLEXSPI1_BASE + 0x200U)
#define IMXRT_FLEXSPI1_MOD_CTRL0_SUSPEND 0x00000002U
#define IMXRT_FLEXSPI1_INT_PRG_CMD_DONE 0x00000001U
#define IMXRT_FLEXSPI1_INT_CMD_ERR 0x00000008U
#define IMXRT_FLEXSPI1_INT_READ_FIFO_FULL 0x00000020U
#define IMXRT_FLEXSPI1_INT_WRITE_FIFO_EMPTY 0x00000040U
#define IMXRT_FLEXSPI1_LUT_KEY_VALUE 0x5af05af0U
#define IMXRT_FLEXSPI1_LUT_CTRL_LOCK 0x00000001U
#define IMXRT_FLEXSPI1_LUT_CTRL_UNLOCK 0x00000002U
#define IMXRT_FLEXSPI1_CTRL1_CAS_MASK 0x00007800U
#define IMXRT_FLEXSPI1_CTRL1_CAS_SHIFT 11U
#define IMXRT_FLEXSPI1_PRG_LENGTH(x) ((x)&0x0000ffffU)
#define IMXRT_FLEXSPI1_PRG_SEQ_INDEX(x) (((x)&0xfU) << 16U)
#define IMXRT_FLEXSPI1_PRG_RUN 0x00000001U
#define IMXRT_FLEXSPI1_PRG_FIFO_CTRL_CLR 0x00000001U
#define IMXRT_FLEXSPI1_PRG_FIFO_CTRL_WATERMARK(x) ((((((x) + 7U) >> 3U) - 1U) & 0xfU) << 2U)
#define IMXRT_FLEXSPI1_PRG_WRITE_FIFO_STATUS_FILL 0x000000ffU
#define IMXRT_FLEXSI_SLOT_OFFSET(x) ((x)*16U)
#define IMXRT_FLEXSPI_LUT_OPCODE(x) (((x)&0x3fU) << 2U)
#define IMXRT_FLEXSPI_LUT_MODE_SERIAL 0x0U
#define IMXRT_FLEXSPI_LUT_MODE_DUAL 0x1U
#define IMXRT_FLEXSPI_LUT_MODE_QUAD 0x2U
#define IMXRT_FLEXSPI_LUT_MODE_OCT 0x3U
#define IMXRT_FLEXSPI_LUT_OP_STOP 0x00U
#define IMXRT_FLEXSPI_LUT_OP_COMMAND 0x01U
#define IMXRT_FLEXSPI_LUT_OP_CADDR 0x03U
#define IMXRT_FLEXSPI_LUT_OP_RADDR 0x02U
#define IMXRT_FLEXSPI_LUT_OP_DUMMY_CYCLES 0x0cU
#define IMXRT_FLEXSPI_LUT_OP_READ 0x09U
#define IMXRT_FLEXSPI_LUT_OP_WRITE 0x08U
typedef enum imxrt_boot_src {
BOOT_FLEX_SPI,
boot_sd_card,
boot_emmc,
boot_slc_nand,
boot_parallel_nor,
} imxrt_boot_src_e;
typedef struct imxrt_flexspi_lut_insn {
uint8_t value;
uint8_t opcode_mode;
} imxrt_flexspi_lut_insn_s;
typedef struct imxrt_priv {
imxrt_boot_src_e boot_source;
uint32_t mpu_state;
uint32_t flexspi_lut_state;
uint16_t flexspi_cached_commands[4];
imxrt_flexspi_lut_insn_s flexspi_prg_seq_state[4][8];
} imxrt_priv_s;
static imxrt_boot_src_e imxrt_boot_source(uint32_t boot_cfg);
static bool imxrt_enter_flash_mode(target_s *target);
static bool imxrt_exit_flash_mode(target_s *target);
static uint8_t imxrt_spi_build_insn_sequence(target_s *target, uint16_t command, uint16_t length);
static void imxrt_spi_read(target_s *target, uint16_t command, target_addr_t address, void *buffer, size_t length);
static void imxrt_spi_write(
target_s *target, uint16_t command, target_addr_t address, const void *buffer, size_t length);
static void imxrt_spi_run_command(target_s *target, uint16_t command, target_addr_t address);
bool imxrt_probe(target_s *const target)
{
if (target->part_id != 0x88cU)
return false;
imxrt_priv_s *priv = calloc(1, sizeof(imxrt_priv_s));
if (!priv) {
DEBUG_WARN("calloc: failed in %s\n", __func__);
return false;
}
target->target_storage = priv;
target->target_options |= CORTEXM_TOPT_INHIBIT_NRST;
target->driver = "i.MXRT10xx";
#if defined(ENABLE_DEBUG) && (PC_HOSTED == 1 || defined(ESP_LOGD))
const uint8_t boot_mode = (target_mem_read32(target, IMXRT_SRC_BOOT_MODE2) >> 24U) & 3U;
#endif
DEBUG_TARGET("i.MXRT boot mode is %x\n", boot_mode);
const uint32_t boot_cfg = target_mem_read32(target, IMXRT_SRC_BOOT_MODE1);
DEBUG_TARGET("i.MXRT boot config is %08" PRIx32 "\n", boot_cfg);
priv->boot_source = imxrt_boot_source(boot_cfg);
switch (priv->boot_source) {
case BOOT_FLEX_SPI:
DEBUG_TARGET("-> booting from SPI Flash (FlexSPI)\n");
break;
case boot_sd_card:
DEBUG_TARGET("-> booting from SD Card\n");
break;
case boot_emmc:
DEBUG_TARGET("-> booting from eMMC via uSDHC\n");
break;
case boot_slc_nand:
DEBUG_TARGET("-> booting from SLC NAND via SEMC\n");
break;
case boot_parallel_nor:
DEBUG_TARGET("-> booting from parallel Flash (NOR) via SEMC\n");
break;
}
target_add_ram(target, IMXRT_OCRAM1_BASE, IMXRT_OCRAM1_SIZE);
target_add_ram(target, IMXRT_OCRAM2_BASE, IMXRT_OCRAM2_SIZE);
if (priv->boot_source == BOOT_FLEX_SPI) {
imxrt_enter_flash_mode(target);
spi_flash_id_s flash_id;
imxrt_spi_read(target, SPI_FLASH_CMD_READ_JEDEC_ID, 0, &flash_id, sizeof(flash_id));
target->mass_erase = bmp_spi_mass_erase;
target->enter_flash_mode = imxrt_enter_flash_mode;
target->exit_flash_mode = imxrt_exit_flash_mode;
if (flash_id.manufacturer != 0xffU && flash_id.type != 0xffU && flash_id.capacity != 0xffU) {
const uint32_t capacity = 1U << flash_id.capacity;
DEBUG_INFO("SPI Flash: mfr = %02x, type = %02x, capacity = %08" PRIx32 "\n", flash_id.manufacturer,
flash_id.type, capacity);
bmp_spi_add_flash(
target, IMXRT_FLEXSPI_BASE, capacity, imxrt_spi_read, imxrt_spi_write, imxrt_spi_run_command);
} else
DEBUG_INFO("Flash identification failed\n");
imxrt_exit_flash_mode(target);
}
return true;
}
static imxrt_boot_src_e imxrt_boot_source(const uint32_t boot_cfg)
{
const uint8_t boot_src = boot_cfg & 0xf0U;
if (boot_src == 0x00U)
return BOOT_FLEX_SPI;
if ((boot_src & 0xc0U) == 0x40U)
return boot_sd_card;
if ((boot_src & 0xc0U) == 0x80U)
return boot_emmc;
if ((boot_src & 0xe0U) == 0x20U)
return boot_slc_nand;
if (boot_src == 0x10U)
return boot_parallel_nor;
return BOOT_FLEX_SPI;
}
static bool imxrt_enter_flash_mode(target_s *const target)
{
imxrt_priv_s *const priv = (imxrt_priv_s *)target->target_storage;
priv->mpu_state = target_mem_read32(target, IMXRT_MPU_CTRL);
target_mem_write32(target, IMXRT_MPU_CTRL, 0);
target_mem_write32(target, IMXRT_FLEXSPI1_MOD_CTRL0,
target_mem_read32(target, IMXRT_FLEXSPI1_MOD_CTRL0) | IMXRT_FLEXSPI1_MOD_CTRL0_SUSPEND);
target_mem_write32(
target, IMXRT_CCM_CCG6, target_mem_read32(target, IMXRT_CCM_CCG6) & IMXRT_CCM_CCG6_FLEXSPI_CLK_MASK);
target_mem_write32(target, IMXRT_CCM_CSCM1,
(target_mem_read32(target, IMXRT_CCM_CSCM1) & IMXRT_CCM_CSCM1_FLEXSPI_CLK_SEL_MASK) |
IMXRT_CCM_CSCM1_FLEXSPI_CLK_SEL_PLL3_PFD0);
target_mem_write32(target, IMXRT_CCM_ANALOG_PLL3_PFD,
(target_mem_read32(target, IMXRT_CCM_ANALOG_PLL3_PFD) & IMXRT_CCM_ANALOG_PLL_PFD0_FRAC_MASK) | 0x16U);
target_mem_write32(
target, IMXRT_CCM_CCG6, target_mem_read32(target, IMXRT_CCM_CCG6) | IMXRT_CCM_CCG6_FLEXSPI_CLK_ENABLE);
target_mem_write32(target, IMXRT_FLEXSPI1_MOD_CTRL0,
target_mem_read32(target, IMXRT_FLEXSPI1_MOD_CTRL0) & ~IMXRT_FLEXSPI1_MOD_CTRL0_SUSPEND);
target_mem_write32(target, IMXRT_FLEXSPI1_INT, target_mem_read32(target, IMXRT_FLEXSPI1_INT));
target_mem_write32(target, IMXRT_FLEXSPI1_PRG_READ_FIFO_CTRL,
IMXRT_FLEXSPI1_PRG_FIFO_CTRL_WATERMARK(128) | IMXRT_FLEXSPI1_PRG_FIFO_CTRL_CLR);
target_mem_write32(target, IMXRT_FLEXSPI1_PRG_WRITE_FIFO_CTRL,
IMXRT_FLEXSPI1_PRG_FIFO_CTRL_WATERMARK(128) | IMXRT_FLEXSPI1_PRG_FIFO_CTRL_CLR);
priv->flexspi_lut_state = target_mem_read32(target, IMXRT_FLEXSPI1_LUT_CTRL);
if (priv->flexspi_lut_state != IMXRT_FLEXSPI1_LUT_CTRL_UNLOCK) {
target_mem_write32(target, IMXRT_FLEXSPI1_LUT_KEY, IMXRT_FLEXSPI1_LUT_KEY_VALUE);
target_mem_write32(target, IMXRT_FLEXSPI1_LUT_CTRL, IMXRT_FLEXSPI1_LUT_CTRL_UNLOCK);
}
target_mem_read(target, priv->flexspi_prg_seq_state, IMXRT_FLEXSPI1_LUT_BASE, sizeof(priv->flexspi_prg_seq_state));
memset(priv->flexspi_cached_commands, 0, sizeof(priv->flexspi_cached_commands));
return true;
}
static bool imxrt_exit_flash_mode(target_s *const target)
{
const imxrt_priv_s *const priv = (imxrt_priv_s *)target->target_storage;
target_mem_write(target, IMXRT_FLEXSPI1_LUT_BASE, priv->flexspi_prg_seq_state, sizeof(priv->flexspi_prg_seq_state));
if (priv->flexspi_lut_state != IMXRT_FLEXSPI1_LUT_CTRL_UNLOCK) {
target_mem_write32(target, IMXRT_FLEXSPI1_LUT_KEY, IMXRT_FLEXSPI1_LUT_KEY_VALUE);
target_mem_write32(target, IMXRT_FLEXSPI1_LUT_CTRL, priv->flexspi_lut_state);
}
target_mem_write32(target, IMXRT_MPU_CTRL, priv->mpu_state);
return true;
}
static uint8_t imxrt_spi_build_insn_sequence(target_s *const target, const uint16_t command, const uint16_t length)
{
imxrt_priv_s *const priv = (imxrt_priv_s *)target->target_storage;
uint8_t slot = 0;
for (; slot < 4; ++slot) {
if (priv->flexspi_cached_commands[slot] == command)
return slot;
if (priv->flexspi_cached_commands[slot] == 0)
break;
}
if (slot == 4)
slot = 0;
imxrt_flexspi_lut_insn_s sequence[8] = {{0}};
sequence[0].opcode_mode = IMXRT_FLEXSPI_LUT_OPCODE(IMXRT_FLEXSPI_LUT_OP_COMMAND) | IMXRT_FLEXSPI_LUT_MODE_SERIAL;
sequence[0].value = command & SPI_FLASH_OPCODE_MASK;
uint8_t offset = 1;
if ((command & SPI_FLASH_OPCODE_MODE_MASK) == SPI_FLASH_OPCODE_3B_ADDR) {
sequence[offset].opcode_mode =
IMXRT_FLEXSPI_LUT_OPCODE(IMXRT_FLEXSPI_LUT_OP_RADDR) | IMXRT_FLEXSPI_LUT_MODE_SERIAL;
sequence[offset++].value = 24U;
}
if (command & SPI_FLASH_DUMMY_MASK) {
sequence[offset].opcode_mode =
IMXRT_FLEXSPI_LUT_OPCODE(IMXRT_FLEXSPI_LUT_OP_DUMMY_CYCLES) | IMXRT_FLEXSPI_LUT_MODE_SERIAL;
sequence[offset++].value = ((command & SPI_FLASH_DUMMY_MASK) >> SPI_FLASH_DUMMY_SHIFT) * 8U;
}
if (length) {
if (command & SPI_FLASH_DATA_OUT)
sequence[offset].opcode_mode =
IMXRT_FLEXSPI_LUT_OPCODE(IMXRT_FLEXSPI_LUT_OP_WRITE) | IMXRT_FLEXSPI_LUT_MODE_SERIAL;
else
sequence[offset].opcode_mode =
IMXRT_FLEXSPI_LUT_OPCODE(IMXRT_FLEXSPI_LUT_OP_READ) | IMXRT_FLEXSPI_LUT_MODE_SERIAL;
sequence[offset++].value = 0;
}
DEBUG_TARGET("Writing new instruction seqeunce to slot %u\n", slot);
for (size_t idx = 0; idx < 8U; ++idx)
DEBUG_TARGET("%zu: %02x %02x\n", idx, sequence[idx].opcode_mode, sequence[idx].value);
target_mem_write(target, IMXRT_FLEXSPI1_LUT_BASE + IMXRT_FLEXSI_SLOT_OFFSET(slot), sequence, sizeof(sequence));
priv->flexspi_cached_commands[slot] = command;
return slot;
}
static void imxrt_spi_exec_sequence(
target_s *const target, const uint8_t slot, const target_addr_t address, const uint16_t length)
{
const imxrt_priv_s *const priv = (imxrt_priv_s *)target->target_storage;
const uint32_t command = priv->flexspi_cached_commands[slot];
if ((command & SPI_FLASH_OPCODE_MODE_MASK) == SPI_FLASH_OPCODE_3B_ADDR)
target_mem_write32(target, IMXRT_FLEXSPI1_PRG_CTRL0, address);
target_mem_write32(
target, IMXRT_FLEXSPI1_PRG_CTRL1, IMXRT_FLEXSPI1_PRG_SEQ_INDEX(slot) | IMXRT_FLEXSPI1_PRG_LENGTH(length));
target_mem_write32(target, IMXRT_FLEXSPI1_PRG_CMD, IMXRT_FLEXSPI1_PRG_RUN);
}
static void imxrt_spi_wait_complete(target_s *const target)
{
while (!(target_mem_read32(target, IMXRT_FLEXSPI1_INT) & IMXRT_FLEXSPI1_INT_PRG_CMD_DONE))
continue;
target_mem_write32(target, IMXRT_FLEXSPI1_INT, IMXRT_FLEXSPI1_INT_PRG_CMD_DONE);
if (target_mem_read32(target, IMXRT_FLEXSPI1_INT) & IMXRT_FLEXSPI1_INT_CMD_ERR) {
#if defined(ENABLE_DEBUG) && PC_HOSTED == 1
const uint32_t status = target_mem_read32(target, IMXRT_FLEXSPI1_STAT1);
DEBUG_TARGET("Error executing sequence, offset %u, error code %u\n", (uint8_t)(status >> 16U) & 0xfU,
(uint8_t)(status >> 24U) & 0xfU);
#endif
target_mem_write32(target, IMXRT_FLEXSPI1_INT, IMXRT_FLEXSPI1_INT_CMD_ERR);
}
}
static void imxrt_spi_read(target_s *const target, const uint16_t command, const target_addr_t address,
void *const buffer, const size_t length)
{
const uint8_t slot = imxrt_spi_build_insn_sequence(target, command, length);
imxrt_spi_exec_sequence(target, slot, address, length);
imxrt_spi_wait_complete(target);
uint32_t data[32];
target_mem_read(target, data, IMXRT_FLEXSPI1_PRG_READ_FIFO, 128);
memcpy(buffer, data, length);
target_mem_write32(target, IMXRT_FLEXSPI1_INT, IMXRT_FLEXSPI1_INT_READ_FIFO_FULL);
}
static void imxrt_spi_write(target_s *const target, const uint16_t command, const target_addr_t address,
const void *const buffer, const size_t length)
{
const uint8_t slot = imxrt_spi_build_insn_sequence(target, command, length);
imxrt_spi_exec_sequence(target, slot, address, length);
for (uint16_t offset = 0; offset < length; offset += 128U) {
while (
target_mem_read32(target, IMXRT_FLEXSPI1_PRG_WRITE_FIFO_STATUS) & IMXRT_FLEXSPI1_PRG_WRITE_FIFO_STATUS_FILL)
continue;
const uint16_t amount = MIN(128U, (uint16_t)(length - offset));
uint32_t data[32] = {0};
memcpy(data, (const char *)buffer + offset, amount);
target_mem_write(target, IMXRT_FLEXSPI1_PRG_WRITE_FIFO, data, (amount + 3U) & ~3U);
target_mem_write32(target, IMXRT_FLEXSPI1_INT, IMXRT_FLEXSPI1_INT_WRITE_FIFO_EMPTY);
}
imxrt_spi_wait_complete(target);
}
static void imxrt_spi_run_command(target_s *const target, const uint16_t command, const target_addr_t address)
{
const uint8_t slot = imxrt_spi_build_insn_sequence(target, command, 0U);
imxrt_spi_exec_sequence(target, slot, address, 0U);
imxrt_spi_wait_complete(target);
}