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/+/8933
-- gerrit commit bdd42ee831329890e3ec2fe60742e992993b2a6c Author: Brandon Martin <marti...@mothictech.com> Date: Mon May 19 06:30:26 2025 -0400 On-chip algorithm programming for FLEXSPI This considerably improves programming throughput on the order of 4-5x. There are currently several IMXRT1020'isms in the algorithm code with respect to disabling various watchdogs. These will need to be factored out. Change-Id: Ibf61dcaad09d9d3dcea1cbfe4e825c67935cdb93 Signed-off-by: Brandon Martin <marti...@mothictech.com> diff --git a/contrib/loaders/flash/fsl_flexspi_blockwrite.S b/contrib/loaders/flash/fsl_flexspi_blockwrite.S new file mode 100644 index 0000000000..9619c65b69 --- /dev/null +++ b/contrib/loaders/flash/fsl_flexspi_blockwrite.S @@ -0,0 +1,179 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +/*************************************************************************** + * Copyright (C) 2025 by Brandon Martin (Mothic Technologies LLC) * + * marti...@mothictech.com * + ***************************************************************************/ + + .text + .syntax unified + .cpu cortex-m7 + .thumb + .thumb_func + + /* + * Params: + * r0 = flash destination address [in/out] + * r1 = blkcount - 0 indicates success [in/out] + * r2 = blksize in 32-bit words (must be <= IP TX FIFO length) + * r3 = workarea (streaming FIFO) start address + * r4 = workarea (streaming FIFO) end address + * r5 = FLEXSPI register base + */ + +#include "../../../src/flash/nor/fsl_flexspi.h" + + .macro wait_ipcmd +0: + ldr r7, [r5, #REG_INTR] + ands r6, r7, #INTR_IPCMDERR + bne error + ands r6, r7, #INTR_IPCMDDONE + beq 0b + .endm + + .macro clear_intr + mov r6, #(INTR_AHBCMDERR | INTR_IPCMDERR | INTR_AHBCMDGE | INTR_IPCMDGE | INTR_IPCMDDONE | INTR_IPRXWA | \ + INTR_IPTXWE) + str r6, [r5, #REG_INTR] + .endm + + .align 4 + +start: + cpsid i + mov r6, #0 + ldr r7, wdog1 + strh r6, [r7, #8] + mov r6, #0x0030 + strh r6, [r7, #0] + ldr r7, wdog2 + mov r6, #0 + strh r6, [r7, #8] + mov r6, #0x0030 + strh r6, [r7, #0] + ldr r7, rtwdog + ldr r6, [r7] + ands r6, #BIT(7) + beq rtwdog_disabled + ldr r6, rtwdog_unlock + str r6, [r7, #4] + mov r6, #0x00002120 + str r6, [r7] +rtwdog_disabled: + ldr r7, src + ldr r6, src_scr_disable_wdogs + str r6, [r7] + mov r6, #0x1ff + str r6, [r7, #8] + ldr r7, systick + mov r6, #0 + str r6, [r7] + +next_block: + /* Write enable */ + clear_intr + str r0, [r5, #REG_IPCR0] /* flash address */ + mov r6, #(IPCR1_IDATASZ(0) | IPCR1_ISEQID(LUTNUM_WRITE_ENABLE) | IPCR1_ISEQNUM(0)) + str r6, [r5, #REG_IPCR1] /* data length and LUT program info */ + mov r6, #IPCMD_TRG + str r6, [r5, #REG_IPCMD] /* initiate command */ + wait_ipcmd + + /* Configure the TX FIFO for a block */ + lsr r6, r2, #1 + sub r6, r6, #1 + lsl r6, r6, #2 + orr r6, r6, #1 + str r6, [r5, #REG_IPTXFCR] + + /* Load TX FIFO with this block's data */ + mov r8, #0 /* block word count [r8] */ + add r5, r5, #REG_TFDR + ldr r6, [r3, #4] /* read rp [r6] */ +wait_fifo: + ldr r7, [r3, #0] /* read wp [r7] */ + cmp r7, #0 /* abort if wp == 0 */ + beq exit + + cmp r6, r7 /* wait until rp != wp */ + beq wait_fifo + + ldr r7, [r6] /* read from rp */ + str r7, [r5, r8, lsl #2] /* store to IP TX FIFO */ + + add r6, #4 /* rp += 4 */ + cmp r6, r4 /* Wrap? */ + bcc no_wrap + mov r6, r3 + add r6, r6, #8 +no_wrap: + add r8, r8, #1 + cmp r2, r8 /* need more data for block? */ + bne wait_fifo + + /* Entire block written to TX FIFO */ + str r6, [r3, #4] /* Store rp */ + sub r5, r5, #REG_TFDR + + /* Send write command + data */ + clear_intr + str r0, [r5, #REG_IPCR0] /* flash address */ + mov r6, #(IPCR1_ISEQID(LUTNUM_DRV) | IPCR1_ISEQNUM(0)) + orr r6, r6, r2, lsl #2 + str r6, [r5, #REG_IPCR1] /* data length and LUT program info */ + mov r6, #IPCMD_TRG + str r6, [r5, #REG_IPCMD] /* initiate command */ + wait_ipcmd + + /* Read status and wait for completion */ +check_busy: + clear_intr + mov r6, #IPRXFCR_RXWMRK(1) | IPRXFCR_CLRIPRXF + str r6, [r5, #REG_IPRXFCR] + mov r6, #0 + str r6, [r5, #REG_IPCR0] + ldr r6, wait_busy_ipcr1 + str r6, [r5, #REG_IPCR1] + mov r6, #IPCMD_TRG + str r6, [r5, #REG_IPCMD] /* initiate command */ + +wait_status: + wait_ipcmd + + ldr r7, [r5, #REG_RFDR] + ands r6, r7, #SPIFLASH_BSY_BIT + bne check_busy + ands r6, r7, #SPIFLASH_WE_BIT + bne error + + /* Block is complete */ + add r0, r0, r2, lsl #2 /* flash address += blksize - increment after error check since it's output */ + + subs r1, #1 /* blkcount-- */ + bne next_block + b exit + +error: + mov r5, #0 + str r5, [r3, #4] /* set rp = 0 on error */ + +exit: + bkpt #0 + +wait_busy_ipcr1: + .word (IPCR1_IDATASZ(4) | IPCR1_ISEQID(LUTNUM_READ_STATUS) | IPCR1_ISEQNUM(0)) +wdog1: + .word 0x400b8000 +wdog2: + .word 0x400d0000 +rtwdog: + .word 0x400bc000 +rtwdog_unlock: + .word 0xd928c520 +systick: + .word 0xe000e010 +src: + .word 0x400f8000 +src_scr_disable_wdogs: + .word 0x504802a0 diff --git a/contrib/loaders/flash/fsl_flexspi_blockwrite.inc b/contrib/loaders/flash/fsl_flexspi_blockwrite.inc new file mode 100644 index 0000000000..31f88a8205 --- /dev/null +++ b/contrib/loaders/flash/fsl_flexspi_blockwrite.inc @@ -0,0 +1,23 @@ +/* Autogenerated with ../../../src/helper/bin2char.sh */ +0x72,0xb6,0x4f,0xf0,0x00,0x06,0xdf,0xf8,0x38,0x71,0x3e,0x81,0x4f,0xf0,0x30,0x06, +0x3e,0x80,0xdf,0xf8,0x30,0x71,0x4f,0xf0,0x00,0x06,0x3e,0x81,0x4f,0xf0,0x30,0x06, +0x3e,0x80,0x49,0x4f,0x3e,0x68,0x16,0xf0,0x80,0x06,0x04,0xd0,0x47,0x4e,0x7e,0x60, +0x42,0xf2,0x20,0x16,0x3e,0x60,0x47,0x4f,0x47,0x4e,0x3e,0x60,0x40,0xf2,0xff,0x16, +0xbe,0x60,0x43,0x4f,0x4f,0xf0,0x00,0x06,0x3e,0x60,0x4f,0xf0,0x7f,0x06,0x6e,0x61, +0xc5,0xf8,0xa0,0x00,0x4f,0xf4,0x40,0x36,0xc5,0xf8,0xa4,0x60,0x4f,0xf0,0x01,0x06, +0xc5,0xf8,0xb0,0x60,0x6f,0x69,0x17,0xf0,0x08,0x06,0x63,0xd1,0x17,0xf0,0x01,0x06, +0xf8,0xd0,0x4f,0xea,0x52,0x06,0xa6,0xf1,0x01,0x06,0x4f,0xea,0x86,0x06,0x46,0xf0, +0x01,0x06,0xc5,0xf8,0xbc,0x60,0x4f,0xf0,0x00,0x08,0x05,0xf5,0xc0,0x75,0x5e,0x68, +0x1f,0x68,0x00,0x2f,0x51,0xd0,0xbe,0x42,0xfa,0xd0,0x37,0x68,0x45,0xf8,0x28,0x70, +0x06,0xf1,0x04,0x06,0xa6,0x42,0x02,0xd3,0x1e,0x46,0x06,0xf1,0x08,0x06,0x08,0xf1, +0x01,0x08,0x42,0x45,0xec,0xd1,0x5e,0x60,0xa5,0xf5,0xc0,0x75,0x4f,0xf0,0x7f,0x06, +0x6e,0x61,0xc5,0xf8,0xa0,0x00,0x4f,0xf4,0x20,0x26,0x46,0xea,0x82,0x06,0xc5,0xf8, +0xa4,0x60,0x4f,0xf0,0x01,0x06,0xc5,0xf8,0xb0,0x60,0x6f,0x69,0x17,0xf0,0x08,0x06, +0x28,0xd1,0x17,0xf0,0x01,0x06,0xf8,0xd0,0x4f,0xf0,0x7f,0x06,0x6e,0x61,0x4f,0xf0, +0x05,0x06,0xc5,0xf8,0xb8,0x60,0x4f,0xf0,0x00,0x06,0xc5,0xf8,0xa0,0x60,0x0f,0x4e, +0xc5,0xf8,0xa4,0x60,0x4f,0xf0,0x01,0x06,0xc5,0xf8,0xb0,0x60,0x6f,0x69,0x17,0xf0, +0x08,0x06,0x0f,0xd1,0x17,0xf0,0x01,0x06,0xf8,0xd0,0xd5,0xf8,0x00,0x71,0x17,0xf0, +0x01,0x06,0xe1,0xd1,0x17,0xf0,0x02,0x06,0x04,0xd1,0x00,0xeb,0x82,0x00,0x01,0x39, +0x8b,0xd1,0x02,0xe0,0x4f,0xf0,0x00,0x05,0x5d,0x60,0x00,0xbe,0x04,0x00,0x01,0x00, +0x00,0x80,0x0b,0x40,0x00,0x00,0x0d,0x40,0x00,0xc0,0x0b,0x40,0x20,0xc5,0x28,0xd9, +0x10,0xe0,0x00,0xe0,0x00,0x80,0x0f,0x40,0xa0,0x02,0x48,0x50, diff --git a/src/flash/nor/fsl_flexspi.c b/src/flash/nor/fsl_flexspi.c index 06c7ed9aa3..3ee997d505 100644 --- a/src/flash/nor/fsl_flexspi.c +++ b/src/flash/nor/fsl_flexspi.c @@ -44,11 +44,15 @@ #endif #include "imp.h" -#include "spi.h" #include <helper/align.h> +#include <helper/binarybuffer.h> #include <helper/bits.h> #include <helper/time_support.h> +#include <target/algorithm.h> +#include <target/armv7m.h> + +#include "fsl_flexspi.h" #define TIMEOUT_EXEC_IPCMD 100 #define TIMEOUT_RESET 100 @@ -56,166 +60,6 @@ #define TIMEOUT_FLASH_ERASE_CHIP 90000 #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) - -/* - * This LUT sequence number is used only during probe to read flash IDs - * - * The LUT sequence number 0 is the default for FlexSPI NOR (and NAND?) boot - */ -#define LUTNUM_AHB_READ 0 - -/* - * The (only) LUT sequence number to be used by the driver after probing - * - * Other LUT sequence numbers will not be touched and can be used by - * application code without impeding operating by the driver without controller - * re-initialization if some other basic conditions are met. - * - * The LUT sequence number 10 is not used by boot procedures nor the Freescale - * sample code - */ -#define LUTNUM_DRV 10 - -/* 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: @@ -289,6 +133,8 @@ static int read_rxfifo(struct flash_bank *bank, uint8_t *buf, uint32_t datalen) uint32_t nbytes; uint8_t bytes[4]; + LOG_DEBUG("want %" PRIx32 "B", datalen); + ret = target_write_u32(target, io_base + REG_IPRXFCR, IPRXFCR_RXWMRK(datalen / 8 - 1)); if (ret != ERROR_OK) goto err; @@ -431,8 +277,8 @@ err: return ret; } -static int execute_ipcmd_read(struct flash_bank *bank, uint8_t opcode, - void *buf, uint32_t datalen) +static int execute_ipcmd_read_with_lutnum(struct flash_bank *bank, uint8_t opcode, + void *buf, uint32_t datalen, uint32_t lutnum) { struct target *target = bank->target; struct fsl_flexspi_flash_bank *flexspi_info = bank->driver_priv; @@ -456,7 +302,7 @@ static int execute_ipcmd_read(struct flash_bank *bank, uint8_t opcode, lutval[lutidx / 2] |= LUT_DEF(lutidx, OPCODE_STOP, 0, 0); lutidx++; - ret = write_lut_memory(bank, lutval, LUTNUM_DRV); + ret = write_lut_memory(bank, lutval, lutnum); if (ret != ERROR_OK) goto err; @@ -481,7 +327,7 @@ static int execute_ipcmd_read(struct flash_bank *bank, uint8_t opcode, ret = target_write_u32(target, io_base + REG_IPCR1, - IPCR1_IDATASZ(datalen) | IPCR1_ISEQID(LUTNUM_DRV) | IPCR1_ISEQNUM(0)); + IPCR1_IDATASZ(datalen) | IPCR1_ISEQID(lutnum) | IPCR1_ISEQNUM(0)); if (ret != ERROR_OK) goto err; @@ -527,6 +373,12 @@ err: return ret; } +static int execute_ipcmd_read(struct flash_bank *bank, uint8_t opcode, + void *buf, uint32_t datalen) +{ + return execute_ipcmd_read_with_lutnum(bank, opcode, buf, datalen, LUTNUM_DRV); +} + static int execute_ipcmd_write(struct flash_bank *bank, uint8_t opcode, const void *buf, uint32_t datalen) { @@ -649,7 +501,7 @@ static int read_status_reg(struct flash_bank *bank, uint8_t *statusreg) *statusreg = 0; - ret = execute_ipcmd_read(bank, SPIFLASH_READ_STATUS, statusreg, sizeof(*statusreg)); + ret = execute_ipcmd_read_with_lutnum(bank, SPIFLASH_READ_STATUS, statusreg, sizeof(*statusreg), LUTNUM_READ_STATUS); if (ret != ERROR_OK) goto err; @@ -665,7 +517,7 @@ static int write_enable(struct flash_bank *bank) LOG_DEBUG("reached"); - ret = execute_ipcmd_read(bank, SPIFLASH_WRITE_ENABLE, NULL, 0); + ret = execute_ipcmd_read_with_lutnum(bank, SPIFLASH_WRITE_ENABLE, NULL, 0, LUTNUM_WRITE_ENABLE); if (ret != ERROR_OK) goto err; @@ -700,22 +552,13 @@ static int poll_flash_status(struct flash_bank *bank, uint8_t value, uint8_t mas 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) +static int setup_write_lut(struct flash_bank *bank, uint8_t opcode, + uint32_t writelen) { - struct target *target = bank->target; - struct fsl_flexspi_flash_bank *flexspi_info = bank->driver_priv; - uint32_t io_base = flexspi_info->io_base; + LOG_DEBUG("opcode: %02x, writelen: %u", opcode, writelen); + uint32_t lutval[4] = {}; int lutidx = 0; - uint8_t status; - int ret; - - LOG_DEBUG("%" PRIu32 "B to 0x%08" PRIx32, 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); @@ -732,18 +575,36 @@ static int execute_page_program(struct flash_bank *bank, uint8_t opcode, 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); + if (writelen > 0) { + lutval[lutidx / 2] |= LUT_DEF(lutidx, OPCODE_WRITE, lut_pad(1), writelen); lutidx++; } lutval[lutidx / 2] |= LUT_DEF(lutidx, OPCODE_STOP, 0, 0); lutidx++; - ret = write_lut_memory(bank, lutval, LUTNUM_DRV); + return write_lut_memory(bank, lutval, LUTNUM_DRV); +} + +static int program_page(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; + uint8_t status; + int ret; + + LOG_DEBUG("%" PRIu32 "B to 0x%08" PRIx32, datalen, flashaddr); + + ret = write_enable(bank); if (ret != ERROR_OK) goto err; + ret = setup_write_lut(bank, opcode, datalen); + if (ret != ERROR_OK) + return ret; + /* Clear any stray pending status */ ret = target_write_u32(target, io_base + REG_INTR, @@ -810,6 +671,122 @@ err: return ret; } +/* Kinetis Program-LongWord Microcodes */ +static const uint8_t flexspi_flash_blockwrite_algo[] = { +#include "../../../contrib/loaders/flash/fsl_flexspi_blockwrite.inc" +}; + +static int program_block(struct flash_bank *bank, uint8_t opcode, + const void *buf, uint32_t flashaddr, uint32_t datalen, uint32_t blksize) +{ + struct target *target = bank->target; + struct fsl_flexspi_flash_bank *flexspi_info = bank->driver_priv; + uint32_t progdata_size; + struct working_area *algo; + struct working_area *progdata; + struct reg_param reg_params[6]; + struct armv7m_algorithm armv7m_info; + uint32_t end_address; + uint32_t blkcount; + int ret; + uint8_t statusreg; + + /* Set up driver LUT for block write */ + ret = setup_write_lut(bank, opcode, blksize); + if (ret != ERROR_OK) + return ret; + + /* Run an extra read status command to ensure LUTNUM_READ_STATUS is set up */ + ret = read_status_reg(bank, &statusreg); + if (ret != ERROR_OK) + return ret; + + /* Likewise for write enable */ + ret = write_enable(bank); + if (ret != ERROR_OK) + return ret; + + /* allocate working area with flash programming code */ + if (target_alloc_working_area(target, sizeof(flexspi_flash_blockwrite_algo), + &algo) != ERROR_OK) { + LOG_WARNING("no working area available, can't do block memory writes"); + ret = ERROR_TARGET_RESOURCE_NOT_AVAILABLE; + goto out_noalgo; + } + + ret = target_write_buffer(target, algo->address, + sizeof(flexspi_flash_blockwrite_algo), flexspi_flash_blockwrite_algo); + if (ret != ERROR_OK) + goto out_noprogdata; + + /* memory buffer, size must be (multiple of blksize) plus 8 */ + progdata_size = target_get_working_area_avail(target) & ~(sizeof(uint32_t) - 1); + if (progdata_size < (blksize * 2 + 8)) { + LOG_WARNING("large enough working area not available for block writes"); + ret = ERROR_TARGET_RESOURCE_NOT_AVAILABLE; + goto out_noprogdata; + } + + /* Anything bigger than 32k seems superfluous */ + progdata_size = MIN(progdata_size, 32768 + 8); + + /* Ensure it's a multiple of the blksize plus the extra 8 bytes for the rp/wp */ + progdata_size = ALIGN_DOWN(progdata_size, blksize) + 8; + + if (target_alloc_working_area(target, progdata_size, &progdata) != ERROR_OK) { + LOG_ERROR("allocating working area failed"); + ret = ERROR_TARGET_RESOURCE_NOT_AVAILABLE; + goto out_noprogdata; + } + + blkcount = datalen / blksize; + + armv7m_info.common_magic = ARMV7M_COMMON_MAGIC; + armv7m_info.core_mode = ARM_MODE_THREAD; + + init_reg_param(®_params[0], "r0", 32, PARAM_IN_OUT); /* flash address */ + init_reg_param(®_params[1], "r1", 32, PARAM_OUT); /* block count */ + init_reg_param(®_params[2], "r2", 32, PARAM_OUT); /* block length */ + init_reg_param(®_params[3], "r3", 32, PARAM_OUT); /* progdata start */ + init_reg_param(®_params[4], "r4", 32, PARAM_OUT); /* progdata end */ + init_reg_param(®_params[5], "r5", 32, PARAM_OUT); /* FLEXSPI base */ + + buf_set_u32(reg_params[0].value, 0, 32, flashaddr); + buf_set_u32(reg_params[1].value, 0, 32, blkcount); + buf_set_u32(reg_params[2].value, 0, 32, blksize / sizeof(uint32_t)); + buf_set_u32(reg_params[3].value, 0, 32, progdata->address); + buf_set_u32(reg_params[4].value, 0, 32, + progdata->address + progdata->size); + buf_set_u32(reg_params[5].value, 0, 32, flexspi_info->io_base); + + ret = target_run_flash_async_algorithm(target, buf, blkcount, blksize, + 0, NULL, ARRAY_SIZE(reg_params), reg_params, + progdata->address, progdata->size, + algo->address, 0, &armv7m_info); + + if (ret == ERROR_FLASH_OPERATION_FAILED) { + end_address = buf_get_u32(reg_params[0].value, 0, 32); + + LOG_ERROR("Error writing flash at %08" PRIx32, end_address); + } else if (ret != ERROR_OK) { + LOG_ERROR("Error executing flash block programming algorithm"); + } + + destroy_reg_param(®_params[0]); + destroy_reg_param(®_params[1]); + destroy_reg_param(®_params[2]); + destroy_reg_param(®_params[3]); + destroy_reg_param(®_params[4]); + + target_free_working_area(target, progdata); +out_noprogdata: + target_free_working_area(target, algo); +out_noalgo: + + return ret; +} + + static int erase_sector(struct flash_bank *bank, unsigned int sector) { struct target *target = bank->target; @@ -821,7 +798,7 @@ static int erase_sector(struct flash_bank *bank, unsigned int sector) uint8_t status; int ret; - LOG_DEBUG("reached"); + LOG_DEBUG("number %u", sector); ret = write_enable(bank); if (ret != ERROR_OK) @@ -1305,19 +1282,34 @@ static int fsl_flexspi_write(struct flash_bank *bank, const uint8_t *buffer, * thereof. */ if (offset % wrsize) { - ret = execute_page_program(bank, flexspi_info->dev.pprog_cmd, buffer, offset, + ret = program_page(bank, flexspi_info->dev.pprog_cmd, buffer, offset, offset % flexspi_info->dev.pagesize); if (ret != ERROR_OK) goto err; } + /* Attempt to program the entire block using on-chip algorithm */ + uint32_t blockwrlen = ALIGN_DOWN(count, wrsize); + LOG_DEBUG("Block program %" PRIu32 "B at 0x%08" PRIx32, blockwrlen, offset); + ret = program_block(bank, flexspi_info->dev.pprog_cmd, buffer, offset, + blockwrlen, wrsize); + if (ret == ERROR_OK) { + count -= blockwrlen; + offset += blockwrlen; + buffer += blockwrlen; + } else { + LOG_WARNING("Can't use block program; falling back to page program"); + return ret; + } + + /* Program by page whatever we couldn't program as a block */ while (count > 0) { if (count < wrsize) wrsize = count; - ret = execute_page_program(bank, flexspi_info->dev.pprog_cmd, - buffer, offset, wrsize); + ret = program_page(bank, flexspi_info->dev.pprog_cmd, buffer, offset, + wrsize); if (ret != ERROR_OK) goto err; diff --git a/src/flash/nor/fsl_flexspi.h b/src/flash/nor/fsl_flexspi.h new file mode 100644 index 0000000000..c199647276 --- /dev/null +++ b/src/flash/nor/fsl_flexspi.h @@ -0,0 +1,198 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +/*************************************************************************** + * Copyright (C) 2022 by Brandon Martin and Mothic Technologies LLC * + * marti...@mothictech.com * + ***************************************************************************/ + +#include "spi.h" + +/* Make the BIT macro available from assembly */ +#ifndef BIT +# define BIT(x) (1 << (x)) +#endif + +/* 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) + +/* + * This LUT sequence ID is used during probe to read flash IDs and then is + * left configured so that the FLEXSPI can provide the usual linear + * memory-mapped access to the attached serial flash + * + * The LUT sequence ID 0 is the default for FlexSPI NOR (and NAND?) boot + * and is used by every example from Freescale/NXP that I've ever seen for AHB + * reads, though it doesn't strictly have to be. + */ +#define LUTNUM_AHB_READ 0 + +/* + * This LUT sequence ID is used to read flash status since it's a common + * thing to have to do interleaved with other actions + * + * The LUT sequence ID 1 is pretty much universally used for this purpose + * throughout Freescale/NXP's example/HAL code, and we at least attempt to use + * it in a compatible way. + */ +#define LUTNUM_READ_STATUS 1 + +/* + * This LUT sequence ID is used to perform write enable since it's also a + * common thing to have to do interleaved with other actions + * + * Like with read status, LUT ID 3 seems to be used for this pretty much + * everywhere at least for NOR flash. + */ +#define LUTNUM_WRITE_ENABLE 3 + +/* + * The usual LUT sequence ID to be used by the driver after probing for + * general activities other than reading status and performing write enable + * + * Other LUT sequence numbers will not be touched and can be used by + * application code without impeding operating by the driver without controller + * re-initialization if some other basic conditions are met. + * + * The LUT sequence number 10 is not used by boot procedures nor the Freescale + * sample code + */ +#define LUTNUM_DRV 10 + +/* 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 + --