#include "general.h"
#include "target.h"
#include "target_internal.h"
#include "cortex.h"
#define HC32L110_FLASH_BASE 0x00000000U
#define HC32L110_FLASH_SECTOR_SIZE 512U
#define HC32L110_FLASH_WRITE_SIZE 4U
#define HC32L110_FLASH_SIZE 0x00100c70U
#define HC32L110_FLASH_CTRL_BASE 0x40020000U
#define HC32L110_FLASH_CR (HC32L110_FLASH_CTRL_BASE + 0x020U)
#define HC32L110_FLASH_BYPASS (HC32L110_FLASH_CTRL_BASE + 0x02cU)
#define HC32L110_FLASH_SLOCK (HC32L110_FLASH_CTRL_BASE + 0x030U)
#define HC32L110_FLASH_CR_BUSY (1U << 4U)
#define HC32L110_FLASH_CR_OP_READ 0U
#define HC32L110_FLASH_CR_OP_PROGRAM 1U
#define HC32L110_FLASH_CR_OP_ERASE_SECTOR 2U
#define HC32L110_FLASH_CR_OP_ERASE_CHIP 3U
static bool hc32l110_enter_flash_mode(target_s *target);
static bool hc32l110_flash_prepare(target_flash_s *flash);
static bool hc32l110_flash_done(target_flash_s *flash);
static bool hc32l110_flash_erase(target_flash_s *flash, target_addr_t addr, size_t length);
static bool hc32l110_flash_write(target_flash_s *flash, target_addr_t dest, const void *src, size_t length);
static bool hc32l110_mass_erase(target_s *target);
static void hc32l110_add_flash(target_s *target, const uint32_t flash_size)
{
target_flash_s *flash = calloc(1, sizeof(*flash));
if (!flash) {
DEBUG_ERROR("calloc: failed in %s\n", __func__);
return;
}
flash->start = HC32L110_FLASH_BASE;
flash->length = flash_size;
flash->blocksize = HC32L110_FLASH_SECTOR_SIZE;
flash->writesize = HC32L110_FLASH_WRITE_SIZE;
flash->erased = 0xffU;
flash->erase = hc32l110_flash_erase;
flash->write = hc32l110_flash_write;
flash->prepare = hc32l110_flash_prepare;
flash->done = hc32l110_flash_done;
target_add_flash(target, flash);
}
bool hc32l110_probe(target_s *target)
{
const uint32_t flash_size = target_mem_read32(target, HC32L110_FLASH_SIZE);
switch (flash_size) {
case 16384:
target_add_ram(target, 0x2000000, 2048);
break;
case 32768:
target_add_ram(target, 0x2000000, 4096);
break;
default:
return false;
}
target->driver = "HC32L110";
target->enter_flash_mode = hc32l110_enter_flash_mode;
target->mass_erase = hc32l110_mass_erase;
hc32l110_add_flash(target, flash_size);
return true;
}
static void hc32l110_flash_cr_unlock(target_s *const target)
{
target_mem_write32(target, HC32L110_FLASH_BYPASS, 0x5a5aU);
target_mem_write32(target, HC32L110_FLASH_BYPASS, 0xa5a5U);
}
static bool hc32l110_check_flash_completion(target_s *const target, const uint32_t timeout_ms)
{
platform_timeout_s timeout;
platform_timeout_set(&timeout, timeout_ms);
uint32_t status = HC32L110_FLASH_CR_BUSY;
while (status & HC32L110_FLASH_CR_BUSY) {
status = target_mem_read32(target, HC32L110_FLASH_CR);
if (target_check_error(target) || platform_timeout_is_expired(&timeout))
return false;
}
return true;
}
static void hc32l110_slock_lock_all(target_s *const target)
{
hc32l110_flash_cr_unlock(target);
target_mem_write32(target, HC32L110_FLASH_SLOCK, 0);
}
static void hc32l110_slock_unlock_all(target_s *const target)
{
hc32l110_flash_cr_unlock(target);
target_mem_write32(target, HC32L110_FLASH_SLOCK, 0xffffU);
}
static bool hc32l110_enter_flash_mode(target_s *const target)
{
target_reset(target);
const uint32_t pc = 0xfffffffeU;
return target_reg_write(target, CORTEX_REG_PC, &pc, sizeof(pc)) == sizeof(pc);
}
static bool hc32l110_flash_prepare(target_flash_s *const flash)
{
hc32l110_flash_cr_unlock(flash->t);
switch (flash->operation) {
case FLASH_OPERATION_WRITE:
target_mem_write32(flash->t, HC32L110_FLASH_CR, HC32L110_FLASH_CR_OP_PROGRAM);
break;
case FLASH_OPERATION_ERASE:
target_mem_write32(flash->t, HC32L110_FLASH_CR, HC32L110_FLASH_CR_OP_ERASE_SECTOR);
break;
default:
DEBUG_WARN("unsupported operation %u", flash->operation);
return false;
}
hc32l110_slock_unlock_all(flash->t);
return true;
}
static bool hc32l110_flash_done(target_flash_s *const flash)
{
hc32l110_slock_lock_all(flash->t);
return true;
}
static bool hc32l110_flash_erase(target_flash_s *const flash, const target_addr_t addr, const size_t length)
{
(void)length;
target_mem_write32(flash->t, addr, 0);
return hc32l110_check_flash_completion(flash->t, 1000);
}
static bool hc32l110_flash_write(
target_flash_s *const flash, const target_addr_t dest, const void *const src, const size_t length)
{
(void)length;
target_mem_write32(flash->t, dest, *(const uint32_t *)src);
return hc32l110_check_flash_completion(flash->t, 1000);
}
static bool hc32l110_mass_erase(target_s *target)
{
hc32l110_enter_flash_mode(target);
hc32l110_flash_cr_unlock(target);
target_mem_write32(target, HC32L110_FLASH_CR, HC32L110_FLASH_CR_OP_ERASE_CHIP);
if (!hc32l110_check_flash_completion(target, 500))
return false;
hc32l110_slock_unlock_all(target);
target_mem_write32(target, 0, 0);
const bool result = hc32l110_check_flash_completion(target, 4000);
hc32l110_slock_lock_all(target);
return result;
}