This is an automated email from Gerrit. "Brandon Martin <marti...@mothictech.com>" just uploaded a new patch set to Gerrit, which you can find at https://review.openocd.org/c/openocd/+/7348
-- gerrit commit 147a2466ce336c1a85203064c14dc7f2d25686fe Author: Brandon Martin <marti...@mothictech.com> Date: Mon Oct 31 16:10:47 2022 -0400 flash/nor/fsl_flexspi: Initial support for FLEXSPI This is the Freescale/NXP "FLEXSPI" [Q/D]SPI controller found on the i.MXRT series Cortex-M7 parts. This is tested against Winbond W25Q32JV/FV on an IMXRT1021. There's support for just enough to make read, write, and erase sector work right now. No target algorithm code is needed or used. Signed-off-by: Brandon Martin <marti...@mothictech.com> Change-Id: I90607c46c21944d855d98e2a027ba1ab96a1e545 diff --git a/src/flash/nor/Makefile.am b/src/flash/nor/Makefile.am index f04f0d206a..be731de945 100644 --- a/src/flash/nor/Makefile.am +++ b/src/flash/nor/Makefile.am @@ -33,6 +33,7 @@ NOR_DRIVERS = \ %D%/fespi.c \ %D%/fm3.c \ %D%/fm4.c \ + %D%/fsl_flexspi.c \ %D%/jtagspi.c \ %D%/kinetis.c \ %D%/kinetis_ke.c \ diff --git a/src/flash/nor/drivers.c b/src/flash/nor/drivers.c index b9353d8208..d57a3ec751 100644 --- a/src/flash/nor/drivers.c +++ b/src/flash/nor/drivers.c @@ -33,6 +33,7 @@ extern const struct flash_driver faux_flash; extern const struct flash_driver fm3_flash; extern const struct flash_driver fm4_flash; extern const struct flash_driver fespi_flash; +extern const struct flash_driver fsl_flexspi_flash; extern const struct flash_driver jtagspi_flash; extern const struct flash_driver kinetis_flash; extern const struct flash_driver kinetis_ke_flash; @@ -109,6 +110,7 @@ static const struct flash_driver * const flash_drivers[] = { &fm3_flash, &fm4_flash, &fespi_flash, + &fsl_flexspi_flash, &jtagspi_flash, &kinetis_flash, &kinetis_ke_flash, diff --git a/src/flash/nor/fsl_flexspi.c b/src/flash/nor/fsl_flexspi.c new file mode 100644 index 0000000000..67e9c31b91 --- /dev/null +++ b/src/flash/nor/fsl_flexspi.c @@ -0,0 +1,1223 @@ +// SPDX-License-Identifier: GPL-2.0-or-later + + +/*************************************************************************** + * Copyright (C) 2022 by Brandon Martin and Mothic Technologies LLC * + * marti...@mothictech.com * + ***************************************************************************/ + +/* + * Based on the U-Boot fsl-qspi driver (C) 2013-2020 Freescale/NXP and others + * and stmqspi OpenOCD driver (C) 2016-2019 Andreas Bolsch / (C) 2010 Antonio + * Borneo + */ + +/* + * The Freescale (NXP) FlexSPI controller found in the i.MXRT "Crossover" MCU + * line is somewhat similar to the QSPI controller found in the larger i.MX + * parts and is a typical controller focused on SPI memories providing + * programmatic/transactional access to all of the connected device functions + * as well as memory-mapped linear access to the memory. Unlike some + * controllers, writing to the device is supported in the linear memory-mapped + * mode with some restrictions though this driver does not use this + * functionality. + * + * In general, once the bank is probed, the connected memory device will be + * available via memory-mapped linear access any time another operation is not + * outstanding. + * + * Limitations of this driver: + * + * - Only supports a single attached memory device which must be on CS A1 + * (which is mandatory for it to be the boot device, anyway). Parallel mode + * combining two QSPI flash memories into a single 8-bit wide memory is not + * supported nor is combination mode for a single 8-bit wide device + * (octo-SPI). + * + * - Does not support DDR mode even if the attached flash device does. Usually + * these devices also support SDR, so the only consequence is potentially + * slower access. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "imp.h" +#include "spi.h" + +#include <helper/align.h> +#include <helper/bits.h> +#include <helper/time_support.h> + +#define TIMEOUT_EXEC_IPCMD 100 +#define TIMEOUT_RESET 100 +#define TIMEOUT_FLASH_ERASE 2000 +#define TIMEOUT_FLASH_PROGRAM 250 + +/* These are the base addresses used on the IMXRT1020 */ +#define FLEXSPI_DEFAULT_IOBASE 0x402A8000 +#define FLEXSPI_LINEAR_AHBASE 0x60000000 +#define FLEXSPI_TX_FIFO_AHBASE 0x7F800000 +#define FLEXSPI_RX_FIFO_AHBASE 0x7FC00000 + +/* The documentation implies that these may be chip-specific tweakables */ +#define FLEXSPI_AHB_TX_BUFSZ 64 +#define FLEXSPI_AHB_RX_BUFSZ 1024 +#define FLEXSPI_IP_TX_FIFOSZ 128 +#define FLEXSPI_IP_RX_FIFOSZ 128 + +/* On-chip registers and fields */ +#define REG_MCR0 0x00 +#define MCR0_SWRESET BIT(0) +#define MCR0_MDIS BIT(1) +#define MCR0_ARDFEN BIT(6) +#define MCR0_ATDFEN BIT(7) + +#define REG_AHBCR 0x0C +#define AHBCR_ALIGNMENT(x) (((x) & 0x03) << 20) +#define AHBCR_READSZALIGN BIT(10) +#define AHBCR_READADDROPT BIT(6) +#define AHBCR_PREFETCHEN BIT(5) +#define AHBCR_BUFFERABLEEN BIT(4) +#define AHBCR_CACHABLEEN BIT(3) +#define AHBCR_APAREN BIT(0) + +#define REG_INTR 0x14 +#define INTR_IPCMDDONE BIT(0) +#define INTR_IPCMDGE BIT(1) +#define INTR_AHBCMDGE BIT(2) +#define INTR_IPCMDERR BIT(3) +#define INTR_AHBCMDERR BIT(4) +#define INTR_IPRXWA BIT(5) +#define INTR_IPTXWE BIT(6) +#define INTR_SCKSTOPBYRD BIT(8) +#define INTR_SCKSTOPBYWR BIT(9) +#define INTR_AHBBUSTTIMEOUT BIT(10) +#define INTR_SEQTIMEOUT BIT(11) +#define INTR_IPCMDSECUREVIO BIT(16) + +#define REG_LUTKEY 0x18 +#define LUTKEY_MAGIC 0x5AF05AF0 + +#define REG_LUTCR 0x1C +#define LUTCR_LOCK BIT(0) +#define LUTCR_UNLOCK BIT(1) +#define LUTCR_PROTECT BIT(2) + +#define REG_AHBRXBUF0CR0 0x20 +#define REG_AHBRXBUF1CR0 0x24 +#define REG_AHBRXBUF2CR0 0x28 +#define REG_AHBRXBUF3CR0 0x2C +#define AHBRXBUFnCR0_BUFSZ(x) (((x) & 0xFF) << 0) +#define AHBRXBUFnCR0_MSTRID(x) (((x) & 0x0F) << 16) +#define AHBRXBUFnCR0_PRIORITY(x) (((x) & 0x03) << 24) +#define AHBRXBUFnCR0_REGIONEN BIT(30) +#define AHBRXBUFnCR0_PREFETCHEN BIT(31) + +#define REG_FLSHA1CR0 0x60 +#define FLSHnnCR0_FLASHSZ(x) (((x) & 0x7FFFFF) << 0) + +#define REG_FLSHA1CR1 0x70 +#define FLSHnnCR1_TCSS(x) (((x) & 0x1F) << 0) +#define FLSHnnCR1_TCSH(x) (((x) & 0x1F) << 5) +#define FLSHnnCR1_WA BIT(10) +#define FLSHnnCR1_CAS(x) (((x) & 0x0F) << 11) +#define FLSHnnCR1_CSINTERVALUNIT BIT(15) +#define FLSHnnCR1_CSINTERVAL(x) (((x) & 0xFFFF) << 16) + +#define REG_FLSHA1CR2 0x80 +#define FLSHnnCR2_ARDSEQID(x) (((x) & 0x0F) << 0) +#define FLSHnnCR2_ARDSEQNUM(x) (((x) & 0x07) << 5) +#define FLSHnnCR2_AWRSEQID(x) (((x) & 0x0F) << 8) +#define FLSHnnCR2_AWRSEQNUM(x) (((x) & 0x07) << 13) +#define FLSHnnCR2_AWRWAIT(x) (((x) & 0x0FFF) << 16) +#define FLSHnnCR2_AWRWAITUNIT(x) (((x) & 0x07) << 28) +#define AWRWAITUNIT_2AHB 0 +#define AWRWAITUNIT_8AHB 1 +#define AWRWAITUNIT_32AHB 2 +#define AWRWAITUNIT_128AHB 3 +#define AWRWAITUNIT_512AHB 4 +#define AWRWAITUNIT_2048AHB 5 +#define AWRWAITUNIT_8192AHB 6 +#define AWRWAITUNIT_32768AHB 7 +#define FLSHnnCR2_CLRINSTRPTR BIT(31) + +#define REG_FLSHCR4 0x94 +#define FLSHCR4_WMOPT1 BIT(0) +#define FLSHCR4_WMENA BIT(2) +#define FLSHCR4_WMENB BIT(3) +#define FLSHCR4_PAR_WM(x) (((x) & 0x03) << 9) +#define FLSHCR4_PAR_ADDR_ADJ_DIS BIT(11) + +#define REG_IPCR0 0xA0 +#define IPCR0_SFAR(x) (x) + +#define REG_IPCR1 0xA4 +#define IPCR1_IDATASZ(x) (((x) & 0xFFFF) << 0) +#define IPCR1_ISEQID(x) (((x) & 0x0F) << 16) +#define IPCR1_ISEQNUM(x) (((x) & 0x0F) << 24) +#define IPCR1_IPAREN BIT(31) + +#define REG_IPCMD 0xB0 +#define IPCMD_TRG BIT(0) + +#define REG_IPRXFCR 0xB8 +#define IPRXFCR_CLRIPRXF BIT(0) +#define IPRXFCR_RXDMAEN BIT(1) +#define IPRXFCR_RXWMRK(x) (((x) & 0x0F) << 2) + +#define REG_IPTXFCR 0xBC +#define IPTXFCR_CLRIPTXF BIT(0) +#define IPTXFCR_TXDMAEN BIT(1) +#define IPTXFCR_TXWMRK(x) (((x) & 0x0F) << 2) + +#define REG_STS1 0xE4 + +#define REG_RFDR 0x100 +#define REG_TFDR 0x180 + +#define REG_LUT_BASE 0x200 +#define REG_LUT(x) (REG_LUT_BASE + (x) * 4) +#define LUTNUM_AHB_READ 0 // This is the default for FlexSPI NOR (and NAND?) boot +#define LUTNUM_DRV 10 // This LUT sequence number is not used by boot procedures nor the Freescale sample code + +/* Instruction set for the LUT register (SDR only) */ +#define OPCODE_STOP 0 +#define OPCODE_CMD 1 +#define OPCODE_RADDR 2 +#define OPCODE_CADDR 3 +#define OPCODE_MODE1 4 +#define OPCODE_MODE2 5 +#define OPCODE_MODE4 6 +#define OPCODE_MODE8 7 +#define OPCODE_WRITE 8 +#define OPCODE_READ 9 +#define OPCODE_LEARN 10 +#define OPCODE_DATSZ 11 +#define OPCODE_DUMMY 12 +#define OPCODE_JMP_ON_CS 31 + +/* + * Macro for constructing the LUT entries with the following + * register layout: + * + * ----------------------------------------------------- + * | OPCODE1 | PAD1 | OPRND1 | OPCODE0 | PAD0 | OPRND0 | + * ----------------------------------------------------- + */ +#define LUT_DEF(idx, opc, pad, opr) \ + ((((opc) << 10) | ((pad) << 8) | (opr)) << (((idx) % 2) * 16)) + +struct fsl_flexspi_flash_bank { + bool probed; + struct flash_device dev; + uint32_t io_base; +}; + +static int lut_pad(int x) +{ + switch (x) { + case 1: + return 0; + case 2: + return 1; + case 4: + return 2; + case 8: + return 3; + default: + return -1; + } +} + + +static int poll_reg(struct target *target, uint32_t value, uint32_t mask, uint32_t io_addr, unsigned int timeout) +{ + long long endtime; + + endtime = timeval_ms() + timeout; + do { + uint32_t regval; + int ret; + + ret = target_read_u32(target, io_addr, ®val); + if (ret != ERROR_OK) + return ret; + + if ((regval & mask) == value) { + LOG_DEBUG("Polled register at %08x done: regval=%08x", io_addr, regval); + return ERROR_OK; + } + + LOG_DEBUG("Polling register at %08x: regval=%08x", io_addr, regval); + alive_sleep(1); + } while (timeval_ms() < endtime); + + LOG_ERROR("Timeout while polling register at %08x", io_addr); + return ERROR_TIMEOUT_REACHED; +} + +static int read_rxfifo(struct flash_bank *bank, uint8_t *buf, uint32_t datalen) +{ + struct target *target = bank->target; + struct fsl_flexspi_flash_bank *flexspi_info = bank->driver_priv; + uint32_t rx_fifo = flexspi_info->io_base + REG_RFDR; + uint32_t io_base = flexspi_info->io_base; + int ret; + uint32_t i; + uint32_t val; + + ret = target_write_u32(target, io_base + REG_IPRXFCR, IPRXFCR_RXWMRK(datalen / 8 - 1)); + if (ret != ERROR_OK) + goto err; + + for (i = 0; i < ALIGN_DOWN(datalen, 4); i += 4) { + ret = target_read_u32(target, rx_fifo + i, &val); + if (ret != ERROR_OK) + goto err; + + memcpy(buf + i, &val, 4); + } + + if (i < datalen) { + ret = target_read_u32(target, rx_fifo + i, &val); + memcpy(buf + i, &val, datalen - i); + if (ret != ERROR_OK) + goto err; + } + + ret = target_write_u32(target, io_base + REG_INTR, INTR_IPRXWA); + if (ret != ERROR_OK) + goto err; + + return ERROR_OK; + +err: + return ret; +} + +static int fill_txfifo(struct flash_bank *bank, const uint8_t *buf, uint32_t datalen) +{ + struct target *target = bank->target; + struct fsl_flexspi_flash_bank *flexspi_info = bank->driver_priv; + uint32_t tx_fifo = flexspi_info->io_base + REG_TFDR; + uint32_t io_base = flexspi_info->io_base; + int ret; + uint32_t i; + uint32_t val; + + ret = target_write_u32(target, io_base + REG_IPTXFCR, IPTXFCR_TXWMRK(datalen / 8 - 1)); + if (ret != ERROR_OK) + goto err; + + ret = poll_reg(target, INTR_IPTXWE, INTR_IPTXWE, io_base + REG_INTR, TIMEOUT_EXEC_IPCMD); + if (ret != ERROR_OK) + goto err; + + for (i = 0; i < ALIGN_DOWN(datalen, 4); i += 4) { + memcpy(&val, buf + i, 4); + ret = target_write_u32(target, tx_fifo + i, val); + if (ret != ERROR_OK) + goto err; + } + + if (i < datalen) { + val = 0; + memcpy(&val, buf + i, datalen - i); + ret = target_write_u32(target, tx_fifo + i, val); + if (ret != ERROR_OK) + goto err; + } + + ret = target_write_u32(target, io_base + REG_INTR, INTR_IPTXWE); + if (ret != ERROR_OK) + goto err; + + return ERROR_OK; + +err: + return ret; +} + +static int write_lut_memory(struct flash_bank *bank, const uint32_t *lutval, uint8_t lutnum) +{ + struct target *target = bank->target; + struct fsl_flexspi_flash_bank *flexspi_info = bank->driver_priv; + uint32_t io_base = flexspi_info->io_base; + int ret; + + LOG_DEBUG("%s: LUT %02x/lutval[0:%08x, 1:%08x, 2:%08x, 3:%08x]", + __func__, lutnum, lutval[0], lutval[1], lutval[2], lutval[3]); + + /* Unlock LUT memory */ + ret = target_write_u32(target, io_base + REG_LUTKEY, LUTKEY_MAGIC); + if (ret != ERROR_OK) + goto err; + + ret = target_write_u32(target, io_base + REG_LUTCR, LUTCR_UNLOCK); + if (ret != ERROR_OK) + goto err; + + /* Fill LUT sequence */ + for (int i = 0; i < 4; i++) { + ret = target_write_u32(target, io_base + REG_LUT(lutnum * 4 + i), lutval[i]); + if (ret != ERROR_OK) + goto err; + } + + /* Lock LUT memory */ + ret = target_write_u32(target, io_base + REG_LUTKEY, LUTKEY_MAGIC); + if (ret != ERROR_OK) + goto err; + + ret = target_write_u32(target, io_base + REG_LUTCR, LUTCR_LOCK); + if (ret != ERROR_OK) + goto err; + + return ERROR_OK; + +err: + return ret; +} + +static int execute_ipcmd_read(struct flash_bank *bank, uint8_t opcode, + void *buf, uint32_t datalen) +{ + struct target *target = bank->target; + struct fsl_flexspi_flash_bank *flexspi_info = bank->driver_priv; + uint32_t io_base = flexspi_info->io_base; + uint32_t lutval[4] = {}; + int lutidx = 0; + uint32_t intr, sts1; + int ret; + + LOG_DEBUG("%s with opcode 0x%02x", __func__, opcode); + + /* Prepare sequence LUT */ + lutval[lutidx / 2] |= LUT_DEF(lutidx, OPCODE_CMD, lut_pad(1), opcode); + lutidx++; + + if (datalen > 0) { + lutval[lutidx / 2] |= LUT_DEF(lutidx, OPCODE_READ, lut_pad(1), datalen); + lutidx++; + } + + lutval[lutidx / 2] |= LUT_DEF(lutidx, OPCODE_STOP, 0, 0); + lutidx++; + + ret = write_lut_memory(bank, lutval, LUTNUM_DRV); + if (ret != ERROR_OK) + goto err; + + /* Reset FIFO states */ + ret = target_write_u32(target, io_base + REG_IPRXFCR, IPRXFCR_CLRIPRXF); + if (ret != ERROR_OK) + goto err; + + ret = target_write_u32(target, io_base + REG_IPTXFCR, IPTXFCR_CLRIPTXF); + if (ret != ERROR_OK) + goto err; + + /* Clear any stray pending status */ + ret = target_write_u32( + target, + io_base + REG_INTR, + INTR_AHBCMDERR | INTR_IPCMDERR | INTR_AHBCMDGE | INTR_IPCMDGE); + + /* Execute the command */ + ret = target_write_u32(target, io_base + REG_IPCR0, IPCR0_SFAR(0)); + if (ret != ERROR_OK) + goto err; + + ret = target_write_u32( + target, + io_base + REG_IPCR1, + IPCR1_IDATASZ(datalen) | IPCR1_ISEQID(LUTNUM_DRV) | IPCR1_ISEQNUM(0)); + if (ret != ERROR_OK) + goto err; + + ret = target_write_u32(target, io_base + REG_IPCMD, IPCMD_TRG); + if (ret != ERROR_OK) + goto err; + + /* Wait for completion */ + ret = poll_reg(target, INTR_IPCMDDONE, INTR_IPCMDDONE, io_base + REG_INTR, TIMEOUT_EXEC_IPCMD); + if (ret != ERROR_OK) + goto err; + + /* Check for error */ + ret = target_read_u32(target, io_base + REG_INTR, &intr); + if (ret != ERROR_OK) + goto err; + + if (intr & INTR_IPCMDERR) { + ret = target_read_u32(target, io_base + REG_STS1, &sts1); + if (ret != ERROR_OK) + goto err; + + LOG_ERROR("Error executing IP command. sts1=%08" PRIx32, sts1); + ret = ERROR_FLASH_OPERATION_FAILED; + goto err; + } + + /* Read result */ + if (datalen > 0) { + ret = read_rxfifo(bank, buf, datalen); + if (ret != ERROR_OK) + goto err; + } + + /* Clear status */ + ret = target_write_u32(target, io_base + REG_INTR, INTR_IPCMDDONE | INTR_IPRXWA | INTR_IPTXWE); + if (ret != ERROR_OK) + goto err; + + return ERROR_OK; + +err: + return ret; +} + +static int read_flash_id(struct flash_bank *bank, uint32_t *id) +{ + int ret; + + LOG_DEBUG("%s", __func__); + + *id = 0; + + ret = execute_ipcmd_read(bank, SPIFLASH_READ_ID, id, sizeof(*id)); + if (ret != ERROR_OK) + goto err; + + return ERROR_OK; + +err: + return ret; +} + +static int read_status_reg(struct flash_bank *bank, uint8_t *statusreg) +{ + int ret; + + LOG_DEBUG("%s", __func__); + + *statusreg = 0; + + ret = execute_ipcmd_read(bank, SPIFLASH_READ_STATUS, statusreg, sizeof(*statusreg)); + if (ret != ERROR_OK) + goto err; + + return ERROR_OK; + +err: + return ret; +} + +static int write_enable(struct flash_bank *bank) +{ + int ret; + + LOG_DEBUG("%s", __func__); + + ret = execute_ipcmd_read(bank, SPIFLASH_WRITE_ENABLE, NULL, 0); + if (ret != ERROR_OK) + goto err; + + return ERROR_OK; + +err: + return ret; +} + +static int poll_flash_status(struct flash_bank *bank, uint8_t value, uint8_t mask, int timeout) +{ + uint8_t status; + int ret; + long long endtime; + + LOG_DEBUG("%s", __func__); + + endtime = timeval_ms() + timeout; + do { + /* Read flash status register(s) */ + ret = read_status_reg(bank, &status); + if (ret != ERROR_OK) + return ret; + + if ((status & mask) == value) + return ret; + + alive_sleep(25); + } while (timeval_ms() < endtime); + + LOG_ERROR("timeout"); + return ERROR_TIMEOUT_REACHED; +} + +static int execute_page_program(struct flash_bank *bank, uint8_t opcode, + const void *buf, uint32_t flashaddr, uint32_t datalen) +{ + struct target *target = bank->target; + struct fsl_flexspi_flash_bank *flexspi_info = bank->driver_priv; + uint32_t io_base = flexspi_info->io_base; + uint32_t lutval[4] = {}; + int lutidx = 0; + uint8_t status; + int ret; + + LOG_DEBUG("%s %" PRIu32 "B to 0x%08" PRIx32, __func__, datalen, flashaddr); + + ret = write_enable(bank); + if (ret != ERROR_OK) + goto err; + + /* Prepare sequence LUT */ + lutval[lutidx / 2] |= LUT_DEF(lutidx, OPCODE_CMD, lut_pad(1), opcode); + lutidx++; + + uint8_t naddrbytes = 1; + if (bank->size > (1 << 8)) + naddrbytes++; + if (bank->size > (1 << 16)) + naddrbytes++; + if (bank->size > (1 << 24)) + naddrbytes++; + + lutval[lutidx / 2] |= LUT_DEF(lutidx, OPCODE_RADDR, lut_pad(1), naddrbytes * 8); + lutidx++; + + if (datalen > 0) { + lutval[lutidx / 2] |= LUT_DEF(lutidx, OPCODE_WRITE, lut_pad(1), datalen); + lutidx++; + } + + lutval[lutidx / 2] |= LUT_DEF(lutidx, OPCODE_STOP, 0, 0); + lutidx++; + + ret = write_lut_memory(bank, lutval, LUTNUM_DRV); + if (ret != ERROR_OK) + goto err; + + /* Clear any stray pending status */ + ret = target_write_u32( + target, + io_base + REG_INTR, + INTR_AHBCMDERR | INTR_IPCMDERR | INTR_AHBCMDGE | INTR_IPCMDGE); + + /* Reset FIFO states */ + ret = target_write_u32(target, io_base + REG_IPRXFCR, IPRXFCR_CLRIPRXF); + if (ret != ERROR_OK) + goto err; + + ret = target_write_u32(target, io_base + REG_IPTXFCR, IPTXFCR_CLRIPTXF); + if (ret != ERROR_OK) + goto err; + + /* Fill the TX FIFO with the data */ + ret = fill_txfifo(bank, buf, datalen); + if (ret != ERROR_OK) + goto err; + + /* Execute the command */ + ret = target_write_u32(target, io_base + REG_IPCR0, IPCR0_SFAR(flashaddr)); + if (ret != ERROR_OK) + goto err; + + ret = target_write_u32( + target, + io_base + REG_IPCR1, + IPCR1_IDATASZ(datalen) | IPCR1_ISEQID(LUTNUM_DRV) | IPCR1_ISEQNUM(0)); + if (ret != ERROR_OK) + goto err; + + ret = target_write_u32(target, io_base + REG_IPCMD, IPCMD_TRG); + if (ret != ERROR_OK) + goto err; + + /* Wait for controller completion */ + ret = poll_reg(target, INTR_IPCMDDONE, INTR_IPCMDDONE, io_base + REG_INTR, TIMEOUT_EXEC_IPCMD); + if (ret != ERROR_OK) + goto err; + + /* + * Check status register to see if the flash device appears to have + * accepted the command and is now executing it. If BSY and WE are clear, + * we assume program already completed. If BSY is clear but WE is still + * set, the command wasn't accepted. + */ + ret = read_status_reg(bank, &status); + if (ret != ERROR_OK) + goto err; + + if (((status & SPIFLASH_BSY_BIT) == 0) && ((status & SPIFLASH_WE_BIT) != 0)) { + LOG_ERROR("Page program command not accepted by flash. status=0x%02x", status); + ret = ERROR_FLASH_OPERATION_FAILED; + goto err; + } + + /* Poll flash device for end of internally timed Page Program operation */ + ret = poll_flash_status(bank, 0, SPIFLASH_BSY_BIT, TIMEOUT_FLASH_PROGRAM); + if (ret != ERROR_OK) + goto err; + + return ERROR_OK; + +err: + return ret; +} + +static int erase_sector(struct flash_bank *bank, unsigned int sector) +{ + struct target *target = bank->target; + struct fsl_flexspi_flash_bank *flexspi_info = bank->driver_priv; + uint32_t io_base = flexspi_info->io_base; + uint32_t flashaddr = bank->sectors[sector].offset; + uint32_t lutval[4] = {}; + int lutidx = 0; + uint8_t status; + int ret; + + LOG_DEBUG("%s", __func__); + + ret = write_enable(bank); + if (ret != ERROR_OK) + goto err; + + /* Prepare sequence LUT for Erase Sector */ + lutval[lutidx / 2] |= LUT_DEF(lutidx, OPCODE_CMD, lut_pad(1), flexspi_info->dev.erase_cmd); + lutidx++; + + uint8_t naddrbytes = 1; + if (bank->size > (1 << 8)) + naddrbytes++; + if (bank->size > (1 << 16)) + naddrbytes++; + if (bank->size > (1 << 24)) + naddrbytes++; + + lutval[lutidx / 2] |= LUT_DEF(lutidx, OPCODE_RADDR, lut_pad(1), naddrbytes * 8); + lutidx++; + + lutval[lutidx / 2] |= LUT_DEF(lutidx, OPCODE_STOP, 0, 0); + lutidx++; + + ret = write_lut_memory(bank, lutval, LUTNUM_DRV); + if (ret != ERROR_OK) + goto err; + + /* Execute the command */ + ret = target_write_u32(target, io_base + REG_IPCR0, IPCR0_SFAR(flashaddr)); + if (ret != ERROR_OK) + goto err; + + ret = target_write_u32( + target, + io_base + REG_IPCR1, + IPCR1_IDATASZ(0) | IPCR1_ISEQID(LUTNUM_DRV) | IPCR1_ISEQNUM(0)); + if (ret != ERROR_OK) + goto err; + + ret = target_write_u32(target, io_base + REG_IPCMD, IPCMD_TRG); + if (ret != ERROR_OK) + goto err; + + /* Wait for completion of FLEXSPI command cycle */ + ret = poll_reg(target, INTR_IPCMDDONE, INTR_IPCMDDONE, io_base + REG_INTR, TIMEOUT_EXEC_IPCMD); + if (ret != ERROR_OK) + goto err; + + /* + * Check status register to see if the flash device appears to have + * accepted the command and is now executing it. If BSY and WE are clear, + * we assume erase already completed. If BSY is clear but WE is still set, + * the command wasn't accepted. + */ + ret = read_status_reg(bank, &status); + if (ret != ERROR_OK) + goto err; + + if (((status & SPIFLASH_BSY_BIT) == 0) && ((status & SPIFLASH_WE_BIT) != 0)) { + LOG_ERROR("Sector erase command not accepted by flash. status=0x%02x", status); + ret = ERROR_FLASH_OPERATION_FAILED; + goto err; + } + + /* Erase takes a long time, so some sort of progress message is a good idea */ + LOG_DEBUG("erasing sector %4u", sector); + + /* Poll the flash device for end of internally timed Sector Erase operation */ + ret = poll_flash_status(bank, 0, SPIFLASH_BSY_BIT, TIMEOUT_FLASH_ERASE); + if (ret != ERROR_OK) + goto err; + + return ERROR_OK; + +err: + return ret; +} + +static int default_setup(struct flash_bank *bank) +{ + struct target *target = bank->target; + struct fsl_flexspi_flash_bank *flexspi_info = bank->driver_priv; + uint32_t io_base = flexspi_info->io_base; + int ret; + uint32_t mcr0; + + LOG_DEBUG("%s", __func__); + + /* + * There are a bunch of non-zero default/reserved values that are to be + * preserved in MCR0 + */ + ret = target_read_u32(target, io_base + REG_MCR0, &mcr0); + if (ret != ERROR_OK) + goto err; + + /* Reset the module */ + ret = target_write_u32(target, io_base + REG_MCR0, (mcr0 | MCR0_SWRESET) & (~MCR0_MDIS)); + if (ret != ERROR_OK) + goto err; + + ret = poll_reg(target, 0, MCR0_SWRESET, io_base + REG_MCR0, TIMEOUT_RESET); + if (ret != ERROR_OK) + goto err; + + /* Disable the module during configuration */ + ret = target_write_u32(target, io_base + REG_MCR0, mcr0 | MCR0_MDIS); + if (ret != ERROR_OK) + goto err; + + /* + * AHB buffer 3 is the fallback buffer - configure it and deactivate + * all the others by setting their size to 0. + */ + ret = target_write_u32(target, io_base + REG_AHBRXBUF0CR0, 0); + if (ret != ERROR_OK) + goto err; + + ret = target_write_u32(target, io_base + REG_AHBRXBUF1CR0, 0); + if (ret != ERROR_OK) + goto err; + + ret = target_write_u32(target, io_base + REG_AHBRXBUF2CR0, 0); + if (ret != ERROR_OK) + goto err; + + ret = target_write_u32( + target, + io_base + REG_AHBRXBUF3CR0, + AHBRXBUFnCR0_BUFSZ(FLEXSPI_AHB_RX_BUFSZ / 8) | AHBRXBUFnCR0_REGIONEN | AHBRXBUFnCR0_PREFETCHEN); + if (ret != ERROR_OK) + goto err; + + ret = target_write_u32( + target, + io_base + REG_AHBCR, + AHBCR_PREFETCHEN | AHBCR_BUFFERABLEEN | AHBCR_CACHABLEEN | AHBCR_READADDROPT); + if (ret != ERROR_OK) + goto err; + + /* Set up Flash A1 as non-zero size so we can probe it */ + ret = target_write_u32(target, io_base + REG_FLSHA1CR0, FLSHnnCR0_FLASHSZ(64)); + if (ret != ERROR_OK) + goto err; + + /* Enable the module and set FIFO access to IPB */ + ret = target_write_u32(target, io_base + REG_MCR0, (mcr0 & ~(MCR0_MDIS | MCR0_ARDFEN | MCR0_ATDFEN))); + if (ret != ERROR_OK) + goto err; + + return ERROR_OK; + +err: + return ret; +} + +static int fsl_flexspi_erase(struct flash_bank *bank, unsigned int first, + unsigned int last) +{ + struct target *target = bank->target; + struct fsl_flexspi_flash_bank *flexspi_info = bank->driver_priv; + unsigned int sector; + int ret; + + LOG_DEBUG("%s: from sector %u to sector %u", __func__, first, last); + + if (target->state != TARGET_HALTED) { + LOG_ERROR("Target not halted"); + return ERROR_TARGET_NOT_HALTED; + } + + if (!(flexspi_info->probed)) { + LOG_ERROR("Flash bank not probed"); + return ERROR_FLASH_BANK_NOT_PROBED; + } + + if (flexspi_info->dev.erase_cmd == 0x00) { + LOG_ERROR("Sector erase not available for this device"); + return ERROR_FLASH_OPER_UNSUPPORTED; + } + + if ((last < first) || (last >= bank->num_sectors)) { + LOG_ERROR("Flash sector invalid"); + return ERROR_FLASH_SECTOR_INVALID; + } + + for (sector = first; sector <= last; sector++) { + if (bank->sectors[sector].is_protected) { + LOG_ERROR("Flash sector %u protected", sector); + return ERROR_FLASH_PROTECTED; + } + } + + for (sector = first; sector <= last; sector++) { + ret = erase_sector(bank, sector); + if (ret != ERROR_OK) + break; + alive_sleep(10); + keep_alive(); + } + + if (ret != ERROR_OK) + LOG_ERROR("Flash sector_erase failed on sector %u", sector); + + return ret; +} + +static int fsl_flexspi_protect(struct flash_bank *bank, int set, + unsigned int first, unsigned int last) +{ + unsigned int sector; + + for (sector = first; sector <= last; sector++) + bank->sectors[sector].is_protected = set; + + if (set) + LOG_WARNING("setting soft protection only, not related to flash's hardware write protection"); + + return ERROR_OK; +} + +static int fsl_flexspi_write(struct flash_bank *bank, const uint8_t *buffer, + uint32_t offset, uint32_t count) +{ + struct target *target = bank->target; + struct fsl_flexspi_flash_bank *flexspi_info = bank->driver_priv; + unsigned int sector; + int ret; + + LOG_DEBUG("%s: offset=0x%08" PRIx32 " count=0x%08" PRIx32, + __func__, offset, count); + + if (target->state != TARGET_HALTED) { + LOG_ERROR("Target not halted"); + return ERROR_TARGET_NOT_HALTED; + } + + if (!(flexspi_info->probed)) { + LOG_ERROR("Flash bank not probed"); + return ERROR_FLASH_BANK_NOT_PROBED; + } + + if (offset + count > bank->size) { + LOG_WARNING("Write beyond end of flash. Extra data discarded."); + count = bank->size - offset; + } + + /* Check sector protection */ + for (sector = 0; sector < bank->num_sectors; sector++) { + /* Start offset in or before this sector? */ + /* End offset in or behind this sector? */ + if ((offset < (bank->sectors[sector].offset + bank->sectors[sector].size)) && + ((offset + count - 1) >= bank->sectors[sector].offset) && + bank->sectors[sector].is_protected) { + LOG_ERROR("Flash sector %u protected", sector); + return ERROR_FLASH_PROTECTED; + } + } + + uint32_t wrsize = (flexspi_info->dev.pagesize < FLEXSPI_IP_TX_FIFOSZ) ? + flexspi_info->dev.pagesize : + FLEXSPI_IP_TX_FIFOSZ; + + /* Write the lesser of one page or the TX FIFO size at a time */ + while (count > 0) { + if (count < wrsize) + wrsize = count; + + ret = execute_page_program(bank, flexspi_info->dev.pprog_cmd, + buffer, offset, wrsize); + if (ret != ERROR_OK) + goto err; + + count -= wrsize; + offset += wrsize; + buffer += wrsize; + } + + return ERROR_OK; + +err: + return ret; +} + +static int fsl_flexspi_read(struct flash_bank *bank, uint8_t *buffer, + uint32_t offset, uint32_t count) +{ + struct target *target = bank->target; + struct fsl_flexspi_flash_bank *flexspi_info = bank->driver_priv; + + LOG_DEBUG("%s: offset=0x%08" PRIx32 " count=0x%08" PRIx32, + __func__, offset, count); + + if (target->state != TARGET_HALTED) { + LOG_ERROR("Target not halted"); + return ERROR_TARGET_NOT_HALTED; + } + + if (!(flexspi_info->probed)) { + LOG_ERROR("Flash bank not probed"); + return ERROR_FLASH_BANK_NOT_PROBED; + } + + if (offset + count > bank->size) { + LOG_WARNING("Read beyond end of flash. Extra data to be ignored."); + count = bank->size - offset; + } + + /* Simply read from the linear AHB port */ + return target_read_buffer(target, bank->base + offset, count, buffer); +} + +static int fsl_flexspi_probe(struct flash_bank *bank) +{ + struct target *target = bank->target; + struct fsl_flexspi_flash_bank *flexspi_info = bank->driver_priv; + struct flash_sector *sectors = NULL; + uint32_t io_base = flexspi_info->io_base; + uint32_t lutkey; + uint32_t id; + uint32_t lutval[4] = {}; + int lutidx = 0; + int ret; + + LOG_DEBUG("%s", __func__); + + if (flexspi_info->probed) { + free(bank->sectors); + bank->sectors = NULL; + } + + flexspi_info->probed = false; + + ret = target_read_u32(target, io_base + REG_LUTKEY, &lutkey); + if (ret == ERROR_OK && lutkey == LUTKEY_MAGIC) { + LOG_DEBUG("Found FLEXSPI at 0x%08" PRIx64 " with io_base 0x%08" PRIx32, + bank->base, io_base); + } else { + LOG_ERROR("Did not find FLEXSPI at io_base 0x%08" PRIx32 " (LUTKEY=0x%08" PRIx32 ")", + io_base, lutkey); + return ERROR_FAIL; + } + + /* Put the controller into a sane/default configuration */ + ret = default_setup(bank); + if (ret != ERROR_OK) + goto err; + + /* Attempt to read the ID of the device on CS A1 */ + ret = read_flash_id(bank, &id); + if (ret != ERROR_OK) + goto err; + + LOG_DEBUG("CS A1 flash id %06" PRIx32, id); + + /* Match with OpenOCD's list of known SPI flash memories */ + for (const struct flash_device *p = flash_devices; id && p->name ; p++) { + if (p->device_id == id) { + memcpy(&flexspi_info->dev, p, sizeof(flexspi_info->dev)); + if (p->size_in_bytes / 4096) + LOG_INFO("flash A1 \'%s\' id = 0x%06" PRIx32 " size = %" PRIu32 + " KiB", p->name, id, p->size_in_bytes / 1024); + else + LOG_INFO("flash A1 \'%s\' id = 0x%06" PRIx32 " size = %" PRIu32 + " B", p->name, id, p->size_in_bytes); + break; + } + } + + /* Configure flash size */ + bank->size = flexspi_info->dev.size_in_bytes; + + ret = target_write_u32(target, io_base + REG_FLSHA1CR0, FLSHnnCR0_FLASHSZ(flexspi_info->dev.size_in_bytes / 1024)); + if (ret != ERROR_OK) + goto err; + + /* Configure the AHB read LUT */ + if (flexspi_info->dev.qread_cmd) { + lutval[lutidx / 2] |= LUT_DEF(lutidx, OPCODE_CMD, lut_pad(1), flexspi_info->dev.qread_cmd); + lutidx++; + + uint8_t naddrbytes = 1; + if (bank->size > (1 << 8)) + naddrbytes++; + if (bank->size > (1 << 16)) + naddrbytes++; + if (bank->size > (1 << 24)) + naddrbytes++; + + lutval[lutidx / 2] |= LUT_DEF(lutidx, OPCODE_RADDR, lut_pad(4), naddrbytes * 8); + lutidx++; + + LOG_INFO("Assuming 2 mode cycles (0xF0) + 4 dummy cycles for qread cmd 0x%02X", flexspi_info->dev.qread_cmd); + + lutval[lutidx / 2] |= LUT_DEF(lutidx, OPCODE_MODE8, lut_pad(4), 0xF0); + lutidx++; + + lutval[lutidx / 2] |= LUT_DEF(lutidx, OPCODE_DUMMY, lut_pad(4), 4); + lutidx++; + + lutval[lutidx / 2] |= LUT_DEF(lutidx, OPCODE_READ, lut_pad(4), 1); + lutidx++; + + lutval[lutidx / 2] |= LUT_DEF(lutidx, OPCODE_STOP, 0, 0); + lutidx++; + } else if (flexspi_info->dev.read_cmd) { + lutval[lutidx / 2] |= LUT_DEF(lutidx, OPCODE_CMD, lut_pad(1), flexspi_info->dev.read_cmd); + lutidx++; + + uint8_t naddrbytes = 1; + if (bank->size > (1 << 8)) + naddrbytes++; + if (bank->size > (1 << 16)) + naddrbytes++; + if (bank->size > (1 << 24)) + naddrbytes++; + + lutval[lutidx / 2] |= LUT_DEF(lutidx, OPCODE_RADDR, lut_pad(1), naddrbytes * 8); + lutidx++; + + LOG_INFO("Assuming no dummy cycles for single-io read cmd 0x%02X", flexspi_info->dev.read_cmd); + + lutval[lutidx / 2] |= LUT_DEF(lutidx, OPCODE_READ, lut_pad(1), 1); + lutidx++; + + lutval[lutidx / 2] |= LUT_DEF(lutidx, OPCODE_STOP, 0, 0); + lutidx++; + } + + ret = write_lut_memory(bank, lutval, LUTNUM_AHB_READ); + if (ret != ERROR_OK) + goto err; + + ret = target_write_u32(target, io_base + REG_FLSHA1CR2, + FLSHnnCR2_CLRINSTRPTR | FLSHnnCR2_ARDSEQNUM(0) | FLSHnnCR2_ARDSEQID(LUTNUM_AHB_READ)); + if (ret != ERROR_OK) + goto err; + + /* Create and fill sectors array */ + bank->num_sectors = flexspi_info->dev.size_in_bytes / flexspi_info->dev.sectorsize; + sectors = malloc(sizeof(struct flash_sector) * bank->num_sectors); + if (!sectors) { + LOG_ERROR("not enough memory"); + ret = ERROR_FAIL; + goto err; + } + + for (unsigned int sector = 0; sector < bank->num_sectors; sector++) { + sectors[sector].offset = sector * flexspi_info->dev.sectorsize; + sectors[sector].size = flexspi_info->dev.sectorsize; + sectors[sector].is_erased = -1; + sectors[sector].is_protected = 0; + } + + bank->sectors = sectors; + flexspi_info->probed = true; + + return ERROR_OK; + +err: + LOG_ERROR("Error communicating to target FLEXSPI module: %d", ret); + return ret; +} + + +static int fsl_flexspi_auto_probe(struct flash_bank *bank) +{ + struct fsl_flexspi_flash_bank *flexspi_info = bank->driver_priv; + + if (flexspi_info->probed) + return ERROR_OK; + + return fsl_flexspi_probe(bank); +} + +static int fsl_flexspi_protect_check(struct flash_bank *bank) +{ + /* Not relevant - controller has no protection features */ + return ERROR_OK; +} + +static int fsl_flexspi_get_info(struct flash_bank *bank, struct command_invocation *cmd) +{ + struct fsl_flexspi_flash_bank *flexspi_info = bank->driver_priv; + + if (!(flexspi_info->probed)) { + command_print_sameline(cmd, "\nFLEXSPI flash bank not probed yet\n"); + return ERROR_FLASH_BANK_NOT_PROBED; + } + + command_print_sameline(cmd, "flash A1 \'%s\', device id = 0x%06" PRIx32 + ", flash size = %" PRIu32 "%sB\n(page size = %" PRIu32 + ", read = 0x%02" PRIx8 ", qread = 0x%02" PRIx8 + ", pprog = 0x%02" PRIx8 ", mass_erase = 0x%02" PRIx8 + ", sector size = %" PRIu32 " %sB, sector_erase = 0x%02" PRIx8 ")", + flexspi_info->dev.name, flexspi_info->dev.device_id, + bank->size / 4096 ? bank->size / 1024 : bank->size, + bank->size / 4096 ? "Ki" : "", flexspi_info->dev.pagesize, + flexspi_info->dev.read_cmd, flexspi_info->dev.qread_cmd, + flexspi_info->dev.pprog_cmd, flexspi_info->dev.chip_erase_cmd, + flexspi_info->dev.sectorsize / 4096 ? + flexspi_info->dev.sectorsize / 1024 : flexspi_info->dev.sectorsize, + flexspi_info->dev.sectorsize / 4096 ? "Ki" : "", + flexspi_info->dev.erase_cmd); + + return ERROR_OK; +} + + +FLASH_BANK_COMMAND_HANDLER(fsl_flexspi_flash_bank_command) +{ + struct fsl_flexspi_flash_bank *flexspi_info; + + LOG_DEBUG("%s", __func__); + + flexspi_info = calloc(1, sizeof(struct fsl_flexspi_flash_bank)); + if (!flexspi_info) { + LOG_ERROR("not enough memory"); + return ERROR_FAIL; + } + + bank->driver_priv = flexspi_info; + flexspi_info->io_base = FLEXSPI_DEFAULT_IOBASE; + + return ERROR_OK; +} + +struct flash_driver fsl_flexspi_flash = { + .name = "fsl_flexspi", + .flash_bank_command = fsl_flexspi_flash_bank_command, + .erase = fsl_flexspi_erase, + .protect = fsl_flexspi_protect, + .write = fsl_flexspi_write, + .read = fsl_flexspi_read, + .probe = fsl_flexspi_probe, + .auto_probe = fsl_flexspi_auto_probe, + .erase_check = default_flash_blank_check, + .protect_check = fsl_flexspi_protect_check, + .info = fsl_flexspi_get_info, + .free_driver_priv = default_flash_free_driver_priv, +}; diff --git a/src/jtag/drivers/libjaylink b/src/jtag/drivers/libjaylink index 0d23921a05..9aa7a5957c 160000 --- a/src/jtag/drivers/libjaylink +++ b/src/jtag/drivers/libjaylink @@ -1 +1 @@ -Subproject commit 0d23921a05d5d427332a142d154c213d0c306eb1 +Subproject commit 9aa7a5957c07bb6e862fc1a6d3153d109c7407e4 --