diff --git a/contrib/loaders/flash/npcx/Makefile b/contrib/loaders/flash/npcx/Makefile new file mode 100644 index 000000000..293bd02da --- /dev/null +++ b/contrib/loaders/flash/npcx/Makefile @@ -0,0 +1,65 @@ +# SPDX-License-Identifier: GPL-2.0-or-later + +BIN2C = ../../../../src/helper/bin2char.sh + +# Toolchain used in makefile +CROSS_COMPILE ?= arm-none-eabi- +CC = $(CROSS_COMPILE)gcc +CPLUS = $(CROSS_COMPILE)g++ +CPP = $(CROSS_COMPILE)cpp +LD = $(CROSS_COMPILE)gcc +AS = $(CROSS_COMPILE)as +OBJCOPY = $(CROSS_COMPILE)objcopy +OBJDUMP = $(CROSS_COMPILE)objdump +OBJSIZE = $(CROSS_COMPILE)size + +TARGET = npcx_algo +OBJS := npcx_flash.o +FLAGS = -mthumb -Os -ffunction-sections -fdata-sections -g -gdwarf-3 --specs=nano.specs +FLAGS += -gstrict-dwarf -Wall -fno-strict-aliasing --asm + +CFLAGS = -c -I. -mcpu=cortex-m4 -fpack-struct + +PRE_LD_FILE = npcx_flash.lds +LD_FILE = npcx_flash_generated.lds +LDFLAGS = -Wl,-Map,lfw.map -Wl,-T$(LD_FILE) -nostartfiles + +all: $(TARGET).inc + +# Implicit rules +%.o: %.c + -@ echo CC $@ from $< + @$(CC) $< $(FLAGS) $(CFLAGS) -o $@ + + $(LD_FILE): $(PRE_LD_FILE) + -@ echo Generate $@ from $< + -@$(CPP) $(PRE_LD_FILE) | grep -v '^#' >>$(LD_FILE) + +$(TARGET).elf: $(OBJS) $(LD_FILE) + -@ echo LD $@ from $< + @$(LD) -o $@ $< $(FLAGS) $(LDFLAGS) + +%.bin: %.elf + -@ echo OBJCOPY $@ from $< + -@ $(OBJCOPY) $< -O binary $@ + -@ $(OBJSIZE) $< --format=berkeley + +%.inc: %.bin + @echo 'Building target: $@' + @echo 'Invoking Bin2Char Script' + $(BIN2C) < $< > $@ + rm $< $*.elf + @echo 'Finished building target: $@' + @echo ' ' + +clean: + @echo 'Cleaning Targets and Build Artifacts' + rm -rf *.inc *.bin *.elf *.map + rm -rf *.o *.d + rm -rf $(LD_FILE) + @echo 'Finished clean' + @echo ' ' + +.PRECIOUS: %.bin + +.PHONY: all clean diff --git a/contrib/loaders/flash/npcx/npcx_algo.inc b/contrib/loaders/flash/npcx/npcx_algo.inc new file mode 100644 index 000000000..4312fdb1b --- /dev/null +++ b/contrib/loaders/flash/npcx/npcx_algo.inc @@ -0,0 +1,60 @@ +/* Autogenerated with ../../../../src/helper/bin2char.sh */ +0x08,0xb5,0xdf,0xf8,0x08,0xd0,0x00,0xf0,0x2f,0xf9,0x00,0x00,0x48,0x15,0x0c,0x20, +0x03,0x4b,0x18,0x70,0x19,0x72,0x08,0x33,0x1a,0x78,0xd2,0x09,0xfc,0xd1,0x70,0x47, +0x16,0x00,0x02,0x40,0x70,0xb5,0x11,0x4c,0x23,0x78,0x03,0xf0,0xfd,0x03,0x23,0x70, +0xc0,0x21,0x05,0x20,0xff,0xf7,0xec,0xff,0x0d,0x4a,0x0e,0x49,0x6f,0xf0,0x7f,0x43, +0x6f,0xf0,0x2e,0x05,0x10,0x46,0x15,0x70,0x06,0x78,0xf6,0x09,0xfc,0xd1,0x0e,0x78, +0xf6,0x07,0x01,0xd5,0x01,0x3b,0xf6,0xd1,0x22,0x78,0x42,0xf0,0x02,0x02,0x00,0x2b, +0x22,0x70,0x0c,0xbf,0x03,0x20,0x00,0x20,0x70,0xbd,0x00,0xbf,0x1f,0x00,0x02,0x40, +0x1e,0x00,0x02,0x40,0x1a,0x00,0x02,0x40,0x08,0xb5,0xc0,0x21,0x06,0x20,0xff,0xf7, +0xc7,0xff,0xff,0xf7,0xcf,0xff,0x28,0xb9,0x03,0x4b,0x1b,0x78,0x13,0xf0,0x02,0x0f, +0x08,0xbf,0x02,0x20,0x08,0xbd,0x00,0xbf,0x1a,0x00,0x02,0x40,0xf8,0xb5,0x12,0x4c, +0x23,0x78,0x03,0xf0,0xfd,0x03,0x23,0x70,0x10,0x4b,0x17,0x46,0xc0,0xf3,0x07,0x42, +0x1a,0x70,0xc0,0xf3,0x07,0x22,0xc0,0xb2,0x03,0xf8,0x01,0x2c,0x0e,0x46,0x03,0xf8, +0x02,0x0c,0xe8,0x21,0x02,0x20,0xff,0xf7,0xa3,0xff,0x00,0x25,0xae,0x42,0x04,0xd8, +0x23,0x78,0x43,0xf0,0x02,0x03,0x23,0x70,0xf8,0xbd,0x78,0x5d,0xe0,0x21,0xff,0xf7, +0x97,0xff,0x01,0x35,0xf2,0xe7,0x00,0xbf,0x1f,0x00,0x02,0x40,0x19,0x00,0x02,0x40, +0x70,0x47,0x2d,0xe9,0xf0,0x41,0x00,0xf1,0xff,0x06,0x26,0xf0,0xff,0x06,0x34,0x1a, +0x8c,0x42,0x28,0xbf,0x0c,0x46,0x80,0x46,0x0d,0x46,0x17,0x46,0x5c,0xb1,0xff,0xf7, +0xb3,0xff,0x58,0xb9,0x3a,0x46,0xa1,0xb2,0x40,0x46,0xff,0xf7,0xbf,0xff,0xff,0xf7, +0x81,0xff,0x18,0xb9,0x27,0x44,0x2c,0x1b,0x14,0xb9,0x20,0x46,0xbd,0xe8,0xf0,0x81, +0xb4,0xf5,0x80,0x7f,0x25,0x46,0x28,0xbf,0x4f,0xf4,0x80,0x75,0xff,0xf7,0x9c,0xff, +0x00,0x28,0xf3,0xd1,0x3a,0x46,0xa9,0xb2,0x30,0x46,0xff,0xf7,0xa7,0xff,0xff,0xf7, +0x69,0xff,0x00,0x28,0xea,0xd1,0x2f,0x44,0x2e,0x44,0x64,0x1b,0xe4,0xe7,0x00,0x00, +0x2d,0xe9,0xf0,0x47,0x14,0x4e,0x15,0x4f,0xdf,0xf8,0x54,0x80,0x05,0x46,0x0c,0x46, +0x8a,0x46,0x05,0xeb,0x04,0x09,0xa9,0xeb,0x0a,0x09,0xba,0xf1,0x00,0x0f,0x02,0xd1, +0x50,0x46,0xbd,0xe8,0xf0,0x87,0xff,0xf7,0x77,0xff,0x00,0x28,0xf9,0xd1,0xc9,0xf3, +0x07,0x43,0x33,0x70,0xc9,0xf3,0x07,0x23,0x5f,0xfa,0x89,0xf9,0x3b,0x70,0xc8,0x21, +0x20,0x20,0x88,0xf8,0x00,0x90,0xff,0xf7,0x33,0xff,0xff,0xf7,0x3b,0xff,0x00,0x28, +0xe7,0xd1,0xaa,0xf5,0x80,0x5a,0xdc,0xe7,0x19,0x00,0x02,0x40,0x18,0x00,0x02,0x40, +0x17,0x00,0x02,0x40,0x08,0xb5,0xff,0xf7,0x57,0xff,0x38,0xb9,0xc0,0x21,0xc7,0x20, +0xff,0xf7,0x1e,0xff,0xbd,0xe8,0x08,0x40,0xff,0xf7,0x24,0xbf,0x08,0xbd,0x00,0x00, +0x38,0xb5,0xff,0xf7,0x49,0xff,0x04,0x46,0xc0,0xb9,0x0d,0x4b,0x0d,0x4d,0xf2,0x21, +0x28,0x70,0x18,0x70,0x01,0x20,0xff,0xf7,0x0b,0xff,0xff,0xf7,0x13,0xff,0x04,0x46, +0x60,0xb9,0xc1,0x21,0x05,0x20,0xff,0xf7,0x03,0xff,0x2b,0x78,0x2b,0xb9,0xc1,0x21, +0x35,0x20,0xff,0xf7,0xfd,0xfe,0x2b,0x78,0x03,0xb1,0x02,0x24,0x20,0x46,0x38,0xbd, +0x1b,0x00,0x02,0x40,0x1a,0x00,0x02,0x40,0x10,0xb5,0xc3,0x21,0x04,0x46,0x9f,0x20, +0xff,0xf7,0xee,0xfe,0x06,0x4b,0x07,0x4a,0x19,0x78,0x01,0x33,0x00,0x20,0x1b,0x78, +0x12,0x78,0x1b,0x02,0x43,0xea,0x01,0x43,0x13,0x43,0x23,0x60,0x10,0xbd,0x00,0xbf, +0x1a,0x00,0x02,0x40,0x1c,0x00,0x02,0x40,0x08,0xb5,0x10,0x22,0x00,0x21,0x00,0xf0, +0x4d,0xf8,0x00,0x20,0x08,0xbd,0x00,0x00,0x73,0xb5,0x21,0x48,0x20,0x4c,0xff,0xf7, +0xf3,0xff,0x20,0x4a,0x13,0x78,0x43,0xf0,0x80,0x03,0x13,0x70,0xff,0xf7,0xb0,0xff, +0x05,0x46,0x58,0xb9,0x1c,0x4e,0xe3,0x68,0x00,0x2b,0xfc,0xd0,0xa3,0x68,0x01,0x3b, +0x03,0x2b,0x2a,0xd8,0xdf,0xe8,0x03,0xf0,0x04,0x18,0x20,0x23,0xe5,0x60,0xfd,0xe7, +0x01,0xa8,0xff,0xf7,0xc1,0xff,0xa8,0xb9,0x01,0x9b,0x33,0x70,0x1a,0x0a,0x1b,0x0c, +0x72,0x70,0xb3,0x70,0xf0,0x70,0x23,0x7b,0x25,0x73,0x63,0x7b,0x65,0x73,0xa3,0x7b, +0xa5,0x73,0xe3,0x7b,0xe5,0x73,0xde,0xe7,0x20,0x68,0x61,0x68,0xff,0xf7,0x48,0xff, +0x00,0x28,0xf0,0xd0,0xe0,0x60,0xfe,0xe7,0xff,0xf7,0x74,0xff,0xf8,0xe7,0x20,0x68, +0x61,0x68,0x32,0x46,0xff,0xf7,0x05,0xff,0xf2,0xe7,0x01,0x20,0xf2,0xe7,0x00,0xbf, +0x00,0x00,0x0c,0x20,0x10,0x30,0x0c,0x40,0x10,0x00,0x0c,0x20,0xf0,0xb5,0x05,0x00, +0x83,0x07,0x4e,0xd0,0x54,0x1e,0x00,0x2a,0x46,0xd0,0x0a,0x06,0x12,0x0e,0x03,0x00, +0x03,0x26,0x02,0xe0,0x01,0x35,0x01,0x3c,0x3e,0xd3,0x01,0x33,0x2a,0x70,0x33,0x42, +0xf8,0xd1,0x03,0x2c,0x2f,0xd9,0xff,0x22,0x0a,0x40,0x15,0x02,0x15,0x43,0x2a,0x04, +0x15,0x43,0x0f,0x2c,0x38,0xd9,0x27,0x00,0x10,0x3f,0x3f,0x09,0x3e,0x01,0xb4,0x46, +0x1e,0x00,0x1a,0x00,0x10,0x36,0x66,0x44,0x15,0x60,0x55,0x60,0x95,0x60,0xd5,0x60, +0x10,0x32,0xb2,0x42,0xf8,0xd1,0x0f,0x26,0x0c,0x22,0x01,0x37,0x3f,0x01,0x26,0x40, +0xdb,0x19,0x37,0x00,0x22,0x42,0x1a,0xd0,0x3e,0x1f,0xb6,0x08,0xb4,0x00,0xa4,0x46, +0x1a,0x00,0x1c,0x1d,0x64,0x44,0x20,0xc2,0xa2,0x42,0xfc,0xd1,0x03,0x24,0x01,0x36, +0xb6,0x00,0x9b,0x19,0x3c,0x40,0x00,0x2c,0x06,0xd0,0x09,0x06,0x1c,0x19,0x09,0x0e, +0x19,0x70,0x01,0x33,0x9c,0x42,0xfb,0xd1,0xf0,0xbc,0x02,0xbc,0x08,0x47,0x34,0x00, +0xf1,0xe7,0x14,0x00,0x03,0x00,0xbc,0xe7,0x27,0x00,0xdd,0xe7, diff --git a/contrib/loaders/flash/npcx/npcx_flash.c b/contrib/loaders/flash/npcx/npcx_flash.c new file mode 100644 index 000000000..d60624ae8 --- /dev/null +++ b/contrib/loaders/flash/npcx/npcx_flash.c @@ -0,0 +1,342 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +/* + * Copyright (C) 2020 by Nuvoton Technology Corporation + * Mulin Chao + * Wealian Liao + */ + +#include +#include +#include "npcx_flash.h" + +/*---------------------------------------------------------------------------- + * NPCX flash driver + *----------------------------------------------------------------------------*/ +static void flash_execute_cmd(uint8_t code, uint8_t cts) +{ + /* Set UMA code */ + NPCX_UMA_CODE = code; + /* Execute UMA flash transaction by CTS setting */ + NPCX_UMA_CTS = cts; + /* Wait for transaction completed */ + while (NPCX_IS_BIT_SET(NPCX_UMA_CTS, NPCX_UMA_CTS_EXEC_DONE)) + ; +} + +static void flash_cs_level(uint8_t level) +{ + /* Program chip select pin to high/low level */ + if (level) + NPCX_SET_BIT(NPCX_UMA_ECTS, NPCX_UMA_ECTS_SW_CS1); + else + NPCX_CLEAR_BIT(NPCX_UMA_ECTS, NPCX_UMA_ECTS_SW_CS1); +} + +static void flash_set_address(uint32_t dest_addr) +{ + uint8_t *addr = (uint8_t *)&dest_addr; + + /* Set target flash address */ + NPCX_UMA_AB2 = addr[2]; + NPCX_UMA_AB1 = addr[1]; + NPCX_UMA_AB0 = addr[0]; +} + +void delay(uint32_t i) +{ + while (i--) + ; +} + +static int flash_wait_ready(uint32_t timeout) +{ + /* Chip Select down. -- Burst mode */ + flash_cs_level(0); + + /* Command for Read status register */ + flash_execute_cmd(NPCX_CMD_READ_STATUS_REG, NPCX_MASK_CMD_ONLY); + while (timeout > 0) { + /* Read status register */ + NPCX_UMA_CTS = NPCX_MASK_RD_1BYTE; + while (NPCX_IS_BIT_SET(NPCX_UMA_CTS, NPCX_UMA_CTS_EXEC_DONE)) + ; + + if (!(NPCX_UMA_DB0 & NPCX_SPI_FLASH_SR1_BUSY)) + break; + + if (--timeout > 0) + delay(100); + + }; /* Wait for Busy clear */ + + /* Chip Select high. */ + flash_cs_level(1); + + if (timeout == 0) + return NPCX_FLASH_STATUS_FAILED_TIMEOUT; + + return NPCX_FLASH_STATUS_OK; +} + +static int flash_write_enable(void) +{ + /* Write enable command */ + flash_execute_cmd(NPCX_CMD_WRITE_EN, NPCX_MASK_CMD_ONLY); + + /* Wait for flash is not busy */ + int status = flash_wait_ready(NPCX_FLASH_ABORT_TIMEOUT); + if (status != NPCX_FLASH_STATUS_OK) + return status; + + if (NPCX_UMA_DB0 & NPCX_SPI_FLASH_SR1_WEL) + return NPCX_FLASH_STATUS_OK; + else + return NPCX_FLASH_STATUS_FAILED; +} + +static void flash_burst_write(uint32_t dest_addr, uint16_t bytes, + const uint8_t *data) +{ + /* Chip Select down -- Burst mode */ + flash_cs_level(0); + + /* Set write address */ + flash_set_address(dest_addr); + /* Start programming */ + flash_execute_cmd(NPCX_CMD_FLASH_PROGRAM, NPCX_MASK_CMD_WR_ADR); + for (uint32_t i = 0; i < bytes; i++) { + flash_execute_cmd(*data, NPCX_MASK_CMD_WR_ONLY); + data++; + } + + /* Chip Select up */ + flash_cs_level(1); +} + +/* The data to write cannot cross 256 Bytes boundary */ +static int flash_program_write(uint32_t addr, uint32_t size, + const uint8_t *data) +{ + int status = flash_write_enable(); + if (status != NPCX_FLASH_STATUS_OK) + return status; + + flash_burst_write(addr, size, data); + return flash_wait_ready(NPCX_FLASH_ABORT_TIMEOUT); +} + +int flash_physical_write(uint32_t offset, uint32_t size, const uint8_t *data) +{ + int status; + uint32_t trunk_start = (offset + 0xff) & ~0xff; + + /* write head */ + uint32_t dest_addr = offset; + uint32_t write_len = ((trunk_start - offset) > size) ? size : (trunk_start - offset); + + if (write_len) { + status = flash_program_write(dest_addr, write_len, data); + if (status != NPCX_FLASH_STATUS_OK) + return status; + data += write_len; + } + + dest_addr = trunk_start; + size -= write_len; + + /* write remaining data*/ + while (size > 0) { + write_len = (size > NPCX_FLASH_WRITE_SIZE) ? + NPCX_FLASH_WRITE_SIZE : size; + + status = flash_program_write(dest_addr, write_len, data); + if (status != NPCX_FLASH_STATUS_OK) + return status; + + data += write_len; + dest_addr += write_len; + size -= write_len; + } + + return NPCX_FLASH_STATUS_OK; +} + +int flash_physical_erase(uint32_t offset, uint32_t size) +{ + /* Alignment has been checked in upper layer */ + for (; size > 0; size -= NPCX_FLASH_ERASE_SIZE, + offset += NPCX_FLASH_ERASE_SIZE) { + /* Enable write */ + int status = flash_write_enable(); + if (status != NPCX_FLASH_STATUS_OK) + return status; + + /* Set erase address */ + flash_set_address(offset); + /* Start erase */ + flash_execute_cmd(NPCX_CMD_SECTOR_ERASE, NPCX_MASK_CMD_ADR); + /* Wait erase completed */ + status = flash_wait_ready(NPCX_FLASH_ABORT_TIMEOUT); + if (status != NPCX_FLASH_STATUS_OK) + return status; + } + + return NPCX_FLASH_STATUS_OK; +} + +int flash_physical_erase_all(void) +{ + /* Enable write */ + int status = flash_write_enable(); + if (status != NPCX_FLASH_STATUS_OK) + return status; + + /* Start erase */ + flash_execute_cmd(NPCX_CMD_CHIP_ERASE, NPCX_MASK_CMD_ONLY); + + /* Wait erase completed */ + status = flash_wait_ready(NPCX_FLASH_ABORT_TIMEOUT); + if (status != NPCX_FLASH_STATUS_OK) + return status; + + return NPCX_FLASH_STATUS_OK; +} + +int flash_physical_clear_stsreg(void) +{ + /* Enable write */ + int status = flash_write_enable(); + if (status != NPCX_FLASH_STATUS_OK) + return status; + + NPCX_UMA_DB0 = 0x0; + NPCX_UMA_DB1 = 0x0; + + /* Write status register 1/2 */ + flash_execute_cmd(NPCX_CMD_WRITE_STATUS_REG, NPCX_MASK_CMD_WR_2BYTE); + + /* Wait writing completed */ + status = flash_wait_ready(NPCX_FLASH_ABORT_TIMEOUT); + if (status != NPCX_FLASH_STATUS_OK) + return status; + + /* Read status register 1/2 for checking */ + flash_execute_cmd(NPCX_CMD_READ_STATUS_REG, NPCX_MASK_CMD_RD_1BYTE); + if (NPCX_UMA_DB0 != 0x00) + return NPCX_FLASH_STATUS_FAILED; + flash_execute_cmd(NPCX_CMD_READ_STATUS_REG2, NPCX_MASK_CMD_RD_1BYTE); + if (NPCX_UMA_DB0 != 0x00) + return NPCX_FLASH_STATUS_FAILED; + + return NPCX_FLASH_STATUS_OK; +} + +int flash_get_id(uint32_t *id) +{ + flash_execute_cmd(NPCX_CMD_READ_ID, NPCX_MASK_CMD_RD_3BYTE); + *id = NPCX_UMA_DB0 << 16 | NPCX_UMA_DB1 << 8 | NPCX_UMA_DB2; + + return NPCX_FLASH_STATUS_OK; +} + +/*---------------------------------------------------------------------------- + * flash loader function + *----------------------------------------------------------------------------*/ +uint32_t flashloader_init(struct npcx_flash_params *params) +{ + /* Initialize params buffers */ + memset(params, 0, sizeof(struct npcx_flash_params)); + + return NPCX_FLASH_STATUS_OK; +} + +/*---------------------------------------------------------------------------- + * Functions + *----------------------------------------------------------------------------*/ +/* flashloader parameter structure */ +__attribute__ ((section(".buffers.g_cfg"))) +volatile struct npcx_flash_params g_cfg; +/* data buffer */ +__attribute__ ((section(".buffers.g_buf"))) +uint8_t g_buf[NPCX_FLASH_LOADER_BUFFER_SIZE]; + +int main(void) +{ + uint32_t id; + + /* set buffer */ + flashloader_init((struct npcx_flash_params *)&g_cfg); + + /* Avoid F_CS0 toggles while programming the internal flash. */ + NPCX_SET_BIT(NPCX_DEVALT(0), NPCX_DEVALT0_NO_F_SPI); + + /* clear flash status registers */ + int status = flash_physical_clear_stsreg(); + if (status != NPCX_FLASH_STATUS_OK) { + while (1) + g_cfg.sync = status; + } + + while (1) { + /* wait command*/ + while (g_cfg.sync == NPCX_FLASH_LOADER_WAIT) + ; + + /* command handler */ + switch (g_cfg.cmd) { + case NPCX_FLASH_CMD_GET_FLASH_ID: + status = flash_get_id(&id); + if (status == NPCX_FLASH_STATUS_OK) { + g_buf[0] = id & 0xff; + g_buf[1] = (id >> 8) & 0xff; + g_buf[2] = (id >> 16) & 0xff; + g_buf[3] = 0x00; + } + break; + case NPCX_FLASH_CMD_ERASE_SECTORS: + status = flash_physical_erase(g_cfg.addr, g_cfg.len); + break; + case NPCX_FLASH_CMD_ERASE_ALL: + status = flash_physical_erase_all(); + break; + case NPCX_FLASH_CMD_PROGRAM: + status = flash_physical_write(g_cfg.addr, + g_cfg.len, + g_buf); + break; + default: + status = NPCX_FLASH_STATUS_FAILED_UNKNOWN_COMMAND; + break; + } + + /* clear & set result for next command */ + if (status != NPCX_FLASH_STATUS_OK) { + g_cfg.sync = status; + while (1) + ; + } else { + g_cfg.sync = NPCX_FLASH_LOADER_WAIT; + } + } + + return 0; +} + +__attribute__ ((section(".stack"))) +__attribute__ ((used)) +static uint32_t stack[NPCX_FLASH_LOADER_STACK_SIZE / 4]; +extern uint32_t _estack; +extern uint32_t _bss; +extern uint32_t _ebss; + +__attribute__ ((section(".entry"))) +void entry(void) +{ + /* set sp from end of stack */ + __asm(" ldr sp, =_estack - 4"); + + main(); + + __asm(" bkpt #0x00"); +} diff --git a/contrib/loaders/flash/npcx/npcx_flash.h b/contrib/loaders/flash/npcx/npcx_flash.h new file mode 100644 index 000000000..cc4f1ad50 --- /dev/null +++ b/contrib/loaders/flash/npcx/npcx_flash.h @@ -0,0 +1,179 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +/* + * Copyright (C) 2020 by Nuvoton Technology Corporation + * Mulin Chao + * Wealian Liao + */ + +#ifndef OPENOCD_LOADERS_FLASH_NPCX_NPCX_FLASH_H +#define OPENOCD_LOADERS_FLASH_NPCX_NPCX_FLASH_H + +#include "npcx_flash_config.h" + +/* Bit functions */ +#define NPCX_SET_BIT(reg, bit) ((reg) |= (0x1 << (bit))) +#define NPCX_CLEAR_BIT(reg, bit) ((reg) &= (~(0x1 << (bit)))) +#define NPCX_IS_BIT_SET(reg, bit) (((reg) >> (bit)) & (0x1)) + +/* Field functions */ +#define NPCX_GET_POS_FIELD(pos, size) (pos) +#define NPCX_GET_SIZE_FIELD(pos, size) (size) +#define NPCX_FIELD_POS(field) NPCX_GET_POS_##field +#define NPCX_FIELD_SIZE(field) NPCX_GET_SIZE_##field +/* Read field functions */ +#define NPCX_GET_FIELD(reg, field) \ + _NPCX_GET_FIELD_((reg), NPCX_FIELD_POS(field), NPCX_FIELD_SIZE(field)) +#define _NPCX_GET_FIELD_(reg, f_pos, f_size) \ + (((reg) >> (f_pos)) & ((1 << (f_size)) - 1)) +/* Write field functions */ +#define NPCX_SET_FIELD(reg, field, value) \ + _NPCX_SET_FIELD_((reg), NPCX_FIELD_POS(field), NPCX_FIELD_SIZE(field), (value)) +#define _NPCX_SET_FIELD_(reg, f_pos, f_size, value) \ + ((reg) = ((reg) & (~(((1 << (f_size)) - 1) << (f_pos)))) | ((value) << (f_pos))) + +/* Register definitions */ +#define NPCX_REG32_ADDR(addr) ((volatile uint32_t *)(addr)) +#define NPCX_REG16_ADDR(addr) ((volatile uint16_t *)(addr)) +#define NPCX_REG8_ADDR(addr) ((volatile uint8_t *)(addr)) + +#define NPCX_HW_BYTE(addr) (*NPCX_REG8_ADDR(addr)) +#define NPCX_HW_WORD(addr) (*NPCX_REG16_ADDR(addr)) +#define NPCX_HW_DWORD(addr) (*NPCX_REG32_ADDR(addr)) + +/* Devalt */ +#define NPCX_SCFG_BASE_ADDR 0x400C3000 +#define NPCX_DEVCNT NPCX_HW_BYTE(NPCX_SCFG_BASE_ADDR + 0x000) +#define NPCX_DEVALT(n) NPCX_HW_BYTE(NPCX_SCFG_BASE_ADDR + 0x010 + (n)) + +#define NPCX_DEVCNT_HIF_TYP_SEL_FIELD FIELD(2, 2) +#define NPCX_DEVCNT_JEN0_HEN 4 +#define NPCX_DEVCNT_JEN1_HEN 5 +#define NPCX_DEVCNT_F_SPI_TRIS 6 + +/* Pin-mux for SPI/FIU */ +#define NPCX_DEVALT0_SPIP_SL 0 +#define NPCX_DEVALT0_GPIO_NO_SPIP 3 +#define NPCX_DEVALT0_F_SPI_CS1_2 4 +#define NPCX_DEVALT0_F_SPI_CS1_1 5 +#define NPCX_DEVALT0_F_SPI_QUAD 6 +#define NPCX_DEVALT0_NO_F_SPI 7 + +/* Flash Interface Unit (FIU) registers */ +#define NPCX_FIU_BASE_ADDR 0x40020000 +#define NPCX_FIU_CFG NPCX_HW_BYTE(NPCX_FIU_BASE_ADDR + 0x000) +#define NPCX_BURST_CFG NPCX_HW_BYTE(NPCX_FIU_BASE_ADDR + 0x001) +#define NPCX_RESP_CFG NPCX_HW_BYTE(NPCX_FIU_BASE_ADDR + 0x002) +#define NPCX_SPI_FL_CFG NPCX_HW_BYTE(NPCX_FIU_BASE_ADDR + 0x014) +#define NPCX_UMA_CODE NPCX_HW_BYTE(NPCX_FIU_BASE_ADDR + 0x016) +#define NPCX_UMA_AB0 NPCX_HW_BYTE(NPCX_FIU_BASE_ADDR + 0x017) +#define NPCX_UMA_AB1 NPCX_HW_BYTE(NPCX_FIU_BASE_ADDR + 0x018) +#define NPCX_UMA_AB2 NPCX_HW_BYTE(NPCX_FIU_BASE_ADDR + 0x019) +#define NPCX_UMA_DB0 NPCX_HW_BYTE(NPCX_FIU_BASE_ADDR + 0x01A) +#define NPCX_UMA_DB1 NPCX_HW_BYTE(NPCX_FIU_BASE_ADDR + 0x01B) +#define NPCX_UMA_DB2 NPCX_HW_BYTE(NPCX_FIU_BASE_ADDR + 0x01C) +#define NPCX_UMA_DB3 NPCX_HW_BYTE(NPCX_FIU_BASE_ADDR + 0x01D) +#define NPCX_UMA_CTS NPCX_HW_BYTE(NPCX_FIU_BASE_ADDR + 0x01E) +#define NPCX_UMA_ECTS NPCX_HW_BYTE(NPCX_FIU_BASE_ADDR + 0x01F) +#define NPCX_UMA_DB0_3 NPCX_HW_DWORD(NPCX_FIU_BASE_ADDR + 0x020) +#define NPCX_FIU_RD_CMD NPCX_HW_BYTE(NPCX_FIU_BASE_ADDR + 0x030) +#define NPCX_FIU_DMM_CYC NPCX_HW_BYTE(NPCX_FIU_BASE_ADDR + 0x032) +#define NPCX_FIU_EXT_CFG NPCX_HW_BYTE(NPCX_FIU_BASE_ADDR + 0x033) +#define NPCX_FIU_UMA_AB0_3 NPCX_HW_DWORD(NPCX_FIU_BASE_ADDR + 0x034) + +/* FIU register fields */ +#define NPCX_RESP_CFG_IAD_EN 0 +#define NPCX_RESP_CFG_DEV_SIZE_EX 2 +#define NPCX_UMA_CTS_A_SIZE 3 +#define NPCX_UMA_CTS_C_SIZE 4 +#define NPCX_UMA_CTS_RD_WR 5 +#define NPCX_UMA_CTS_DEV_NUM 6 +#define NPCX_UMA_CTS_EXEC_DONE 7 +#define NPCX_UMA_ECTS_SW_CS0 0 +#define NPCX_UMA_ECTS_SW_CS1 1 +#define NPCX_UMA_ECTS_SEC_CS 2 +#define NPCX_UMA_ECTS_UMA_LOCK 3 + +/* Flash UMA commands for npcx internal SPI flash */ +#define NPCX_CMD_READ_ID 0x9F +#define NPCX_CMD_READ_MAN_DEV_ID 0x90 +#define NPCX_CMD_WRITE_EN 0x06 +#define NPCX_CMD_WRITE_STATUS 0x50 +#define NPCX_CMD_READ_STATUS_REG 0x05 +#define NPCX_CMD_READ_STATUS_REG2 0x35 +#define NPCX_CMD_WRITE_STATUS_REG 0x01 +#define NPCX_CMD_FLASH_PROGRAM 0x02 +#define NPCX_CMD_SECTOR_ERASE 0x20 +#define NPCX_CMD_PROGRAM_UINT_SIZE 0x08 +#define NPCX_CMD_PAGE_SIZE 0x00 +#define NPCX_CMD_READ_ID_TYPE 0x47 +#define NPCX_CMD_FAST_READ 0x0B +#define NPCX_CMD_CHIP_ERASE 0xC7 + +/* + * Status registers for SPI flash + */ +#define NPCX_SPI_FLASH_SR2_SUS (1 << 7) +#define NPCX_SPI_FLASH_SR2_CMP (1 << 6) +#define NPCX_SPI_FLASH_SR2_LB3 (1 << 5) +#define NPCX_SPI_FLASH_SR2_LB2 (1 << 4) +#define NPCX_SPI_FLASH_SR2_LB1 (1 << 3) +#define NPCX_SPI_FLASH_SR2_QE (1 << 1) +#define NPCX_SPI_FLASH_SR2_SRP1 (1 << 0) +#define NPCX_SPI_FLASH_SR1_SRP0 (1 << 7) +#define NPCX_SPI_FLASH_SR1_SEC (1 << 6) +#define NPCX_SPI_FLASH_SR1_TB (1 << 5) +#define NPCX_SPI_FLASH_SR1_BP2 (1 << 4) +#define NPCX_SPI_FLASH_SR1_BP1 (1 << 3) +#define NPCX_SPI_FLASH_SR1_BP0 (1 << 2) +#define NPCX_SPI_FLASH_SR1_WEL (1 << 1) +#define NPCX_SPI_FLASH_SR1_BUSY (1 << 0) + +#define NPCX_MASK_CMD_ONLY (0xC0) +#define NPCX_MASK_CMD_ADR (0xC0 | 0x08) +#define NPCX_MASK_CMD_ADR_WR (0xC0 | 0x20 | 0x08 | 0x01) +#define NPCX_MASK_RD_1BYTE (0xC0 | 0x10 | 0x01) +#define NPCX_MASK_RD_2BYTE (0xC0 | 0x10 | 0x02) +#define NPCX_MASK_RD_3BYTE (0xC0 | 0x10 | 0x03) +#define NPCX_MASK_RD_4BYTE (0xC0 | 0x10 | 0x04) +#define NPCX_MASK_CMD_RD_1BYTE (0xC0 | 0x01) +#define NPCX_MASK_CMD_RD_2BYTE (0xC0 | 0x02) +#define NPCX_MASK_CMD_RD_3BYTE (0xC0 | 0x03) +#define NPCX_MASK_CMD_RD_4BYTE (0xC0 | 0x04) +#define NPCX_MASK_CMD_WR_ONLY (0xC0 | 0x20) +#define NPCX_MASK_CMD_WR_1BYTE (0xC0 | 0x20 | 0x10 | 0x01) +#define NPCX_MASK_CMD_WR_2BYTE (0xC0 | 0x20 | 0x10 | 0x02) +#define NPCX_MASK_CMD_WR_ADR (0xC0 | 0x20 | 0x08) + +/* Flash loader parameters */ +struct __attribute__((__packed__)) npcx_flash_params { + uint32_t addr; /* Address in flash */ + uint32_t len; /* Number of bytes */ + uint32_t cmd; /* Command */ + uint32_t sync; /* Handshake signal */ +}; + +/* Flash trigger signal */ +enum npcx_flash_handshake { + NPCX_FLASH_LOADER_WAIT = 0x0, /* Idle */ + NPCX_FLASH_LOADER_EXECUTE = 0xFFFFFFFF /* Execute Command */ +}; + +/* Flash loader command */ +enum npcx_flash_commands { + NPCX_FLASH_CMD_NO_ACTION = 0, /* No action, default value */ + NPCX_FLASH_CMD_GET_FLASH_ID, /* Get the internal flash ID */ + NPCX_FLASH_CMD_ERASE_SECTORS, /* Erase unprotected sectors */ + NPCX_FLASH_CMD_ERASE_ALL, /* Erase all */ + NPCX_FLASH_CMD_PROGRAM, /* Program data */ +}; + +/* Status */ +enum npcx_flash_status { + NPCX_FLASH_STATUS_OK = 0, + NPCX_FLASH_STATUS_FAILED_UNKNOWN_COMMAND, + NPCX_FLASH_STATUS_FAILED, + NPCX_FLASH_STATUS_FAILED_TIMEOUT, +}; + +#endif /* OPENOCD_LOADERS_FLASH_NPCX_NPCX_FLASH_H */ diff --git a/contrib/loaders/flash/npcx/npcx_flash.lds b/contrib/loaders/flash/npcx/npcx_flash.lds new file mode 100644 index 000000000..0d782523a --- /dev/null +++ b/contrib/loaders/flash/npcx/npcx_flash.lds @@ -0,0 +1,58 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#include "npcx_flash_config.h" + +/* Application memory map */ +MEMORY { + /* buffer + parameters */ + BUFFER (RWX) : ORIGIN = NPCX_FLASH_LOADER_PARAMS_ADDR, + LENGTH = NPCX_FLASH_LOADER_PARAMS_SIZE + NPCX_FLASH_LOADER_BUFFER_SIZE + + PROGRAM (RWX) : ORIGIN = NPCX_FLASH_LOADER_PROGRAM_ADDR, + LENGTH = NPCX_FLASH_LOADER_PROGRAM_SIZE +} + +/* Sections used for flashing */ +SECTIONS +{ + .buffers (NOLOAD) : + { + _buffers = .; + *(.buffers.g_cfg) + *(.buffers.g_buf) + *(.buffers*) + _ebuffers = .; + } > BUFFER + + .text : + { + _text = .; + *(.entry*) + *(.text*) + _etext = .; + } > PROGRAM + + .data : + { _data = .; + *(.rodata*) + *(.data*) + _edata = .; + } > PROGRAM + + .bss : + { + __bss_start__ = .; + _bss = .; + *(.bss*) + *(COMMON) + _ebss = .; + __bss_end__ = .; + } > PROGRAM + + .stack (NOLOAD) : + { + _stack = .; + *(.stack*) + _estack = .; + } > PROGRAM +} diff --git a/contrib/loaders/flash/npcx/npcx_flash_config.h b/contrib/loaders/flash/npcx/npcx_flash_config.h new file mode 100644 index 000000000..9ec1c5e33 --- /dev/null +++ b/contrib/loaders/flash/npcx/npcx_flash_config.h @@ -0,0 +1,31 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +/* + * Copyright (C) 2021 by Nuvoton Technology Corporation + * Mulin Chao + * Wealian Liao + */ + +#ifndef OPENOCD_LOADERS_FLASH_NPCX_NPCX_FLASH_CONFIG_H +#define OPENOCD_LOADERS_FLASH_NPCX_NPCX_FLASH_CONFIG_H + +#define NPCX_FLASH_ABORT_TIMEOUT 0xFFFFFF + +/* NPCX chip information */ +#define NPCX_FLASH_WRITE_SIZE 256L /* One page size for write */ +#define NPCX_FLASH_ERASE_SIZE 0x1000 + +/* NPCX flash loader information */ +#define NPCX_FLASH_LOADER_WORKING_ADDR 0x200C0000 +#define NPCX_FLASH_LOADER_PARAMS_ADDR NPCX_FLASH_LOADER_WORKING_ADDR +#define NPCX_FLASH_LOADER_PARAMS_SIZE 16 +#define NPCX_FLASH_LOADER_BUFFER_ADDR (NPCX_FLASH_LOADER_PARAMS_ADDR + NPCX_FLASH_LOADER_PARAMS_SIZE) +#define NPCX_FLASH_LOADER_BUFFER_SIZE NPCX_FLASH_ERASE_SIZE +#define NPCX_FLASH_LOADER_PROGRAM_ADDR (NPCX_FLASH_LOADER_BUFFER_ADDR + NPCX_FLASH_LOADER_BUFFER_SIZE) +#define NPCX_FLASH_LOADER_PROGRAM_SIZE 0x1000 + +/* Stack size in byte. 4 byte size alignment */ +#define NPCX_FLASH_LOADER_STACK_SIZE 400 + + +#endif /* OPENOCD_LOADERS_FLASH_NPCX_NPCX_FLASH_CONFIG_H */ diff --git a/doc/openocd.texi b/doc/openocd.texi index 2759a39d3..9c94c7168 100644 --- a/doc/openocd.texi +++ b/doc/openocd.texi @@ -6777,6 +6777,17 @@ Show information about flash driver. @end deffn +@deffn {Flash Driver} {npcx} +All versions of the NPCX microcontroller families from Nuvoton include internal +flash. The NPCX flash driver supports the NPCX family of devices. The driver +automatically recognizes the specific version's flash parameters and +autoconfigures itself. The flash bank starts at address 0x64000000. + +@example +flash bank $_FLASHNAME npcx 0x64000000 0 0 0 $_TARGETNAME +@end example +@end deffn + @deffn {Flash Driver} {nrf5} All members of the nRF51 microcontroller families from Nordic Semiconductor include internal flash and use ARM Cortex-M0 core. diff --git a/src/flash/nor/Makefile.am b/src/flash/nor/Makefile.am index 532670436..a5ef42210 100644 --- a/src/flash/nor/Makefile.am +++ b/src/flash/nor/Makefile.am @@ -44,6 +44,7 @@ NOR_DRIVERS = \ %D%/mrvlqspi.c \ %D%/niietcm4.c \ %D%/non_cfi.c \ + %D%/npcx.c \ %D%/nrf5.c \ %D%/numicro.c \ %D%/ocl.c \ diff --git a/src/flash/nor/drivers.c b/src/flash/nor/drivers.c index 6eadc756b..3e35c0954 100644 --- a/src/flash/nor/drivers.c +++ b/src/flash/nor/drivers.c @@ -56,6 +56,7 @@ extern const struct flash_driver mdr_flash; extern const struct flash_driver mrvlqspi_flash; extern const struct flash_driver msp432_flash; extern const struct flash_driver niietcm4_flash; +extern const struct flash_driver npcx_flash; extern const struct flash_driver nrf5_flash; extern const struct flash_driver nrf51_flash; extern const struct flash_driver numicro_flash; @@ -130,6 +131,7 @@ static const struct flash_driver * const flash_drivers[] = { &mrvlqspi_flash, &msp432_flash, &niietcm4_flash, + &npcx_flash, &nrf5_flash, &nrf51_flash, &numicro_flash, diff --git a/src/flash/nor/npcx.c b/src/flash/nor/npcx.c new file mode 100644 index 000000000..af623e577 --- /dev/null +++ b/src/flash/nor/npcx.c @@ -0,0 +1,524 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +/* + * Copyright (C) 2020 by Nuvoton Technology Corporation + * Mulin Chao + * Wealian Liao + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "imp.h" +#include +#include +#include +#include "../../../contrib/loaders/flash/npcx/npcx_flash.h" + +/* NPCX flash loader */ +const uint8_t npcx_algo[] = { +#include "../../../contrib/loaders/flash/npcx/npcx_algo.inc" +}; + +#define NPCX_FLASH_TIMEOUT_MS 8000 +#define NPCX_FLASH_BASE_ADDR 0x64000000 + +/* flash list */ +enum npcx_flash_device_index { + NPCX_FLASH_256KB = 0, + NPCX_FLASH_512KB = 1, + NPCX_FLASH_1MB = 2, + NPCX_FLASH_UNKNOWN, +}; + +struct npcx_flash_bank { + const char *family_name; + uint32_t sector_length; + bool probed; + enum npcx_flash_device_index flash; + struct working_area *working_area; + struct armv7m_algorithm armv7m_info; + const uint8_t *algo_code; + uint32_t algo_size; + uint32_t algo_working_size; + uint32_t buffer_addr; + uint32_t params_addr; +}; + +struct npcx_flash_info { + char *name; + uint32_t id; + uint32_t size; +}; + +static const struct npcx_flash_info flash_info[] = { + [NPCX_FLASH_256KB] = { + .name = "256KB Flash", + .id = 0xEF4012, + .size = 256 * 1024, + }, + [NPCX_FLASH_512KB] = { + .name = "512KB Flash", + .id = 0xEF4013, + .size = 512 * 1024, + }, + [NPCX_FLASH_1MB] = { + .name = "1MB Flash", + .id = 0xEF4014, + .size = 1024 * 1024, + }, + [NPCX_FLASH_UNKNOWN] = { + .name = "Unknown Flash", + .size = 0xFFFFFFFF, + }, +}; + +static int npcx_init(struct flash_bank *bank) +{ + struct target *target = bank->target; + struct npcx_flash_bank *npcx_bank = bank->driver_priv; + + /* Check for working area to use for flash helper algorithm */ + if (npcx_bank->working_area) { + target_free_working_area(target, npcx_bank->working_area); + npcx_bank->working_area = NULL; + } + + int retval = target_alloc_working_area(target, npcx_bank->algo_working_size, + &npcx_bank->working_area); + if (retval != ERROR_OK) + return retval; + + /* Confirm the defined working address is the area we need to use */ + if (npcx_bank->working_area->address != NPCX_FLASH_LOADER_WORKING_ADDR) { + LOG_ERROR("%s: Invalid working address", npcx_bank->family_name); + LOG_INFO("Hint: Use '-work-area-phys 0x%" PRIx32 "' in your target configuration", + NPCX_FLASH_LOADER_WORKING_ADDR); + target_free_working_area(target, npcx_bank->working_area); + npcx_bank->working_area = NULL; + return ERROR_TARGET_RESOURCE_NOT_AVAILABLE; + } + + /* Write flash helper algorithm into target memory */ + retval = target_write_buffer(target, NPCX_FLASH_LOADER_PROGRAM_ADDR, + npcx_bank->algo_size, npcx_bank->algo_code); + if (retval != ERROR_OK) { + LOG_ERROR("%s: Failed to load flash helper algorithm", + npcx_bank->family_name); + target_free_working_area(target, npcx_bank->working_area); + npcx_bank->working_area = NULL; + return retval; + } + + /* Initialize the ARMv7 specific info to run the algorithm */ + npcx_bank->armv7m_info.common_magic = ARMV7M_COMMON_MAGIC; + npcx_bank->armv7m_info.core_mode = ARM_MODE_THREAD; + + /* Begin executing the flash helper algorithm */ + retval = target_start_algorithm(target, 0, NULL, 0, NULL, + NPCX_FLASH_LOADER_PROGRAM_ADDR, 0, + &npcx_bank->armv7m_info); + if (retval != ERROR_OK) { + LOG_ERROR("%s: Failed to start flash helper algorithm", + npcx_bank->family_name); + target_free_working_area(target, npcx_bank->working_area); + npcx_bank->working_area = NULL; + return retval; + } + + /* + * At this point, the algorithm is running on the target and + * ready to receive commands and data to flash the target + */ + + return retval; +} + +static int npcx_quit(struct flash_bank *bank) +{ + struct target *target = bank->target; + struct npcx_flash_bank *npcx_bank = bank->driver_priv; + + /* Regardless of the algo's status, attempt to halt the target */ + (void)target_halt(target); + + /* Now confirm target halted and clean up from flash helper algorithm */ + int retval = target_wait_algorithm(target, 0, NULL, 0, NULL, 0, + NPCX_FLASH_TIMEOUT_MS, &npcx_bank->armv7m_info); + + target_free_working_area(target, npcx_bank->working_area); + npcx_bank->working_area = NULL; + + return retval; +} + +static int npcx_wait_algo_done(struct flash_bank *bank, uint32_t params_addr) +{ + struct target *target = bank->target; + struct npcx_flash_bank *npcx_bank = bank->driver_priv; + uint32_t status_addr = params_addr + offsetof(struct npcx_flash_params, sync); + uint32_t status; + int64_t start_ms = timeval_ms(); + + do { + int retval = target_read_u32(target, status_addr, &status); + if (retval != ERROR_OK) + return retval; + + keep_alive(); + + int64_t elapsed_ms = timeval_ms() - start_ms; + if (elapsed_ms > NPCX_FLASH_TIMEOUT_MS) + break; + } while (status == NPCX_FLASH_LOADER_EXECUTE); + + if (status != NPCX_FLASH_LOADER_WAIT) { + LOG_ERROR("%s: Flash operation failed, status=0x%" PRIx32, + npcx_bank->family_name, + status); + return ERROR_FAIL; + } + + return ERROR_OK; +} + +static enum npcx_flash_device_index npcx_get_flash_id(struct flash_bank *bank, uint32_t *flash_id) +{ + struct target *target = bank->target; + struct npcx_flash_bank *npcx_bank = bank->driver_priv; + struct npcx_flash_params algo_params; + + if (target->state != TARGET_HALTED) { + LOG_ERROR("Target not halted"); + return ERROR_TARGET_NOT_HALTED; + } + + int retval = npcx_init(bank); + if (retval != ERROR_OK) + return retval; + + /* Set up algorithm parameters for get flash ID command */ + target_buffer_set_u32(target, (uint8_t *)&algo_params.cmd, NPCX_FLASH_CMD_GET_FLASH_ID); + target_buffer_set_u32(target, (uint8_t *)&algo_params.sync, NPCX_FLASH_LOADER_WAIT); + + /* Issue flash helper algorithm parameters for get flash ID */ + retval = target_write_buffer(target, npcx_bank->params_addr, + sizeof(algo_params), (uint8_t *)&algo_params); + if (retval != ERROR_OK) { + (void)npcx_quit(bank); + return retval; + } + + target_buffer_set_u32(target, (uint8_t *)&algo_params.sync, NPCX_FLASH_LOADER_EXECUTE); + retval = target_write_buffer(target, npcx_bank->params_addr, + sizeof(algo_params), (uint8_t *)&algo_params); + + /* If no error, wait for finishing */ + if (retval == ERROR_OK) { + retval = npcx_wait_algo_done(bank, npcx_bank->params_addr); + if (retval == ERROR_OK) + target_read_u32(target, NPCX_FLASH_LOADER_BUFFER_ADDR, flash_id); + } + + /* Regardless of errors, try to close down algo */ + (void)npcx_quit(bank); + + return retval; +} + +static int npcx_get_flash(uint32_t flash_id) +{ + for (uint32_t i = 0; i < ARRAY_SIZE(flash_info) - 1; i++) { + if (flash_info[i].id == flash_id) + return i; + } + + return NPCX_FLASH_UNKNOWN; +} + +static int npcx_probe(struct flash_bank *bank) +{ + struct npcx_flash_bank *npcx_bank = bank->driver_priv; + uint32_t sector_length = NPCX_FLASH_ERASE_SIZE; + uint32_t flash_id; + + /* Set up appropriate flash helper algorithm */ + npcx_bank->algo_code = npcx_algo; + npcx_bank->algo_size = sizeof(npcx_algo); + npcx_bank->algo_working_size = NPCX_FLASH_LOADER_PARAMS_SIZE + + NPCX_FLASH_LOADER_BUFFER_SIZE + + NPCX_FLASH_LOADER_PROGRAM_SIZE; + npcx_bank->buffer_addr = NPCX_FLASH_LOADER_BUFFER_ADDR; + npcx_bank->params_addr = NPCX_FLASH_LOADER_PARAMS_ADDR; + + int retval = npcx_get_flash_id(bank, &flash_id); + if (retval != ERROR_OK) + return retval; + + npcx_bank->flash = npcx_get_flash(flash_id); + + unsigned int num_sectors = flash_info[npcx_bank->flash].size / sector_length; + + bank->sectors = calloc(num_sectors, sizeof(struct flash_sector)); + if (!bank->sectors) { + LOG_ERROR("Out of memory"); + return ERROR_FAIL; + } + + bank->base = NPCX_FLASH_BASE_ADDR; + bank->num_sectors = num_sectors; + bank->size = num_sectors * sector_length; + bank->write_start_alignment = 0; + bank->write_end_alignment = 0; + npcx_bank->sector_length = sector_length; + + for (unsigned int i = 0; i < num_sectors; i++) { + bank->sectors[i].offset = i * sector_length; + bank->sectors[i].size = sector_length; + bank->sectors[i].is_erased = -1; + bank->sectors[i].is_protected = 0; + } + + /* We've successfully determined the stats on the flash bank */ + npcx_bank->probed = true; + + /* If we fall through to here, then all went well */ + return ERROR_OK; +} + +static int npcx_auto_probe(struct flash_bank *bank) +{ + struct npcx_flash_bank *npcx_bank = bank->driver_priv; + int retval = ERROR_OK; + + if (!npcx_bank->probed) + retval = npcx_probe(bank); + + return retval; +} + +FLASH_BANK_COMMAND_HANDLER(npcx_flash_bank_command) +{ + struct npcx_flash_bank *npcx_bank; + + if (CMD_ARGC < 6) + return ERROR_COMMAND_SYNTAX_ERROR; + + npcx_bank = calloc(1, sizeof(struct npcx_flash_bank)); + if (!npcx_bank) { + LOG_ERROR("Out of memory"); + return ERROR_FAIL; + } + + /* Initialize private flash information */ + npcx_bank->family_name = "npcx"; + npcx_bank->sector_length = NPCX_FLASH_ERASE_SIZE; + + /* Finish initialization of bank */ + bank->driver_priv = npcx_bank; + bank->next = NULL; + + return ERROR_OK; +} + +static int npcx_chip_erase(struct flash_bank *bank) +{ + struct target *target = bank->target; + struct npcx_flash_bank *npcx_bank = bank->driver_priv; + struct npcx_flash_params algo_params; + + if (target->state != TARGET_HALTED) { + LOG_ERROR("Target not halted"); + return ERROR_TARGET_NOT_HALTED; + } + + /* Make sure we've probed the flash to get the device and size */ + int retval = npcx_auto_probe(bank); + if (retval != ERROR_OK) + return retval; + + retval = npcx_init(bank); + if (retval != ERROR_OK) + return retval; + + /* Set up algorithm parameters for chip erase command */ + target_buffer_set_u32(target, (uint8_t *)&algo_params.cmd, NPCX_FLASH_CMD_ERASE_ALL); + target_buffer_set_u32(target, (uint8_t *)&algo_params.sync, NPCX_FLASH_LOADER_WAIT); + + /* Set algorithm parameters */ + retval = target_write_buffer(target, npcx_bank->params_addr, + sizeof(algo_params), (uint8_t *)&algo_params); + if (retval != ERROR_OK) { + (void)npcx_quit(bank); + return retval; + } + + /* Issue flash helper algorithm parameters for chip erase */ + target_buffer_set_u32(target, (uint8_t *)&algo_params.sync, NPCX_FLASH_LOADER_EXECUTE); + retval = target_write_buffer(target, npcx_bank->params_addr, + sizeof(algo_params), (uint8_t *)&algo_params); + + /* If no error, wait for chip erase finish */ + if (retval == ERROR_OK) + retval = npcx_wait_algo_done(bank, npcx_bank->params_addr); + + /* Regardless of errors, try to close down algo */ + (void)npcx_quit(bank); + + return retval; +} + +static int npcx_erase(struct flash_bank *bank, unsigned int first, + unsigned int last) +{ + struct target *target = bank->target; + struct npcx_flash_bank *npcx_bank = bank->driver_priv; + struct npcx_flash_params algo_params; + + if (target->state != TARGET_HALTED) { + LOG_ERROR("Target not halted"); + return ERROR_TARGET_NOT_HALTED; + } + + if ((first == 0) && (last == (bank->num_sectors - 1))) { + /* Request chip erase */ + return npcx_chip_erase(bank); + } + + uint32_t address = first * npcx_bank->sector_length; + uint32_t length = (last - first + 1) * npcx_bank->sector_length; + + /* Make sure we've probed the flash to get the device and size */ + int retval = npcx_auto_probe(bank); + if (retval != ERROR_OK) + return retval; + + retval = npcx_init(bank); + if (retval != ERROR_OK) + return retval; + + /* Set up algorithm parameters for erase command */ + target_buffer_set_u32(target, (uint8_t *)&algo_params.addr, address); + target_buffer_set_u32(target, (uint8_t *)&algo_params.len, length); + target_buffer_set_u32(target, (uint8_t *)&algo_params.cmd, NPCX_FLASH_CMD_ERASE_SECTORS); + target_buffer_set_u32(target, (uint8_t *)&algo_params.sync, NPCX_FLASH_LOADER_WAIT); + + /* Set algorithm parameters */ + retval = target_write_buffer(target, npcx_bank->params_addr, + sizeof(algo_params), (uint8_t *)&algo_params); + if (retval != ERROR_OK) { + (void)npcx_quit(bank); + return retval; + } + + /* Issue flash helper algorithm parameters for erase */ + target_buffer_set_u32(target, (uint8_t *)&algo_params.sync, NPCX_FLASH_LOADER_EXECUTE); + retval = target_write_buffer(target, npcx_bank->params_addr, + sizeof(algo_params), (uint8_t *)&algo_params); + + /* If no error, wait for erase to finish */ + if (retval == ERROR_OK) + retval = npcx_wait_algo_done(bank, npcx_bank->params_addr); + + /* Regardless of errors, try to close down algo */ + (void)npcx_quit(bank); + + return retval; +} + +static int npcx_write(struct flash_bank *bank, const uint8_t *buffer, + uint32_t offset, uint32_t count) +{ + struct target *target = bank->target; + struct npcx_flash_bank *npcx_bank = bank->driver_priv; + struct npcx_flash_params algo_params; + + if (target->state != TARGET_HALTED) { + LOG_ERROR("Target not halted"); + return ERROR_TARGET_NOT_HALTED; + } + + /* Make sure we've probed the flash to get the device and size */ + int retval = npcx_auto_probe(bank); + if (retval != ERROR_OK) + return retval; + + retval = npcx_init(bank); + if (retval != ERROR_OK) + return retval; + + /* Initialize algorithm parameters to default values */ + target_buffer_set_u32(target, (uint8_t *)&algo_params.cmd, NPCX_FLASH_CMD_PROGRAM); + + uint32_t address = offset; + + while (count > 0) { + uint32_t size = (count > NPCX_FLASH_LOADER_BUFFER_SIZE) ? + NPCX_FLASH_LOADER_BUFFER_SIZE : count; + + /* Put the data into buffer */ + retval = target_write_buffer(target, npcx_bank->buffer_addr, + size, buffer); + if (retval != ERROR_OK) { + LOG_ERROR("Unable to write data to target memory"); + break; + } + + /* Update algo parameters for flash write */ + target_buffer_set_u32(target, (uint8_t *)&algo_params.addr, address); + target_buffer_set_u32(target, (uint8_t *)&algo_params.len, size); + target_buffer_set_u32(target, (uint8_t *)&algo_params.sync, NPCX_FLASH_LOADER_WAIT); + + /* Set algorithm parameters */ + retval = target_write_buffer(target, npcx_bank->params_addr, + sizeof(algo_params), (uint8_t *)&algo_params); + if (retval != ERROR_OK) + break; + + /* Issue flash helper algorithm parameters for flash write */ + target_buffer_set_u32(target, (uint8_t *)&algo_params.sync, NPCX_FLASH_LOADER_EXECUTE); + retval = target_write_buffer(target, npcx_bank->params_addr, + sizeof(algo_params), (uint8_t *)&algo_params); + if (retval != ERROR_OK) + break; + + /* Wait for flash write finish */ + retval = npcx_wait_algo_done(bank, npcx_bank->params_addr); + if (retval != ERROR_OK) + break; + + count -= size; + buffer += size; + address += size; + } + + /* Regardless of errors, try to close down algo */ + (void)npcx_quit(bank); + + return retval; +} + +static int npcx_info(struct flash_bank *bank, struct command_invocation *cmd) +{ + struct npcx_flash_bank *npcx_bank = bank->driver_priv; + + command_print_sameline(cmd, "%s flash: %s\n", + npcx_bank->family_name, + flash_info[npcx_bank->flash].name); + + return ERROR_OK; +} + +const struct flash_driver npcx_flash = { + .name = "npcx", + .flash_bank_command = npcx_flash_bank_command, + .erase = npcx_erase, + .write = npcx_write, + .read = default_flash_read, + .probe = npcx_probe, + .auto_probe = npcx_auto_probe, + .erase_check = default_flash_blank_check, + .info = npcx_info, + .free_driver_priv = default_flash_free_driver_priv, +}; diff --git a/tcl/board/npcx_evb.cfg b/tcl/board/npcx_evb.cfg new file mode 100644 index 000000000..4f28bc964 --- /dev/null +++ b/tcl/board/npcx_evb.cfg @@ -0,0 +1,8 @@ +# SPDX-License-Identifier: GPL-2.0-or-later + +# Nuvoton NPCX Evaluation Board + +source [find interface/jlink.cfg] +transport select swd + +source [find target/npcx.cfg] diff --git a/tcl/target/npcx.cfg b/tcl/target/npcx.cfg new file mode 100644 index 000000000..1a21e1f7f --- /dev/null +++ b/tcl/target/npcx.cfg @@ -0,0 +1,51 @@ +# SPDX-License-Identifier: GPL-2.0-or-later + +# script for Nuvoton NPCX Cortex-M4 Series + +# Adapt based on what transport is active. +source [find target/swj-dp.tcl] + +# Set Chipname +if { [info exists CHIPNAME] } { + set _CHIPNAME $CHIPNAME +} else { + set _CHIPNAME NPCX_M4 +} + +# SWD DAP ID of Nuvoton NPCX Cortex-M4. +if { [info exists CPUDAPID ] } { + set _CPUDAPID $CPUDAPID +} else { + set _CPUDAPID 0x4BA00477 +} + +# Work-area is a space in RAM used for flash programming +# By default use 32kB +if { [info exists WORKAREASIZE] } { + set _WORKAREASIZE $WORKAREASIZE +} else { + set _WORKAREASIZE 0x8000 +} + +# Debug Adapter Target Settings +swj_newdap $_CHIPNAME cpu -irlen 4 -expected-id $_CPUDAPID +dap create $_CHIPNAME.dap -chain-position $_CHIPNAME.cpu +set _TARGETNAME $_CHIPNAME.cpu +target create $_TARGETNAME cortex_m -endian little -dap $_CHIPNAME.dap + +$_TARGETNAME configure -work-area-phys 0x200c0000 -work-area-size $_WORKAREASIZE -work-area-backup 0 + +# Initial JTAG/SWD speed +# For safety purposes, set for the lowest cpu clock configuration +# 4MHz / 6 = 666KHz, so use 600KHz for it +adapter speed 600 + +# For safety purposes, set for the lowest cpu clock configuration +$_TARGETNAME configure -event reset-start {adapter speed 600} + +# use sysresetreq to perform a system reset +cortex_m reset_config sysresetreq + +# flash configuration +set _FLASHNAME $_CHIPNAME.flash +flash bank $_FLASHNAME npcx 0x64000000 0 0 0 $_TARGETNAME