1
0
mirror of https://git.code.sf.net/p/openocd/code synced 2024-11-25 18:06:24 +00:00
openocd/contrib/loaders/flash/npcx/npcx_flash.c
Luca Hung 62f76b2169 flash/nor: add support for Nuvoton NPCX4/K3 series flash
Added NPCX flash driver to support the Nuvoton NPCX4/K3 series
microcontrollers. Add config file for these series.

Change-Id: I0b6e128fa51146b561f422e23a98260594b1f138
Signed-off-by: Luca Hung <YCHUNG0@nuvoton.com>
Signed-off-by: Mulin CHao <mlchao@nuvoton.com>
Reviewed-on: https://review.openocd.org/c/openocd/+/7794
Tested-by: jenkins
Reviewed-by: Tomas Vanek <vanekt@fbl.cz>
Reviewed-by: Antonio Borneo <borneo.antonio@gmail.com>
2023-09-08 22:01:12 +00:00

400 lines
9.5 KiB
C

// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Copyright (C) 2020 by Nuvoton Technology Corporation
* Mulin Chao <mlchao@nuvoton.com>
* Wealian Liao <WHLIAO@nuvoton.com>
*/
#include <stdint.h>
#include <string.h>
#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(&reg1, &reg2);
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(&reg1, &reg2);
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");
}