This is an automated email from Gerrit. Hellosun Wu ([email protected]) just uploaded a new patch set to Gerrit, which you can find at http://openocd.zylin.com/5339
-- gerrit commit cedcc8435a9e48280a489f67bbb25834fb44a659 Author: Hellosun Wu <[email protected]> Date: Fri Oct 18 16:11:39 2019 +0800 flash: Add new ndsspi driver support The AndeShape Platform family uses ATCSPI200 as flash interface. Add basic support for: -AE210P -AE250 Tested on ADP-XC7KFF676 and Corvette-F1 Development Platform with 4MB SPI flash. Change-Id: Ic1a4a02a9a28279b5e00bc0122543a933a80c672 Signed-off-by: Hellosun Wu <[email protected]> Co-authored-by: yating <[email protected]> diff --git a/contrib/loaders/flash/ndsspi/Makefile b/contrib/loaders/flash/ndsspi/Makefile new file mode 100644 index 0000000..ed262a8 --- /dev/null +++ b/contrib/loaders/flash/ndsspi/Makefile @@ -0,0 +1,28 @@ +BIN2C = ../../../../src/helper/bin2char.sh + +CROSS_COMPILE ?= riscv64-unknown-elf- + +CC=$(CROSS_COMPILE)gcc +OBJCOPY=$(CROSS_COMPILE)objcopy +OBJDUMP=$(CROSS_COMPILE)objdump + +CFLAGS = -march=rv32i -mabi=ilp32 -x assembler-with-cpp -nostdlib -nostartfiles + +all: ndsspi200.inc + +.PHONY: clean + +%.elf: %.S + $(CC) $(CFLAGS) $< -o $@ + +%.lst: %.elf + $(OBJDUMP) -S $< > $@ + +%.bin: %.elf + $(OBJCOPY) -Obinary $< $@ + +%.inc: %.bin + $(BIN2C) < $< > $@ + +clean: + -rm -f *.elf *.lst *.bin *.inc diff --git a/contrib/loaders/flash/ndsspi/ndsspi200.S b/contrib/loaders/flash/ndsspi/ndsspi200.S new file mode 100644 index 0000000..fde494c --- /dev/null +++ b/contrib/loaders/flash/ndsspi/ndsspi200.S @@ -0,0 +1,152 @@ +#define RA_WAIT ra +#define RA_CLEAR_FIFO sp +#define RA_EXEC_CMD gp +#define RA_READ_STATUS tp +#define RA_WRITE_ENABLE s0 +#define RA_WIP s1 + + .global _start +_start: +command_table: + j main //address 0x0 + ebreak //address 0x4 + j erase //address 0x8 + j tx //address 0xc + +main: + lbu t0, 0(a0) + addi a0, a0, 1 + la t1, command_table + add t0, t0, t1 + jr t0 + +// 0xf0b00034:SPI Status Register +// Set t2 value to check status(ex SPIActive:set t2 0x1) +wait: + li a1, 0xf0b00034 + lw a2, 0(a1) + and a2, a2, t2 + bnez a2, wait + jalr x0, RA_WAIT, 0x0 + +// 0xf0b00030:SPI Control Register +// Reset RXFIFORST and TXFIFORST +clear_fifo: + li a1, 0xf0b00030 + lw a2, 0(a1) + ori a2, a2, 0x2 + ori a2, a2, 0x4 + sw a2, 0(a1) + jalr x0, RA_CLEAR_FIFO, 0x0 + +// 0xf0b0002c:SPI Data Register +// 0xf0b00020:SPI Transfer Control Register +// 0xf0b00024:SPI Command Register +// Set t0 as flash command(ex write_enable command:set t0 0x6) +// Set t1 as transfer information(ex transfer mode:read/write, tx/rx count) +// Set dummy value to trigger SPI Transfer +exec_cmd: + li t2, 0x1 + jal RA_WAIT, wait + jal RA_CLEAR_FIFO, clear_fifo + li a2, 0xf0b0002c + sw t0, 0(a2) + li a2, 0xf0b00020 + sw t1, 0(a2) + li a1, 0x0 + li a2, 0xf0b00024 + sw a1, 0(a2) + jalr x0, RA_EXEC_CMD, 0x0 + +// Execute RDSR(read status register) command: set t0 to flash command 0x5, +// set t1 to transfer information 0x3000000 +// (transfer mode:write, read and write count:0 + 1(command 0x5:1byte)) +// +// After execute command, wait to read data : set t2 0x4000 to wait Receive FIFO not Empty +// Read status register value(from SPI Data Register) store at a2 and return +read_status: + li t0, 0x5 + li t1, 0x3000000 + jal RA_EXEC_CMD, exec_cmd + li t2, 0x4000 + jal RA_WAIT, wait + li a1, 0xf0b0002c + lw a2, 0(a1) + jalr x0, RA_READ_STATUS, 0x0 + +// Execute WREN(write enable) command: +// set t0 to flash command 0x6 +// set t1 to transfer information 0x1000000 +// (transfer mode:write only and write count:0 + 1(command 0x6:1byte)) +// +// After execute WREN command, call read status function to get status register value(store at a2) +// Check bit 1(write enable latch) is 1 (means write enable) +write_enable: + li t0, 0x6 + li t1, 0x1000000 + jal RA_EXEC_CMD, exec_cmd + jal RA_READ_STATUS, read_status + andi a2, a2, 0x2 + beqz a2, write_enable + jalr x0, RA_WRITE_ENABLE, 0x0 + +// Call read status function to get status register value(store at a2) +// Check status register value bit 0(write in progress bit) is 0 (means not in write operation) +wip: + jal RA_READ_STATUS, read_status + andi a2, a2, 0x1 + bnez a2, wip + jalr x0, RA_WIP, 0x0 + +// Erase step: +// set write enable and check -> +// execute sector erase command -> +// check not in write operation +// +// Execute SE(sector erase) command: +// set t0 to flash command(read from address:a0) +// set t1 to transfer information 0x1003000 +// (transfer mode:write only and write count:3 + 1(command t0:4bytes)) +erase: + jal RA_WRITE_ENABLE, write_enable + lw t0, 0(a0) + addi a0, a0, 4 + li t1, 0x1003000 + jal RA_EXEC_CMD, exec_cmd + jal RA_WIP, wip + j main + +// TX step: +// set write enable and check -> +// execute page program command -> +// loop tx_word -> +// check not in write operation +// +// Execute PP(page program) command: +// set t0 to flash command(read from address:a0) +// set t1 to transfer information 0x1103000 +// (transfer mode:write only and write count:0x103 + 1 +// (command t0:4bytes and tx data:0x100bytes(one page size))) +// +// tx_word: +// set t2 0x800000 to wait Transmit FIFO not Full and +// send one word tx data to SPI Data Register +tx: + jal RA_WRITE_ENABLE, write_enable + lw t0, 0(a0) + addi a0, a0, 4 + li t1, 0x1103000 + jal RA_EXEC_CMD, exec_cmd + li a3, 0x0 + li a4, 0x40 +tx_word: + li t2, 0x800000 + jal RA_WAIT, wait + lw a1, 0(a0) + addi a0, a0, 4 + li a2, 0xf0b0002c + sw a1, 0(a2) + addi a3, a3, 1 + bltu a3, a4, tx_word + jal RA_WIP, wip + j main diff --git a/contrib/loaders/flash/ndsspi/ndsspi200.inc b/contrib/loaders/flash/ndsspi/ndsspi200.inc new file mode 100644 index 0000000..9f7946c --- /dev/null +++ b/contrib/loaders/flash/ndsspi/ndsspi200.inc @@ -0,0 +1,22 @@ +/* Autogenerated with ../../../../src/helper/bin2char.sh */ +0x6f,0x00,0x00,0x01,0x73,0x00,0x10,0x00,0x6f,0x00,0xc0,0x0d,0x6f,0x00,0x40,0x0f, +0x83,0x42,0x05,0x00,0x13,0x05,0x15,0x00,0x17,0x03,0x00,0x00,0x13,0x03,0x83,0xfe, +0xb3,0x82,0x62,0x00,0x67,0x80,0x02,0x00,0xb7,0x05,0xb0,0xf0,0x93,0x85,0x45,0x03, +0x03,0xa6,0x05,0x00,0x33,0x76,0x76,0x00,0xe3,0x18,0x06,0xfe,0x67,0x80,0x00,0x00, +0xb7,0x05,0xb0,0xf0,0x93,0x85,0x05,0x03,0x03,0xa6,0x05,0x00,0x13,0x66,0x26,0x00, +0x13,0x66,0x46,0x00,0x23,0xa0,0xc5,0x00,0x67,0x00,0x01,0x00,0x93,0x03,0x10,0x00, +0xef,0xf0,0x9f,0xfc,0x6f,0xf1,0xdf,0xfd,0x37,0x06,0xb0,0xf0,0x13,0x06,0xc6,0x02, +0x23,0x20,0x56,0x00,0x37,0x06,0xb0,0xf0,0x13,0x06,0x06,0x02,0x23,0x20,0x66,0x00, +0x93,0x05,0x00,0x00,0x37,0x06,0xb0,0xf0,0x13,0x06,0x46,0x02,0x23,0x20,0xb6,0x00, +0x67,0x80,0x01,0x00,0x93,0x02,0x50,0x00,0x37,0x03,0x00,0x03,0xef,0xf1,0x1f,0xfc, +0xb7,0x43,0x00,0x00,0xef,0xf0,0x5f,0xf8,0xb7,0x05,0xb0,0xf0,0x93,0x85,0xc5,0x02, +0x03,0xa6,0x05,0x00,0x67,0x00,0x02,0x00,0x93,0x02,0x60,0x00,0x37,0x03,0x00,0x01, +0xef,0xf1,0xdf,0xf9,0x6f,0xf2,0x1f,0xfd,0x13,0x76,0x26,0x00,0xe3,0x06,0x06,0xfe, +0x67,0x00,0x04,0x00,0x6f,0xf2,0x1f,0xfc,0x13,0x76,0x16,0x00,0xe3,0x1c,0x06,0xfe, +0x67,0x80,0x04,0x00,0x6f,0xf4,0x5f,0xfd,0x83,0x22,0x05,0x00,0x13,0x05,0x45,0x00, +0x37,0x33,0x00,0x01,0xef,0xf1,0x9f,0xf6,0xef,0xf4,0xdf,0xfd,0x6f,0xf0,0x5f,0xf1, +0x6f,0xf4,0x9f,0xfb,0x83,0x22,0x05,0x00,0x13,0x05,0x45,0x00,0x37,0x33,0x10,0x01, +0xef,0xf1,0xdf,0xf4,0x93,0x06,0x00,0x00,0x13,0x07,0x00,0x04,0xb7,0x03,0x80,0x00, +0xef,0xf0,0x9f,0xf0,0x83,0x25,0x05,0x00,0x13,0x05,0x45,0x00,0x37,0x06,0xb0,0xf0, +0x13,0x06,0xc6,0x02,0x23,0x20,0xb6,0x00,0x93,0x86,0x16,0x00,0xe3,0xe0,0xe6,0xfe, +0xef,0xf4,0x5f,0xf9,0x6f,0xf0,0xdf,0xec, diff --git a/src/flash/nor/Makefile.am b/src/flash/nor/Makefile.am index 34f91ce..3224af9 100644 --- a/src/flash/nor/Makefile.am +++ b/src/flash/nor/Makefile.am @@ -29,6 +29,7 @@ NOR_DRIVERS = \ %D%/esirisc_flash.c \ %D%/faux.c \ %D%/fespi.c \ + %D%/ndsspi200.c \ %D%/fm3.c \ %D%/fm4.c \ %D%/jtagspi.c \ diff --git a/src/flash/nor/drivers.c b/src/flash/nor/drivers.c index 551f389..c3259be 100644 --- a/src/flash/nor/drivers.c +++ b/src/flash/nor/drivers.c @@ -44,6 +44,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 ndsspi200_flash; extern const struct flash_driver jtagspi_flash; extern const struct flash_driver kinetis_flash; extern const struct flash_driver kinetis_ke_flash; @@ -114,6 +115,7 @@ static const struct flash_driver * const flash_drivers[] = { &fm3_flash, &fm4_flash, &fespi_flash, + &ndsspi200_flash, &jtagspi_flash, &kinetis_flash, &kinetis_ke_flash, diff --git a/src/flash/nor/ndsspi200.c b/src/flash/nor/ndsspi200.c new file mode 100644 index 0000000..afbeef2 --- /dev/null +++ b/src/flash/nor/ndsspi200.c @@ -0,0 +1,1296 @@ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "imp.h" +#include "spi.h" +#include <jtag/jtag.h> +#include "target/riscv/program.h" +#include "target/algorithm.h" + +#define REG_SMU_BASE 0xF0100000 +#define CPE_SPIB_BASE 0xF0B00000 + +unsigned int spib200_base = REG_SMU_BASE, spib200_ctrl = 0; + +#define SPIB_REG_DCTRL (spib200_base+0x20) +#define SPIB_REG_CMD (spib200_base+0x24) +#define SPIB_REG_DATA (spib200_base+0x2c) +#define SPIB_REG_CTRL (spib200_base+0x30) +#define SPIB_REG_FIFOST (spib200_base+0x34) +#define SPIB_REG_REGTIMING (spib200_base+0x40) + +/*-- Data Control Reg --*/ +#define SPIB_DCTRL_CMDEN_MASK 0x40000000 +#define SPIB_DCTRL_ADDREN_MASK 0x20000000 +#define SPIB_DCTRL_TRAMODE_MASK 0x0f000000 +#define SPIB_DCTRL_WCNT_MASK 0x001ff000 +#define SPIB_DCTRL_DYCNT_MASK 0x00000600 +#define SPIB_DCTRL_RCNT_MASK 0x000001ff +#define SPIB_DCTRL_CMDEN_OFFSET 30 +#define SPIB_DCTRL_ADDREN_OFFSET 29 +#define SPIB_DCTRL_TRAMODE_OFFSET 24 +#define SPIB_DCTRL_WCNT_OFFSET 12 +#define SPIB_DCTRL_DYCNT_OFFSET 9 +#define SPIB_DCTRL_RCNT_OFFSET 0 +/*-- Control Reg --*/ +#define SPIB_CTRL_TXFRST_MASK 0x00000004 +#define SPIB_CTRL_RXFRST_MASK 0x00000002 +/*-- FIFO Status Reg --*/ +#define SPIB_FIFOST_TXFFL_MASK 0x00800000 +#define SPIB_FIFOST_RXFEM_MASK 0x00004000 +#define SPIB_FIFOST_SPIBSY_MASK 0x00000001 +/*-- SPIB transfer mode--*/ +#define SPIB_TM_WRonly 0x1 +#define SPIB_TM_RDonly 0x2 +#define SPIB_TM_WR_RD 0x3 + +#define FLASH_RETRY_TIMES 100 + +/*-- SPI OP --*/ +#define SPIROM_OP_RDID 0x9f /*-- manufacturer/device ID read --*/ +#define SPIROM_OP_RDSR 0x05 /*-- read status register --*/ +#define SPIROM_OP_WREN 0x06 /*-- write enable --*/ +#define SPIROM_OP_SE 0x20 /*-- sector erase --*/ +#define SPIROM_OP_WRSR 0x01 /*-- write status register --*/ +#define SPIROM_OP_PP 0x02 /*-- page program --*/ + +#define SPIROM_ID_MASK 0x00ffffff +/*-- bit mask for status register --*/ +#define SPIROM_SR_WIP_MASK 0x01 /*-- write in progress --*/ +#define SPIROM_SR_WEL_MASK 0x02 /*-- write enable --*/ +#define SPIROM_SR_BP_MASK 0x3C /*-- BP protection mode --*/ + +/* SPI ROM cmd */ +#define SPIROM_CMD_RDID 0x1 +#define SPIROM_CMD_RDST 0x2 +#define SPIROM_CMD_WREN 0x3 +#define SPIROM_CMD_ERASE 0x5 +#define SPIROM_CMD_PROGRAM 0x6 +#define SPIROM_CMD_WRSR 0xD + +struct ndsspi200_flash_bank { + int probed; + struct flash_device *dev; +}; + +uint8_t algorithm_bin[] = { +#include "../../../contrib/loaders/flash/ndsspi/ndsspi200.inc" +}; + +#define STEP_EXIT 0x4 +#define STEP_ERASE 0x8 +#define STEP_TX 0xc +#define STEP_NOP 0xff + +uint32_t erase_used = 5; +uint32_t tx_used = 5; + +struct algorithm_steps { + uint32_t size; + uint32_t used; + uint8_t **steps; +}; + +static int inw(struct flash_bank *bank, uint32_t get_address, uint32_t *get_value) +{ + struct target *target = bank->target; + int retValue = target_read_u32(target, get_address, get_value); + return retValue; +} + +static int outw(struct flash_bank *bank, uint32_t set_address, uint32_t set_value) +{ + struct target *target = bank->target; + int retValue = target_write_u32(target, set_address, set_value); + return retValue; +} + +static int spib_get_regtiming(struct flash_bank *bank, unsigned int *get_value) +{ + if (inw(bank, SPIB_REG_REGTIMING, get_value) != ERROR_OK) { + LOG_ERROR("read spib timing reg(addr:%x) failed", SPIB_REG_REGTIMING); + return ERROR_FAIL; + } + return ERROR_OK; +} + +static int spib_set_regtiming(struct flash_bank *bank, unsigned int data) +{ + if (outw(bank, SPIB_REG_REGTIMING, data) != ERROR_OK) { + LOG_ERROR("write spib timing reg:%x value:%x failed", SPIB_REG_REGTIMING, data); + return ERROR_FAIL; + } + return ERROR_OK; +} + +static int spib_get_ctrl(struct flash_bank *bank, unsigned int *get_value) +{ + if (inw(bank, SPIB_REG_CTRL, get_value) != ERROR_OK) { + LOG_ERROR("read spib ctrl reg(addr:%x) failed", SPIB_REG_CTRL); + return ERROR_FAIL; + } + return ERROR_OK; +} + +static int spib_set_ctrl(struct flash_bank *bank, unsigned int data) +{ + if (outw(bank, SPIB_REG_CTRL, data) != ERROR_OK) { + LOG_ERROR("write spib ctrl reg:%x value:%x failed", SPIB_REG_CTRL, data); + return ERROR_FAIL; + } + return ERROR_OK; +} + +static int spib_get_data(struct flash_bank *bank, unsigned int *get_value) +{ + if (inw(bank, SPIB_REG_DATA, get_value) != ERROR_OK) { + LOG_ERROR("read spib data reg(addr:%x) failed", SPIB_REG_DATA); + return ERROR_FAIL; + } + return ERROR_OK; +} + +static int spib_set_data(struct flash_bank *bank, unsigned int data) +{ + if (outw(bank, SPIB_REG_DATA, data) != ERROR_OK) { + LOG_ERROR("write spib data reg:%x value:%x failed", SPIB_REG_DATA, data); + return ERROR_FAIL; + } + return ERROR_OK; +} + +static int spib_set_dctrl(struct flash_bank *bank, unsigned int data) +{ + if (outw(bank, SPIB_REG_DCTRL, data) != ERROR_OK) { + LOG_ERROR("write spib dctrl reg:%x value:%x failed", SPIB_REG_DCTRL, data); + return ERROR_FAIL; + } + return ERROR_OK; +} + +static int spib_set_cmd(struct flash_bank *bank, unsigned int data) +{ + if (outw(bank, SPIB_REG_CMD, data) != ERROR_OK) { + LOG_ERROR("write spib cmd reg:%x value:%x failed", SPIB_REG_CMD, data); + return ERROR_FAIL; + } + return ERROR_OK; +} + +static int spib_get_fifost(struct flash_bank *bank, unsigned int *get_value) +{ + if (inw(bank, SPIB_REG_FIFOST, get_value) != ERROR_OK) { + LOG_ERROR("read spib status reg(addr:%x) failed", SPIB_REG_FIFOST); + return ERROR_FAIL; + } + return ERROR_OK; +} + +static unsigned int spib_get_busy(struct flash_bank *bank) +{ + unsigned int reg = 0; + if (spib_get_fifost(bank, ®) != ERROR_OK) + return 1; + return reg & SPIB_FIFOST_SPIBSY_MASK; +} + +static unsigned int spib_wait_spi(struct flash_bank *bank) +{ + unsigned int i; + unsigned int timeout = FLASH_RETRY_TIMES; + + for (i = 1; i < timeout; i++) { + if (spib_get_busy(bank) == 0) + return 0; + alive_sleep(1); + } + LOG_ERROR("spib_wait_spi: timeout\n"); + return 1; +} + +static unsigned int spib_get_rx_empty(struct flash_bank *bank) +{ + unsigned int reg = 0; + if (inw(bank, SPIB_REG_FIFOST, ®) != ERROR_OK) { + LOG_ERROR("read spib status_reg:%x failed", SPIB_REG_FIFOST); + return 1; + } + return reg & SPIB_FIFOST_RXFEM_MASK; +} + +static void spib_clr_fifo(struct flash_bank *bank) +{ + spib200_ctrl |= (SPIB_CTRL_TXFRST_MASK | SPIB_CTRL_RXFRST_MASK); + spib_set_ctrl(bank, spib200_ctrl); + spib_get_ctrl(bank, &spib200_ctrl); + LOG_DEBUG("SPIB Control REG value = 0x%08x\n", spib200_ctrl); +} + +static unsigned int spib_prepare_dctrl( + unsigned int cmden, + unsigned int addren, + unsigned int tm, + unsigned int wcnt, + unsigned int dycnt, + unsigned int rcnt) +{ + unsigned int v[8]; + unsigned int i; + unsigned int dctrl = 0x0; + + v[0] = ((cmden << SPIB_DCTRL_CMDEN_OFFSET) & SPIB_DCTRL_CMDEN_MASK); + v[1] = ((addren << SPIB_DCTRL_ADDREN_OFFSET) & SPIB_DCTRL_ADDREN_MASK); + v[2] = ((tm << SPIB_DCTRL_TRAMODE_OFFSET) & SPIB_DCTRL_TRAMODE_MASK); + v[3] = ((wcnt << SPIB_DCTRL_WCNT_OFFSET) & SPIB_DCTRL_WCNT_MASK); + v[4] = ((dycnt << SPIB_DCTRL_DYCNT_OFFSET) & SPIB_DCTRL_DYCNT_MASK); + v[5] = ((rcnt << SPIB_DCTRL_RCNT_OFFSET) & SPIB_DCTRL_RCNT_MASK); + + for (i = 0; i < 6; i++) + dctrl |= v[i]; + return dctrl; +} + +static unsigned int spirom_prepare_cmd(unsigned int cmd, unsigned int addr) +{ + unsigned int b0 = (cmd & 0xff); + unsigned int b1 = (((addr >> 16) & 0xff) << 8); + unsigned int b2 = (((addr >> 8) & 0xff) << 16); + unsigned int b3 = ((addr & 0xff) << 24); + unsigned int word = (b0 | b1 | b2 | b3); + return word; +} + +static int spib_exe_cmd(struct flash_bank *bank, unsigned int op_addr, unsigned int spib_dctrl) +{ + /*-- push flash command into tx fifo --*/ + if (spib_set_data(bank, op_addr) != ERROR_OK) + return ERROR_FAIL; + /*-- set dctrl --*/ + if (spib_set_dctrl(bank, spib_dctrl) != ERROR_OK) + return ERROR_FAIL; + /*-- set dummy command to trigger transation start --*/ + if (spib_set_cmd(bank, 0x0) != ERROR_OK) + return ERROR_FAIL; + return ERROR_OK; +} + +static int spib_tx_data(struct flash_bank *bank, unsigned int *pTxdata, int TxBytes) +{ + unsigned int i, j; + unsigned int TxWords = (TxBytes / 4); + unsigned int *p_src_buffer = (unsigned int *)pTxdata; + unsigned int timeout = FLASH_RETRY_TIMES; + unsigned int reg, spib_tx_full; + + for (i = 0; i < TxWords; i++) { + for (j = 0; j < timeout; j++) { + if (spib_get_fifost(bank, ®) != ERROR_OK) + return ERROR_FAIL; + spib_tx_full = reg & SPIB_FIFOST_TXFFL_MASK; + if (spib_tx_full == 0) + break; + } + if (spib_tx_full == 1) { + LOG_ERROR("spib_set_fifo: write fifo timeout\n"); + return ERROR_FAIL; + } + if (spib_set_data(bank, *p_src_buffer++) != ERROR_OK) + return ERROR_FAIL; + } + return ERROR_OK; +} + +static unsigned int spirom_cmd_send( + struct flash_bank *bank, + unsigned int cmd, + unsigned int addr, + unsigned int bytes, + unsigned int *pdata, + unsigned int *Retdata) +{ + unsigned int i; + unsigned int timeout = FLASH_RETRY_TIMES; + unsigned int spib_dctrl; + unsigned int op_addr; + unsigned int data = 0; + unsigned int spib_busy = 0; + unsigned int spib_rx_empty; + + /*-- wait if there is active transaction --*/ + spib_busy = spib_wait_spi(bank); + if (spib_busy != 0) { + *Retdata = 0; + return 1; + } + /*-- clear tx/rx fifo --*/ + spib_clr_fifo(bank); + + if (cmd == SPIROM_CMD_RDID) { + op_addr = SPIROM_OP_RDID; + spib_dctrl = spib_prepare_dctrl(0x0, 0x0, SPIB_TM_WR_RD, 0, 0, 2); + if (spib_exe_cmd(bank, op_addr, spib_dctrl) != ERROR_OK) + return 1; + /*-- wait data completion --*/ + for (i = 1; i < timeout; i++) { + spib_rx_empty = spib_get_rx_empty(bank); + if (spib_rx_empty == 0) + break; + } + if (spib_rx_empty != 0) { + LOG_ERROR("RDID timeout\n"); + *Retdata = 0; + return 1; + } + if (spib_get_data(bank, &data) != ERROR_OK) + return 1; + } else if (cmd == SPIROM_CMD_RDST) { + /*-- execute command --*/ + op_addr = SPIROM_OP_RDSR; + spib_dctrl = spib_prepare_dctrl(0x0, 0x0, SPIB_TM_WR_RD, 0, 0, 0); + if (spib_exe_cmd(bank, op_addr, spib_dctrl) != ERROR_OK) + return 1; + /*-- wait data completion --*/ + for (i = 1; i < timeout; i++) { + spib_rx_empty = spib_get_rx_empty(bank); + if (spib_rx_empty == 0) + break; + } + if (spib_rx_empty != 0) { + LOG_DEBUG("RDST timeout\n"); + *Retdata = 0; + return 1; + } + if (spib_get_data(bank, &data) != ERROR_OK) + return 1; + } else if (cmd == SPIROM_CMD_WREN) { + /*-- execute command --*/ + op_addr = SPIROM_OP_WREN; + spib_dctrl = spib_prepare_dctrl(0x0, 0x0, SPIB_TM_WRonly, 0, 0, 0); + if (spib_exe_cmd(bank, op_addr, spib_dctrl) != ERROR_OK) + return 1; + /*-- wait completion --*/ + spib_busy = spib_wait_spi(bank); + if (spib_busy != 0) { + LOG_ERROR("WREN timeout\n"); + *Retdata = 0; + return 1; + } + } else if (cmd == SPIROM_CMD_ERASE) { + /*-- execute command --*/ + op_addr = spirom_prepare_cmd(SPIROM_OP_SE, addr); + spib_dctrl = spib_prepare_dctrl(0x0, 0x0, SPIB_TM_WRonly, 3, 0, 0); + if (spib_exe_cmd(bank, op_addr, spib_dctrl) != ERROR_OK) + return 1; + /*-- wait completion --*/ + spib_busy = spib_wait_spi(bank); + if (spib_busy != 0) { + LOG_ERROR("ERASE timeout\n"); + *Retdata = 0; + return 1; + } + } else if (cmd == SPIROM_CMD_PROGRAM) { + /*-- execute command --*/ + op_addr = spirom_prepare_cmd(SPIROM_OP_PP, addr); + spib_dctrl = spib_prepare_dctrl(0x0, 0x0, SPIB_TM_WRonly, 3+bytes, 0, 0); + if (spib_exe_cmd(bank, op_addr, spib_dctrl) != ERROR_OK) + return 1; + /*-- write data --*/ + if (spib_tx_data(bank, pdata, bytes) != ERROR_OK) + return 1; + } else if (cmd == SPIROM_CMD_WRSR) { + /*-- execute command --*/ + op_addr = (SPIROM_OP_WRSR | (addr<<8)); + spib_dctrl = spib_prepare_dctrl(0x0, 0x0, SPIB_TM_WRonly, 1, 0, 0); + if (spib_exe_cmd(bank, op_addr, spib_dctrl) != ERROR_OK) + return 1; + /*-- wait completion --*/ + spib_busy = spib_wait_spi(bank); + if (spib_busy != 0) { + LOG_ERROR("ERASE timeout\n"); + *Retdata = 0; + return 1; + } + } else { + LOG_ERROR("wrong cmd\n"); + *Retdata = 0; + return 1; + } + *Retdata = data; + return 0; +} + +static int platform_init(struct flash_bank *bank) +{ + LOG_DEBUG("%s", __func__); + unsigned int smu_id_reg = 0, smu_base; + + spib200_base = CPE_SPIB_BASE; + if (spib_get_ctrl(bank, &spib200_ctrl) != ERROR_OK) + return ERROR_FAIL; + LOG_DEBUG("SPIB Control REG value = 0x%08x\n", spib200_ctrl); + + smu_base = REG_SMU_BASE; + if (inw(bank, smu_base, &smu_id_reg) != ERROR_OK) { + LOG_ERROR("read smu_base:%x failed", smu_base); + return ERROR_FAIL; + } + LOG_DEBUG("SMU_VER_ID = 0x%08x\n", smu_id_reg); + return ERROR_OK; +} + +static int mxic200_check(struct flash_bank *bank, unsigned int *get_id) +{ + LOG_DEBUG("%s", __func__); + unsigned int spib_timing, result, RetData; + /* Set SCLK period = ((0+1)*2)*(Period of the SPI clock source) */ + if (spib_get_regtiming(bank, &spib_timing) != ERROR_OK) + return ERROR_FAIL; + + RetData = (spib_timing & (~0xFF)); + if (spib_set_regtiming(bank, RetData) != ERROR_OK) + return ERROR_FAIL; + + if (spib_get_regtiming(bank, &spib_timing) != ERROR_OK) + return ERROR_FAIL; + LOG_DEBUG("mxic200_check: REGTIMING=%x\n", spib_timing); + + result = spirom_cmd_send(bank, SPIROM_CMD_RDID, 0x0, 0, NULL, &RetData); + if (result != 0) { + LOG_DEBUG("ERROR: read spi rom id fail\n"); + return ERROR_FAIL; + } + RetData &= SPIROM_ID_MASK; + LOG_DEBUG("mxic200: ROM ID = 0x%08x\n", RetData); + *get_id = RetData; + return 0; +} + +static int mxic200_erase(struct flash_bank *bank, unsigned int EraseSectorIndex) +{ + unsigned int EraseAddrStart, EraseSize, j; + unsigned int result, RetData, timeout = FLASH_RETRY_TIMES; + + EraseAddrStart = bank->sectors[EraseSectorIndex].offset; + EraseSize = bank->sectors[EraseSectorIndex].size; + /*---------------------*/ + /*-- ERASE procedure */ + /*---------------------*/ + for (j = 1; j < timeout; j++) { + /*-- write enable --*/ + result = spirom_cmd_send(bank, SPIROM_CMD_WREN, 0x0, 0, NULL, &RetData); + if (result != 0) { + LOG_ERROR("mxic200_erase: (erase) enable write fail\n"); + return ERROR_FAIL; + } + /*-- get enable status --*/ + result = spirom_cmd_send(bank, SPIROM_CMD_RDST, 0x0, 0, NULL, &RetData); + if (result != 0) { + LOG_ERROR("mxic200_erase: (erase) get write enable status fail\n"); + return ERROR_FAIL; + } + if (RetData & SPIROM_SR_BP_MASK) { + LOG_ERROR("mxic200_erase: Flash block locked.\n"); + return ERROR_FAIL; + } else if (RetData & SPIROM_SR_WEL_MASK) + break; + } + if ((RetData & SPIROM_SR_WEL_MASK) == 0) { + LOG_ERROR("mxic200_erase: (erase) status %x, write enable is not set\n", RetData); + return ERROR_FAIL; + } + + LOG_DEBUG("erasing block %03d (0x%06x ~ 0x%06x)\n", EraseSectorIndex, EraseAddrStart, EraseAddrStart + EraseSize); + + /*-- erase --*/ + result = spirom_cmd_send(bank, SPIROM_CMD_ERASE, EraseAddrStart, 0, NULL, &RetData); + if (result != 0) { + LOG_ERROR("mxic200_erase: rom erase fail\n"); + return ERROR_FAIL; + } + + /*-- get erase status --*/ + for (j = 1; j < timeout; j++) { + result = spirom_cmd_send(bank, SPIROM_CMD_RDST, 0x0, 0, NULL, &RetData); + if (result != 0) { + LOG_ERROR("mxic200_erase: get erase status fail\n"); + return ERROR_FAIL; + } + if ((RetData & SPIROM_SR_WIP_MASK) == 0) + break; + } + if ((RetData & SPIROM_SR_WIP_MASK) != 0) { + LOG_ERROR("mxic200_erase: status %x, erase is still in progress\n", RetData); + return ERROR_FAIL; + } + return ERROR_OK; +} + +static int mxic200_set_wrsr(struct flash_bank *bank, unsigned int uiStat) +{ + LOG_DEBUG("%s", __func__); + unsigned int i, j, RetData, timeout = FLASH_RETRY_TIMES, result; + + for (i = 1; i < timeout; i++) { + /*-- write enable --*/ + result = spirom_cmd_send(bank, SPIROM_CMD_WREN, 0x0, 0, NULL, &RetData); + if (result != 0) { + LOG_ERROR("mxic200_set_wrsr: (lock/unlock) enable write fail\n"); + return ERROR_FAIL; + } + + /*-- get enable status --*/ + for (j = 1; j < timeout; j++) { + result = spirom_cmd_send(bank, SPIROM_CMD_RDST, 0x0, 0, NULL, &RetData); + if (result != 0) { + LOG_ERROR("mxic200_set_wrsr: (lock/unlock) get write enable status fail\n"); + return ERROR_FAIL; + } + if ((RetData & SPIROM_SR_WIP_MASK) == 0) + break; + } + if (RetData & SPIROM_SR_WEL_MASK) + break; + } + if ((RetData & SPIROM_SR_WEL_MASK) == 0) { + LOG_ERROR("mxic200_set_wrsr: (lock/unlock) status %x, write enable is not set\n", RetData); + return ERROR_FAIL; + } + /*-- set WRSR --*/ + result = spirom_cmd_send(bank, SPIROM_CMD_WRSR, uiStat, 0, NULL, &RetData); + if (result != 0) { + LOG_ERROR("mxic200_set_wrsr: (lock/unlock) write BP0~3 value fail\n"); + return ERROR_FAIL; + } + + /*-- get status --*/ + for (j = 1; j < timeout; j++) { + result = spirom_cmd_send(bank, SPIROM_CMD_RDST, 0x0, 0, NULL, &RetData); + if (result != 0) { + LOG_ERROR("mxic200_set_wrsr: (lock/unlock) get status fail\n"); + return ERROR_FAIL; + } + if ((RetData & SPIROM_SR_WIP_MASK) == 0) + break; + } + return ERROR_OK; +} + +static int mxic200_lock(struct flash_bank *bank, unsigned int FlashAddr, unsigned int DataSize) +{ + LOG_DEBUG("%s", __func__); + unsigned int RetData = 0, result; + result = spirom_cmd_send(bank, SPIROM_CMD_RDST, 0x0, 0, NULL, &RetData); + if (result != 0) { + LOG_ERROR("mxic200_lock: read status fail\n"); + return ERROR_FAIL; + } + if (mxic200_set_wrsr(bank, RetData | 0x3c) != ERROR_OK) { + LOG_ERROR("mxic200_lock: set lock fail\n"); + return ERROR_FAIL; + } + result = spirom_cmd_send(bank, SPIROM_CMD_RDST, 0x0, 0, NULL, &RetData); + if (result != 0) { + LOG_ERROR("mxic200_lock: read status fail\n"); + return ERROR_FAIL; + } + if ((RetData & 0x3c) != 0x3c) { + LOG_ERROR("mxic200_lock check lock status = %08x\n", RetData); + return ERROR_FAIL; + } + return ERROR_OK; +} + +static int mxic200_unlock(struct flash_bank *bank, unsigned int FlashAddr, unsigned int DataSize) +{ + LOG_DEBUG("%s", __func__); + unsigned int RetData = 0, result; + result = spirom_cmd_send(bank, SPIROM_CMD_RDST, 0x0, 0, NULL, &RetData); + if (result != 0) { + LOG_ERROR("mxic200_unlock: read status fail\n"); + return ERROR_FAIL; + } + if (mxic200_set_wrsr(bank, RetData & ~0x3c) != ERROR_OK) { + LOG_ERROR("mxic200_unlock: set unlock fail\n"); + return ERROR_FAIL; + } + result = spirom_cmd_send(bank, SPIROM_CMD_RDST, 0x0, 0, NULL, &RetData); + if (result != 0) { + LOG_ERROR("mxic200_unlock: read status fail\n"); + return ERROR_FAIL; + } + if ((RetData & 0x3c) != 0) { + LOG_ERROR("mxic200_unlock check unlock status = %08x\n", RetData); + return ERROR_FAIL; + } + return ERROR_OK; +} + +static int mxic200_page_program( + struct flash_bank *bank, + unsigned int FlashAddr, + unsigned char *start, + unsigned int PageCnt) +{ + unsigned int result, RetData, i; + unsigned int j, timeout = FLASH_RETRY_TIMES, page_size = 0; + struct ndsspi200_flash_bank *ndsspi200_info = bank->driver_priv; + + page_size = ndsspi200_info->dev->pagesize; + /*---------------------------*/ + /*-- PAGE PROGRAM procedure */ + /*---------------------------*/ + for (i = 0; i < PageCnt; i++) { + printf("program addr = 0x%x, cur_page = 0x%x ...\n", FlashAddr, i); + fflush(stdout); + /*-- write enable --*/ + for (j = 1; j < timeout; j++) { + result = spirom_cmd_send(bank, SPIROM_CMD_WREN, 0x0, 0, NULL, &RetData); + if (result != 0) { + LOG_ERROR("(program) page %d enable write fail\n", i); + return ERROR_FAIL; + } + /*-- get enable status --*/ + result = spirom_cmd_send(bank, SPIROM_CMD_RDST, 0x0, 0, NULL, &RetData); + if (result != 0) { + LOG_ERROR("get (program) page %d write enable status fail\n", i); + return ERROR_FAIL; + } + if (RetData & SPIROM_SR_BP_MASK) { + LOG_ERROR("Flash block locked.\n"); + return ERROR_FAIL; + } else if (RetData & SPIROM_SR_WEL_MASK) + break; + } + if ((RetData & SPIROM_SR_WEL_MASK) == 0) { + LOG_ERROR("(program) page %d, write enable is not set (status %x)\n", i, RetData); + return ERROR_FAIL; + } + result = spirom_cmd_send(bank, SPIROM_CMD_PROGRAM, FlashAddr, page_size, (unsigned int *)start, &RetData); + if (result != 0) { + LOG_ERROR("(program) page %d fail\n", i); + return ERROR_FAIL; + } + /*-- ckeck completion --*/ + for (j = 1; j < timeout; j++) { + result = spirom_cmd_send(bank, SPIROM_CMD_RDST, 0x0, 0, NULL, &RetData); + if (result != 0) { + LOG_ERROR("get (program) page %d write enable status fail\n", i); + return ERROR_FAIL; + } + if ((RetData & SPIROM_SR_WIP_MASK) == 0) + break; + } + if ((RetData & SPIROM_SR_WEL_MASK) != 0) { + LOG_ERROR("(program) page %d, write enable is not clear (status %x)\n", i, RetData); + return ERROR_FAIL; + } + start += page_size; + FlashAddr += page_size; + } /* for (i = 0; i < PageCnt; i++) */ + return ERROR_OK; +} + +FLASH_BANK_COMMAND_HANDLER(ndsspi200_flash_bank_command) +{ + LOG_DEBUG("%s", __func__); + LOG_DEBUG("%s, %s, base=0x%" TARGET_PRIxADDR ", size=0x%08x", + bank->name, bank->driver->name, bank->base, bank->size); + + struct ndsspi200_flash_bank *ndsspi200_info; + + if (CMD_ARGC < 6) + return ERROR_COMMAND_SYNTAX_ERROR; + + ndsspi200_info = malloc(sizeof(struct ndsspi200_flash_bank)); + if (ndsspi200_info == NULL) { + LOG_ERROR("not enough memory"); + return ERROR_FAIL; + } + + bank->driver_priv = ndsspi200_info; + ndsspi200_info->probed = 0; + + return ERROR_OK; +} + +static int ndsspi200_probe(struct flash_bank *bank) +{ + LOG_DEBUG("%s", __func__); + + struct ndsspi200_flash_bank *ndsspi200_info = bank->driver_priv; + struct flash_sector *sectors; + uint32_t id = 0; + int retval; + + if (ndsspi200_info->probed) + free(bank->sectors); + ndsspi200_info->probed = 0; + + if (platform_init(bank) != ERROR_OK) + return ERROR_FAIL; + + /* read and decode flash ID; returns in SW mode */ + retval = mxic200_check(bank, &id); + if (retval != ERROR_OK) + return retval; + + ndsspi200_info->dev = NULL; + for (const struct flash_device *p = flash_devices; p->name ; p++) + if (p->device_id == id) { + ndsspi200_info->dev = (struct flash_device *)p; + break; + } + + if (!ndsspi200_info->dev) { + LOG_ERROR("Unknown flash device (ID 0x%08" PRIx32 ")", id); + printf("Unknown flash device (ID 0x%08" PRIx32 ")\n", id); + fflush(stdout); + return ERROR_FAIL; + } + + LOG_INFO("Found flash device \'%s\' (ID 0x%08x), baseaddr = 0x%" TARGET_PRIxADDR, + ndsspi200_info->dev->name, ndsspi200_info->dev->device_id, bank->base); + LOG_INFO("size_in_bytes 0x%x", (unsigned int)ndsspi200_info->dev->size_in_bytes); + LOG_INFO("sectorsize 0x%x", (unsigned int)ndsspi200_info->dev->sectorsize); + LOG_INFO("pagesize 0x%x", ndsspi200_info->dev->pagesize); + + /* Set correct size value */ + bank->size = ndsspi200_info->dev->size_in_bytes; + + /* create and fill sectors array */ + bank->num_sectors = + ndsspi200_info->dev->size_in_bytes / ndsspi200_info->dev->sectorsize; + LOG_INFO("number of sectors 0x%x", bank->num_sectors); + sectors = malloc(sizeof(struct flash_sector) * bank->num_sectors); + if (sectors == NULL) { + LOG_ERROR("not enough memory"); + return ERROR_FAIL; + } + /* SW protect */ + for (int sector = 0; sector < bank->num_sectors; sector++) { + sectors[sector].offset = sector * ndsspi200_info->dev->sectorsize; + sectors[sector].size = ndsspi200_info->dev->sectorsize; + sectors[sector].is_protected = 0; + } + + bank->sectors = sectors; + ndsspi200_info->probed = 1; + return ERROR_OK; +} + +static int ndsspi200_auto_probe(struct flash_bank *bank) +{ + LOG_DEBUG("%s", __func__); + + struct ndsspi200_flash_bank *ndsspi200_info = bank->driver_priv; + + if (ndsspi200_info->probed) + return ERROR_OK; + + return ndsspi200_probe(bank); +} + +static int ndsspi200_protect(struct flash_bank *bank, int set, + int first, int last) +{ + LOG_DEBUG("%s", __func__); + int sector, result; + + if ((first < 0) || (last < first) || (last >= bank->num_sectors)) { + LOG_ERROR("Flash sector invalid"); + return ERROR_FLASH_SECTOR_INVALID; + } + + for (sector = first; sector <= last; sector++) + bank->sectors[sector].is_protected = set; + + /* SMP mode */ + if (set) + result = mxic200_lock(bank, 0, 0); + else + result = mxic200_unlock(bank, 0, 0); + + return result; +} + +static struct algorithm_steps *ndsspi_as_new(uint32_t size) +{ + struct algorithm_steps *as = calloc(1, sizeof(struct algorithm_steps)); + as->size = size; + as->steps = calloc(size, sizeof(as->steps[0])); + return as; +} + +static struct algorithm_steps *ndsspi_as_delete(struct algorithm_steps *as) +{ + for (unsigned step = 0; step < as->used; step++) { + free(as->steps[step]); + as->steps[step] = NULL; + } + free(as->steps); + free(as); + return NULL; +} + +static int ndsspi_as_empty(struct algorithm_steps *as) +{ + for (uint32_t s = 0; s < as->used; s++) { + if (as->steps[s][0] != STEP_NOP) + return 0; + } + return 1; +} + +/* Return size of compiled program. */ +static uint32_t ndsspi_as_compile(struct algorithm_steps *as, uint8_t *target, + uint32_t target_size, uint32_t page_size) +{ + uint32_t offset = 0; + bool finish_early = false; + uint32_t tx_size = page_size; + + LOG_DEBUG("as->used %d", as->used); + for (uint32_t s = 0; s < as->used && !finish_early; s++) { + uint32_t bytes_left = target_size - offset; + LOG_DEBUG("as->steps[%d][0] 0x%x", s, as->steps[s][0]); + + switch (as->steps[s][0]) { + case STEP_NOP: + break; + case STEP_TX: + if ((tx_size + tx_used + 1) > bytes_left) { + if (s == 0) + LOG_ERROR("allocate size for data too small"); + finish_early = true; + break; + } + memcpy(target + offset, as->steps[s], tx_size + tx_used); + offset += tx_size + tx_used; + break; + case STEP_ERASE: + if ((erase_used + 1) > bytes_left) { + if (s == 0) + LOG_ERROR("allocate size for data too small"); + finish_early = true; + break; + } + memcpy(target + offset, as->steps[s], erase_used); + offset += erase_used; + break; + default: + assert(0); + } + if (!finish_early) + as->steps[s][0] = STEP_NOP; + } + assert(offset + 1 <= target_size); + target[offset++] = STEP_EXIT; + + LOG_DEBUG("%d-byte program:", offset); + for (uint32_t i = 0; i < offset;) { + char buf[80]; + for (uint32_t x = 0; i < offset && x < 16; x++, i++) + sprintf(buf + x*3, "%02x ", target[i]); + LOG_DEBUG("%s", buf); + } + + return offset; +} + +static void ndsspi_as_add_erase(struct algorithm_steps *as, uint32_t addr) +{ + LOG_DEBUG("sector_addr=%d, as->used %d", addr, as->used); + uint32_t op_addr = spirom_prepare_cmd(SPIROM_OP_SE, addr); + assert(as->used < as->size); + as->steps[as->used] = malloc(erase_used); + as->steps[as->used][0] = STEP_ERASE; + as->steps[as->used][1] = (op_addr & 0xff); + as->steps[as->used][2] = (op_addr & 0xff00) >> 8; + as->steps[as->used][3] = (op_addr & 0xff0000) >> 16; + as->steps[as->used][4] = (op_addr & 0xff000000) >> 24; + as->used++; +} + +static void ndsspi_as_add_tx( + struct algorithm_steps *as, + uint32_t offset, + uint32_t tx_count, + const uint8_t *tx_data, + uint32_t page_size) +{ + LOG_DEBUG("tx_count=%d, as->used %d", tx_count, as->used); + uint32_t step_count = page_size, op_addr; + while (tx_count > 0) { + op_addr = spirom_prepare_cmd(SPIROM_OP_PP, offset); + assert(as->used < as->size); + as->steps[as->used] = malloc(step_count + tx_used); + as->steps[as->used][0] = STEP_TX; + as->steps[as->used][1] = (op_addr & 0xff); + as->steps[as->used][2] = (op_addr & 0xff00) >> 8; + as->steps[as->used][3] = (op_addr & 0xff0000) >> 16; + as->steps[as->used][4] = (op_addr & 0xff000000) >> 24; + memcpy(as->steps[as->used] + tx_used, tx_data, step_count); + as->used++; + tx_data += step_count; + offset += step_count; + tx_count -= step_count; + } +} + +static void exec_fencei(struct target *target) +{ + /* send fence.i check not write to cache */ + LOG_DEBUG("send fence.i"); + struct riscv_program program; + riscv_program_init(&program, target); + riscv_program_fence_i(&program); + if (riscv_program_exec(&program, target) != ERROR_OK) + LOG_ERROR("Unable to execute fence.i"); +} + +static int ndsspi_steps_execute( + struct algorithm_steps *as, + struct flash_bank *bank, + struct working_area *algorithm_wa, + struct working_area *data_wa) +{ + LOG_DEBUG("%s", __func__); + + struct target *target = bank->target; + struct ndsspi200_flash_bank *ndsspi200_info = bank->driver_priv; + uint32_t page_size = ndsspi200_info->dev->pagesize; + struct reg_param reg_params[1]; + int num_used_reg = 0, retval = ERROR_OK; + + int xlen = riscv_xlen(target); + + /* use a0 to store cmd address */ + num_used_reg = 1; + init_reg_param(®_params[0], "a0", xlen, PARAM_OUT); + buf_set_u64(reg_params[0].value, 0, xlen, data_wa->address); + + while (!ndsspi_as_empty(as)) { + keep_alive(); + uint8_t *data_buf = malloc(data_wa->size); + uint32_t bytes = ndsspi_as_compile(as, data_buf, data_wa->size, page_size); + LOG_DEBUG("write data to 0x%" TARGET_PRIxADDR ": %d bytes", data_wa->address, bytes); + if (bytes == 1) { + LOG_ERROR("only get 1 byte data"); + goto err; + } + + exec_fencei(target); + retval = target_write_buffer(target, data_wa->address, bytes, data_buf); + free(data_buf); + if (retval != ERROR_OK) { + LOG_ERROR("Failed to write data to 0x%" TARGET_PRIxADDR ": %d", data_wa->address, retval); + goto err; + } + retval = target_run_algorithm(target, 0, NULL, num_used_reg, reg_params, + algorithm_wa->address, algorithm_wa->address + STEP_EXIT , 100000, NULL); + if (retval != ERROR_OK) { + LOG_ERROR("Failed to execute algorithm at 0x%" TARGET_PRIxADDR ": %d", algorithm_wa->address, retval); + goto err; + } + } + + LOG_DEBUG("target_run_algorithm finish !!"); +err: + return retval; +} + +static int ndsspi200_erase(struct flash_bank *bank, int first, int last) +{ + LOG_DEBUG("%s: from sector %d to sector %d", __func__, first, last); + + struct target *target = bank->target; + struct ndsspi200_flash_bank *ndsspi200_info = bank->driver_priv; + int retval = ERROR_OK; + int sector; + unsigned int EraseAddrStart; + + if (target->state != TARGET_HALTED) { + LOG_ERROR("Target not halted"); + return ERROR_TARGET_NOT_HALTED; + } + + if ((first < 0) || (last < first) || (last >= bank->num_sectors)) { + LOG_ERROR("Flash sector invalid"); + return ERROR_FLASH_SECTOR_INVALID; + } + + if (!(ndsspi200_info->probed)) { + LOG_ERROR("Flash bank not probed"); + return ERROR_FLASH_BANK_NOT_PROBED; + } + + /* SW protect */ + for (sector = first; sector <= last; sector++) { + if (bank->sectors[sector].is_protected) { + LOG_ERROR("Flash sector %d protected", sector); + return ERROR_FAIL; + } + } + + struct working_area *nds_algorithm_wa; + if (target_alloc_working_area(target, sizeof(algorithm_bin), + &nds_algorithm_wa) != ERROR_OK) { + LOG_WARNING("Couldn't allocate %zd-byte working area.", + sizeof(algorithm_bin)); + nds_algorithm_wa = NULL; + } else { + exec_fencei(target); + retval = target_write_buffer(target, nds_algorithm_wa->address, sizeof(algorithm_bin), algorithm_bin); + if (retval != ERROR_OK) { + LOG_ERROR("Failed to write code to 0x%" TARGET_PRIxADDR ": %d", nds_algorithm_wa->address, retval); + target_free_working_area(target, nds_algorithm_wa); + nds_algorithm_wa = NULL; + } else { + LOG_DEBUG("write code to 0x%" TARGET_PRIxADDR ": 0x%x bytes", nds_algorithm_wa->address, + (unsigned int)sizeof(algorithm_bin)); + } + } + + struct working_area *nds_data_wa = NULL; + uint32_t data_wa_size = (last - first + 1) * erase_used + 1; + while (1) { + if (data_wa_size < (erase_used + 1)) { + LOG_ERROR("Couldn't allocate the small data working area: %d.", erase_used + 1); + target_free_working_area(target, nds_algorithm_wa); + nds_algorithm_wa = NULL; + } + if (target_alloc_working_area_try(target, data_wa_size, &nds_data_wa) == ERROR_OK) + break; + data_wa_size /= 2; + } + + /* algorithm mode */ + if (nds_algorithm_wa) { + struct algorithm_steps *nds_as = ndsspi_as_new(last - first + 1); + for (sector = first; sector <= last; sector++) { + EraseAddrStart = bank->sectors[sector].offset; + ndsspi_as_add_erase(nds_as, EraseAddrStart); + } + retval = ndsspi_steps_execute(nds_as, bank, nds_algorithm_wa, nds_data_wa); + if (retval != ERROR_OK) + LOG_DEBUG("algorithm erase failed"); + + target_free_working_area(target, nds_algorithm_wa); + target_free_working_area(target, nds_data_wa); + ndsspi_as_delete(nds_as); + } else { + for (sector = first; sector <= last; sector++) { + retval = mxic200_erase(bank, sector); + if (retval != ERROR_OK) { + LOG_DEBUG("slow mode erase failed"); + break; + } + keep_alive(); + } + } + + return retval; +} + +static int ndsspi200_read(struct flash_bank *bank, + uint8_t *buffer, uint32_t offset, uint32_t count) +{ + LOG_DEBUG("offset=0x%08x, count=0x%08x", offset, count); + return target_read_buffer(bank->target, offset + bank->base, count, buffer); +} + +static int ndsspi200_write(struct flash_bank *bank, const uint8_t *buffer, + uint32_t offset, uint32_t count) +{ + struct target *target = bank->target; + struct ndsspi200_flash_bank *ndsspi200_info = bank->driver_priv; + uint32_t cur_count, cur_offset, page_size, page_offset, cmd_size; + int sector; + int retval = ERROR_OK; + uint8_t *tmp_buffer = NULL; + + LOG_DEBUG("offset=0x%08x, count=0x%08x", offset, count); + + if (target->state != TARGET_HALTED) { + LOG_ERROR("Target not halted"); + printf("Target not halted\n"); + fflush(stdout); + return ERROR_TARGET_NOT_HALTED; + } + + if (offset + count > ndsspi200_info->dev->size_in_bytes) { + LOG_WARNING("Write past end of flash. Extra data discarded."); + count = ndsspi200_info->dev->size_in_bytes - 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 %d protected", sector); + printf("Flash sector %d protected\n", sector); + fflush(stdout); + return ERROR_FAIL; + } + } + + page_size = ndsspi200_info->dev->pagesize; + tmp_buffer = malloc(page_size); + if (tmp_buffer == NULL) { + LOG_ERROR("not enough memory"); + return ERROR_FAIL; + } + + struct working_area *nds_algorithm_wa; + if (target_alloc_working_area(target, sizeof(algorithm_bin), + &nds_algorithm_wa) != ERROR_OK) { + LOG_WARNING("Couldn't allocate %zd-byte working area.", + sizeof(algorithm_bin)); + nds_algorithm_wa = NULL; + } else { + exec_fencei(target); + retval = target_write_buffer(target, nds_algorithm_wa->address, sizeof(algorithm_bin), algorithm_bin); + if (retval != ERROR_OK) { + LOG_ERROR("Failed to write code to 0x%" TARGET_PRIxADDR ": %d", nds_algorithm_wa->address, retval); + target_free_working_area(target, nds_algorithm_wa); + nds_algorithm_wa = NULL; + } else { + LOG_DEBUG("write code to 0x%" TARGET_PRIxADDR ": 0x%x bytes", + nds_algorithm_wa->address, (unsigned int)sizeof(algorithm_bin)); + } + } + + struct working_area *nds_data_wa = NULL; + if (count < page_size) + cmd_size = page_size * 2; + else + cmd_size = count * 2; + uint32_t data_wa_size = cmd_size; + while (1) { + if (data_wa_size < (page_size + tx_used + 1)) { + LOG_ERROR("Couldn't allocate the small data working area: %d.", page_size + tx_used + 1); + target_free_working_area(target, nds_algorithm_wa); + nds_algorithm_wa = NULL; + } + if (target_alloc_working_area_try(target, data_wa_size, &nds_data_wa) == ERROR_OK) + break; + data_wa_size /= 2; + } + + /* add 5 for offset not page_aligned or count%page_size not 0 */ + struct algorithm_steps *nds_as = ndsspi_as_new(count / page_size + 5); + + /* unaligned(page) buffer head */ + if ((offset % page_size) != 0) { + /* read the 1st-unaligned-page, memcpy unaligned data */ + page_offset = (offset/page_size) * page_size; + ndsspi200_read(bank, tmp_buffer, page_offset, page_size); + + cur_offset = (offset % page_size); + cur_count = (page_size - cur_offset); + if (cur_count > count) + cur_count = count; + + memcpy(tmp_buffer+cur_offset, buffer, cur_count); + + if (nds_algorithm_wa) + ndsspi_as_add_tx(nds_as, page_offset, page_size, tmp_buffer, page_size); + else { + retval = mxic200_page_program(bank, page_offset, (unsigned char *)tmp_buffer, 1); + if (retval != ERROR_OK) + goto ndsspi200_write_finish; + } + + buffer += cur_count; + offset += cur_count; + count -= cur_count; + } + + /* central part, aligned words */ + while (count >= page_size) { + cur_count = (count/page_size) * page_size; + + if (nds_algorithm_wa) + ndsspi_as_add_tx(nds_as, offset, cur_count, buffer, page_size); + else { + retval = mxic200_page_program(bank, offset, (unsigned char *)buffer, + (cur_count + (page_size - 1)) / page_size); + if (retval != ERROR_OK) + goto ndsspi200_write_finish; + } + + buffer += cur_count; + offset += cur_count; + count -= cur_count; + } + + /* buffer tail */ + if (count > 0) { + /* read the last-unaligned-page, memcpy unaligned data */ + ndsspi200_read(bank, tmp_buffer, offset, page_size); + memcpy(tmp_buffer, buffer, count); + if (nds_algorithm_wa) + ndsspi_as_add_tx(nds_as, offset, page_size, tmp_buffer, page_size); + else { + retval = mxic200_page_program(bank, offset, (unsigned char *)tmp_buffer, 1); + if (retval != ERROR_OK) + goto ndsspi200_write_finish; + } + } + + if (nds_algorithm_wa) + retval = ndsspi_steps_execute(nds_as, bank, nds_algorithm_wa, nds_data_wa); + +ndsspi200_write_finish: + free(tmp_buffer); + ndsspi_as_delete(nds_as); + if (nds_algorithm_wa) { + target_free_working_area(target, nds_algorithm_wa); + target_free_working_area(target, nds_data_wa); + } + return retval; +} + +static int ndsspi200_protect_check(struct flash_bank *bank) +{ + LOG_DEBUG("%s", __func__); + /* Nothing to do. Protection is only handled in SW. */ + return ERROR_OK; +} + +static int ndsspi200_get_info(struct flash_bank *bank, char *buf, int buf_size) +{ + LOG_DEBUG("%s", __func__); + struct ndsspi200_flash_bank *ndsspi200_info = bank->driver_priv; + + if (!(ndsspi200_info->probed)) { + snprintf(buf, buf_size, + "\nNDSSPI flash bank not probed yet\n"); + return ERROR_OK; + } + + snprintf(buf, buf_size, "\nNDSSPI flash information:\n" + " Device \'%s\' (ID 0x%08" PRIx32 ")\n", + ndsspi200_info->dev->name, ndsspi200_info->dev->device_id); + + return ERROR_OK; +} + +const struct flash_driver ndsspi200_flash = { + .name = "ndsspi200", + .flash_bank_command = ndsspi200_flash_bank_command, + .auto_probe = ndsspi200_auto_probe, + .probe = ndsspi200_probe, + .protect = ndsspi200_protect, + .erase = ndsspi200_erase, + .read = ndsspi200_read, + .write = ndsspi200_write, + .erase_check = default_flash_blank_check, + .protect_check = ndsspi200_protect_check, + .info = ndsspi200_get_info, +}; diff --git a/src/flash/nor/spi.c b/src/flash/nor/spi.c index af72ffc..93a8095 100644 --- a/src/flash/nor/spi.c +++ b/src/flash/nor/spi.c @@ -128,6 +128,7 @@ const struct flash_device flash_devices[] = { FLASH_ID("issi is25wp256d", 0x13, 0xec, 0x12, 0xdc, 0xc7, 0x0019709d, 0x100, 0x10000, 0x2000000), FLASH_ID("issi is25lp512m", 0x13, 0xec, 0x12, 0xdc, 0xc7, 0x001a609d, 0x100, 0x10000, 0x4000000), FLASH_ID("issi is25wp512m", 0x13, 0xec, 0x12, 0xdc, 0xc7, 0x001a709d, 0x100, 0x10000, 0x4000000), + FLASH_ID("nds mxic", 0x03, 0xeb, 0x02, 0x20, 0xc7, 0x003525c2, 0x100, 0x1000, 0x100000), /* FRAM, no erase commands, no write page or sectors */ FRAM_ID("fu mb85rs16n", 0x03, 0, 0x02, 0x00010104, 0x800), -- _______________________________________________ OpenOCD-devel mailing list [email protected] https://lists.sourceforge.net/lists/listinfo/openocd-devel
