// 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" /* flashloader parameter structure */ __attribute__ ((section(".buffers.g_cfg"))) static volatile struct npcx_flash_params g_cfg; /* data buffer */ __attribute__ ((section(".buffers.g_buf"))) static uint8_t g_buf[NPCX_FLASH_LOADER_BUFFER_SIZE]; /*---------------------------------------------------------------------------- * NPCX flash driver *----------------------------------------------------------------------------*/ static void flash_init(void) { if (g_cfg.fiu_ver == NPCX_FIU_NPCK) { /* Set pinmux to SHD flash */ NPCX_DEVCNT = 0x80; NPCX_DEVALT(0) = 0xC0; NPCX_DEVALT(4) = 0x00; } else { /* Avoid F_CS0 toggles while programming the internal flash. */ NPCX_SET_BIT(NPCX_DEVALT(0), NPCX_DEVALT0_NO_F_SPI); } } 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) { int sw_cs = 0; if (g_cfg.fiu_ver == NPCX_FIU_NPCX) { sw_cs = 1; } else if (g_cfg.fiu_ver == NPCX_FIU_NPCX_V2) { sw_cs = 0; } else if (g_cfg.fiu_ver == NPCX_FIU_NPCK) { sw_cs = 1; /* Unlock UMA before pulling down CS in NPCK series */ if (level) NPCX_CLEAR_BIT(NPCX_FIU_MSR_IE_CFG, NPCX_FIU_MSR_IE_CFG_UMA_BLOCK); else NPCX_SET_BIT(NPCX_FIU_MSR_IE_CFG, NPCX_FIU_MSR_IE_CFG_UMA_BLOCK); } /* Program chip select pin to high/low level */ if (level) NPCX_SET_BIT(NPCX_UMA_ECTS, sw_cs); else NPCX_CLEAR_BIT(NPCX_UMA_ECTS, sw_cs); } static void flash_set_address(uint32_t dest_addr) { uint8_t *addr = (uint8_t *)&dest_addr; /* Set target flash address */ NPCX_UMA_DB0 = addr[2]; NPCX_UMA_DB1 = addr[1]; NPCX_UMA_DB2 = addr[0]; } static void delay(uint32_t i) { while (i--) __asm__ volatile ("nop"); } 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_3BYTE); 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); } static void flash_get_stsreg(uint8_t *reg1, uint8_t *reg2) { /* Read status register 1/2 for checking */ flash_execute_cmd(NPCX_CMD_READ_STATUS_REG, NPCX_MASK_CMD_RD_1BYTE); *reg1 = NPCX_UMA_DB0; flash_execute_cmd(NPCX_CMD_READ_STATUS_REG2, NPCX_MASK_CMD_RD_1BYTE); *reg2 = NPCX_UMA_DB0; } /* 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); } static int flash_physical_clear_stsreg(void) { int status; uint8_t reg1, reg2; /* Read status register 1/2 for checking */ flash_get_stsreg(®1, ®2); if (reg1 == 0x00 && reg2 == 0x00) return NPCX_FLASH_STATUS_OK; /* Enable write */ 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_get_stsreg(®1, ®2); if (reg1 != 0x00 || reg2 != 0x00) return NPCX_FLASH_STATUS_FAILED; return NPCX_FLASH_STATUS_OK; } static 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); /* Configure fiu and clear status registers if needed */ flash_init(); status = flash_physical_clear_stsreg(); if (status != NPCX_FLASH_STATUS_OK) return status; 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; } static int flash_physical_erase(uint32_t offset, uint32_t size) { /* Configure fiu */ flash_init(); /* clear flash status registers */ int status = flash_physical_clear_stsreg(); if (status != NPCX_FLASH_STATUS_OK) return status; /* 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_WR_3BYTE); /* Wait erase completed */ status = flash_wait_ready(NPCX_FLASH_ABORT_TIMEOUT); if (status != NPCX_FLASH_STATUS_OK) return status; } return NPCX_FLASH_STATUS_OK; } static int flash_physical_erase_all(void) { int status; /* Configure fiu and clear status register if needed */ flash_init(); status = flash_physical_clear_stsreg(); if (status != NPCX_FLASH_STATUS_OK) return status; /* Enable write */ 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; } static int flash_get_id(uint32_t *id) { flash_init(); 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 *----------------------------------------------------------------------------*/ static 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 *----------------------------------------------------------------------------*/ static int main(void) { uint32_t id, status; /* set buffer */ flashloader_init((struct npcx_flash_params *)&g_cfg); 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"); }